Forking Rails

rails git

Sat Jan 05 15:51:00 -0800 2008

For a while, everything that we did in Heroku to extend the functionality of Rails, or interoperate with it, has been done through extension mechanisms. Monkeypatching via plugins, use of the somewhat obscure Mongrel GemPlugin, and tweaking of the user’s Rails app files directly. (We try to avoid that last one whenever possible. Early on we did a lot of it, but more recently we’ve managed to avoid it almost entirely, much to my relief.)

All of this is quite a testament to the extensibility of Rails, Mongrel, Nginx, etc. I think it’s safe to say that we’re bending these tools in ways that go well outside the common use cases. But even the most supple reed can only bend so far. Certain areas (for example, script/generate) can’t be monkey patched through standard mechanisms. And so earlier this week I decided it was time to fork Rails.

Since I'm now a fan of Git, this turned out to be a good way to maintain our fork. Steve or Pablo will tell you how. Maintaining a parallel branch has, so far, proven to be quite easy - even fun.

Packaging up the modified version for use is just a matter of running “rake package” in each module’s subdirectory (i.e. activerecord, activesupport, railties, etc). The resulting gem is dropped into pkg/ under each module. The gem can then be copied to each user’s app server as it boots and installed with gem install, which overwrites the standard gem if it already exists. (I’m still trying to figure out a way to run the package script without building the documentation, which takes ages. I ended up commenting out the body of the generate_rails_framework_doc task as a stopgap.)

One thing I still haven’t decided on is how to maintain multiple versions. I wish that there was a syntax for environment.rb that looked something like:

RAILS_GEM_VERSION = '2.0.*'

Personally, I rarely care which minor rev I’m running - the latest version available on whatever box the app is running on is usually just fine. For Heroku apps, they should definitely use whatever the latest minor rev is. But note that setting one catch-all with an environment variable isn’t adequate, because each app should be configured to use a particular major rev, and we need to respect that. Hopefully I’ll come up with something better on this eventually.