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.
Changing the `Proc` binding during invocation
You can use instance_eval
:
class Bar
def do_something
validator = AllValidator.new.age_validator
# Evaluate validator in the context of self.
instance_eval &validator
end
end
If you want to pass arguments (as mentioned in the comment below), you can use instance_exec
instead of instance_eval
if you use Ruby 1.9 or Ruby 1.8.7:
class Bar
def do_something
validator = AllValidator.new.age_validator
# instance_exec is like instance_eval with arguments.
instance_exec 5, &validator
end
end
If you have to make it work with Ruby 1.8.6 and below as well, your best bet is to bind the proc as a method of Bar
:
class Bar
def do_something
self.class.send :define_method, :validate, &AllValidator.new.age_validator
self.validate 5
end
end
An alternative is to implement instance_exec
older Ruby versions (example here). All it does is define a method before calling it, and undefining it after you're done.
Binding of Procs
foo.instance_eval &proc
instance_eval
can take a block instead of a string, and the &
operator turns the proc into a block for use with the method call.
How can I call a Proc that takes a block in a different context?
To solve this, you need to re-bind the Proc to the new class.
Here's your solution, leveraging some good code from Rails core_ext:
require 'rspec'
# Same as original post
class SomeClass
def instance_method(x)
"Hello #{x}"
end
end
# Same as original post
class AnotherClass
def instance_method(x)
"Goodbye #{x}"
end
def make_proc
Proc.new do |x, &block|
instance_method(block.call(x))
end
end
end
### SOLUTION ###
# From activesupport lib/active_support/core_ext/kernel/singleton_class.rb
module Kernel
# Returns the object's singleton class.
def singleton_class
class << self
self
end
end unless respond_to?(:singleton_class) # exists in 1.9.2
# class_eval on an object acts like singleton_class.class_eval.
def class_eval(*args, &block)
singleton_class.class_eval(*args, &block)
end
end
# From activesupport lib/active_support/core_ext/proc.rb
class Proc #:nodoc:
def bind(object)
block, time = self, Time.now
object.class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end
# Here's the method you requested
def change_scope_of_proc(new_self, proc)
return proc.bind(new_self)
end
# Same as original post
describe "change_scope_of_proc" do
it "should change the instance method that is called" do
some_class = SomeClass.new
another_class = AnotherClass.new
proc = another_class.make_proc
fixed_proc = change_scope_of_proc(some_class, proc)
result = fixed_proc.call("Wor") do |x|
"#{x}ld"
end
result.should == "Hello World"
end
end
Proc.new binding change and loops logic abstraction
You can't have your cake and eat it too. If you want return
from the proc to abort the method, it must be in the method's lexical scope* (which is another way to say "it must be defined within the same method").
An alternative is to have proc/lambda return a "stop" value, which caller will use to abort its execution.
(Your experiments with instance_eval/instance_exec were misdirected, sadly. Those methods only change current self
. This problem has nothing to do with current self
, but rather current lexical scope, in which return
is executed.)
* The error you're getting, it is caused by return
trying to return from a method that is no longer running (proc_exec
).
To which level returns a return inside a Proc Object in Ruby?
As a general rule, return
always returns from the closest lexically enclosing method definition expression.
In this case, the closest lexically enclosing method definition expression is def a
, therefore, return
returns from a
.
It does not actually matter that the return
is inside a block in this case. The general rule is, well, general, so it applies regardless of where the return
appears.
If we look more specifically at blocks, though, we can see that it still makes sense: in blocks, local variables are captured lexically, self
is captured lexically, so it makes sense that return
also behaves lexically. It is a general property of blocks that if you want to understand what is going on in a block, you only need to look lexically outwards.
And if we get even more specific, first going from the general rule to blocks, and now from blocks to Proc
s, the behavior still makes sense: a Proc
is essentially a reified block, so it makes sense for a Proc
to behave like a block.
There are some exceptions, though, to the general rule, and one important one are lambdas. Talking about lambdas in Ruby is always a little bit weird because lambdas are Proc
s but they behave differently from Proc
s. IMO, lambdas should have a separate class alongside Proc
s. Since lambdas are Proc
s, it makes it weird to talk about the differences between lambdas and Proc
s which are not lambdas (which don't have a standardized name and thus are confusingly also called Proc
s).
The behavior of a lambda differs from the behavior of a non-lambda Proc
in two ways, one of which is relevant to your question:
- Parameter binding in non-lambda
Proc
s has the same semantics as parameter binding in blocks, whereas parameter binding in lambdas has the same semantics as parameter binding in message sends / method invocations. - In non-lambda
Proc
s,return
returns from the closest lexically enclosing method definition expression, just like in blocks, whereas in lambdas,return
returns from the lambda itself, just likereturn
in methods.
So, in both of these aspects, non-lambda Proc
s behave like blocks and lambdas behave like methods. I memorize it like this: "Proc
" rhymes with "block" and both "lambda" and "method" are Greek.
As you probably know, there are some methods which also alter the behavior of blocks that are passed to them. E.g. instance_eval
and instance_exec
change the value of self
, and define_method
actually does change the behavior of return
.
But since you didn't ask about blocks in general, and also didn't ask about lambdas specifically, and there are no reflective methods in your question, the general rules still applies to non-lambda Proc
s like the one shown in your question: return
returns from the closest lexically enclosing method definition expression.
Related Topics
Ruby Sleep or Delay Less Than a Second
Why Is _File_ Uppercase and _Dir_ Lowercase
How to Alter the Timezone of a Datetime in Ruby
Supporting Ruby 1.9's Hash Syntax in Ruby 1.8
Weird Imoperfection in Ruby Blocks
How to "Soft Delete" User with Devise
How to Merge Two Hashes Without Overwritten Duplicate Keys in Ruby
What's the Difference Between To_A and To_Ary
Paperclip Renaming Files After They'Re Saved
How to Check If a Value Is a Number
How to Find the Namespace/Module Name Programmatically in Ruby on Rails
How to Save an Object to a File
Actionview::Template::Error (Incompatible Character Encodings: Utf-8 and Ascii-8Bit)
What's the Point of Argv in Ruby
Rvm Is Not a Function, Selecting Rubies with 'Rvm Use ...' Will Not Work