For one of my customers I was developing API, wrapped around some older service. This API was meant to be used by new application developed (using RoR of course) by customer itself. Since I was only wrapping old code I could finish quite soon, before main application was ready.
The problem was that API was not defined in every detail upfront. Since I did plain wrapping I did keep old business logic and flow with this API, and new application was not thinking the same way. This was seen before so we have agreed that integration will be done by me and we needed simple way to hook into new application with my integration code. One of obvious ways to achieve it is to use Rails plugin to keep code separated.
Things went OK but I want to share one thing I have learned. Using Rails plugin to wrap code is good idea with one issue – code reloading. Plugin code is not reloaded in development mode (like it is done with controllers or models) so changes in plugin will be visible after application restart. Not very comfortable for development…
Being lazy and didn’t want to restart application with every change I make, I have started search for some workaround:
In module Dependencies
(ActiveSupport::Dependencies
in Rails 2.2, Dependencies
in main namespace in Rails 2.1 and before) is a little, undocumented attribute explicitly_unloadable_constants
, holding array of constants names which will be reloaded with each Rails request. So we just need to add our plugin module name to this array. In config/environments/development.rb
add:
ActiveSupport::Dependencies.explicitly_unloadable_constants << 'MyPlugin'
But with plugin You must allow for that. Rails keeps list of paths where reloading is forbidden by default - it is Dependencies.load_paths_once
. All plugin directories are on this list. So get rid of this (code for Rails 2.2 - Dependencies scoping):
["my_plugin"].each do |plugin_name| reloadable_path = RAILS_ROOT + "/vendor/plugins/#{plugin_name}/lib" ActiveSupport::Dependencies.load_once_paths.delete(reloadable_path) end
Now with each request your plugin will be reloaded (but keep in mind that if you are requiring other files from this plugin, they may not be reloaded - require
does keep own list of already loaded paths)
To be honest, this method I have learned from post on Bill Harding's blog, earlier I was just using Dependencies.load_file
(and plugin directory removal from load_once_paths
) to force reload my plugin:
before_filter :force_plugin_reload def force_plugin_reload ActiveSupport::Dependencies.load_file "my_plugin.rb" if "development" == RAILS_ENV end
Apparently explicitly_unloadable_constants
is much more elegant solution. When do You want to reload something in lib
directory You don't have to remove anything from load_once_paths
- just add Your module name to .explicitly_unloadable_constants
.
Maybe You will find this useful - or do You have other ways how to reload plugins in development mode?
Leave a Reply