Jan 13, 2007
On our way to a more RESTful Slamdot, we ran into a slight snag with the way Rails’ routes were being generated by Resources. We needed to be able to pass the user’s currently active domain name as a part of the URL, which turned out to be a little more difficult than we had hoped.
According to the Rails 1.2 RC1 release notes, the solution was simple:
Action Pack has an all new implementation of Routes that’s both faster and more secure, but it’s also a little stricter. Semicolons and periods are separators, so a /download/:file route which used to match /download/history.txt doesn’t work any more. Use :requirements => { :file => /.*/ } to match the period.
Only not so simple. At the time of this writing, resources don’t allow for a :requirements option to be passed on to the routes being generated. So, using Jamis Buck‘s incredibly-useful articles about routing, we were able to monkey-patch Routing to do exactly what we needed.
module Slamdot
module Routing
module DSL
module MapperExtensions
def self.included(base)
base.alias_method_chain :connect, :domain
base.alias_method_chain :named_route, :domain
end
def connect_with_domain(path, options = {})
options[:requirements] = { :domain => /.*/ } if path.include?(':domain')
connect_without_domain(path, options)
end
def named_route_with_domain(name, path, options = {})
options[:requirements] = { :domain => /.*/ } if path.include?(':domain')
named_route_without_domain(name, path, options)
end
end
end
end
end
ActionController::Routing::RouteSet::Mapper.send :include, Slamdot::Routing::DSL::MapperExtensions
We make use of the alias_method_chain idiom to wrap our custom connect_with_domain and named_route_with_domain methods around Rails’ default behavior. So now, when our routes are being generated, we intercept them and inject our :requirements option (if necessary). Then, we sneakily hand the route construction back over for completion.
That’s all for now!