Automatically reload Rails plugins

I’m writing this blog post in english because it seems to me that many developers still run into this issue. So now it’s up to Google to put them out of their misery…

Automatically reload Rails plugins

I just bought the PDF Plugin Patterns Enhancing Rails 2.1 by Andrew Stewart for getting in touch with best practices for writing a Rails plugin. First of all… this book is awesome. I totally agree to Chris O’Sullivan:

“Bought the [Plugin Patterns] PeepCode PDF…and I gotta say I’m loving it. It’s very well written, and you explain complicated nasty stuff with nice simple examples.”

After reading this book within an hour I started to develop my own plugin acts_as_wizard to make ActiveRecord models suitable for multi-step-forms (aka „Wizards“). I immediately noticed that I had to reload Rails manually every time I changed the plugin code because plugin paths are added to the ActiveSupport::Dependencies.load_once_paths by default. Witold Rugowski suggests adding the plugin to ActiveSupport::Dependencies.explicitly_unloadable_constants and deleting the path from ActiveSupport::Dependencies.load_once_paths or force reloading with ActiveSupport::Dependencies.load_file from a controller.

The wonderful Peepcode book offers a quite easier way…

Reloadable Plugins
If you have done any plugin development at all, you’ll know that changes don’t get picked up until you restart the server. It is tedious.
Happily for us, Rails 2.1 introduced the ability for files in plugins to be reloaded like the rest of the application. To make all plugins reload- able, add the following to your application’s configuration:
You can also switch this on and off per plugin. See the commit log (http://github.com/rails/rails/commit/cc2d6a0b93251fce06bab15c52dbe0a5d5a8342c) for details.

I gave it a try and added config.reload_plugins = true to my environments/development.rb.
It worked – but not for my „acts_as_plugin“. So I wrote an email to the author.

I just bought Plugin Patterns Enhancing Rails 2.1 by Andrew Stewart today.
I’ve a question concerning the following statement on page 95.
In fact it seems that config.reload_plugins = true does not work for acts_as-Plugins, which extend ActiveRecord because ActiveRecord won’t be reloaded anyway (because it’s core part of rails).

Maybe the author has a tip… googling around and trying out did not solve my problem. To be honest… I bought the Peepcode book to avoid this ;-)

After two days I got response from Geoffrey Grosenbach, Mr. Peepcode himself. Cool. And it did the trick. Even cooler.

Here’s another strategy that will definitely work, but requires a bit
of initial configuration.

Summary: Use either unicorn or nginx and Phusion Passenger with my
rstakeout script to automatically restart Passenger every time your
plugin changes.

Unicorn (easy, but untested):

Download my rstakeout.rb script. A copy is here:

http://github.com/EdvardM/rstakeout

Install the unicorn server and start it in your Rails app:

unicorn_rails

Note the PID (“spawned pid=15250″)

In a separate terminal window, run rstakeout.rb, telling it to restart
Unicorn by sending USR2 to unicorn whenever a file in your plugin is
saved:

rstakeout.rb “kill -USR2 15250″ “vendor/plugins/YOUR_PLUGIN/**/*.rb”

Or, a more complicated way (nginx and passenger):

First, install nginx and passenger. Pat Allan has a detailed summary
for running it from inside a Rails app directory here:

http://freelancing-gods.com/posts/script_nginx

If you use his technique, you can start nginx in any Rails directory.

Start nginx (either preconfigured, or with the Passenger Preference
pane if you’re on Mac OS X, or with Pat’s script).

Finally, run this command in your Rails app’s directory to watch the
plugin and restart Passenger:

rstakeout.rb “touch tmp/restart.txt” “vendor/plugins/YOUR_PLUGIN/**/*”

Now, any time you edit a file in your plugin’s directory, Passenger
will be restarted and you’ll be working with the updated code.

Yes, a bit complicated, but I tried something similar here and it did the trick.

Let me know if you try it!

I can confirm that it works. I’m using Passenger and Apache for development on Mac OS X 10.6.2. Great. I just fetched a copy of rstakeout, put it to /usr/local/bin and ran rstakeout.rb “touch tmp/restart.txt” “vendor/plugins/acts_as_wizard/**/*.rb” from my rails application directory. For testing purposes I added a comment to the plugin’s init.rb and rstakeout immediately noticed it.
=> vendor/plugins/acts_as_wizard/init.rb changed, running ‘touch tmp/restart.txt’

By the way… if you install the ruby-growl gem and Growl you’ll get a Fancy-schmancy notification everytime you change something.

Leave a Reply