Routing parameters with a dot
Last week I received an interesting bug report for an application I'm working on at the office. It has a controller with an index action that displays a list of items which can be filtered by tags. A tester reported that every time he chooses to filter the list by a tag that contains a dot, the site returns an error. First this seems strange since tags with a dot worked perfectly fine in tests.
But after digging deeper, I found a surprising reason for this strange error: To make URLs look nicer, I defined an extra route like this:
map.things 'things/:tag', :controller => 'things', :action => 'index',
:tag => nil
The intention was, to have /things/foo
instead of /things&tag=foo
as the URL. This actually worked fine -- except for cases when the tag contained a dot.
Rails seems to magically append :format
to every rule so that different result formats are possible with one rule (like /things.html
and /things.json
). If a tag contained a dot, the URL requested was /items/foo.bar
, which Rails parsed into :tag => 'foo', :format => 'bar'
.
So the controller's index action was requested to display all entries for tag foo
and output results in format bar
-- which is unknown and unsupported and therefore resulted in an error page.
This behaviour seems to be hardcoded into Rails and I couldn't find a way to disable it. But luckily a workaround is pretty easy. The trick is to make the tag parameter's regular expression greedy, so that it covers the format part as well. Using the :requirements
option to a route, we can set the regexp for a parameter:
map.things 'things/:tag', :controller => 'things', :action => 'index',
:tag => nil, :requirements => { :tag => /.+/ }
Now it works as intended. :format
is always ignored now and the whole string is put into params[:tag]
.