如何调用在不同上下文中占用块的Proc?

Dav*_*ani 13 ruby metaprogramming block

举个例子Proc:

proc = Proc.new {|x,y,&block| block.call(x,y,self.instance_method)}
Run Code Online (Sandbox Code Playgroud)

它需要两个参数,x和y,以及一个块.

我想使用不同的self值来执行该块.像这样的东西几乎可以工作:

some_object.instance_exec("x arg", "y arg", &proc)
Run Code Online (Sandbox Code Playgroud)

但是,这不允许您传入一个块.这也行不通

some_object.instance_exec("x arg", "y arg", another_proc, &proc)
Run Code Online (Sandbox Code Playgroud)

也没有

some_object.instance_exec("x arg", "y arg", &another_proc, &proc)
Run Code Online (Sandbox Code Playgroud)

我不知道还有什么可以在这里工作.这是可能的,如果是这样,你怎么做?

编辑:基本上如果您可以通过更改change_scope_of_proc方法来传递此rspec文件,您已解决了我的问题.

require 'rspec'

class SomeClass
  def instance_method(x)
    "Hello #{x}"
  end
end

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

def change_scope_of_proc(new_self, proc)
  # TODO fix me!!!
  proc
end

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
Run Code Online (Sandbox Code Playgroud)

joe*_*son 8

要解决此问题,您需要将Proc重新绑定到新类.

这是您的解决方案,利用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
Run Code Online (Sandbox Code Playgroud)