この前、後輩のPRのレビューを行なっていたら、scopeの使い方に一貫性がなかったので、これは説明資料を作っておいたほうが後々にも使えていいなと思ったので、スライドを作っておきました。
ここ最近プレゼンテーションzenを読んでいたので、文字を減らして画像を多めに話すようにしてみました。ちなみに、第二版がある模様。
- 作者: Garr Reynolds,熊谷小百合
- 出版社/メーカー: 丸善出版
- 発売日: 2014/02/21
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
スピーカーノートのほうに解説を書いているので、そちらを読んでもらえればいいかなと思いますが、大した量でもないのでざっくりと書いておきます。
scopeはActiveRecord::Relationを返すべし
scopeは絞り込み条件に名前をつけられる機能です。
うまく使うためには、ActiveRecord::Relation
を返すようにしなければなりません。
scopeが必ずActiveRecord::Relation
を返すのであれば、以下のようにメソッドチェーンで容易に条件を絞り込むことができます。
# 論理削除されていない、ここ1ヶ月でログインしたユーザ User.without_soft_destroyed.logged_in_after(1.month.ago) # 公開済みで作者がTomさんの記事 Article.published.created_by(tom) # 検索可能で100件以上出店しているファッションのお店 Shop.searchable.opened_over(100).industry_by(fashion)
メソッドとscopeをどう使い分けるのか?
私の場合は、scopeは単純なwhere句を定義するときに使います。 数行になりそうな場合は、scopeとして分割できないかを検討して、複数のscopeを定義して、メソッドチェーンで利用します。 また、if文を含む場合はメソッドにしているかなと思います。 scopeはあくまでもシンプルな条件のみにしています。
メソッドはActiveRecord::Relation
以外を返すときや、ActiveRecord::Relation
を返すにしても、処理が複雑な場合、などにしています。
mergeメソッドと組み合わせて使うと便利
joinした場合のwhere句の定義はHashの定義のネストになって、SQLを書くのと殆ど変わらないくらいになっていきますが、mergeメソッドを使うとシンプルに扱うことができます。
# joinした際の検索条件の記述は大変… UserGroup.joins(:users).where(users: { id: User.without_soft_destroyed }) # mergeを使うと、楽だし綺麗 UserGroup.joins(:users).merge(User.without_soft_destroyed)
それぞれto_sql
メソッドを使って、比較してみると少し違いますが(INNER JOINのONの条件が変わってた)、動作は同じです。
ActiveRecord::Relation
はコードをシンプルにできる機能が揃っているので、便利に使っていきましょう。