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

ActiverecordRuby on-Rails-4

Activerecord Problem Overview


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?

Activerecord Solutions


Solution 1 - Activerecord

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

Solution 2 - Activerecord

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.

Solution 3 - Activerecord

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

Solution 4 - Activerecord

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkennView Question on Stackoverflow
Solution 1 - ActiverecordkennView Answer on Stackoverflow
Solution 2 - ActiverecordOlivier LacanView Answer on Stackoverflow
Solution 3 - ActiverecordJason RustView Answer on Stackoverflow
Solution 4 - ActiverecordAndrew HackingView Answer on Stackoverflow