概要
ActiveRecord
には、class method
と同じような挙動をするscope
が定義できるclass method
とscope
を使い分ける方針として下記に統一すると良いと考える- 戻り値として
ActiveRecord::Relation
を期待する場合はscope
- 戻り値として
ActiveRecord::Relation
を期待しない場合はclass method
- 戻り値として
はじめに
ホワイトヘルスケア エンジニアの林です。
本記事は、Rails 初学者が scope
と class method
の使い分けに悩み、最終的にどのように使い分けるか考察した内容を記載しています。
scope
とは
公式より scope
の説明を抜粋します。
よく使うクエリをスコープに設定すると、関連オブジェクトやモデルへのメソッド呼び出しとして参照できるようになります。スコープでは、where、joins、includes など、これまでに登場したメソッドをすべて使えます。どのスコープメソッドも、常に ActiveRecord::Relation オブジェクトを返します。
私はホワイトヘルスケアに join するまで Django を扱っていましたが、この scope
という概念は Django にありません。また、プロジェクトに途中から join したこともあり、コード内に定義されていた scope
や class method
を見て、ふわっとした理解から入ったため、使い分けに悩みました。
使い分けに悩んだ点は、scope
と class method
で同じ挙動を定義できる点です。
例えば、以下の scope
と class method
は同じ挙動をします。
class Article < ApplicationRecord # 公式ページに挙げられているscope例 scope :published, -> { where(published: true) } # 上記scopeと同じ挙動をするclass method def self.method_published where(published: true) end end
Ruby には for 文のように、同じ挙動を異なる表現で実装できるということもあり、使い分ける必要があるのか非常に悩みました。
scope の位置付けと class method の使い分け考察
使い分けを考えた際、Rails における scope
の役割や位置付けは何かを調べました。結論として、位置付けに関しては、全てこの一文に集約されると考えています。
どのスコープメソッドも、常に
ActiveRecord::Relation
オブジェクトを返します。
また、scope
という単語が表す「範囲」という意味からも、レコードの範囲を絞るために使う(= filter として使う= ActiveRecord::Relation
オブジェクトを返す)ことが想定されている役割であると考えました。
(余談)個人的には、この一文は Rails における scope
の位置付けを示しており、コーディング規約(もっというと Rails の思想)に関わるため、もっと強い言葉にした方が良いと思っています。
では、scope
はレコードの範囲を絞るために使う、とした場合に、class method
はどのように扱うべきか?
結論としては、ActiveRecord::Relation
オブジェクトを返さない場合は全て class method
にする、という結論に至りました。
上記を実装方針とすることで、scope
と class method
の役割が明確かつ分離されるため、レビュー時やリーディング時の認知負荷軽減にも役立つと考えています。(私が認識していなかっただけで、そのような実装方針は定まっていました)。
まとめ
scope
と class method
の使い分けをまとめると以下になります。
- 戻り値として
ActiveRecord::Relation
を期待する場合はscope
- 戻り値として
ActiveRecord::Relation
を期待しない場合はclass method
- ex.1.
ActiveRecord::Relation
の値を変更する(戻り値は成功/失敗を表す bool) - ex.2. 特定条件に合わせたレコードを作成する(戻り値は作成したレコード)
- ex.1.
このような実装方針とすることで、認知負荷を下げ統一のある実装になると考えています。
採用情報
Whitehealthcare に興味が出てきた方はこちらからチェックをどうぞ