概要
Railsで開発している中で、「scopeと通常のメソッド定義では何が違うの?」となったので調べました。
結論としては、**「やることはどっちも同じ。どっちを使うかは好みの問題」**だそうです。
RailsのModelで使うscope
Railsガイドによれば
スコープを設定することで、関連オブジェクトやモデルへのメソッド呼び出しとして参照される、よく使用されるクエリを指定することができます。スコープでは、where、joins、includesなど、これまでに登場したすべてのメソッドを使用できます。どのスコープメソッドも、常にActiveRecord::Relationオブジェクトを返します。このオブジェクトに対して、別のスコープを含む他のメソッド呼び出しを行なうこともできます。
scopeを使ってメソッド定義することで、事前にscope内で指定した実行してほしいクエリを渡すことができます。
複数回使うようなクエリがある場合にはそれをモデル内でscopeを使って定義しておけば、可読性や再利用性を高めることができます。
また、scope同士はメソッドチェーンで繋ぐことも可能なので、より小さなレベルでscope Aとscope Bに切り出しておき、それらをつなぎ合わせたscope Cとして定義することも可能です。
# 出版済みの書籍絞り込み
scope :published, -> { where(published: true) }
# 出版日を降順で並べ替え
scope :sort_publication, -> { order(publication: :desc) }
# 出版済みの書籍を出版日で降順に並べ替え
scope :get_published, ->{ published.sort_publication }
scopeでの定義とクラスメソッドの定義の違い
以下でもわかるように、スコープでのメソッドの設定は、クラスメソッドの定義と完全に同じ (というよりクラスメソッドの定義そのもの) です。どちらの形式を使用するかは好みの問題です。
引用: Railsガイド
やってることは完全に同じです。
scopeを使って定義しようが、def ~ endのクラスメソッドで定義しようが、違いはありません。
(1)
scope :published, -> { where(published: true) }
(2)
def self.published
where(published: true)
end
上記(1)と(2)は全く同じです。
ただ、個人的にはscopeを使えば1行で済むのでコードの可読性も上がりそうで好きなのと、クラスメソッドとインスタンスメソッドの区別をより明確にする意味も込めて、def ~ endのクラスメソッドで定義するよりもscopeを使いたいなーという感じでいます。
この辺は企業によって違ってきたりするんでしょうか。
コントローラーでクエリを直接呼び出したり、さらにはクエリを複数繋いだりすると読みづらくなったりRailsのDRYの原則にも合わなくなってくると思うので、個人的には避けていきたいところです。
考慮しておくべき点
-
scopeにも引数を渡すことができますが、そのような場合にはRailsガイドではクラスメソッドを利用することを推奨しています
スコープに引数を渡す機能は、クラスメソッドによって提供される機能を単に複製したものです。
したがって、スコープで引数を使用するのであれば、クラスメソッドとして定義する方が推奨されます。クラスメソッドにした場合でも、関連オブジェクトからアクセス可能です。
-
scope内で条件文を使用する時、その評価の結果がfalseになった場合にはクラスメソッドはnilを返すが、scopeではActiveRecord::Relationオブジェクトを返す- 条件文の評価によって挙動を変える場合などにはこの点を考慮しておく必要がある?
まとめ
-
scopeでの定義とクラスメソッドでの定義には違いはない。使い分けは好みの問題。 -
scopeを使う方がよりシンプルで読みやすくかける -
scopeは小さく切っておくことでコードの再利用性も上がる
参照
Ruby on Rails ガイド/Active Record クエリインターフェイス
備考
-
scopeのメソッド名では、in_***という命名が割と多いらしい - パーツとして再利用できそう/しそうなやつだけ
scopeで切り出すのが良い?