78

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?

kenn
  • 3,303
  • 2
  • 29
  • 42
  • Are you sure that "`all` runs the query" stuff isn't just an artifact the console? [The source](https://github.com/rails/rails/blob/8606a7fbe9367e9ae37ad058dd07f0dd38daf015/activerecord/lib/active_record/scoping/named.rb#L24) suggests that it should work just fine. – mu is too short Aug 13 '13 at 02:29
  • Yes, I'm sure. https://github.com/rails/activerecord-deprecated_finders/blob/v1.0.3/lib/active_record/deprecated_finders/relation.rb#L161-L169 – kenn Aug 13 '13 at 04:11
  • But you're not getting that warning so you're getting the `all` from `scoping/named.rb`, right? And the `all` from `scoping/named.rb` is, AFAIK, what `Model.all` uses. – mu is too short Aug 13 '13 at 04:16
  • Huh? https://github.com/rails/rails/blob/v4.0.0/activerecord/activerecord.gemspec#L28 – kenn Aug 13 '13 at 04:18
  • Hmm. You are getting that warning. Sigh, WTF big pile of confused nonsense is Rails up to now. Is it still shark week 'cuz I'm thinking that Rails4 has jumped it. – mu is too short Aug 13 '13 at 04:19
  • lol yeah. I just need something better than where(nil) for that. – kenn Aug 13 '13 at 04:21
  • So the problem is `.all.all` rather than just `.all`. Perhaps a shameful `is_a? ActiveRecord::Relation` check would make the pain go away. I feel dirty, I think I have to go wash my brain out with beer now. – mu is too short Aug 13 '13 at 04:24
  • Came up to same problem, looks like it's rails bug – welldan97 Aug 20 '13 at 10:01
  • @kenn Did you create an issue for this in rails' bugtracker? – gucki Nov 04 '13 at 09:06
  • 1
    https://github.com/rails/rails/issues/12756 – kenn Nov 04 '13 at 16:18

4 Answers4

66

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

kenn
  • 3,303
  • 2
  • 29
  • 42
  • 1
    The deprecation warning says to use `load`. – shanemcd Sep 15 '13 at 19:56
  • it says to use `load` IF you want to eager-loading, and in any case it takes a parameter (condition), so by now `where(nil)` (or `true` or `{}` or `1`) seems to be the best replacement of `scoped` – ecoologic Mar 19 '14 at 07:50
  • Does not work for my case: `user.active_section.scoped.uniq(false)` works, `user.active_section.all.uniq(false)` or `user.active_section.where(nil).uniq(false)` does not. – Skully Apr 23 '14 at 14:07
  • 1
    How about rails 5? – Chamnap Aug 29 '17 at 14:50
25

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.

Olivier Lacan
  • 2,636
  • 2
  • 24
  • 22
  • 2
    Great, thanks for the update! However if you're a gem maintainer you must continue to use `where(nil)` until 4.0.x becomes unsupported... – kenn Dec 27 '13 at 00:57
  • This is a very old thread but we are upgrading only now and must maintain support for Rails 3 and 4 as well. Is it reasonable to do something along the lines of `if ActiveRecord::VERSION::MAJOR == 3 then Model.scoped else Model.all end`? – astorije Jan 12 '17 at 00:02
9

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
rwb
  • 4,309
  • 8
  • 36
  • 59
Jason Rust
  • 346
  • 4
  • 7
  • It's irrelevant - try `User.all.all;` and you get the same warning. Unfortunately it won't be fixed until Rails 4.x or even Rails 5. https://github.com/rails/rails/issues/12756 – kenn Dec 03 '13 at 00:33
5

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.

Andrew Hacking
  • 6,296
  • 31
  • 37