Tech Racho エンジニアの「?」を「!」に。
  • 開発

Rails: モデルの外では名前付きスコープだけを使おう(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

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メソッド群よりもよい結果が得られるかどうか」です。

ツイートより

関連記事

Ruby/Railsのプロ開発者としての5年間を振り返る(翻訳)

Rails: ActiveRecord::Relationで生SQLは避けよう(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。