概要
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
で切り出すのが良い?