Reputation: 32355
I'm working on a gem that sets properties on ActiveRecord models (such as table_name
) dynamically based on a user config option.
I have an initializer that achieves this. My problem however is that in dev mode, these classes are reloaded, so they don't maintain these values set.
So I thought I'd use a railtie to hook into the point where these files are reloaded and run my config again on the models. My problem however is that config.to_prepare
in the railtie appears to run before the reload!
actually takes place. I can prove this with a bit of logging:
module MyMod
class Railtie < Rails::Railtie
config.to_prepare do
Rails.logger.debug("Contact object_id: #{Contact.object_id}")
end
end
end
if I load up my console, I get the first log:
Contact object_id: 2202692040
If I check Contact.object_id
it matches up:
Contact.object_id #=> 2202692040
Then I reload!
reload!
Rails logger from my to_prepare
logs:
Contact object_id: 2202692040
So it still has the old object_id, but when I check it in the console:
Contact.object_id #=> 2197355080
Which is the newly loaded class object id.
So how do I get to_prepare
to run after the files are reloaded? Using Rails 3.0.10
I've also tried manually attaching this action to the after_prepare
callback on the ActionDispatch::Callbacks
like so:
initializer "apartment.init" do
ActionDispatch::Callbacks.set_callback(:prepare, :after) do
Rails.logger.debug("Contact object_id: #{Contact.object_id}")
end
end
It does indeed run the callback after the config.to_prepare
but it still appears to happen before
the files are reloaded... I get the same behaviour as above.
Upvotes: 15
Views: 3665
Reputation: 486
Write an initializer that, if cache_classes
is false
, uses ActionDispatch::Reloader
to set a to_prepare
callback that runs your gem's installation routine.
initializer 'foobar.install' do
if Rails.configuration.cache_classes
FooBar.install!
else
ActionDispatch::Reloader.to_prepare do
FooBar.install!
end
end
end
It'll work both in the console with the reload!
method and in the Rack application server.
Upvotes: 18
Reputation: 9094
I believe the Rails reloader only unhooks the constants. The models are reloaded with autoloading when the constants are referenced in your app.
In your callback, I think you have to trigger the load manually by referencing all the models. Maybe your gem can keep a list of all the models that include it, then just simply reference the constants to autoload them...
model_names.each { |model_name| model_name.constantize }
You can build the list with self.included
:
module MyGem
self.included(base)
@model_names ||= Set.new
@model_names += base.to_s
end
end
Upvotes: 0