Rails 3.2.8の話。ActiveRecordは長いのでARと略す。
find
やfirst
の実装自体は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::Querying
をextend
し、この中でAR::Relation
のインスタンスに対してfind
メソッド等をdeleagate
することで使えるようにしている。
class AR::Base extend Querying end class AR::Querying delegate :find, :first, ..., :to => :scoped end
そしてAR::Base#scoped
はAR::Relation
を作って返すので、結果としてAR::FinderMethods
で定義されたfind
やfirst
が呼び出せる。よくできてるなぁ。
なお、このModule#delegate
はActiveSupportで定義されているメソッドで、引数に与えたメソッドの呼び出しを: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
すればよい。