Ruby’s Binding Class (binding objects)

Instances of the Binding class (binding objects) capture the environment bindings (variables, methods, and self) at any point of a Ruby program (scope is also referred to as ‘environment’ or ‘context’), so the bindings can be reused later, when the scope has changed. The Kernel#binding() method creates binding objects and is often used in conjunction with the Kernel#eval() method.

class A
  def hi
    @a = 'a'
    b = 'b'
    binding
  end
end

binding = A.new.hi # returns a binding object
eval("b.concat('aaaaa')", binding) # => "baaaaa"
eval("self", binding) # => "baaaaa" # => #<A:0x007f87022c8ce8 @a="a">
eval("instance_variable_get('@a')", binding) # => "a"

In the A#hi method, the scope is captured in a binding object and re-access the scope in the eval() method, even though the scope of the program has changed when the eval() method is called. Ruby supports closures, which is a language features that allow scope to be captured and reused when the scope is changed. See this blog post (https://codequizzes.wordpress.com/2014/04/29/why-ruby-procs-are-closures/) for more about closures in Ruby.

Here’s a description of the Binding class from the wonderful book, Metaprogramming Ruby:

A Binding is a whole scope packaged as an object. The idea is that you can create a Binding to capture the local scope and carry it around. Later, you can execute code in that scope by using the Binding object in conjunction with eval( ), instance_eval( ), or class_eval( ). You can create a binding with the Kernel#binding() method.

The TOPLEVEL_BINDING object stores a reference to the top-level scope:

@blah = 'moo'
module M
  def self.hi
    eval("@blah", TOPLEVEL_BINDING)
  end
end

M::hi # => 'moo'

The @blah instance_variable is bound to the main object, which is also know as the top-level scope. The top-level scope can be accessed in the M.hi method with the TOPLEVEL_BINDING constant. Again, Ruby supports closures because scope can be captured and reaccessed, even when the scope has changed.

The Binding class also defines an eval() method that can be used on binding objects. This is semantically the same as passing a binding object to the Kernel#eval() method, but syntactically different:

class Cat
  def sad
    sound = 'meow'
    binding
  end
end

binding = Cat.new.sad
binding.eval("sound") # => 'meow'

# Kernel#eval() can be used instead of Binding#eval()
eval("sound", binding) # => 'meow'
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s