Ответ 1
Это не ошибка или странность, после некоторых исследований я нашел свой специально предназначенный.
Прежде всего,
-
scope
возвращаетActiveRecord::Relation
-
Если нулевые записи запрограммированы на возврат всех записей который снова является
ActiveRecord::Relation
вместоnil
Идея заключается в том, чтобы сделать область привязки (т.е.) одной из разниц < между scope
и class methods
Пример:
Позволяет использовать следующий сценарий: пользователи смогут фильтровать сообщения по статусам, упорядочивая самые последние обновленные. Достаточно просто, дайте возможность писать для этого:
class Post < ActiveRecord::Base
scope :by_status, -> status { where(status: status) }
scope :recent, -> { order("posts.updated_at DESC") }
end
И мы можем называть их свободно следующим образом:
Post.by_status('published').recent
# SELECT "posts".* FROM "posts" WHERE "posts"."status" = 'published'
# ORDER BY posts.updated_at DESC
Или с предоставленным пользователем параметром:
Post.by_status(params[:status]).recent
# SELECT "posts".* FROM "posts" WHERE "posts"."status" = 'published'
# ORDER BY posts.updated_at DESC
До сих пор так хорошо. Теперь переместите их в методы класса, просто для сравнения:
class Post < ActiveRecord::Base
def self.by_status(status)
where(status: status)
end
def self.recent
order("posts.updated_at DESC")
end
end
Помимо использования нескольких дополнительных строк, никаких больших улучшений. Но теперь, что произойдет, если параметр status равен нулю или пустое?
Post.by_status(nil).recent
# SELECT "posts".* FROM "posts" WHERE "posts"."status" IS NULL
# ORDER BY posts.updated_at DESC
Post.by_status('').recent
# SELECT "posts".* FROM "posts" WHERE "posts"."status" = ''
# ORDER BY posts.updated_at DESC
Ооопс, я не думаю, что мы хотели разрешить эти запросы, не так ли? С областями мы можем легко исправить это, добавив условие присутствия в нашу область действия:
scope :by_status, -> status { where(status: status) if status.present? }
Там мы идем:
Post.by_status(nil).recent
# SELECT "posts".* FROM "posts" ORDER BY posts.updated_at DESC
Post.by_status('').recent
# SELECT "posts".* FROM "posts" ORDER BY posts.updated_at DESC
Высокий. Теперь попробуем сделать то же самое с нашим методом любимого класса:
class Post < ActiveRecord::Base
def self.by_status(status)
where(status: status) if status.present?
end
end
Запуск:
Post.by_status('').recent
NoMethodError: undefined method `recent' for nil:NilClass
И: bomb:. Разница в том, что область всегда будет возвращать отношение, тогда как наша простая реализация метода класса не будет. Метод класса должен выглядеть следующим образом:
def self.by_status(status)
if status.present?
where(status: status)
else
all
end
end
Обратите внимание, что Im возвращает все для случая nil/blank, который в Rails 4 возвращает отношение (он ранее возвращал Array элементов из базы данных). В Rails 3.2.x вместо этого вы должны использовать область действия. И вот мы идем:
Post.by_status('').recent
# SELECT "posts".* FROM "posts" ORDER BY posts.updated_at DESC
Итак, совет здесь: никогда не возвращайте nil из метода класса, который должен работать как область, иначе вы нарушите условие целостности, подразумеваемое областями, которые всегда возвращают отношение.
Длинная история:
Независимо от того, какие области предназначены для возврата ActiveRecord::Relation
, чтобы сделать его цепочки. Если вы ожидаете результатов first
, last
или find
, вы должны использовать class methods
Источник: http://blog.plataformatec.com.br/2013/02/active-record-scopes-vs-class-methods/