This project is archived and is in readonly mode.
without_exclusive_scope and default_scope issues
Reported by Conrad Taylor | July 25th, 2009 @ 05:55 AM | in 3.x
I'm seeing the following issues with both without_exclusive_scope and default_scope in Rails 2.3.3. For example, given the following:
class Message < ActiveRecord::Base
  
  default_scope :conditions => { :ignored => false }, :order => "rating DESC"
  named_scope :positive, :conditions => { :rating => 'positive' }
  named_scope :neutral, :conditions =>  { :rating => 'neutral' }
  named_scope :negative, :conditions => { :rating => 'negative' }
  named_scope :ignored, :conditions =>  { :ignored => true }
  
end
In the console,
> Message.ignored
=>  SELECT * FROM "messages" WHERE ("messages"."ignored" = 'f') ORDER BY rating DESC
Next, when trying to use the 'with_exclusive_scope method, the following message is generated:
>> Message.with_exclusive_scope { find( :all ) }
NoMethodError: protected method `with_exclusive_scope' called for #<Class:0x793538>
    from /opt/local/lib/ruby/gems/1.9.1/gems/activerecord-2.3.3/lib/active_record/base.rb:1959:in `method_missing'
    from (irb):9
    from /opt/local/bin/irb:12:in `<main>'
Comments and changes to this ticket
- 
        

Matt Jones July 26th, 2009 @ 12:47 AM
As to the second half, with_exclusive_scope was made protected along with with_scope a while back. You're probably better off wrapping the with_exclusive_scope call inside a model method.
There's definitely something weird going on with the first case - if you create a second model class, identical except for the default scope and try:
...then you get the correct behavior.MessageWithoutDefault.scoped(:conditions => { :ignored => false }).ignoredI'm investigating and will post an update (hopefully) soon.
 - 
        

Matt Jones July 26th, 2009 @ 03:38 AM
On further investigation, here's the situation:
Chaining two named_scopes does something (somewhat) unexpected - they nest in the reverse order.
In other words, this:
executes as:MessageWithoutDefault.scoped(:conditions => { :ignored => false }).ignored.all
MessageWithoutDefault.with_scope([PARAMS FOR IGNORED]) do MessageWithoutDefault.with_scope([PARAMS FOR IGNORED => FALSE]) do
MessageWithoutDefault.find(:all)
would if with_scope was public. The outer with_scope parameters take precedence. This works fine normally, but adding a default_scope is equivalent to this:
end end
MessageWithoutDefault.with_scope([PARAMS FOR DEFAULT SCOPE]) do # NOTE: not actually executed - default scope is inserted differently MessageWithoutDefault.with_scope([PARAMS FOR IGNORED]) do
MessageWithoutDefault.with_scope([PARAMS FOR IGNORED => FALSE]) do MessageWithoutDefault.find(:all) end
which produces the (observed) incorrect results.
end endThere's a bigger issue here, though; with_scope tends to flatten Hash conditions to strings, making it hard to override :conditions in default_scope. For instance, doing this:
Message.ignored.scoped(:conditions => ['rating LIKE ?', 'p%'])
=> SELECT * FROM "messages" WHERE ((("messages"."ignored" = 'f') AND (rating LIKE 'p%')) AND ("messages"."ignored" = 't')) ORDER BY rating DESC
which will never return any records. The issue here is that the first part of the query (before the last AND) is generated as a string by merge_conditions, so there's no chance to override it.
Maybe some of the ActiveRelation stuff going on in the GSoC will help with this?
 - 
            
        

Chuck Hoffman November 24th, 2009 @ 05:24 PM
I seem to be experiencing this same issue. Here's a description I posted earlier at https://gist.github.com/ea8322b9c7383835101c
class MyModel < ActiveRecord::Base belongs_to :parent_model # ... default_scope :conditions => { :is_hidden => false } named_scope :primary, :conditions => { :is_primary => true }, :order => "position" named_scope :secondary, :conditions => { :is_primary => false }, :order => "position" p = ParentModel.first p.my_models #<-- works fine p.my_models.primary #<-- works fine p.my_models.secondary #<-- works fine # what I need is a finder method/scope to find on the condition { :is_hidden => true } # preferably one that also works through the belongs_to :parent association. # here are some things I've tried: named_scope :hidden, :conditions => { :is_hidden => true } # p.my_models.hidden seems to end up acting identically to p.my_models.secondary in this # case. Might also be the case if called on the class as MyModel.hidden # (see https://rails.lighthouseapp.com/projects/8994/tickets/2953-without_exclusive_scope-and-default_scope-issues) def self.find_with_hidden(*args) self.with_exclusive_scope { find(*args) } end # here I'm adapting the find_with_destroyed method shown on # http://blog.semanticart.com/using_default_scope_to_recreate_acts_as_paranoid/ # unfortunately, it won't work correctly through an association: calling # p.my_models.find_with_hidden(:all) returns all MyModels, not just those that # belong to p -- in other words, with_exclusive_scope is TOO exclusive because # it loses the association scope. # But if I try passing the above more conditions to get the results I want, I get errors: MyModel.find_with_hidden(:conditions => { :parent_model_id => 1 }) # gets this error: # ActiveRecord::RecordNotFound: Couldn't find MyModel without an ID # from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:1567:in `find_from_ids' # from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:616:in `find' # from /home/chuck/projects/diyseo/app/models/page_keyword.rb:34:in `find_with_business_name' # from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2143:in `with_scope' # from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2151:in `with_exclusive_scope' # from /home/chuck/projects/diyseo/app/models/page_keyword.rb:34:in `find_with_business_name' MyModel.find_with_hidden(:conditions => { :is_hidden => true }) # ActiveRecord::RecordNotFound: Couldn't find MyModel without an ID # from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:1567:in `find_from_ids' # from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:616:in `find' # from /home/chuck/projects/diyseo/app/models/page_keyword.rb:34:in `find_with_business_name' # from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2143:in `with_scope' # from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.4/lib/active_record/base.rb:2151:in `with_exclusive_scope' # from /home/chuck/projects/diyseo/app/models/page_keyword.rb:34:in `find_with_business_name' # Likewise if I try calling the above from an instance method on MyModel like this: def self.hidden_for(parent) MyModel.find_with_hidden(:conditions => { :parent_model_id => parent.id, :is_hidden => true }) end # calling: p.my_models.hidden_for(p) # or: MyModel.hidden_for(p) # both result in the "Couldn't find MyModel without an ID" error above - 
        

 - 
        

Rohit Arondekar October 9th, 2010 @ 04:01 AM
- State changed from new to stale
 - Importance changed from  to 
 
Marking ticket as stale. If this is still an issue please leave a comment with suggested changes, creating a patch with tests, rebasing an existing patch or just confirming the issue on a latest release or master/branches.
 - 
            
        

 
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>