Bindings

The eval method may take an optional “binding” argument that, if provided, causes the evaluation to be done within a specific scope or “context.” It probably won’t come as any surprise to discover that, in Ruby, a binding is an object that is an instance of the Binding class. You can return a binding using the binding method. The documentation of eval in the Ruby class library provides this example:

binding.rb

def getBinding(str)
    return binding()
end
str = "hello"
puts( eval( "str + ' Fred'" ) )                    #=> "hello Fred"
puts( eval( "str + ' Fred'", getBinding("bye") ) ) #=> "bye Fred"

Simple as it may look, this example may take a bit of thinking about in order to understand what’s going on. Essentially, the first call to puts evaluates str in the current scope where it has a “hello” value. The second call to puts evaluates str in the scope of the getBinding() method where it has a “bye” value. In this example, str happens to be passed as an argument, but this is not a requirement. In the rewritten version here, I’ve made str a local variable inside getBinding(). The effect is the same:

binding2.rb

def getBinding()
    str = "bye"
    return binding()
end
str = "hello"
puts( eval( "str + ' Fred'" )   )                  #=> "hello Fred"
puts( eval( "str + ' Fred'", getBinding() ) )      #=> "bye Fred"
puts( eval( "str + ' Fred'" )   )                  #=> "hello Fred"

Note that binding is a private method of Kernel. The getBinding method is able to call binding within the current context and return the current value of str. At the time of the first call to eval, the context is the main object, and the value of the local variable, str, is used; in the second call, the context moves inside the getBinding method, and the local value of str is now that of the str argument or variable within that method. The context may also be defined by a class. In binding3.rb, you can see that the values of the instance variable @mystr varies according to the class. So, what happens when you eval those variables with different bindings?

binding3.rb

class MyClass
   @@x = " x"
   def initialize(s)
      @mystr = s
   end
   def getBinding
      return binding()
   end
end

class MyOtherClass
   @@x = " y"
   def initialize(s)
      @mystr = s
   end
   def getBinding
      return binding()
   end
end

@mystr = self.inspect
@@x = " some other value"

ob1 = MyClass.new("ob1 string")
ob2 = MyClass.new("ob2 string")
ob3 = MyOtherClass.new("ob3 string")

puts(eval("@mystr << @@x", ob1.getBinding))
puts(eval("@mystr << @@x", ob2.getBinding))
puts(eval("@mystr << @@x", ob3.getBinding))
puts(eval("@mystr << @@x", binding))

In Ruby 1.8, you see the following output, showing that the bindings for both the instance variable, @mystr, and the class variable, @@x, are applied:

ob1 string x
ob2 string x
ob3 string y
main some other value

But in Ruby 1.9, only the binding of the instance variable is applied; the class variable in the current (main) context is always used:

ob1 string some other value
ob2 string some other value
ob3 string some other value
main some other value

Does this mean class variables in given bindings are ignored? Let’s try an experiment. Just comment out the assignment to @@x in the main context:

# @@x = " some other value"

Now run the program again. This time, Ruby 1.9 displays this:

ob1 string x
ob2 string x
ob3 string y
...uninitialized class variable @@x in Object (NameError)

Clearly, Ruby 1.9 does evaluate class variables within a binding. However, it gives preference to class variables, if they exist, in the current binding. You need to be aware of this difference if you are migrating Ruby 1.8 programs to Ruby 1.9 or newer.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.171.212