Ruby ミックスインとスーパー メソッドの呼び出し
-
08-06-2019 - |
質問
さて、私は重複を削除し、全体的に作業を楽にするために、小さな Rails アプリのコードをリファクタリングしてきました (私は楽な生活が好きなので)。このリファクタリングの一環として、2 つのモデルに共通するコードを、必要な場所に含めることができるモジュールに移動することが含まれています。
ここまでは順調ですね。うまくいきそうなのですが、どうすれば回避できるかわからない問題が発生しました。このモジュール (私は送信可能と呼んでいます) は、ファックス、電子メール、またはドキュメントの PDF の印刷を処理するコードになります。たとえば、注文書と社内販売注文書 (想像上 ISO と省略されます) があります。
私が遭遇した問題は、オブジェクトがロードされた後にいくつかの変数を初期化したいということです(スペルが正しくない人のために初期化してください:P)。そのため、私は after_initialize 針。問題ない...さらにミックスインを追加し始めるまで。
私が抱えている問題は、 after_initialize
私のミックスインのいずれかにあるので、 素晴らしい 最初に呼び出して他のミックスインを確認します after_initialize
電話がかかってきます。これは素晴らしいことですが、最終的に super を呼び出すと、呼び出すスーパーがないためにエラーが発生します。
十分に混乱していない場合に備えて、次の小さな例を示します。
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
したがって、各ミックスインに after_initialize 呼び出しがある場合、 素晴らしい 電話、どうすれば最後に止められますか 素晴らしい エラーが発生してから呼び出しますか?スーパー メソッドを呼び出す前に、そのメソッドが存在するかどうかをテストするにはどうすればよいですか?
解決
これを使用できます:
super if defined?(super)
以下に例を示します。
class A
end
class B < A
def t
super if defined?(super)
puts "Hi from B"
end
end
B.new.t
他のヒント
やってみました alias_method_chain
?基本的にすべてを連鎖させることができます after_initialize
呼び出します。これはデコレータのように機能します。新しいメソッドごとに新しい機能層が追加され、残りの処理を行うために「オーバーライドされた」メソッドにコントロールが渡されます。
包含クラス (継承元のクラス) ActiveRecord::Base
, 、この場合、これは Iso
) できた 独自に定義する after_initialize
, 、したがって、以外の解決策はありません alias_method_chain
(または元のエイリアスを保存する他のエイリアス)コードを上書きする危険があります。@Orion Edwardsのソリューションは私が思いつく限り最高のものです。他にもありますが、それらは 遠い もっとハックっぽい。
alias_method_chain
また、after_initialize メソッドの名前付きバージョンを作成できるという利点もあります。つまり、重要なまれなケースで呼び出し順序をカスタマイズできることになります。それ以外の場合は、包含クラスにミックスインが含まれる順序に左右されることになります。
後で:
すべてのコールバックのデフォルトの空の実装の作成について、ruby-on-rails-core メーリング リストに質問を投稿しました。いずれにせよ、保存プロセスではそれらすべてがチェックされるため、なぜそこにあるべきではないのかわかりません。唯一の欠点は、余分な空のスタック フレームが作成されることですが、これは既知の実装ではかなり安価です。
そこに簡単な条件を投げ込むだけです。
super if respond_to?('super')
無駄なメソッドを追加しなくても大丈夫です。素敵で清潔です。
スーパー メソッドが存在するかどうかを確認するのではなく、単に定義することもできます
class ActiveRecord::Base
def after_initialize
end
end
これは私のテストでは機能し、既存のコードを壊すことはありません。それを定義する他のすべてのクラスはとにかくこのメソッドを黙ってオーバーライドするだけであるためです。