Skip to content

Instantly share code, notes, and snippets.

@dariodaich
Last active August 23, 2016 22:09
Show Gist options
  • Save dariodaich/850d037ff49f08d51a3cec69aa8a7c2e to your computer and use it in GitHub Desktop.
Save dariodaich/850d037ff49f08d51a3cec69aa8a7c2e to your computer and use it in GitHub Desktop.
Concise explanations of scopes in Ruby, and a few quizzes.

Scopes in Ruby

What is Scope?

Scope is a program space within which certain types of variables are visible.

Why is Global State evil? Main reasons:

  1. it is hard to track values and assignments of variables throughout the program
  2. since there is only one namespace available, naming variables, which is hard, becomes even harder

Variable types and Scopes in Ruby

Following are types of variables in Ruby:

  1. Global variables - $foo Available from anywhere.
  2. Class variables - @@foo
  • available within class they are defined
  • available within subclasses of that class
  • also available within instances of the class in which they are defined, and in instances of their subclasses
  1. Instance variables - @foo Available only in an instances of their class.
  2. Local variables - foo_bar Depends on the scope in which they are defined in.

Local Variables

  • Instance variables do not need an explicit assignment when they are defined. nil gets assigned to them by default. In contrast, local variables must have an explicit assignment.

      @foo # => nil
      bar  # => what is the name of the error that gets raised? [1] _________
    
      #if false
        #foobar = "foobar"
      #end
      p foobar
    
      # What happens when program runs with 'false' in the if statement? [2]
      # What happens when we remove the if statement? [3]
    
      # What has precedence, method or a variable? [4]
      def boo
        :boo
      end
      boo = :BOO
      p boo
      # Is it possible to get to the method in this case? [5.1]
      # How? [5.2]

Instance variables

  • in contrast to local variables, get binded to an object and not to a certain scope
  • they change with each new object, local variables with each new scope

Scope Gates

  • are "lines" which create new scope, and are invoked with keywords:
    • class
    • module
    • def
  • each time program encounters one of those keywords, new scope gets created
foo = :foo
p local_variables [6.1]

class A
  bar = :bar
  p local_variables [6.2]

  def foobar
    foobar = :foobar
    p local_variables [6.3]
  end
end

A.new.foobar
p local_variables [6.4]
# What is the output of each call to 'local_variables'?

Blocks and Scope

  • blocks are not Scope Gates, but they do allow creation of local variables defined within them
a = [1, 2, 3]

a.each do |n; hello|
  p n
  hi = "hi"
end

p hi # => What will this variable reference result in? [7]
  • block variables can be used to shadow outer variables
foo = :foo
[1, 2, 3].each { |n; foo|  p n, foo }
# What is the value of 'foo' within the block? [8]
p foo
  • new block scope gets created on each iteration
2.times do
  foo ||= 5
  p foo

  foo *= 2
  p foo
end
# => What is the output? [9]
  • behaviour with procs
def foo
  bar = :bar
  -> { bar }
end

bar = :BAR
p foo.call
# => Output? [10]

boo = 1
l = -> { boo }
boo = 2
3
p l.call

# Output? [11]

Answers:

  • [1] - NameError
  • [2] - it gets assigned to 'nil'
  • [3] - gets set to 'foobar'
  • [4] - variables > methods
  • [5.1] - yes
  • [5.2] - using parentheses
  • [6.1] - [:foo]
  • [6.2] - [:bar]
  • [6.3] - [:foobar]
  • [6.4] - [:foo]
  • [7] - NameError: undefined local variable ...
  • [8] - nil
  • [9] - 1st run '5 10', 2nd run '5 10' (because new scope gets created on each iteration)
  • [10] - :bar
  • [11] - 2

Quiz

$alpha = "alpha"

class Foo
  $gama = "gama"
  @@delta = "delta"

  def foo_first
    p $alpha
  end

  def foo_second
    $beta = "beta"
  end
end

class Bar < Foo
  attr_reader :zeta

  p $gama # (1)

  def initialize
    @zeta = "zeta"
  end

  def bar_first
    p @@delta
  end

  def bar_second
    p @@epsilon
  end
end

class Doo < Bar
  attr_reader :zeta

  eta = "eta"

  def self.eta
    p eta
    eta = "ETA"
    p eta
  end

  def self.eta_2
    eta = "ETA 2"
    p eta
  end

  def doo_first
    @@epsilon = "epsilon"
  end
end

# Global variables
f = Foo.new
f.foo_first  # (2)
p $beta      # (3)
f.foo_second
p $beta      # (4)

# Class variables
b = Bar.new
b.bar_first  # (5)

d = Doo.new
d.doo_first
b.bar_second # (6)

# Instance variables
p b.zeta     # (7)
p d.zeta     # (8)

# Local variables
Doo.eta      # (9)
Doo.eta_2    # (10)

Answers:

  • 1 - "gama"
  • 2 - "alpha"
  • 3 - nil
  • 4 - "beta"
  • 5 - "delta"
  • 6 - NameError
  • 7 - "zeta"
  • 8 - nil
  • 9 - SystemStackError
  • 10 - "ETA 2"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment