Dynamic Finder
Ruby On Rails の Dynamic Finderの仕組みが不思議です。
BOOKSテーブルに対応するモデルBookクラスに対して
Book.find_by_isbn('1234')
とかやると、BOOKSテーブルから値が返されます。
select * from BOOKS where isbn = '1234'
と等価です。
一方、Bookクラスの実装はというと
class Book < ActiveRecord::Base end
なわけです。find_by_isbn なんてメソッド定義がありません。
Javaの場合、少なくともインタフェース定義しないとメソッドは呼べません。
どうやってるんでしょうか。
Rubyの場合、呼び出したメソッドが存在しないと、そのクラスの method_missing というメソッドに処理が委譲されるんですね。
その仕組を利用して、method_missing の中で、find_by_isbn の字句を解析して、SQLを組み立てているようです。
method_missing なんて、動的型付け言語らしい機能です。
こういうのがRubyの面白いところなのかな。
ActiveRecord3.0.9のbase.rbの中を見て見ました。
def method_missing(method_id, *arguments, &block) if match = DynamicFinderMatch.match(method_id) attribute_names = match.attribute_names super unless all_attributes_exists?(attribute_names) if match.finder? options = arguments.extract_options! relation = options.any? ? construct_finder_arel(options, current_scoped_methods) : scoped relation.send :find_by_attributes, match, attribute_names, *arguments
このコードを読み解く力はまだ私にはありません。
finderの命名規約にマッチした場合に、finder処理を組み立てているんだ、というのはなんとなく読み取れます。
ちなみに、Book.find_by_isbnのBOOKSテーブルとisbnカラムがDBに存在することはいつチェックするんですかね?Railsの中でDB定義のコンフィグとかは持っていないように見えるから、とりあえずSQLを投げてしまうのだろうか?あるいは、DBのメタデータにアクセスする?
上のコードを読み進めていけばそのうちわかるのでしょう。がんばります。
それにしても、変数の型がすぐにわからないから、どうやってコードを追っていけばいいのか、まだよくわかってない。一つ一つクラス定義を探しながらやっていくしかないんだよね。こういうところはJavaより少し面倒だ。