With Rails 4, Model.scoped is deprecated but Model.all can't replace it

Starting Rails 4, Model.scoped is now deprecated.

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

But, there's a difference inModel.scoped and Model.all, that is, scoped.scoped returns a scope, while all.all runs the query.

On Rails 3:

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

On 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

There are use cases in libraries / concerns that returns scoped when there's a conditional to do something or nothing, like so:

module AmongConcern
extend ActiveSupport::Concern


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


where(id: ids)
end
end
end

If you'd change this scoped to all, you'd face random problems depending where the among was used in the scope chain. For instance, Model.where(some: value).among(ids) would run the query instead of returning a scope.

What I want is an idempotent method on ActiveRecord::Relation that simply returns a scope.

What should I do here?

21158 次浏览

It seems that where(nil) is a real replacement of scoped, which works both on Rails 3 and 4. :(

In addition to using where(nil) you can also call clone if you know that self is a Relation and get the identical behaviour of calling scoped with no args, sans the deprecation warning.

EDIT

I am now using this code as a drop in replacement for scoped since I don't like using where(nil) everywhere I need to get hold of the current scope:

     # 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

I don't see why the AR authors couldn't have done something similar since as the OP points out all and scoped do not behave the same.

As mentioned in one of the comments, all is supposed to return a scope according to the docs.

The docs are correct -- it does return an ActiveRecord::Relation, but you have to use a semi-colon if you want to see it in the console:

pry(main)> u = User.all;


pry(main)> u.class


=> ActiveRecord::Relation::ActiveRecord_Relation_User

On Rails 4.1 (beta 1), the following works:

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

So it appears this issue has been fixed, and in 4.1.0 Model.scoped has been removed altogether.