2016-12-05 12 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を「適切に」クラスにアタッチするにはどうすればよいですか。つまり、切り離されたメソッドを呼び出す新しいメソッドを定義することはありません。


注:クラスの機能を一時的に変更する別の方法には興味がありません。私は具体的には、メソッドを別のメソッドに置き換え、元のメソッドをきれいに復元する方法を知りたいと思っています。

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

しかし、私は、これは副作用の自由であるならば、完全にはわからない:

+0

'backup = Foo.instance_method(:destroy_target)' 'なし' –

+0

特定の 'Foo'インスタンスまたはクラス全体、つまりすべての' Foo'に対してメソッドを置き換えますか? – Stefan

答えて

1

は、これが動作しているように見えます。誰かが確かに私がコメントを感謝したいと思っている場合。

これも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であれば、あなたがTypeError: wrong argument type Module (expected Class)を取得したい、直接refine Fooを呼び出すことはできません。

ただし、絞り込むことができますその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'の例で更新されました –

関連する問題