There's an issue with default_scopes, where :conditions set there can't be overridden by other scopes.
In short, if you have a model with a boolean :active and a default_scope:
default_scope :conditions => { :active => true }
Then this scope will not work:
named_scope :inactive, :conditions => { :active => false }
The DB query that gets executed keeps the default_scope value of :active. Note that non-overlapping keys in :conditions will be merged correctly.
Original post on rails-talk:
http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/43cc596a2d62f07b
Lighthouse ticket:
https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/2953
Can somebody more knowledgeable about named_scope and friends take a look?
Thanks,
--Matt Jones
I haven’t been tracking 2.3.3 or 3.x so maybe this is some new magic that I’m unaware of…
But in case it’s not, my understanding is that the only way to get the default_scope not to do what it does is to use with_exclusive_scope - this explicitly tells Rails to ignore existing scopes (including the default).
I’ve always assumed that this is intentional, and I think that default_scope should be difficult to exclude.
Maybe there’s room to have exclusive named_scopes (:exclusive => true ?) - but I definitely vote for something really explicit for when you’re intentionally overriding a default scope.
Can somebody more knowledgeable about named_scope and friends take a
look?
The problem is that the current scope merging code is implemented from
the perspective of merging *arguments to find*, whereas what people
sometimes want (as is the case here) is merging relations / sets. A
simpler example contrasting the two differences comes from order. If
you want to merge :order=>"first_name " and :order=>"last_name ",
there are two different answers and both make sense.
If you're talking about sorting sets then order declarations are last in wins:
@some_list.sort_by(&:first_name).sort_by(&:last_name)
The ordering by the first_name is completely irrelevant. But if you
think about it from an SQL perspective then you probably want:
SELECT * FROM `some_lists` ORDER BY first_name, last_name.
Which is pretty drastically different. Perhaps miloops can let us
know what's coming in ARel's branch?
Having said all that, don't use defaut_scope unless you want it
basically everywhere. The only way you can turn it off is with a
class method which calls with_exclusive_scope.
Perhaps the name “default_scope” is a little misleading, implying that this scope is the default unless you pick something different. Another name like base_scope, parent_scope, or master_scope could address the mismatch between expected and actual functionality.
Perhaps the name "default_scope" is a little misleading, implying that this
scope is the default unless you pick something different. Another name like
base_scope, parent_scope, or master_scope could address the mismatch between
expected and actual functionality.
FWIW I believe we took the name from you
Yeah, master or base scope would have worked better, but seems it's
not worth changing just for that.
Right, and I think that was the original poster's (from rails-talk) issue, even if it wasn't stated: there are situations where a full with_exclusive_scope is not only undesired but incorrect. For instance, this scenario:
User has_many :messages
Message has an active flag; default_scope has the :active => true condition
There's no straightforward way to get with_exclusive_scope into an expression like:
@user.messages.subject_like('foo').inactive
The issue is that most implementations that *would* allow this are unworkable because of the early conversion to string conditions.
I'll also add that I can't wait to see how the ARel stuff is going, as that would help make this a lot easier...
--Matt Jones