Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing the `Proc` binding during invocation

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.

like image 448
Harish Shetty Avatar asked Mar 02 '10 05:03

Harish Shetty


People also ask

What does &: mean in Ruby?

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.

What is &Block in Ruby?

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.

How does Proc work in Ruby?

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.

What is binding in Ruby?

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.


1 Answers

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.

like image 68
molf Avatar answered Oct 21 '22 06:10

molf