2008-08-14 6 views
34

私は小さなRailsアプリでコードをリファクタリングして重複を取り除き、一般的に私の人生を楽にしています。このリファクタリングの一部は、2つのモデルに共通するコードを、必要な場所に含めることができるモジュールに移動することでした。Rubyミックスとスーパーメソッドを呼び出す

これまでのところ、とても良いです。それはうまくいくように見えるが、私はちょうど私が周りを回避する方法がわからないという問題にヒットしました。モジュール(私はsendableと呼ばれています)は、単にドキュメントのPDFをファックス送信、電子メール送信、または印刷するコードになります。したがって、たとえば、私は発注書を持っており、内部販売注文(想像上ISOと略記)を持っています。

私が打ち砕いた問題は、オブジェクトがロードされた後にいくつかの変数を初期化する(Pの綴りが正しくない人のために初期化される)ので、after_initializeフックを使用しています。問題はありません...ミックスインをもう少し追加するまで。

私が持っている問題は、私は私のミックスインのいずれかでafter_initializeを持つことができるということですので、私は確かに他のミックスインafter_initializeコールが呼び出さ作るために開始時にスーパー呼び出しを含める必要があり。私はスーパーに電話するまでは素晴らしいです。電話するスーパーがないので、エラーが発生します。ミックスインのそれぞれがafter_initializeコールを持っている場合、私はどのように停止することができ、スーパーコールで、

class Iso < ActiveRecord::Base 
    include Shared::TracksSerialNumberExtension 
    include Shared::OrderLines 
    extend Shared::Filtered 
    include Sendable::Model 

    validates_presence_of :customer 
    validates_associated :lines 

    owned_by    :customer 
    order_lines    :despatched # Mixin 

    tracks_serial_numbers :items # Mixin 

    sendable :customer      # Mixin 

    attr_accessor :address 

    def initialize(params = nil) 
    super 
    self.created_at ||= Time.now.to_date 
    end 
end 

ので:ここ

は、私は十分に混乱していない場合には、少し例ですその最後の スーパーからの呼び出しエラーを発生させる?私はそれを呼び出す前にスーパーメソッドが存在することをどのようにテストできますか?

答えて

40

あなたはこれを使用することができます:それは一般的なソリューションではありませんので、

class A 
end 

class B < A 
    def t 
    super if defined?(super) 
    puts "Hi from B" 
    end 
end 

B.new.t 
0

のではなく、スーパーメソッドが存在するかどうかをチェックする、あなただけのこれが私のテストで動作し、それを定義するすべての他のクラスので、既存のコードのいずれかを壊すべきではありません

class ActiveRecord::Base 
    def after_initialize 
    end 
end 

それを定義することができますとにかくこのメソッドを黙ってオーバーライドするだけです

+1

Downvoted:ここ

super if defined?(super) 

は一例です。あなたがスーパーが存在するかどうか分からないので、ActiveRecord :: Base#after_initializeをmonkeypatchすることで、ActiveRecordがベース#after_initializeを追加するか、またはそのアーリティが変更された場合にコードが壊れるポイントを作成します;それが定義されていれば、それを条件付きで呼び出す方がはるかに良いです。 – yaauie

+0

@yaauie - 確かに、monkeypatchの前にmethods.include?(:after_initialize) 'を' raise 'oh noにすることはできましたが、それは例をもっと理解しにくくするでしょう...巻き込まれるのはとても簡単ですここでの実際のレッスン(ちょうどベースメソッドをパッチする)がノイズで失われるというすべてのエッジケースを詳しく説明します。 –

+1

monkeypatchingは一般的な解決策ではなく、一般的には奨励するべきではありません。あなたが継承チェーンを完全に制御することができない場合、*動作する可能性のある一回限りのモンキーパッチを含む答えは、不必要に複雑で脆弱なコードにつながります。 – yaauie

3

alias_method_chainを試しましたか?基本的にすべてのafter_initializeコールをチェーンすることができます。これはデコレータのように機能します。新しいメソッドが追加されるたびに、新しい機能レイヤが追加され、コントロールをオーバーライドメソッドに渡して残りの処理を行います。

3

(この場合Isoある、ActiveRecord::Baseから継承するもの)を含むクラスはafter_initialize独自に定義することができるので、任意alias_method_chain以外の溶液(または元を保存し、他の折り返し)がコードを上書きするリスク。 @オリオンエドワーズのソリューションは、私が考え出すことができる最高です。他にもありますが、far hackishです。

には、after_initializeメソッドの名前付きバージョンを作成するメリットがあります。これは、ごくまれにしかコールオーダーをカスタマイズできないことを意味します。さもなければ、あなたはミックスインを含むクラスを含むどのような順序の慈悲でもあります。後で

:私はすべてのコールバックのデフォルトの空の実装を作成に関するルビーオンレール・コアメーリングリストに質問を投稿した

。とにかく、セービングプロセスでそれらがすべてチェックされるので、なぜそこにいてはいけないのかわかりません。唯一の欠点は余分な空のスタックフレームを作成することですが、これは既知の実装すべてでかなり安いです。

2

あなたはただそこに迅速な条件を投げることができます。

super if respond_to?('super') 

を、あなたは罰金あってはならない - 何も追加役に立たない方法。素敵で清潔です。

+0

これは機能していない** **です。 respond_to?( 'super')は常にfalseを返します。 – averell

関連する問題