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.

Default scope and class reloading

#5497

This crashes in Rails 3.0.0 (rc, rc1 and final). Clone this source to get a clean project that reproduces the bug: http://github.com/josei/default_scope_bug

Bascially, the project considers the following models:

class Conversation < ActiveRecord::Base
  has_many :readings
  has_many :users, :through => :readings
  default_scope order('conversations.updated_at desc')
end
class Reading < ActiveRecord::Base
  belongs_to :user
  belongs_to :conversation
end
class User < ActiveRecord::Base
  has_many :readings
  has_many :conversations, :through => :readings
end

Perform these steps to reproduce the bug:

$ rake db:migrate

$ rails c

irb(main):001:0> User.create :name=>'Joe'
=> #<User id: 1, name: "Joe", created_at: "2010-08-30 12:25:41", updated_at: "2010-08-30 12:25:41">
irb(main):002:0> User.first.conversations.create :title=>'New conversation'
=> #<Conversation id: 1, title: "New conversation", created_at: "2010-08-30 12:25:55", updated_at: "2010-08-30 12:25:55">
irb(main):003:0> User.first.conversations.find(Conversation.first.id).users.first.is_a?(User)
=> true
irb(main):004:0> reload!
Reloading...
=> true
irb(main):005:0> User.first.conversations.find(Conversation.first.id).users.first.is_a?(User)
=> false

You can also check object_ids for class before reloading and after; it looks like there's a problem in class versioning. The line that causes the problem is default_scope at conversation.rb. You can try commenting out that line and repeating the last steps to check it now works:

$ rails c

irb(main):001:0> User.first.conversations.find(Conversation.first.id).users.first.is_a?(User)
=> true
irb(main):002:0> reload!
Reloading...
=> true
irb(main):003:0> User.first.conversations.find(Conversation.first.id).users.first.is_a?(User)
=> true

This is not a problem with class caching disabled, but is annoying in development mode. It seems that default_scope breaks class reloading and something related with @reflection in association_proxy.rb.

Reported by Jose · August 30th, 2010 @ 01:59 PM

State: resolved
Milestone: 3.0.2
Assigned to: Xavier Noria Xavier Noria
Importance: High

Activity

  1. Andrew White
    Andrew White
    • Milestone cleared.
    • State changed from new to open
    • Assigned user set to Andrew White
    • Importance changed from to High

    Tricky one to track down this. It turns out that AR stores the default scope in a Thread.current variable. This keeps a copy of the class the first time it is loaded and when it's unloaded after each request it becomes stale.

    A quick fix is to use the deprecated hash syntax for order:

      default_scope :order => 'conversations.updated_at desc'
    

    September 1st, 2010 @ 12:23 PM

  2. Jose
    Jose

    The quick fix doesn't change the behaviour, at least in my case (Rails 3.0.0 + Ruby 1.8.7).

    September 1st, 2010 @ 12:46 PM

  3. Andrew White
    Andrew White

    Yes, you're right - sorry I thought I'd checked it worked.

    Add this block to config/environment/development.rb and it should work even with the new syntax:

      config.to_prepare do
        Thread.current.keys.each{ |k| Thread.current[k] = nil if k.to_s =~ /_scoped_methods$/ }
      end
    

    Basically, it clears out all the cached default scope on each request for all models. This only matters in development so keep it inside development.rb until 3.0.1 gets released.

    September 1st, 2010 @ 01:08 PM

  4. Jose
    Jose

    Brilliant, it works :)

    September 1st, 2010 @ 01:43 PM

  5. Andrew White
    Andrew White

    Patch for master which adds a callback to ActiveSupport::Dependencies.remove_unloadable_constants! in one commit and then uses that feature to reset the default scope when the class is unloaded in a second commit.

    September 1st, 2010 @ 03:18 PM

  6. Andrew White
  7. Andrew White
    Andrew White
    • Assigned user changed from Andrew White to Xavier Noria

    September 1st, 2010 @ 03:22 PM

  8. Andrew White
  9. Andrew White
  10. Repository
    Repository
    • State changed from open to resolved

    (from [aefa11be11a0a6b0c98b776c9e6422b2e0e4b13d]) Reset default scope in Thread.current when class is unloaded [#5497 Default scope and class reloading state:resolved]

    Signed-off-by: Xavier Noria fxn@hashref.com
    http://github.com/rails/rails/commit/aefa11be11a0a6b0c98b776c9e6422...

    September 1st, 2010 @ 09:58 PM

  11. Repository
  12. Jeremy Kemper