2016-12-05 94 views
3

說我有這個類:如何幹淨地「重新連接」Ruby中的分離方法?

class Foo 
    def destroy_target(target) 
    Missile.launch(target) 
    end 
end 

我想暫時中性的Foo的破壞力,例如用於測試目的,所以我這樣做:

backup = Foo.instance_method(:destroy_target) 

class Foo 
    def destroy_target(target) 
    Pillow.launch(target) 
    end 
end 

我的問題是:如何將原始方法「重新附加」到Foo,好像它從未被覆蓋過?

我知道我能做到這一點:

class Foo 
    def destroy_target(target) 
    backup.bind(self).call(target) 
    end 
end 

但顯然這不是最佳的,因爲我現在包裝原始的功能。我希望能夠無限次地分離和重新附加該方法,而不會增加任何開銷。

問了一個不同的方法;我如何將DetachedMethod附加到「正確」類中,即不定義調用分離函數的新方法。


注:我不感興趣,臨時改變一個類的功能的替代方法。我特別想知道如何用不同的方法替換一個方法,然後乾淨地恢復原始方法。

+0

'backup = Foo.instance_method(:destroy_target)'''s' –

+0

您是否想要替換特定'Foo'實例或類全等的方法,即所有'Foo'? – Stefan

回答

1

這似乎工作:

Foo.instance_exec { 
    define_method(:destroy_target, backup) 
} 

但我不能完全肯定這是否是無副作用。如果有人知道我會很欣賞評論。

這也似乎如果Foo工作是這樣定義的模塊:

module Foo 
    extend self 

    def destroy_target(target) 
    Missile.launch(target) 
    end 
end 
+0

'define_method'似乎是正確的,但我會使用'Foo.send(:define_method,:destroy_target,backup)' - 它看起來不那麼有創意。 – Stefan

2

我測試了第一個例子,它似乎很好地工作。我找不到任何副作用,但這並不意味着沒有。

你覺得refinements

一類

class Missile 
    def self.launch(t) 
    puts "MISSILE -> #{t}" 
    end 
end 

class Pillow 
    def self.launch(t) 
    puts "PILLOW -> #{t}" 
    end 
end 

class Foo 
    def destroy_target(target) 
    Missile.launch(target) 
    end 
end 

module PillowLauncher 
    refine Foo do 
    def destroy_target(target) 
     Pillow.launch(target) 
    end 
    end 
end 

module Test 
    using PillowLauncher 
    Foo.new.destroy_target("Tatooine") 
    #=> PILLOW -> Tatooine 
end 

Foo.new.destroy_target("Tatooine") 
#=> MISSILE -> Tatooine 

它可能帶來的是一個有點比你的例子更加規範易懂的優點。

對於模塊

如果FooModule,你不能直接調用refine Foo,你會得到一個TypeError: wrong argument type Module (expected Class)

你可以,但是,改善其singleton_class

module Foo 
    def self.destroy_target(target) 
    Missile.launch(target) 
    end 
end 

module PillowLauncher 
    refine Foo.singleton_class do 
    def destroy_target(target) 
     Pillow.launch(target) 
    end 
    end 
end 

module Test 
    using PillowLauncher 
    Foo.destroy_target('Tatooine') 
    #=> PILLOW -> Tatooine 
end 

Foo.destroy_target('Tatooine') 
#=> MISSILE -> Tatooine 

我不知道你注意:

我不感興趣,臨時改變 的功能替代方法類。我特意想知道如何用 替換一個不同方法的方法,然後乾淨地恢復原來的 方法。

我建議的代碼似乎都可以。

+0

細化看起來非常酷,但顯然他們只能使用類(而不是模塊)。在我目前的用例中,我需要它使用模塊。在Ruby 2.3之前,他們也必須在頂層激活,即他們不能像你演示的那樣在類或模塊內激活。 – Hubro

+0

我用2.1.5和2.2.1測試了這個代碼。我真的很驚訝它的工作,因爲文檔只提到了頂級的改進。 –

+1

@Hubro:更新了「Module Foo」的示例 –

相關問題