Ruby Freaks Lounge

第43回 Rails 3を支える名脇役たち その1 - Arel -

この記事を読むのに必要な時間:およそ 5.5 分

Arelで生まれ変わったActiveRecord 3

最後に,話をActiveRecord 3に戻して,Arelが実際にどう活用されているかを見てみましょう。前述のとおり,ActiveRecord 3の正体はArelをラップして作られた高機能O/Rマッパーなのですが,この際に,Railsらしく生のArelよりはだいぶユーザーに優しいDSLが提供されています。

従来のActiveRecordは,モデルのクラスのfind()メソッドの引数にRubyのHashをキーワード引数っぽく使った以下のようなスタイルでクエリを発行していました※2)⁠

ActiveRecord 2.3の普通のクエリ

books = Book.all(:joins => :author, :conditions => {:price => 3000, :authors => {:name => 'david'}}, :order => :published_date, :limit => 10)
※2
このころのAR::Base#allでは,結局内部でAR::Base#find(:all)が呼ばれています。

このとき発行されるSQL

SELECT "books".* FROM "books" INNER JOIN "authors" ON "authors"."id" = "books"."author_id"
WHERE ("books"."price" = 3000) AND ("authors"."name" = 'david') ORDER BY published_date LIMIT 10

しかし,RubyのHashで表現できないような条件になると,とたんに「SQL文を文字列で組み立てる」感じの大昔からお馴染みの手法に逆戻りせざるを得ないという,いささか残念な状況に陥ってしまうのでした。

ActiveRecord 2.3で,条件に不等号が1つ入った場合のクエリ

books = Book.all(:joins => :author,
:conditions => ['price > ? and authors.name = ?', 3000, 'david'], :order => :published_date, :limit => 10)

このとき発行されるSQL

SELECT "books".* FROM "books" INNER JOIN "authors" ON "authors"."id" = "books"."author_id"
WHERE (price > 3000 and authors.name like '%david%') ORDER BY published_date LIMIT 10

また,例えばユーザーの入力に応じて動的に条件を組み立てるような機能を実装しようとすると,引数のHashや文字列を事前にごにょごにょとmergeしたりする羽目になってしまい,⁠Rubyだから頑張ればたいがい1~2行で記述できるとは言っても)決して美しいとは言い難いコードになってしまっていました。

一方,ActiveRecord 3では,これと同じ内容の問い合わせは以下のように記述することになります。

Rails 3の普通のクエリ

books = Book.joins(:author).where('price > ?', 3000).order(:published_date).limit(10) & Author.where(:name => 'david')

このとき発行されるSQL

*何も発行されない*

さらに,この一連の条件の塊に対して,後から条件を動的に追加することができるようになっています。

Rails 3でクエリに動的に条件を追加する

books = books.where('published_date > ?', 3.years.ago.to_date)
books.each do |user|  # SQLはこのeachの呼び出しの時点で初めて発行される
  puts books.title
end

このとき発行されるSQL

# SELECT "books".* FROM "books" INNER JOIN "authors" ON "authors"."id" = "books"."author_id"
WHERE (price > 3000) AND ("authors"."name" = 'david') AND (published_date > '2007-06-14') ORDER BY published_date LIMIT 10

つまり,ActiveRecord 3は,⁠文字列ベースで組み立てたSQLを何かのメソッド呼び出しでドカッと発行する」というスタイルを遠く離れて,Rubyで記述された複数の小さな論理的な条件を重ね合わせているうちにいつの間にか適切な集合演算が行われてしまっている,というような感覚に変わっています。

そう,まるで:conditionsも:orderも:joinsも,⁠すべて動的なnamed_scopeになってしまった」かのようです。

さて,ここまでお読みいただいた皆さんには,どうやってこんな魔法のようなDSLが実現できてしまっているのか,もうご理解いただけていますよね?

まとめ

以上,駆け足でご紹介しましたが,Arelにはまだまだ紹介しきれていない機能もいろいろありますし,まだ若いプロダクトなので,これから更なる発展を遂げることが期待されます。Rackの登場に後押しされてWebサーバーやWEBアプリケーション・フレームワークのプロダクトが数多く世に登場したのと同じく,今後はArelの肩に乗っかってRubyで作られた新世代O/Rマッパーやデータ永続化の仕組みがこれからどんどん作られていく流れになっていくかもしれません。楽しみですね!

著者プロフィール

松田明(まつだあきら)

フリーランスのRailsプログラマー/Railsコンサルタント。
流しのフェロー。現職は株式会社groovesフェロー。
地域Rubyハッカーコミュニティ"Asakusa.rb"主催。
一児(美少女)の父。