Is it possible to change the binding of a procedure during invocation time?
class AllValidator
def age_validator
Proc.new {|value| self.age > value }
end
end
class Bar
attr_accessor :age
def doSomething
validator = AllValidator.new.age_validator
validator.call(25) # How to pass self as the binding?
end
end
In the code above, how do I change the binding of the proc during invocation?
Is there a way to pass the binding much like the eval
function?
Note
If the example above were real, I would use mixin
/inheritence
etc. I am using the code to demonstrate my problem scenario.
What you are seeing is the & operator applied to a :symbol . In a method argument list, the & operator takes its operand, converts it to a Proc object if it isn't already (by calling to_proc on it) and passes it to the method as if a block had been used.
The &block is a way of sending a piece of Ruby code in to a method and then evaluating that code in the scope of that method.
A Proc object is an encapsulation of a block of code, which can be stored in a local variable, passed to a method or another Proc, and can be called. Proc is an essential concept in Ruby and a core of its functional programming features.
To keep track of the current scope, Ruby uses bindings, which encapsulate the execution context at each position in the code. The binding method returns a Binding object which describes the bindings at the current position.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With