ActiveRecordでArelを使いやすくするライブラリ activerecord-blockwhereを作ってみた


お久しぶりです。寺岡です。
自作のgemライブラリactiverecord-blockwhereを作ってみたのでご紹介します。

ライブラリの紹介

activerecord-blockwhereは、ActiveRecordに検索条件の組立を行うDSLを提供します。
DSLはwhereに与えたブロック内で記述でき、Arelの条件を短いコードで記述できます。

ActiveRecord3から導入されたArelはよく出来たライブラリで、非常に強力なクエリ構築機能があります。

しかし、個々のモデルクラスから利用する場合、後述するarel_tableや論理演算の関係でインターフェースが煩雑になるため敬遠しがちでした。
もっと簡潔に使えればいいのに、もったいないな~。と常々思っていたのです。

そんな時、Ruby2.0のRefinementsを利用した activerecord-refinements に出会いました。
(Refinementsの仕様変更により、惜しくも正式版では使えなくなってしまいましたが…)

このライブラリのおかげで、「whereのブロック引数は空いてるんだ!」という事実に気付き、勢いに任せて作ったのがactiverecord-blockwhereです。

発想的にありがちなので、既出のライブラリと被ってないかちょっと不安です。

ActiveRecordで複雑な条件を指定したい!

Hashによる検索条件の指定

ActiveRecordのwhereは、引数にHashを与えて条件を指定することができます。

値に配列を指定することにより、INで検索することもできます。

しかし、NOTやOR、LIKEなどの検索を行おうとした場合、Hashで与える条件では実現出来ません。
そんな場合、whereにSQL構文とbindパラメータを与えることで解決できます。

ORを含む条件 SQL版

解決はしましたが、この記述方法はSQLの一部がロジックに現れてしまうのであまり綺麗なやり方とは言えません。

検索方法によってインターフェースを変える(HashとSQLを使い分ける)という方法も感心しません。
今までHashで与えていたパラメータを、NOT条件を一つ追加するためにSQLとbindパラメータに書き直す…なんて作業はやりたくないですよね。

複雑なクエリはArelに任せろ?

ORを含む条件 Arel版

複雑な条件を指定したい。でもSQLとbindパラメータは使いたくない。
そんな時にはActiveRecord(3以降)においてSQL構築を担うライブラリ、Arelを利用します。

先ほどの条件もArelを使えばこんなに風にシンプル――

…に書くことはできませんでした。

ArelのAttributeオブジェクトを取得するにはarel_tableを経由する必要があるため、どうしても冗長なコードになってしまいます。
また、orやandなどの論理演算を使うとメソッド呼び出しのネストが深くなってしまうのも可読性を下げる要因ですね。

activerecord-blockwhereを使う

ORを含む条件 blockwhere版

activerecord-blockwhereを導入すると、先ほどの条件を以下のように記述出来ます。

かなり直感的になった気がしませんか?

ブロック内では Person.arel_table[:id] の代わりに id でArelのAttributeを取得できます。
述語(条件演算子)として利用できるメソッドはArelのものなので、以下コードで確認することが出来ます。

Arel::Predicationsで定義されているメソッド一覧

※参考: Arel::Predications

述語サンプル

また、条件の結合などを簡潔に記述するため、Arelを拡張して「and or not」に対応する演算子「& | !」を定義しています。

論理演算子サンプル

コントローラから直接使うこともできますが、複雑な条件をscopeやクラスメソッドにまとめる際などにも有用です。
よかったら一度お試しください!


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です