If you’re using a model or controller from an engine in Rails 2.3.2 you may encounter some crazy errors from time to time. Errors like: A copy of ApplicationController has been removed from the module tree but is still active!
Or sometimes the even weirder one: can't dup NilClass
In one situation where you get A copy of ApplicationController has been removed from the module tree but is still active!
it could be because you’re using a plugin or engine that provides controllers and inherits from ApplicationController. Some of the classes inherited or included in your engine controllers may fail to get unloaded and cause trouble after the first request to your system. Add unloadable
inside your controller classes in your engine.
The can't dup NilClass
error really tricked me though. It seemed to be saying something about duping nil class but not really. It was a lie! Well, almost. See, I had a model in my engine like this:
class Account :destroy
attr_accessible :nickname, :email
And I had a module in my app (not in the engine) like this (sort of a reverse-micro-plugin):
module Authentication
module AccountBuddy
def self.included(account)
account.class_eval do
has_many :characters
end
end
end
end
And when I tried to access account.characters
it was all “can’t dup NilClass”. But what it really meant to say was: “I’m returning nil instead of an array containing the characters belonging to this account because I became confused during the loading and unloading of all your crazy ass models. -Love, Rails”
So adding a little unloadable
also fixes that.
class Account < ActiveRecord::Base
unloadable # :destroy
attr_accessible :nickname, :email
What a load off.
A better way
Courtesy of Paul (from the comments)
unloadable
is now deprecated, I think. As far as I am aware, the “right” way to do this is one of the following:
First, you could put something like this in your plugin’s init.rb file:
# This plugin should be reloaded in development mode.
if RAILS_ENV == 'development'
ActiveSupport::Dependencies.load_once_paths.reject!{|x| x =~ /^#{Regexp.escape(File.dirname(__FILE__))}/}
end
Second, you could put something like this in your application’s environment.rb file:
config.reload_plugins = true if RAILS_ENV == ‘development’
If the above did not solve your problem:
Courtesy of Evan Owen (from the comments)
Another possible cause of this problem is that ActiveRecord
also defines self.included
. In order to ensure that ActiveRecord
gets the call and can finish loading the model, a call to super
needs to be added to the end of self.included
like so:
def self.included(account)
account.class_eval do
has_many :characters
end
super # fixes the "can’t dup nil" issue
end
This did not work in my specific case, but may be the correct solution if you are suffering from a similar but different problem.