2012-01-18 38 views
3

我想捕获块(具有关联的名称),但它们的写入范围没有任何改变。下面的代码有两种捕获块的方法(capt_acapt_b)。 capt_a正常工作,我想capt_b以同样的方式工作。是否可以修改capt_b以便与capt_a的效果相同?更改红宝石中捕获的块的范围

class Capturer 
    attr_reader :method, :block 

    def capt_a 
    yield self 
    self 
    end 

    def capt_b(&block) 
    instance_eval(&block) 
    self 
    end 

    def method_missing(method, &block) 
    @method = method 
    @block = block 
    end 
end 

# Example: 
a = Capturer.new.capt_a{|capt| capt.foo{self} }.block 
b = Capturer.new.capt_b{ foo{self} }.block 

a.call # => main 
b.call # => #<Capturer:0x000001008fb5c8 @method=:foo, @block=#<Proc:[email protected]:23>> 
     # I would like 'main' 

回答

5

在@ bioneuralnet建议的方向上进行了一些研究之后,可以创建一个新的Proc做一个新的instance_eval来恢复上下文。初始块的binding用于获取最初的self。因此,这里是一个(有点丑陋)解决方案:

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 

它并不完美,因为它会慢一些,并且由于原始块不会==所得到的块;也许有更好的解决方案?

1

我能找到的唯一的事情就是:

m = self 
b = Capturer.new.capt_b{ foo{m} }.block 

我很可能是错的,但我相信使用instance_eval的是要评估任何使用的“自我”为你正在评估对象反对。我认为,将“主”作为变量分配是确保其被使用的唯一方法。

+0

我的代码只是一个例子;这不仅仅是“自我”,而是我想要恢复的整个背景。 – 2012-01-18 02:25:26

+1

在我上面的例子中,调用'm.instance_eval do ...'会“恢复”该上下文。你是这个意思吗?你也可以找到Binding类的使用。 http://extensions.rubyforge.org/rdoc/classes/Binding.html#M000022我刚刚碰到它。 – bioneuralnet 2012-01-18 02:36:08

+0

事实上,执行'instance_eval'应该(或多或少)恢复正确的上下文。我已发布解决方案,显示这一点。谢谢 – 2012-01-18 03:04:38