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

Rails tips: belongs_to関連付けをリファクタリングしてDRYにする(翻訳)

概要

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

Rails: belongs_to関連付けをリファクタリングしてDRYにする(翻訳)

belongs_toリレーションはRailsアプリで最もよく使われる関連付けなので、皆さまのアプリでも多数使われていると確信しています。さて、JobCategoryという2つのモデルがあるとしましょう。1つのjobは1つのcategoryに属し、1つのcategoryには多くのjobがあるというシンプルな関連付けがなされています。

Categoryモデルにはpublished:booleanという属性があり、そのcategoryのjobを表示してよいかどうかを指定します。ここでの目的は、publishされたカテゴリに割り当てられているjobだけを返すクエリを作成することです。

普通の方法

通常は、以下のような方法を使います。

Job.joins(:category).where(categries: {published: true})

これに何かまずい点があるのでしょうか?別にありません。しかしここでは「ロジックの分離」に着目したいと思います。Jobモデルで何か操作を行う場合、publishされたcategoryだけを取り出すという条件を満たすことを気にかけるべきではありません。これはCategoryモデルに関連するロジックなので、このロジックをそちらに移動しましょう。

ロジックの分離

class Category < ActiveRecord::Base
 has_many :jobs

  def self.publishable
    where(published: true)
  end
end

ここではクラスメソッドの代わりにスコープを使うこともできます(スコープかクラスメソッドかについては別記事をご覧ください【原文リンク切れ】)。今回の場合、クラスメソッドの方が私にとって明確に思えたのでクラスメソッドを使うことにします。これによってJobモデルは次のようになります。

class Job < ActiveRecord::Base
  belongs_to :category

  def self.publishable
    joins(:category).merge(Category.publishable)
  end
end

これで、次のように呼び出せるようになりました。

Job.publishable

改善の理由

メンテナンスするコードがもっとたくさんある場合になぜ2番目のソリューションの方がよいかについて疑問をお持ちの方もいらっしゃるかもしれません。理由は次のとおりです。

  1. ロジックを分離できます。categoryに関連するものはCategoryモデルに配置され、jobに関連するものはJobに配置されています。あなたの同僚は、ロジックをチェックして正確にはどんなクエリが使われるべきかを調べなくても、Job.publishableを呼び出すだけで済みます。

  2. 最初のバージョンのクエリだと、アプリのあちこちにJob.joins(:category).where(categries: {published: true})がばらまかれてしまいます。そのcategoryがpublishされているかどうかを調べるために条件をもっと詳しくチェックしなければならないとしたらどうしますか?ばらまかれているコードをすべて見つけ出すという残念な方法を取らざるを得なくなります。しかし2番目の方法なら、メソッドに変更を加えるだけで済みます。他に何も変更する必要はありません。

  3. 人間にとってより読みやすいコードになります。これはチームで若手開発者を抱えている場合に非常に重要な点です。

  4. Categoryモデルに関連付けられているあらゆるモデルでCategory.publishedを使えるようになります。

Railsでお困りの方にお知らせ

知りたいことがありましたら、twitter または連絡用フォームにてお気軽にお問い合わせください。

関連記事

Rails: テストのリファクタリングでアプリ設計を改良する(翻訳)

CONTACT

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