Skip to content

Instantly share code, notes, and snippets.

@5alamander
Created May 12, 2016 15:24
Show Gist options
  • Save 5alamander/3a91e9ef10466c6bd7240dbaf3567c4b to your computer and use it in GitHub Desktop.
Save 5alamander/3a91e9ef10466c6bd7240dbaf3567c4b to your computer and use it in GitHub Desktop.
Ruby binding of a Proc
# take a note of http://stackoverflow.com/questions/10058996/changing-the-binding-of-a-proc-in-ruby
class Proc
def call_with_vars(vars, *args)
Struct.new(*vars.keys).new(*vars.values).instance_exec(*args, &self)
end
end
# test
lambda { foo }.call_with_vars(:foo => 3) # => 3
lambda { |a| foo + a }.call_with_vars({:foo => 3}, 1) # => 4
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
# test
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) # => 15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment