Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

without_exclusive_scope and default_scope issues

#2953

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>'

Reported by Conrad Taylor · July 25th, 2009 @ 05:55 AM

State: stale
Milestone: 3.x
Assigned to: nobody
Importance: none

Activity

  1. Matt Jones
    Matt Jones

    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:

    MessageWithoutDefault.scoped(:conditions => { :ignored => false }).ignored
    
    ...then you get the correct behavior.

    I'm investigating and will post an update (hopefully) soon.

    July 26th, 2009 @ 12:47 AM

  2. Matt Jones
    Matt Jones

    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:

    MessageWithoutDefault.scoped(:conditions => { :ignored => false }).ignored.all
    
    executes as:
    MessageWithoutDefault.with_scope([PARAMS FOR IGNORED]) do
      MessageWithoutDefault.with_scope([PARAMS FOR IGNORED => FALSE]) do

    MessageWithoutDefault.find(:all)
    
    
    
    
    end end
    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:
    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 =&gt; FALSE]) do
      MessageWithoutDefault.find(:all)
    end
    
    
    
    
    end end
    which produces the (observed) incorrect results.

    There'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?

    July 26th, 2009 @ 03:38 AM

  3. Chuck Hoffman
    Chuck Hoffman

    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
    

    November 24th, 2009 @ 05:24 PM

  4. Jeremy Kemper
    Jeremy Kemper
    • Milestone changed from 2.x to 3.x

    May 4th, 2010 @ 06:48 PM

  5. Rohit Arondekar
    Rohit Arondekar
    • 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.

    October 9th, 2010 @ 04:01 AM

  6. bingbing