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'