kenn
kenn

Reputation: 3343

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?

Upvotes: 78

Views: 21594

Answers (4)

Jason Rust
Jason Rust

Reputation: 346

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

Upvotes: 9

Olivier Lacan
Olivier Lacan

Reputation: 2639

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.

Upvotes: 25

Andrew Hacking
Andrew Hacking

Reputation: 6366

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.

Upvotes: 5

kenn
kenn

Reputation: 3343

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

Upvotes: 67

Related Questions