ActiveRecord::Relationのメソッド…だったっけ?


ActiveRecordのオブジェクトは、メソッドチェーンでどんどん絞り込めるという便利な特性があります。さらに、モデルのクラスメソッドまで借りてきて動作してしまいます。

絞り込みのメソッドチェーン

RailsのActiveRecordによるモデルは、.where(条件1).where(条件2)というように、次々と絞り込んで、最後にまとめてSQLを発行することができます。

これはどういう仕組みなのかというと、.where()ActiveRecord::Relationというクラスのオブジェクトを生成していて、実際にデータが必要になるまでSQLを生成するのを待つ一方で、さらに別な絞り込み条件を指定すると、内部的に条件を組み立てて、最終的なSQLの生成に備えています。なお、pで出力すると全データが出ますが、これはpのために生成したものです。

スコープを使う

scopeの詳細は他項に譲りますが、一言で言えば「検索用にモデルクラスに生やせるクラスメソッド」です。そして、このscopeは、SomeModel.some_conditionのように、直接モデルクラスのメソッドとして使うだけでなくて、SomeModel.where(...).some_conditionというように、メソッドチェーンの途中で呼び出すこともできます。

あれっ?

上で述べたように、.whereの返り値はActiveRecord::Relationであって、SomeModelのクラスではありません。それなのに、なぜ全然違うクラスのクラスメソッドを呼び出せてしまうのでしょうか。

委譲の仕組み

気になったのでソースを追いかけてみると、ActiveRecord::Relationが読み込むモジュールの中にActiveRecord::Delegationというのがありました。

ここのソースを見てみると、単にmethod_missingで流すだけではなく、一度見つけたメソッドを実際に定義することで次からは普通に呼び出せるようにする、なんて処理を行っていました。