高木のブログ

【Rails】スコープとクラスメソッドの違い

2020/08/11

普通に実装していたら違いなんてなくてどっちでもいいじゃんって思ってたけど、明確な違いがあった
今までたまたまそういう実装をしていないからハマらなかっただけで、いつかハマるかもしれないからメモしておく

スコープとクラスメソッドの違い

メソッドチェインができるようにスコープはActiveRecord::Relationを返すようになっている

どういう時にハマる可能性があるか

以下のようなfind_ageスコープを作成

app/models/user.rb
class User < ApplicationRecord
  scope :find_age, ->(age) { find_by(age: age) }
end

条件に一致するレコードがある場合は特に問題ない

[1] pry(main)> User.find_age(27)
  User Load (0.4ms)  SELECT `users`.* FROM `users` WHERE `users`.`age` = 27 LIMIT 1
=> #<User:0x00007f3b4dc32b18
 id: 1,
 name: "takagi",
 age: 27,
 created_at: Sat, 08 Aug 2020 22:07:38 JST +09:00,
 updated_at: Sat, 08 Aug 2020 22:07:38 JST +09:00>

条件に一致するレコードがない場合は全件返ってくる

[1] pry(main)> User.find_age(30)
  User Load (0.5ms)  SELECT `users`.* FROM `users` WHERE `users`.`age` = 30 LIMIT 1
=>   User Load (0.2ms)  SELECT `users`.* FROM `users`
[#<User:0x00007f3b4dd94448
  id: 1,
  name: "takagi",
  age: 27,
  created_at: Sat, 08 Aug 2020 22:07:38 JST +09:00,
  updated_at: Sat, 08 Aug 2020 22:07:38 JST +09:00>,
 #<User:0x00007f3b4dd94380
  id: 2,
  name: "tanaka",
  age: 25,
  created_at: Sat, 08 Aug 2020 22:07:53 JST +09:00,
  updated_at: Sat, 08 Aug 2020 22:07:53 JST +09:00>,
 #<User:0x00007f3b4dd942b8
  id: 3,
  name: "sato",
  age: 32,
  created_at: Sat, 08 Aug 2020 22:09:52 JST +09:00,
  updated_at: Sat, 08 Aug 2020 22:09:52 JST +09:00>]

find_byメソッドは条件に当てはまるレコードがないときはnilを返す仕様
スコープはActiveRecord::Relationを必ず返す仕様なので、nilではなくallと同じ結果のActiveRecord::Relationを返す

わかりやすくnilを返すスコープを用意

scope :return_nil, -> { nil }
[1] pry(main)> User.return_nil
=>   User Load (0.4ms)  SELECT `users`.* FROM `users`
[#<User:0x00007f3b4dd94448
  id: 1,
  name: "takagi",
  age: 27,
  created_at: Sat, 08 Aug 2020 22:07:38 JST +09:00,
  updated_at: Sat, 08 Aug 2020 22:07:38 JST +09:00>,
 #<User:0x00007f3b4dd94380
  id: 2,
  name: "tanaka",
  age: 25,
  created_at: Sat, 08 Aug 2020 22:07:53 JST +09:00,
  updated_at: Sat, 08 Aug 2020 22:07:53 JST +09:00>,
 #<User:0x00007f3b4dd942b8
  id: 3,
  name: "sato",
  age: 32,
  created_at: Sat, 08 Aug 2020 22:09:52 JST +09:00,
  updated_at: Sat, 08 Aug 2020 22:09:52 JST +09:00>]

User.allと同じ結果になる

参考


Pixela