Accessing the scope of the calling object in a block

There's an interesting question over at subWindow about accessing the scope of the calling object in a block.

If access to local variables is desired, you can yield a Binding object from within the method. Just doing this would require writing things like eval "baz", context, where context is the Binding object returned from a call to binding. I used method_missing on Binding to make things a little more readable in the calling code.

To get at instance variables, it seems instance_variable_get is the way to go. Internally, it calls instance_eval on the string that's passed in, which is very similar to what's happening below.

class BindingTest
  attr_accessor :accessible_baz
  def bar
    baz = \"qux\"
    foonum = 23
    @instance_baz = \"instance_baz\"
    @accessible_baz = \"accessible_baz\"
    yield(binding)
  end
end
	
# Extends the binding object
class Binding
  def method_missing( method, *args )
    eval method.to_s, self
  end
end
	
binder = BindingTest.new
	
puts binder.bar { |context| context.foonum.to_s }
puts binder.bar { |context| context.baz }
puts binder.bar { |context| context.accessible_baz }
#the following line fails; only locals work
#puts binder.bar { |context| puts context.instance_baz }
	
puts binder.instance_variables

You could also use instance_variables and local_variables to return lists of those respective variables and evaluate them against the binding.

I haven't actually needed anything like this (and I'm sure it's open to abuse), but it's an interesting question that provides an opportunity to explore some of Ruby's reflection facilities.

romej.com
Steven Romej © 2002-2008