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

Comments

Leave a response

Comment