order、つけ忘れがちですよね。環境が変わると思ってた順番に出なかったりします。

更に厄介なのは、つけ忘れてもなんとなく正しい順番で動いているときで、何かのタイミングで思った順ではなくなってしまうことがあります。というかありました。

多分僕の場合はDBのマイナーアップグレードの影響のように見えました。詳細は追っていないですが、確かに一部でOrder指定が漏れていたので順番が変わってしまったのです。

そういう事態に開発中に気がつけるようにするため、must_be_orderedというgemを作りました。勤めてる会社の社内ハッカソンが8/31にあったので、ちょうどいいネタとばかりにこれを作りました。

gemはこちらにございます。

pi-chan/must_be_ordered
Contribute to pi-chan/must_be_ordered development by creating an account on GitHub.

現時点での使い方を紹介します(インターフェースはそのうち変わっている気がする)。

Rails、というかActiveRecordで使うのでGemfileにこのgemを追加します。

# Gemfile
gem 'must_be_ordered'

んで、bundle installします。

そしたら今度はconfig/initializers/以下に1個ファイルを作って↓のように書きます。

# config/initializers/must_be_ordered.rb

MustBeOrdered.enabled = !Rails.env.production?

# ↓はお好みでON/OFF
MustBeOrdered.raise = true
MustBeOrdered.must_be_ordered_logger = true
MustBeOrdered.rails_logger = true

あとは使いたいクラスの先頭でDSLっぽくmust_be_orderedを呼び出せばOK。

class Item < ApplicationRecord
  must_be_ordered
end

全モデルを監視したいなら、ApplicationRecordにつけると良さそうです。

これでorderのない状態でRelationでクエリが実行されるとエラーなりログなりが出る状態になりました。

試しに既存プロジェクトで上記のような設定を有効にしてrails consoleで試してみました。

対象クラスはUserです。まずはOrder無し。こちらはエラーとなります。

[10] pry(main)> User.where("id > ?", 10)
MustBeOrdered order not applied User:
bin/rails:4:in `require'
bin/rails:4:in `<main>'

続いてOrder有り。エラーは飛びませんね。

[11] pry(main)> User.where("id > ?", 10).order("id")
  User Load (10.7ms)  SELECT "users".* FROM "users" WHERE (id > 10) ORDER BY id
[12] pry(main)>

エラーではアグレッシブすぎるというのであればログだけにして使うと良いです。

仕事のプロジェクトにも入れるぞー。