Change the binding of a Proc in Ruby

You can try the following hack:

class Proc
  def call_with_vars(vars, *args)
    Struct.new(*vars.keys).new(*vars.values).instance_exec(*args, &self)
  end
end

To be used like this:

irb(main):001:0* lambda { foo }.call_with_vars(:foo => 3)
=> 3
irb(main):002:0> lambda { |a| foo + a }.call_with_vars({:foo => 3}, 1)
=> 4

This is not a very general solution, though. It would be better if we could give it Binding instance instead of a Hash and do the following:

l = lambda { |a| foo + a }
foo = 3
l.call_with_binding(binding, 1)  # => 4

Using the following, more complex hack, this exact behaviour can be achieved:

class LookupStack
  def initialize(bindings = [])
    @bindings = bindings
  end

  def method_missing(m, *args)
    @bindings.reverse_each do |bind|
      begin
        method = eval("method(%s)" % m.inspect, bind)
      rescue NameError
      else
        return method.call(*args)
      end
      begin
        value = eval(m.to_s, bind)
        return value
      rescue NameError
      end
    end
    raise NoMethodError
  end

  def push_binding(bind)
    @bindings.push bind
  end

  def push_instance(obj)
    @bindings.push obj.instance_eval { binding }
  end

  def push_hash(vars)
    push_instance Struct.new(*vars.keys).new(*vars.values)
  end

  def run_proc(p, *args)
    instance_exec(*args, &p)
  end
end

class Proc
  def call_with_binding(bind, *args)
    LookupStack.new([bind]).run_proc(self, *args)
  end
end

Basically we define ourselves a manual name lookup stack and instance_exec our proc against it. This is a very flexible mechanism. It not only enables the implementation of call_with_binding, it can also be used to build up much more complex lookup chains:

l = lambda { |a| local + func(2) + some_method(1) + var + a }

local = 1
def func(x) x end

class Foo < Struct.new(:add)
  def some_method(x) x + add end
end

stack = LookupStack.new
stack.push_binding(binding)
stack.push_instance(Foo.new(2))
stack.push_hash(:var => 4)

p stack.run_proc(l, 5)

This prints 15, as expected :)

UPDATE: Code is now also available at Github. I use this for one my projects too now.


class Proc
    def call_with_obj(obj, *args)
        m = nil
        p = self
        Object.class_eval do
            define_method :a_temp_method_name, &p
            m = instance_method :a_temp_method_name; remove_method :a_temp_method_name
        end
        m.bind(obj).call(*args)
    end
end

And then use it as:

class Foo
    def bar
        "bar"
    end
end

p = Proc.new { bar }

bar = "baz"

p.call_with_obj(self) # => baz
p.call_with_obj(Foo.new) # => bar

Perhaps you don't actually need to define a later, but instead only need to set it later.

Or (as below), perhaps you don't actually need a to be a local variable (which itself references an array). Instead, perhaps you can usefully employ a class variable, such as @@a. This works for me, by printing "1":

class SomeClass
  def l
    @l ||= lambda { puts @@a }
  end

  def some_function
    @@a = 1
    l.call
  end
end
SomeClass.new.some_function

Tags:

Ruby