概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Only use named scopes outside models - Andy Croll
- 原文公開日: 2018/03/04
- 著者: Andy Croll
Rails: モデルの外では名前付きスコープだけを使おう(翻訳)
前回の記事では#where
メソッドで「hashスタイル」を使おうというお話をいたしましたが、あまり自分が現実に使うことのなさそうなコード例でした💦。
次のようには書かないこと
コントローラやビューで#where
スコープを使う。
class PostsController < ApplicationController
def index
@posts = Post.where(status: 'published')
end
end
次のように書くこと
名前付きスコープはモデルでのみ定義する。
class Post < ApplicationRecord
scope :published, -> { where(status: 'published') }
end
コントローラなどでは次のように書く。
class PostsController < ApplicationController
def index
@posts = Post.published
end
end
そうすべき理由
この手法を用いることで、コード編成が改善されます。次の2つの点において、長期的な生産性向上のために実に有用です。
- その1: 自分が作り出す概念が名前として具現化されます。それ自体が明快に語ってくれる適切な名前を付けることで、あなたの同僚はもちろん、「未来の自分」にとってもしばしばありがたいものになります。
-
その2: 探す場所が1箇所に絞られます。スコープをモデルの外で定義していると、思いつくままのスコープやら条件やらの定義がコードベースのあちこちにばらまかれることになります。すべての条件が定義される場所がわかっていれば、リファクタリングやデータベースパフォーマンスの最適化でどこをチェックすればよいかで迷うことがなくなります。
そうすべきでない理由があるとすれば
#limit
や単純な#order
やページネーションに関連するスコープぐらいであれば、その場限りの特定のスコープを作ってもほとんど困ることはありません。ActiveRecord::Relation
のメソッド群の構文はかなり明快だからです。
スコープに名前付けするメリットは、明快さをさらに高める場合にのみ有用です。#where
を使わないクエリの場合、シンプルなActiveRecord::Relation
のメソッド群をスコープ内にラップしてもそれ以上理解しやすくなるとは限りません。
scope :by_title, -> { sort(:title) } # メリットないかも
scope :by_updated_at, -> { sort(:updated_at) } # 名前ひどすぎ
scope :recently_updated, -> { sort(updated_at: :desc) } # おそらくやる価値あり
順序が複雑な場合や、ソート順が#where
クエリで指定される条件と強く関連している場合であれば、名前付きスコープは依然としてよい選択肢である点は覚えておくとよいでしょう
#order
や#limit
を使う名前付きスコープを作成するかどうかの判断に使える、うまいヒューリスティック(発見的方法)があります。「その概念には簡単に名前を付けられるかどうか」「既存のActiveRecord::Relation
メソッド群よりもよい結果が得られるかどうか」です。
ツイートより
そうすべき理由とそうすべきでない理由がわかりやすい / Rails: モデルの外では名前付きスコープだけを使おう(翻訳) https://t.co/RVNpx26xdP
— だいしろ🐟♎🍆🦀 (@d14a) June 14, 2018
コントローラーに生のwhere出てきたら泣けるしね / “Rails: モデルの外では名前付きスコープだけを使おう(翻訳)” https://t.co/1fcfN5kUTq
— Ryutaro YOSHIBA (@ryuzee) June 13, 2018