Search code examples
rubybindingscopeblock

How to call a ruby block to get access to the caller bindings


I want to have access to the bindings of the caller. What's wrong here?

require 'test/unit'

class BlocksTest < Test::Unit::TestCase

  class Blocks
    def initialize( &block ); @block = block; end
    def run; instance_eval { @block.call }; end

    def foo; 'foo'; end
  end

  def test_say_foo_via_string
    assert_equal( 'foo', Blocks.new{ 'foo' }.run )
  end
  # => successful

  def test_say_foo_via_caller_method
    assert_equal( 'foo', Blocks.new{ foo }.run )
  end
  # => NameError: undefined local variable or method `foo'

end

Why i don't have access to the caller instance within the given block?


Solution

  • Inside the block you are not in the scope of the Blocks instance, so foo is not visible. You have to pass the object to the block if you want to have access to it:

    class Blocks
      # ...
      def run
        @block.call(self)
      end
      # ...
    end
    
    blk = Blocks.new { |b| b.foo }
    blk.run
    # => "foo"
    

    Alternatively you can pass the block to instance_eval:

    class Blocks
      # ...
      def run
        instance_eval(&@block)
      end
      # ...
    end
    
    blk = Blocks.new { foo }
    blk.run
    # => "foo"