Saturday, 19 June 2010

Problems with class_inheritable_accessor and class reloading in Rails

I was having some very odd issues with WEBrick having odd ActiveRecord errors for the second and subsequent page views; the first page I loaded always worked, then other requests fail with errors like the following:

TypeError in ListsController#show

can't dup NilClass

RAILS_ROOT: /home/russ/git_repos/myrecipecart
Application Trace | Framework Trace | Full Trace

/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/base.rb:2220:in `dup'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/base.rb:2220:in `scoped_methods'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/base.rb:2177:in `with_scope'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/base.rb:2187:in `with_exclusive_scope'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/association_preload.rb:368:in `find_associated_records'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/association_preload.rb:255:in `preload_has_many_association'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/association_preload.rb:120:in `block in preload_one_association'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/association_preload.rb:114:in `each'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/association_preload.rb:114:in `preload_one_association'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/association_preload.rb:91:in `preload_associations'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/association_preload.rb:90:in `block in preload_associations'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/association_preload.rb:90:in `each'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/association_preload.rb:90:in `preload_associations'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/base.rb:1581:in `find_every'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/base.rb:1614:in `find_one'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/base.rb:1600:in `find_from_ids'
/home/russ/git_repos/myrecipecart/vendor/rails/activerecord/lib/active_record/base.rb:619:in `find'
/home/russ/git_repos/myrecipecart/app/models/list.rb:79:in `block in find_for_show'
/home/russ/git_repos/myrecipecart/app/models/list.rb:92:in `block in with_scope_for_show'
/home/russ/git_repos/myrecipecart/app/models/list.rb:91:in `with_scope_for_show'
/home/russ/git_repos/myrecipecart/app/models/list.rb:78:in `find_for_show'
/home/russ/git_repos/myrecipecart/app/controllers/lists_controller.rb:45:in `show'

After seeing a few different callstacks, it seemed to be that the problem was that class instance variables were suddenly becoming nil in some derived classes. For the callstack above, it was this one, from active_record/base.rb:

    # Stores the default scope for the class
    class_inheritable_accessor :default_scoping, :instance_writer => false
    self.default_scoping = []

I still haven't tracked down what is causing the problem, but I have found a workaround that helps me get on with my work; re-enable class caching in WEBrick development mode:

#config/environments/development.rb
...
config.cache_classes = true

The result of this is that application classes are not reloaded with every request, and as a result I don't see the wierd crashes. It is annoying that I then have to restart WEBrick by hand whenever I need it to update the running code, but at least I can test behaviour that requires more than one HTTP request to complete!

If anyone can tell me why I'm seeing these problems with class_inheritable_accessor, I'd appreciate it! I have some supicions (my class definition for List includes a module that dynamically defines some other classes derived from ActiveRecord::Base), but nothing concrete just yet.

No comments:

Post a Comment