I recently updated my RubyGems to 0.9.2. In that upgrade,
require_gem was deprecated in favor of
gem and running Rails would generate a bunch of deprecation warnings. However, it was easy enough to fix by running
rake rails:update in the app root.
Doing the update caused us some problems, though, because we are using the cached_model gem to store objects in memcached. cached_model was required in environment.rb before the
Rails::Initializer.run block but with the new autoloading mechanism, it didn’t work anymore because the Rails classes weren’t at the disposal of cached_model before the initializer block. Instead I got all these weird errors:
$ script/console Loading development environment. /opt/local/lib/ruby/gems/1.8/gems/cached_model-1.3.1/lib/cached_model.rb:21:NameError: uninitialized constant ActiveRecord /opt/local/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/assertions/selector_assertions.rb:525:NoMethodError: undefined method `camelize' for "top":String ./script/../config/../config/../app/controllers/application.rb:1:NameError: uninitialized constant ActionController::Base
Lo and behold, the cached_model docs instruct to put the require line after the initializer block. This is the right way and should be ok in most cases. However, in our app, we also have an observer for the
User class, which is a subclass of
CachedModel. Observers are loaded inside the initializer block (
config.active_record.observers = :user_observer) and when loaded, they also initialize the actual ActiveRecord model they’re observing (in this case
User). But since
CachedModel, which is not loaded yet, we have a chicken and egg problem:
$ rake test:units rake aborted! uninitialized constant CachedModel
We can’t require cached_model before the initializer because
ActiveRecord::Base doesn’t exist then, and we can’t require it after the initializer because it would need to be initialized when the observers are loaded. What’s a man to do?
Rick Olson pointed me to a list of articles (1, 2, 3) that he recently wrote about the Rails initializing mechanism (+ a link to another useful article by Tim Lucas). This lead to a trick that solved my problem. Namely, writing a plugin.
The gist of the trick is that the loading of observers is deferred until all plugins are loaded. So I created an empty plugin called
cached_model_plugin, put the
require 'cached_model' line in its init.rb, and everything started working again. Woo-hoo!
The weird thing about this is that actually, the code in environment.rb after the
Rails::Initializer.run block should also be run before the observers are loaded. However, for whatever reason that didn’t happen in our case, so I’m happy that using a dummy plugin helped.
With the new config/initializers in Rails Edge this hack should become history, but until we update beyond 1.2.X, we’ll be camping happily with my plugin.