views:

303

answers:

2

So we've got a legacy system that tracks places with IDs like "Europe/France/Paris", and I'm building a Rails facade to turn this into URLs like http:// foobar/places/Europe/France/Paris. This requirement is not negotiable, the number of possible levels in unlimited, and we can't escape the slashes.

Setting up routes.rb for http://foobar/places/Europe is trivial:

map.resources :places

...but http:// foobar/places/Europe/France complains "No action responded to Europe". I tried:

map.connect '/places/:id', :controller => 'places', :action => 'show'

...but this gives the same result, as apparently the :id ends at the first '/'. How do I make the ID cover anything and everything after the "places"?

+4  A: 

Have a look at the Routing Guide for full documentation:

http://guides.rubyonrails.org/routing.html

Specifically section "4.9 Route Globbing".

But I think what you really want to do is declare your route like:

map.connect '/places/*id', :controller => 'places', :action => 'index'

Called with a URL like

/places/foo/bar/1

Yields a params[:id] => ["foo", "bar", "1"]

Which you could easily (re)join with "/" to yield the full string you want "foo/bar/1" (you will probably have to re-insert the leading slash manually.

That should get you going.

Cody Caughlan
Brilliant, that almost works! The only problem is that the content is now always rendered as HTML, even if I try to force it with a ".xml" suffix? I'm using the standard respond_to do |format| block.
jpatokal
It looks like route globbing with :format support is a known issue - that is, it doesnt work, as you found out. The bug / patch is here discussed here: http://bit.ly/5LICLH but it looks like its not in the current release of Rails. Since you have no control of the URL portion (the route) the best I can think of is adding a query string parameter (?format=xml or ?format=json) which you can detect in your controller and act accordingly. Otherwise, it looks like you're SOL
Cody Caughlan
A: 

I tweaked Cody's answer above slightly to come up with this:

map.place '/places/*id', :controller => 'places', :action => 'show'
map.connect '/places/*id.:format', :controller => 'places', :action => 'show'

By using map.place instead of map.connect, Rails knows what resource we're dealing with and generated place_url, place_path etc helpers correctly.

Now, the 2nd line should work but doesn't thanks to the bug above, so here's a workaround for places_controller.rb that manually splits the ID and sets the format, defaulting to XML:

id, suffix = params[:id].join('/').split('.') 
params[:format] = suffix ? suffix : "xml"
jpatokal
Nice touch on just grabbing the format from the last path component. That should do the trick!
Cody Caughlan
It dawned on me a bit later that the id may also contain dots, so using .split(/\.(html|xml)$/) is a bit safer.
jpatokal