ActiveRecord のコールバックとオブザーバ(Rails勉強中 #6)

Rails の興味深い実装メモの続き。

ActiveRecord の主な仕事は、DB の読み書きとデータの Validation なわけですが、コールバックを利用すると、Validation の前後や DB 読み書きの前後の、任意のタイミングで好きな処理を割り込ませることができます。

例えば、 Rails本のサンプルでは、

def before_create
  self.hashed_password = User.hash_password(self.password)
end

といった感じで、DBにユーザ情報をインサートする前にパスワードを SHA1 でハッシュしたり、

after_create do |order|
  logger.info "Order #{order.id} created"
end

といった感じで、オーダー情報をDBに格納した後ログに記録する、といったことをモデルの中で実行できるようになっています。

モデルで Validation するのと同様、これもコントローラの処理の簡略化につながり、より見通しの良いコードが書けていい感じですね。

また、上記のようなロギング処理を、モデルにやらせるのは気持ち悪い、という場合には、オブザーバを使ってモデルから処理を追い出すことができます。こんな感じで。

class OrderObserver < ActiceRecord::Observer
  def after_save(an_order)
    an_order.logger.info("Order #{an_order.od} created")
  end
end

クラス名を OrderObserver としておくと、勝手に Order というモデルクラスのオブザーバとなってくれます。

さらに、observe メソッドで監視対象のクラスを複数指定することもできます。

class AuditObserver < ActiveRecord::Observer
  observe Order, Payment, Refund
  def after_save(model)
    model.logger.info("#{model.class.name} #{model.id} created")
  end
end

こんな感じで、複数のモデルクラスに共通する処理を、元のクラスをまったくいじることなく、ひとつのクラスに記述しておくことができます。アスペクト指向っぽいですね。

Catalyst でこれと同じようなことができるのかな、と調べてみたところ、Catalyst::Plugin::Observe なるものが。といっても、ActiveRecord::Observer はモデルに特化しているのに対し、Catalyst::Plugin::Observe はフレームワーク全体で働くオブザーバなので、用途が異なるようです。

特に端的に異なるのは、コールバックイベントの種類ですね。ActiveRecord::Observer は before_validation, after_create などの、Validation や DB読み書きの処理段階に応じたイベントですが、Catalyst::Plugin::Observe は dispatch, prepare_action など、フレームワークの処理段階に応じたイベントとなってます。

Sledge 次期バージョンでは、ActiveRecord の様な Validation、コールバック、オブザーバはぜひ欲しいですね。お前が実装しろ、というのであれば、喜んでコード書きます。(あ、言っちゃった…)