Reputation: 2243
I ran into the class reloading issue while building a ruby gem for my Rails app.
Background
Inside the gem, I have the following class defined
module MyGem
class AppConfigBase
def app_title
"Title Missing"
end
end
end
Inside the Rails app, I created a config/my_gem.rb
file to hold some app specific overrides. (My goal is to let the user of the gem define some information, but have some defaults available in the super class.)
module MyGem
class AppConfig < AppConfigBase
def app_title
"Great App"
end
end
end
At some point inside the gem I call MyGem::AppConfig.new.app_title
and I receive the correct string. Yay!
Next, I decided to push the envelope a bit: I'd like to get autoloading working so developers don't have to restart the rails server every time they change this file.
I first tried to see if Rails was already setup to do this. I deleted the app_title
method out of AppConfig
class. When I reload the page, I still get the "Great App" string returned. :( Rails isn't reloading the config/my_gem.rb
(or anything in the config directory) so this is the expected result.
Next, I tried adding the following initializer:
if Rails.env.development?
ActionDispatch::Callbacks.after do
load "#{Rails.root}/config/my_gem.rb"
end
end
The above initializer partially solves the problem but I'd like to understand why it doesn't completely solve the problem. With that initializer in place,
app_title
method from the subclass, I still get the wrong string (the one in the subclass method...which shouldn't exist anymore).So when I add a method, the load call in the initializer appears to pickup the new method. However, if I delete a method, it doesn't seem to help.
config/my_gem/my_gem.rb
and then add config/my_gem
to the autoloads path in Rails, but I'd prefer not to create this extra directory just get something autoloaded.)Upvotes: 3
Views: 424
Reputation: 62668
This isn't quite how autoloading works. Rails' reloader (which is different from, but dependent on the autoloader mechanism) works by undefining constants, then recreating them by requiring the files that define them. What you really want here is to mark the constant unloadable. In your override:
module MyGem
class AppConfig < AppConfigBase
unloadable
def app_title
"Great App"
end
end
end
This will add the constant to a list of constants to be unloaded per-request, but it will also expect that it can load the constant by loading my_gem/app_config.rb
and expecting it to be on your load paths. For this reason, this kind of overriding typically happens in lib/
rather than in config/
, as the former is in your load path while the latter is not. It also means that the constant probably isn't going to be required on any request except those for which it is used.
It's conventionally expected that changing files in config
will require a restart to apply.
Upvotes: 1