Add routes with a rails plugin or gem
It is possible to define routes in a ruby on rails plugin or gem. Normally, adding routes looks like this:
ActionController::Routing::Routes.draw do |map|
map.connect ':controller/:action/:id'
end
The problem is that the draw method clears the existing routes before adding the new ones (Ruby on Rails 1.2.3: routing.rb):
def draw
clear!
yield Mapper.new(self)
named_routes.install
end
The plugins and gems are loaded first, so any new routes are cleared when config/routes.rb is loaded. One solution is to redefine the clear! method to do nothing:
class << ActionController::Routing::Routes;self;end.class_eval do
define_method :clear!, lambda {}
end
The final result should be included in the plugin or gem:
class << ActionController::Routing::Routes;self;end.class_eval do
define_method :clear!, lambda {}
end
ActionController::Routing::Routes.draw do |map|
map.connect 'newurl', :controller => 'plugin_controller', :action => 'some_action'
end
5 comments
Comments
-
Doesn't it seem kind of excessive to change clear!? Wouldn't it be better to add a new method to Routes - something called add_routes, that does exactly the same thing as draw, except for not calling clear!.
-
Hi Paul, As I explained in my blog post, I guess a cleaner option might be to just include your items and then yield a mapper, so other methods keep doing their mapping. Something like this: #used to allow proper Theming. def draw begin clear! mapper = Mapper.new(self) create_theme_routes(mapper) yield mapper named_routes.install rescue raise end end def create_theme_routes(map) map.named_route 'theme_images', "/themes/:theme/images/*filename", :controller=>'theme', :action=>'images' map.named_route 'theme_stylesheets', "/themes/:theme/stylesheets/*filename", :controller=>'theme', :action=>'stylesheets' map.named_route 'theme_javascript', "/themes/:theme/javascript/*filename", :controller=>'theme', :action=>'javascript' map.connect "/themes/*whatever", :controller=>'theme', :action=>'error' end That worked very well for me. Hope it helps! :) I didn´t try it out with multiple redefinitions by several plug-ins or gems though. I´m kinda newbie anyway! :)
-
Sorry, it didn´t came out very well. I guess if you´d like to check the code you´d rather check my blog post at http://manicprogrammer.com/cs/blogs/heynemann/archive/2007/09/21/theme-support-in-rails-and-a-wild-community.aspx Cheers,
-
My goal with this code is to write a plugin that just works out of the box with its own routes. There are definitely cleaner ways to monkey patch the routes, but then the user would have to change their routes.rb file. With my solution, a user does not have to do anything beyond installing the plugin.
-
There has been discussion about this for a while on the engines-developers list. Most recently here: http://www.ruby-forum.com/topic/126316#563328 My feelings are that it's a mistake to *automatically* include routes from a plugin, because there's no way you can be sure where those routes should go in the order of precidence. If you put your automagic routes at the top, there's no way the user can override any of your plugin routes. If you add them at the bottom, then it's possible the applications own routes will be so generic that your plugin routes will never be reached... either way, it's not going to work well in general cases. The solution I proposed (and implemented) was that the user (read: developer) can easily include routes from a plugin at any point within their routes file, via a new method (map#from_plugin). I think this gives a good tradeoff between ease of use and flexibility in different applications. As I mention in the thread linked above, I don't think that "zero end user intervention" is the best goal to aim for when developing plugins that are "rich".