zargony.com

#[desc = "Struggling through software development and server administration"];

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].