A Ruby program creates a new scope whenever it encounters a scope gate (the def, class, and module keywords). When a new scope is created, local variables from earlier scopes are not available, but the scope gates can be bypassed with closures. Read this blog post if you’re not familiar with Ruby’s scope gates.
The following code does not work because the class keyword creates a new scope:
# top level scope x = 'bob' class A # begin class scope puts x # => error end # end class scope # back in top level scope
The local variable x is defined in the top level scope (outer scope) and is not available in the class scope (inner scope). Ruby closures retain local variable bindings that are in place and can be used to bypass scope gates. Read this blog post if you’re not familiar with Ruby closures.
The Class.new closure captures all the top level local variable bindings and makes them available in the class definition.
# top level scope x = 'bob' A = Class.new do # begin closure puts x end # end closure
Module#class_eval allows scope gates for existing classes to be bypassed.
class BBB; end x = 'bob' # use class_eval to bypass the scope gate for an existing class BBB.class_eval do p x # => local variable is accessible end # local variables are not accessible when # a class is reopened with the class keyword class BBB p x # => error end
Method scope gates are similar to class scope gates and can be bypassed with a closure:
class A # begin class scope x = 'bob' define_method :hi do # begin closure x end # end closure end # end class scope A.new.hi # => 'bob'
The define_method closure captures the class’s local variable bindings and makes them available to the hi() method.
Using closures to bypass scope gates is referred to as flattening the scope.