アラウンドエイリアスでbefore_destroyの挙動を変える

どうやらこのエントリは変態アドベントカレンダー in Summerの5日目のエントリのようです。
前の日のエントリ | 次の日のエントリ

実はワタクシ、このアドベントカレンダーは参加表明だけして、すっかり忘れてましたww 昨日の夜になって思い出したので、なにも仕込みがない状態で書いています。なので変態成分が薄めなのはマジカンベン。
でも変態分は他の人が補給してくれるから問題ないよね?(チラッチラッ

で本題。今回はなぜかRuby(というかRuby on Rails)の話。
ActiveRecordのbefore_destroyなどで処理をおこなうのは、サバンナ*1ではよくあったりする要求なのですが、これって大概同じような処理になることが多いです。でもコールバックの処理内容は自Modelにしか書けないので、複数Modelに似たような処理が記述されて非常にDRYじゃありません。
そんな時には、アラウンドエイリアスでこのような似通った処理をまとめてしまいましょう。

まずは、config/initializers辺りに以下のようにモジュールを定義します

module BeforeProcessExtension
  def before_process  #1
    define_method :destroy_with_something do
      #destroy前にやっておきたい処理を記述...
      self.destroy_without_something
    end

    alias_method :destroy_without_something, :destroy  #2
    alias_method :destroy, :destroy_with_something       #3
  end
end
ActiveRecord::Base.send :extend, AttachFileExtension

ちょっと解説。

  • #1 ここでこのモジュールをModelに組み込むための入り口になるメソッドを定義します。Modelからはこれをコールしてやります。
  • #2 ここが理解できれば、なんてことはない。alias_methodで本来のdestroyメソッドをdestroy_without_somethingという名前に変更してしまいます。
  • #3 そしてここで、destroy_with_somethingをdestroyメソッドに名前を変えてしまいます。

こんな風にエイリアスを付け替えることで、本来のメソッドにはない処理をすることも可能です。
あとはModel側で

class Employee < ActiveRecord::Base
  ...
  before_process
end

とか書いておけば、このEmployeeクラスのdestroyメソッドだけ、別の処理を行うことができます。

もしModel側から何かパラメータを渡したい場合は、モジュールのbefore_processを

def before_process(hoge)
  #引数hogeを使った処理
  ...
end 

と定義して、Model側で

class Employee < ActiveRecord::Base
  ...
  before_process "fuga"
end

このようにパラメータを渡してもおk

また、元のdestroyメソッドを呼びたい場合はdestroy_without_somethingメソッドを呼び出せばいいわけで、とっても楽ですね!
Rubyだとメソッドの名前も動的に変えられてしまうので、こんなことができちゃうんですねぇ・・・おそロシア
アラウンドエイリアスについては「メタプログラミングRuby」に詳しい*2ので、詳細はそちらで。

メタプログラミングRuby

メタプログラミングRuby

でもこれ既に絶版になってるんですよね・・・増刷してくれないんでしょうかね?(´・ω・`)

さて、次は dproject21 さんのようですよ。どんなド変態ネタが飛び出すか、今から楽しみですね!

*1:と書いてエンタープライズと読む

*2:というかそれ見て書いたw