Railsのクエリを書く上で、Squeelというgemを便利に使っていたのですが、別なものに乗り換えることにしました。
Squeelとは
ActiveRecordには各種のクエリメソッドがありますが、それだけでは不足する部分もあります。たとえば、「created_at
が2週間前以前」というような条件をかけようとすると、以下のようになります。
# その1:文字列で書く
Model.where('created_at < ?', 2.weeks.ago)
# その2:Arelを使う
Model.where(Model.arel_table[:created_at].lt 2.weeks.ago)
ただ、これらの方法にはデメリットがあります。
- 文字列で書くと、あとあとの再利用に差し支える場合もありえますし、なにより美しくありません。
- Arelで書くとかなり長くなるし、記法はややこしいし、(Rails 5.0以前では)内部APIなので外から積極的に使っていいものか疑問もあります。
ということで、もっと綺麗に書けるgemとして「Squeel」というのがあります。これはArelをラップして、RubyのDSLとして書けるようにしたものです。
Model.where { created_at < 2.weeks.ago }
Squeelの問題点
Squeelはたしかに便利なのですが、大きな問題点として「ActiveRecordを大胆にモンキーパッチしている」ということがあります。その結果、ActiveRecordがバージョンアップするたびに、大規模な対応作業が必要となってしまう、という構造上の欠点があります。
そんな事情でRails 5対応が遅れていることもあって、「このまま継続して使うわけにも行かないな」と思えてきました。
Squeelなしでできること
ということで、ブロックを渡しているActiveRecordメソッドを調べてみたのですが、意外とSqueelなしで書けるものも多かったです。
- 単なる等値比較
- シンプルなサブクエリ
- 範囲比較
また、ORクエリについてはRails 5から加わることを前提に、現状の4.2では.where.or
で書けるようにしてSqueelから離れることができました。
BabySqueel
それでも書けないものも多いので(日付の大小比較など)、Squeelの後継としてBabySqueelに乗り換えようと考えています。こちらは、ブロックを取るwhere
を.where.has {}
のように別メソッドとすることで、もともとのActiveRecordメソッドをモンキーパッチするコストを削減しています。
教訓
モンキーパッチしたものを維持するには、相応のコストがかかる