2012-05-20 34 views
2

想象一下,您經常需要從某個靜態代碼或方面內的超類中包裝一組方法,但每個方法的結果/例如,圍繞數組方法更新通知以獲取可觀察數組。Ruby塊 - 包裝和自定義超類方法(使用define_method和塊)

我首先想到的,我要像使用一類級別的方法:

class MyArray < Array 
    extend ObserverHelper # defines :wrap_method 

    wrap_notify(:[]=) do |index_or_range, *args| 
    [self[index_or_range], super(index_or_range, *args)] 
    # much nicer it would be to define 
    # removed = self[index_or_range] 
    # added = super(index_or_range, *args) 
    # but this makes it even more complicated (suggestions welcome!) 
    end 
... 
end 

到目前爲止好。如何實現:wrap_method現在?我最有希望的方法達到了這個目標。

require 'observer' 

module ObserverHelper 
    include Observable 

    def wrap_notify(meth, &block) 
    define_method meth,->(*args) { 
     removed, added = instance_exec(*args, &block) 
     changed 
     notify_observers(removed, added) 
     added 
    } 
    end 
end 

錯誤::

  • 如果直接在陣列的子類定義: 超級稱爲如果一個模塊中定義和包括以陣列方法

  • 以外不因爲超級的工作: 「不支持從單一方法定義到多個類的超級方法;如果方法具有適當的返回值,這將在1.9.3或更高版本中修復」或「分段錯誤」)

我搜索了一段時間,但沒有找到一個明智的解決方案來改變閉包的綁定,但是,也許我錯過了什麼,或者你可以解決它沒有?

Rails通過評估字符串來完成這些事情,但還不確定如果我想這樣。性能不是很重要,但會是。因此,我需要關於不同可能性和性能問題的反饋。

最後,我得到了兩個更詳細的解決方案運行。首先,通過實例,沒有超級而自:

wrap_observer(:[]=) do |instance, index_or_range, *args| 
    [instance[index_or_range], instance.send(meth, index_or_range, *args)] 
    end 

另一種使用如wrap_observer instance_method:

def []=(index_or_range, *args) 
    wrap_observer do 
     [self[index_or_range], super(index_or_range, *args)] 
    end 
    end 

我視爲優選的。

DSL的解決方案可能是在沒有包裝的情況下定義方法,而是通過添加它的已定義方法進行迭代。

有沒有更多的方法可以解決這個問題並且可以用我的DSL來維護?

回答

1

重新綁定塊的唯一方法是使用它定義一個方法,該方法也允許使用super。所以,你想定義的兩種方法 - 東西,可以使用模塊來完成:

module ObserverHelper 
    def wrap_notify(meth, options={}, &block) 
    override = Module.new do 
     define_method(meth, &block) 
    end 
    notifier = Module.new do 
     define_method meth do |*args| 
      removed, added = super(*args) 
      options ||= {} 
      changed 
      notify_observers(removed, added) 
      after 
     end 
    end 
    include override 
    include notifier 
    end 
end 
+0

有趣的做法,事實上,它再次超級工作,非常感謝! – Micha

0

此外,我想回答我的問題,因爲這種方法基本上沒有工作。方法更加多樣化(如前面已經提到[] =用數字和範圍,但其他方法的工作原理不),我找到了一個通用的解決方案:

 define_method(meth) do |*args, &block| 
     old_state = Array.new(self) 
     r = super(*args,&block) 
     new_state = Array.new(self) 
     old_state.delete_if{|e| (i = new_state.index(e)) && new_state.delete_at(i)} 
     unless new_state.empty? and old_state.empty? 
      changed 
      notify_observers(old_state, new_state, meth) 
     end 
     r 
     end 

因此,我可以覆蓋一個循環中的所有方法,無需要黑客入侵。基本上我使用它作爲後備和測試參考,而我實現了更專門化的方法。