在Ruby中更改捕获块的范围

时间:2012-01-18 00:05:30

标签: ruby block

我想捕获块(具有关联的名称),但不会改变它们的写入范围。下面的代码有两种捕获块的方法(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:0x000001008fb500@temp.rb:23>>
       # I would like 'main'

2 个答案:

答案 0 :(得分: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 :(得分:1)

我唯一能找到的是:

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

我可能错了,但我相信使用instance_eval将评估“self”作为您正在评估的对象的任何使用。我认为,将“main”指定为变量是确保使用它的唯一方法。