С Rails 4 Model.scoped устарел, но Model.all не может его заменить

Запуск Rails 4, Model.scoped теперь устарел.

DEPRECATION WARNING: Model.scoped is deprecated. Please use Model.all instead.

Но существует разница в Model.scoped и Model.all, то есть scoped.scoped возвращает область, а all.all запускает запрос.

В Rails 3:

> Model.scoped.scoped.is_a?(ActiveRecord::Relation)
=> true

В Rails 4:

> Model.all.all.is_a?(ActiveRecord::Relation)
DEPRECATION WARNING: Relation#all is deprecated. If you want to eager-load a relation, you can call #load (e.g. `Post.where(published: true).load`). If you want to get an array of records from a relation, you can call #to_a (e.g. `Post.where(published: true).to_a`).
=> false

В библиотеках/проблемах есть примеры использования, которые возвращают scoped, когда есть условие сделать что-то или ничего, например:

module AmongConcern
  extend ActiveSupport::Concern

  module ClassMethods
    def among(ids)
      return scoped if ids.blank?

      where(id: ids)
    end
  end
end

Если вы измените этот scoped на all, вы столкнетесь с случайными проблемами, зависящими от того, где among использовался в цепочке областей видимости. Например, Model.where(some: value).among(ids) будет запускать запрос вместо возврата области.

Я хочу использовать метод idempotent на ActiveRecord::Relation, который просто возвращает область.

Что мне здесь делать?

Ответ 1

Кажется, что where(nil) является реальной заменой scoped, которая работает как на Rails 3, так и на 4.: (

Ответ 2

В Rails 4.1 (beta 1) работает следующее:

Model.all.all.is_a?(ActiveRecord::Relation)
=> true

Итак, похоже, эта проблема исправлена, а в 4.1.0 Model.scoped полностью удалена.

Ответ 3

Как упоминалось в одном из комментариев, all должен возвращать область в соответствии с документами.

Документы верны - он возвращает ActiveRecord:: Relation, но вам нужно использовать полуточку, если вы хотите увидеть ее в консоли:

pry(main)> u = User.all;

pry(main)> u.class

=> ActiveRecord::Relation::ActiveRecord_Relation_User

Ответ 4

В дополнение к использованию where(nil) вы также можете вызвать clone, если вы знаете, что self является отношением и получает идентичное поведение вызова scoped без аргументов, без предупреждения об устаревании.

ИЗМЕНИТЬ

Теперь я использую этот код в качестве замены для замены scoped, так как мне не нравится использовать where(nil) всюду, чтобы получить текущую область видимости:

     # config/initializers/scoped.rb
     class ActiveRecord::Base
       # do things the modern way and silence Rails 4 deprecation warnings
       def self.scoped(options=nil)
         options ? where(nil).apply_finder_options(options, true) : where(nil)
       end
     end

Я не понимаю, почему авторы AR не могли сделать что-то подобное, так как OP указывает all и scoped do not вести себя одинаково.