2014-04-25 19 views
1

在一個類的正文中,我想通過一個塊來稱爲with。對於塊的整個生命週期,我想要一個with_value方法可用。如何製作一個持續一個塊的生命週期的Ruby方法?

否則,塊內的所有內容都應該表現得好像它在塊外面一樣。

下面是一個例子:

class C 
    extend M 

    with "some value" do 
    do_something_complicated 
    do_something_complicated 
    do_something_complicated 
    end 
end 

我們可以幾乎用得到:

module M 
    def with(str, &block) 
    Object.new.tap do |wrapper| 
     wrapper.define_singleton_method :with_value do # Here's our with_value 
     str           # method. 
     end 
    end.instance_eval &block 
    end 

    def do_something_complicated      # Push a value onto an 
    (@foo ||= []).push with_value      # array. 
    end 
end 

,但有一個問題:因爲我們正在評估傳遞給with的上下文中的塊一個不同的對象,do_something_complicated不可用。

什麼是正確的方法來解決這個問題?

+0

你可以在'instance_eval'之前包裝'擴展M'。你能說出你的理由嗎?它可以幫助我們提出一個可靠的解決方案。 – Kyle

+0

@Kyle如果你在包裝器上'擴展','do_something_complicated'將推到錯誤的數組(包裝器的'@ foo'而不是'C'的'@ foo'。我正在這樣做一個小的DSL用於包含可組合形式的ActiveRecord對象的一些混亂情況 –

回答

3

這將使with_value僅在該塊內可用。但是,_with_value將在塊內部或外部定義。

module M 
    def _with_value 
    ... 
    end 
    def with(str, &block) 
    alias with_value _with_value 
    block.call 
    undef with_value 
    end 
    ... 
end 

我不知道這是否是一個問題。如果這是一個問題,你需要進一步描述你正在嘗試做什麼。

+0

這感覺非常難看,但它起作用,所以我不能抱怨+1, –

+0

@JohnFeminella對我來說,它並不難看,它具有定義主體(用於'_with_value')與'with'分開,每個塊前後只有一行,如果這對你來說很難看,你就沒有什麼可以做得更好的了 – sawa

+0

我的意思是「醜陋」是因爲任何失敗在塊內部會導致'with_value'仍然存在,污染後續調用的空間。在一個假設的更優雅的解決方案中,語言會考慮到這一點,因爲不能在塊外面看到'with_value'。我無法想到任何事情呃,這就是我給你+1的原因! –

1

基本上,這個想法是使用method_missing將方法調用從虛擬類轉發到調用類。如果您還需要訪問實例變量,則可以將它們從調用類複製到虛擬類,然後在塊返回後再返回。

Ruby gem docile是這樣一個系統的一個非常簡單的實現。我建議你閱讀該存儲庫中的源代碼(不用擔心,它是一個非常小的代碼庫),這是一個DSL示例如何工作的示例。

0

這裏是一個方式,是更接近你的嘗試:

module M 
    def with(str, &block) 
    dup.tap do |wrapper| 
     wrapper.define_singleton_method :with_value do 
     ... 
     end 
    end.instance_eval &block 
    end 
    ... 
end 

dup將複製從那裏with被稱爲類方法的類。

相關問題