There's an echo in my head

日々のメモ。

ActiveRecord::Baseにfindとかfirstっぽいメソッドを生やす。

Rails 3.2.8の話。ActiveRecordは長いのでARと略す。

findfirstの実装自体はAR::FinderMethodsで定義されていて、それをincludeすることでAR::Relationインスタンスuser.postsとかUser.scopedの返り値)から使えるようになっている。

module AR::FinderMethods
  def first(*args)
    :
  end
end

class ActiveRecord::Relation
  include FinderMethods
end

AR::Baseでは、まずAR::Queryingextendし、この中でAR::Relationインスタンスに対してfindメソッド等をdeleagateすることで使えるようにしている。

class AR::Base
  extend Querying
end

class AR::Querying
  delegate :find, :first, ..., :to => :scoped
end

そしてAR::Base#scopedAR::Relationを作って返すので、結果としてAR::FinderMethodsで定義されたfindfirstが呼び出せる。よくできてるなぁ。

なお、このModule#delegateActiveSupportで定義されているメソッドで、引数に与えたメソッドの呼び出しを:toオプションで指定したメソッドの呼び出し結果に対して行うというもの。

require "active_support/core_ext"

class Backend
  def hello
    "Hello!"
  end
end

class Frontend
  def backend
    @backend ||= Backend.new
  end
  
  delegate :hello, :to => :backend
end

f = Frontend.new
f.hello #=> "Hello!"

delegateについては誰かがブログで紹介していたけど、忘れてしまったなぁ…。

図で書いたほうがわかりやすそう&スコープってなんじゃいと言われたら答えられないけど、取り急ぎのメモ。

追記

タイトルの内容をすっかり忘れていた。で、どうやって生やせばいいかというと、こんな感じ。

module ActiveRecord
  module Querying
    delegate :foo, :to => :scoped
  end
  module FinderMethods
    def foo(*args)
      # implement your method here
    end
  end
end

これをconfig/initializers/以下に置くか、lib/以下に置いてrequireすればよい。

このブログに出てくるコードスニペッツは、引用あるいは断りがない限りMITライセンスです。