Изменение области видимости захваченного блока в Ruby - PullRequest
3 голосов
/ 18 января 2012

Я хочу захватить блоки (со связанным именем), но без каких-либо изменений в области, в которой они написаны.Код ниже имеет два способа захвата блока (capt_a и capt_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 ]

5 голосов
/ 18 января 2012

После некоторых исследований в направлении, предложенном @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 голос
/ 18 января 2012

Единственное, что я могу найти, это:

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

Я вполне могу ошибаться, но я полагаю, что использование instance_eval оценит любое использование "self" в качестве объекта, с которым вы оцениваете.Я полагаю, что присвоение «главной» переменной является единственным способом обеспечить ее использование.

...