2012-03-25 7 views
2

PersonAdministratorという2つのRailsモデルがあります。私たちは、モデルレベルでAdministrator秒の除去を禁止している:スーパークラスメソッドを呼び出すためにインスタンス上で単一のメソッドを再定義

class Person < ActiveRecord::Base 
end 

class Administrator < Person 
    def destroy 
    raise "Can't remove administrators." 
    end 
end 

me = Administrator.new 
me.destroy    # raises an exception 

私はテスト中にこの問題を回避できるようにしたいと思いますが、唯一のセットアップとティアダウン中に作成された特定のインスタンスのために。私はクラスの振る舞いを変更したくないので、class_evalremove_methodは実行できません。

def me.destroy 
    super 
end 

またはシングルトンクラスでそれを再定義:

は、私が実際のインスタンスの #destroyメソッドを再定義しようとした

class << me 
    def destroy 
    super 
    end 
end 

をしかしそれらはまだ例外が発生しました。スーパークラスメソッドを暗黙的に呼び出す方法を理解できませんでした。

def destroy! 
    ActiveRecord::Persistence.instance_method(:destroy).bind(self).call 
end 

シングルを指示する任意の簡単な方法があります:私は一種のクラスの動作を変更することなく、私の願望に違反し、(それが実際のActiveRecordのメソッドではありませんので)私自身のdestroy!メソッドを作成することになりましたスーパークラスメソッドを呼び出すインスタンスメソッド?


最終回答:ホルガーちょうどにリンクされて、私は単純に明示的にスーパークラスのメソッドを呼び出すことができた記事に基づいて

def me.destroy 
    self.class.superclass.instance_method(:destroy).bind(self).call 
end 

答えて

3

私はよりテストに優しくするために動作をリファクタリングしようとします。例えば。任意のパラメータを破棄することができます。 i_know_what_im_doingは、破棄を実行するためにはtrueに設定する必要があります。また、あなたのテストでは

class Administrator < Person 
    def before_destroy(record) 
    # You can't destroy me 
    false 
    end 
end 

ようbefore_destroyフックでdestroycancelことができ、あなたはそれを無視して、適切に破棄持ってAdministrator.skip_callback :before_destroyを呼び出すことができます。

最後に、テストでメソッドを上書き/スタブすることができます。あなたはクラスの振る舞いを変更したくないと言いますが、あなたはまだそれをしなければなりません(暗黙のうちに、今度はあなたのdestroy!メソッドでそれを行います)。

+0

ありがとう、ホルガー。あなたの提案は素晴らしいですが、私は 'super 'への私の呼び出しがスーパークラスメソッドを呼び出すことに失敗した理由がほとんど不思議です。それがなぜ起こるかあなたは何か考えがありますか? – Brandan

+0

あなたの構造では、あなたの 'me'インスタンスのシングルトンクラス(eigenclassとも呼ばれる)にオーバーライドされたdestroyメソッドを作成しました。あなたがそのメソッドのスコープ内にいる場合、スーパーチェーンは次のようになります:eigenclass - > class - >モジュールがクラス - >親クラスに含まれています...これはあなたの 'destroy'メソッドの' super'呼び出しを'me'はあなたの' Aministrator'クラスの 'destroy'メソッドを呼び出します。固有クラスの概念の詳細については、[この素晴らしい記事](http://blog.madebydna.com/all/code/2011/06/24/eigenclasses-demystified.html)を参照してください。 –

+0

優秀!その記事は私のためにそれをクリアした。 – Brandan

1

私はRubyのmetaprogramingに慣れていないので、私あなたがそれを変更せずにインスタンス上のスーパークラスのメソッドを呼び出すことができれば答えることはできません。しかし、あなたがaliasでスーパークラスのメソッドにフックを作成することができます。これにより

class Administrator < Person 

    alias :force_destroy :destroy 

    def destroy 
    raise "Can't remove administrators." 
    end 
end 

を、admin.destroyは、例外が発生しますが、admin.force_destroyは、実際にはActiveRecordが破壊呼び出します。

関連する問題