Rails tips: スコープを用いてif条件をシンプルにする(翻訳)
シンプルなスコープを使ってif
条件を簡単にできる状況はとてもよく見かけます。サンプルのクラスでやり方を説明します。
class User < ActiveRecord::Base
has_many :posts
end
class NotificationService
def initialize(company:)
@company = company
end
def notify_user(email:)
user = User.find_by(email: email)
if user.present? && user.posts.any?
Mailer.new_posts_reminder(user.id).deliver
end
end
private
attr_reader :company
end
User
モデルがあって、has_many :posts
によって各ユーザーが複数のposts
を持つことができます。NotificationService
は、ユーザーが新しく投稿を作成したときに、簡単なメールメッセージでユーザーにリマインダーを送信するのに用いられます。
ここではusers.posts.any?
をチェックする必要はありません。これはデータベースレベルで行われるからです。posts
を持つユーザー向けのスコープを作成してみましょう。
class User < ActiveRecord::Base
has_many :posts
scope :with_posts, -> { joins(:posts) }
end
スコープはクラスメソッドとして定義することも可能であり、どちらにするかはあなた次第です。どちらにすべきかよくわからない方は、モデルのクエリをカプセル化する2つの方法をご覧ください。ともあれ、これで更新されたデータベースクエリが使えるようになり、条件をリファクタリングできるようになりました。
class NotificationService
def initialize(company:)
@company = company
end
def notify_user(email:)
user = User.with_posts.find_by(email: email)
user && Mailer.new_posts_reminder(user.id).deliver
end
private
attr_reader :company
end
コードがぐっとシンプルかつ短くなり、しかもクエリが自らの挙動を語るようになりました。ほんのささやかなリファクタリングですが、この違いは大きいものです。
この方法は、今後ユーザーがposts
を持つかどうかを決定する責務を持つコードを変更するときにも役立つのが嬉しい点です。変更前の方法では、ユーザーがposts
を持つかどうかをあちこちでチェックしなければなりませんでしたが、変更後の新しい方法ならUser
モデルのスコープを変更するだけで済みます。
上の変更は、belongs_to
関連付けのクエリのリファクタリングにも関連します。
Railsでお困りの方にお知らせ
知りたいことがありましたら、twitter(訳注: 現在無効です) または連絡用フォームにてお気軽にお問い合わせください。
お知らせ: RSpec & TDDの電子書籍を無料でダウンロード
もっと稼ぎたい方や会社をさらに発展させたい方へ: テスティングのスキルの重要性にお気づきでしょうか?テストを正しく書き始めることが、唯一のファーストステップです。無料でダウンロードいただける私の書籍『RSpec & Test Driven Developmentの無料ebook』をどうぞお役立てください。
概要
原著者の許諾を得て翻訳・公開いたします。
現在上記サイトは以下のlongliveruby.comに移行しており、元記事は参照できなくなっています。
Long Live Ruby – Ruby on Rails Web Development