2012-01-18 34 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