さよならSqueel


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メソッドをモンキーパッチするコストを削減しています。

教訓

モンキーパッチしたものを維持するには、相応のコストがかかる