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

概要 原著者の許諾を得て翻訳・公開いたします。 英語記事: Rails belongs_to association – refactor queries and stay DRY 原文公開日: 2018/01/02 著者: Paweł Dąbrowski Rails: belongs_to関連付けをリファクタリングしてDRYにする(翻訳) belongs_toリレーションはRailsアプリで最もよく使われる関連付けなので、皆さまのアプリでも多数使われていると確信しています。さて、JobとCategoryという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番目のソリューションの方がよいかについて疑問をお持ちの方もいらっしゃるかもしれません。理由は次のとおりです。 ロジックを分離できます。categoryに関連するものはCategoryモデルに配置され、jobに関連するものはJobに配置されています。あなたの同僚は、ロジックをチェックして正確にはどんなクエリが使われるべきかを調べなくても、Job.publishableを呼び出すだけで済みます。 最初のバージョンのクエリだと、アプリのあちこちにJob.joins(:category).where(categries: {published: true})がばらまかれてしまいます。そのcategoryがpublishされているかどうかを調べるために条件をもっと詳しくチェックしなければならないとしたらどうしますか?ばらまかれているコードをすべて見つけ出すという残念な方法を取らざるを得なくなります。しかし2番目の方法なら、メソッドに変更を加えるだけで済みます。他に何も変更する必要はありません。 人間にとってより読みやすいコードになります。これはチームで若手開発者を抱えている場合に非常に重要な点です。 Categoryモデルに関連付けられているあらゆるモデルでCategory.publishedを使えるようになります。 Railsでお困りの方にお知らせ 知りたいことがありましたら、twitter または連絡用フォームにてお気軽にお問い合わせください。 関連記事 Rails: テストのリファクタリングでアプリ設計を改良する(翻訳)