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:
- Apply http://dev.rubyonrails.org/attachment/ticket/9815/remove_inflector_route_reloading.diff
- Change the order of the plugins loaded using environment.rb
- Explicitly call {{{ActionController::Routing::Routes.reload!}}} in your plugin
If you applied the patch and found it helpful, please post a comment to http://dev.rubyonrails.org/ticket/9815 with your comments