Adding patches to Rails now that it's on git

Posted by jeff

It took me a while to figure out how to try to contribute to Rails now that it’s on Lighthouse / Github. Here’s what I do now, and it seems to work:

First, create a clone of the main git repository (not your fork of it – there’s really no reason to fork unless you want others to pull your changes before core accepts them):

Setup your development directories

mkdir rails
cd rails
mkdir patches
git clone git://github.com/rails/rails.git source

Now you have:

rails
|--patches
`--source

Set up your dev branch

Then create a new branch where you’ll store just the changes you make for this patch:

git checkout -b your_patch_name master

Create the patch

Make your test-driven changes and when you are ready to create the patch run:

git diff -p master > ../patches/your_patch_name.patch

Setup a throwaway test branch

If you want to test your change

  • create a new branch and apply the patch (this is useful if other commits have happened since you first created your patch)
  • run the tests and make sure that everything passes
  • clear your changes and delete the test branch
git checkout master
git pull
git checkout -b your_patch_name_test master
git apply ../patches/your_patch_name.patch
git stash ...
git checkout master
git stash clear
git branch -D your_patch_name

Using git stash allows you to move back to the master branch without taking your changes with you – leaving you with a clean master branch. To learn more about git stash syntax, see the git documentation

Continue development

When it’s time to update the code you put in the patch, you can just rebase from the master branch:

git co your_patch_name
git rebase master

Then you can fix whatever changes you need to fix and recreate your patch (or create a new one if your old changes were accepted).

Share you patch

The next step is to go to http://rails.lighthouseapp.com/ and create a ticket. Make sure that you

  • tag it with “patch” as well as whatever else it applies to
  • don’t forget to upload the patch itself.

When you upload, it appear in the middle of the right-hand column as a blue link – it’s hard to find, so look carefully. Then get as many people as possible to grab your changes and test them and add +1’s where necessary.

Summary

When all is said and done you are left with a directory full of patches you can apply, a clean master working copy and individual branches for all of your patches that you can maintain over time. While this was possible with subversion, it’s way cleaner with git. Contributing to rails is easier than ever!

References

New inflections un-patch to Rails core

Posted by jeff

I just posted a new un-patch to http://dev.rubyonrails.org/ticket/9815. I discovered it when I tried to run my webmaster verification plugin that I wrote along with active_merchant and I got a strange error that the route mapper extension that wrote wasn’t defined.

Several hours of digging and debugging later I found that the culprit was a call to the inflector. Rails 2.0.2 makes it difficult to add custom routes in a plugin because it alters order in which routing is initialized with respect to plugins.

In a typical situation rails will load plugins and then setup routes. This makes it possible for things like Jamis Buck’s routing tricks http://weblog.jamisbuck.org/2006/10/26/monkey-patching-rails-extending-routes-2 plugin.

Let’s say we have 2 plugins – PluginA and PluginZ – and the standard inflector.rb in config/initializers. They will be loaded by default in this order:

  • PluginA
  • PluginZ
  • Inflector
  • Routes#reload (the oddly-named reload actually loads the routes for the first time)

Let’s say PluginA defines a custom inflection like so:

1
2
3
Inflector.inflections do |inflect|
  inflect.uncountable 'something'
end

PluginZ defines a custom route mapper, like so:

1
2
3
4
5
6
7
8
module Routing
  module MapperExtensions
    def my_custom_route
      @set.add_route("/my_route", {:controller => "my_controller"})
    end
  end
end
ActionController::Routing::RouteSet::Mapper.send :include, Routing::MapperExtensions

One would assume that it would:

  • call PluginA’s inflector
  • mixes in PluginZ’s route mapper
  • install the routes

However, because of the first patch that was accepted in http://dev.rubyonrails.org/ticket/9815, what actually happened is that rails:

  • calls PluginA’s inflector
  • loads the routes via Routes#load! without mixing in PluginZ’s mapper
  • mixes in PluginZ’s route mapper

So adding custom routes was brittle. If you run the app above with map.my_custom_route in routes.rb it fails, but in a very non-obvious way. The presence of an Inflector block caused a custom-mapped route to be undefined – not exactly a walk in the park to find.

If you found this while searching for a fix to this problem, there are 3 possible solutions:

If you applied the patch and found it helpful, please post a comment to http://dev.rubyonrails.org/ticket/9815 with your comments