Change the Context/Binding Inside a Block in Ruby

How can I use a block to change the execution context in ruby?

It looks like you are sending methods to a class/module, so your example may be simply rewritten with use of Module#class_eval method:

name { Faker::Hacker.class_eval { "#{ingverb} #{adjective} #{noun}" } }

would invoke methods in the block passed to class_eval on Faker::Hacker class.

Changing the context/binding/scope of a ruby block (Rails-specific form_for)

The correct way to do this was to call the block like so: block.call(f) and use concat for the other fields.

  form_for(record, *(args << options.merge(:builder => builder))) do |f|
block.call(f)
concat hidden_field_tag 'key', "#{key}/${filename}"
concat hidden_field_tag 'AWSAccessKeyId', "#{access_key}"
concat hidden_field_tag 'acl', "#{acl}"
concat hidden_field_tag 'success_action_redirect', "#{redirect}"
concat hidden_field_tag 'policy', "#{policy}"
concat hidden_field_tag 'signature', "#{signature}"
end

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

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.

How to change self in a block like instance_eval method do?

You can write a method that accepts a proc argument, and then pass that as a proc argument to instance_eval.

class Foo
def bar(&b)
# Do something here first.
instance_eval &b
# Do something else here afterward, call it again, etc.
end
end

Foo.new.bar { puts self }

Yields

#<Foo:0x100329f00>

Changing the scope of a captured block in Ruby

After some research in the direction @bioneuralnet suggested, it's possible to create a new Proc doing a new instance_eval to restore the context. The binding of the initial block is used to get the initial self. So here is a (somewhat ugly) solution:

  def capture_b(&block)
instance_eval(&block)
the_desired_self = block.binding.eval("self")
bk = @block
@block = Proc.new{ the_desired_self.instance_eval(&bk) }
self
end

It's not perfect, as it will be slower and since the original block won't be == to the resulting block; maybe there's a better solution?



Related Topics



Leave a reply



Submit