Railsでコードを書いていると、検索メソッドをクラスメソッドで書いているとscopeで定義したほうがいいんじゃないかという指摘をもらうことがあったりします。僕もなんとなく慣習なのかと思っていましたが、スコープで書いたほうがいい理由みたいなのが見つかったので書いていきたいと思います。
スコープで書いたほうがいい理由
結論ですが、メソッドチェーンでつないだときにチェーンが途切れないためです。
例えばこんなクラスがあったとします。(if falseに対するツッコミはなしでお願いします。)
class Article < ApplicationRecord
scope :public_articles, -> { where(is_public: true) if false }
end
このArticleクラスに対して定義したメソッドを呼び出すとなにが返るでしょうか?
コンソールで試すとif文がfalseなので実行されていないにも関わらず、ちゃんとActiveRecord_Relationが返ってきていることが分かります。
Article.public_articles.class
=> Article::ActiveRecord_Relation
Article.public_articles
=> Article Load (5.8ms) SELECT `articles`.* FROM `articles`
[#<Article:0x0000aaaad9b6e8e0
id: 1,
title: "aaa",
content: "aaa",
created_at: Wed, 29 Dec 2021 06:06:16.344008000 UTC +00:00,
updated_at: Wed, 29 Dec 2021 06:06:16.344008000 UTC +00:00,
is_public: true>,
#<Article:0x0000aaaad9c41ee8
id: 2,
title: "記事",
content: "内容",
created_at: Wed, 29 Dec 2021 06:06:26.953078000 UTC +00:00,
updated_at: Wed, 29 Dec 2021 06:06:26.953078000 UTC +00:00,
is_public: true>]
一方、これと同じメソッドをクラスメソッドで定義するとどうなるでしょうか?
class Article < ApplicationRecord
def self.public_articles
where(is_public: true) if false
end
end
コンソールを使ってメソッドを呼び出してみます。
Article.public_articles
=> nil
nilが返っちゃいました。if文がfalseなので、実行されずに戻り値がnilになっているわけですね。これでメソッドをチェーンするとエラーになります。
Article.public_articles.recent
NoMethodError: undefined method `recent' for nil:NilClass
クラスメソッドで必ずActiveRecord_Relationが返るようにすればいいですが、スコープを使ったほうが安全なのが分かったと思います。
検索のメソッドはチェーンして使われることを想定しないといけないので、(もしあったら僕ならチェーンできると思っちゃいます)戻り値がnilにならないように配慮しないといけない。それがscopeを使うと簡単にできるということでした。nilにならないようなメソッドでもどうせならscopeに定義するで統一しようぜということで検索系のメソッドはscopeで書くという慣習になっているのだと思いました。想像ですがw
今日は短いですが以上です。
よい年末を!
【Rails】なんで検索のメソッドはクラスメソッドじゃなくてスコープをつかったほうがいいの?|Rubinistを目指す新米エンジニアのTECH BLOG
woxeldqfx http://www.g2x5tyr27588an1uh2jm9509g61lmh3js.org/
[url=http://www.g2x5tyr27588an1uh2jm9509g61lmh3js.org/]uwoxeldqfx[/url]
awoxeldqfx