Rails acts_as_paranoid を覗く

前回のBLOG 続き,

では,init.rbを覗いてみる.(素人なのでー間違っている箇所が多いと思います.ツッコミいただければー幸いです.)

# vendor/plugins/acts_as_paranoid/init.rb

class << ActiveRecord::Base
  def belongs_to_with_deleted(association_id, options = {})
    with_deleted = options.delete :with_deleted
    returning belongs_to_without_deleted(association_id, options) do
      if with_deleted
        reflection = reflect_on_association(association_id)
        association_accessor_methods(reflection,            Caboose::Acts::BelongsToWithDeletedAssociation)
        association_constructor_method(:build,  reflection, Caboose::Acts::BelongsToWithDeletedAssociation)
        association_constructor_method(:create, reflection, Caboose::Acts::BelongsToWithDeletedAssociation)
      end
    end
  end

  alias_method_chain :belongs_to, :deleted
end
ActiveRecord::Base.send :include, Caboose::Acts::Paranoid

1行目からわからない! " class A < B " なら AクラスはBクラスを継承するということだが…. これなんなん!?

これは,クラスメソッドの追加でした.つまり,ActiveRecord::Base クラスに belongs_to_with_deleted メソッドを追加している.下記にちょこっと試してみた!

$ irb
> class Base
>    def hoge
>        'hoge'
>    end
> end
> class << Base
>    def hige
>         'hige'
>    end
> end
>
> Base.hige    # 追加されたクラスメソッドの実行
=> "hige"
> Base.hoge   # インスタンスメソッドだから実行できない
NoMethodError: undefined method `hoge' for Base:Class
> obj = Base.new
> obj.hige       # クラスメソッドだから実行できない
NoMethodError: undefined method `hige' for #<Base:0xb7e8738c>
> obj.hoge      # インスタンスメソッドの実行
=> "hoge"

とういうことで納得.次に「alias_method_chain :belongs_to, :deleted」これ何してんだろ…. これrubyじゃなくてーrailsのメソッドだった…. このファイルを覗くと答えが見れる.

ruby/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/core_ext/module/aliasing.rb`

alias という言葉通りな感じですね. 試しに使ってみる.

$ ./script/console
>> class Gly
>>     def name
>>         'glycine'
>>     end
>> end
>> class G < Gly
>>     def name_with_short
>>         name_without_short + '( GLY )'
>>     end
>>     alias_method_chain :name, :short
>> end
>> Gly.new.name
=> "glycine"
>> G.new.name
=> "glycine( GLY )"

name_without_short は,拡張前のメソッドを呼び出せる. name_with_short は,拡張したメソッドを呼び出せる. この説明だとわかりにくいですよね.

この段階になっても,Model に acts_as_paranoid と書いただけでープラグインがうまく動くことは理解できない. 最後の行の「ActiveRecord::Base.send :include, Caboose::Acts::Paranoid」これですね. ここは,(クラスメソッドが追加された)ActiveRecord::Base に Caboose::Acts::Paranoid モジュールを include している. Paranoidモジュールは,vendor/plugins/acts_as_paranoid/lib/caboose/acts/paranoid.rb にあります. しかし,このモジュールが読み込まれてもー (ClassMethodsモジュール内の)acts_as_paranoid メソッドは,Model から呼び出せない. と思ったらー(Paranoidモジュールの)self.included(base) ってーのが ClassMethodsモジュールをextend してるわ!ということで Model に acts_as_paranoid と書くとー Modelを 読み込んだとき に acts_as_paranoidメソッドが実行される.

メモがてらにモジュールのincludeを試してみる.

$ irb
> module Paranoid
>     def self.included(base)
>         p self    #=> Pranoid
>         p base  #=> Base
>         base.extend ClassMethods
>     end
>
>     module ClassMethods
>        def acts_as_paranoid
>             'ACTS_AS_PARANOID'
>        end
>     end
> end
>
> class Base
>     include Paranoid
> end
>
> Base.acts_as_paranoid
=> "ACT_AS_PARANOID"

これで何とかーModelにacts_as_paranoid と書くことだけでーModelにプラグインが反映されることが理解. いやー疲れた.やっぱ言語勉強しないとね.今回もー toshi78 さんにも協力してもらっちゃいました.

結局,このプラグインを全部解読してませんが,色々と疑問. なんで,こんなにモジュールがネストされているんだろ? また,ActiveRecord::Baseにクラスメソッドを最初に追加したんだろ? だってーこの init.rb は Rails がロードされたときに読み込まれる.だもんでー 論理削除を必要としないModelにまでー不必要なクラスメソッドが追加されちゃいません?パフォーマンス悪くなっちゃうんじゃない? 素人の俺には「ActiveRecord::Base.send :include, Caboose::Acts::Paranoid」の中でーうまく「belongs_to_with_deleted」クラスメソッドを追加する処理をすればいいのではないのかなぁ〜.

と,素人の考えが色々と浮かぶ….

[RubyonRails] [Ruby]

2008/02/19 01:16 | Comments(0)

Comments

Comment Form