I strongly disagree with the "redirect" part of @ryan's answer. It would double your server communication - it will work but it's definitely not a nice solution. The idea of reordering is perfectly correct.
So there is my solution - I'm really not sure if it is ok, but it seems to work somehow.
In your routes.rb:
get ":slug", :to => "cities#show", :constraints => CityConstraint.new, :as => city
get ":slug", :to => "site#show", :as => page
There are two things to mention in the code:
- :slug in the second case - that's quite strange but it has to be named in a same way as in the first route. I was not able to manage it to work with :page as in your case. But I think that's not a big deal
- :constraints - this is normal and documented
The constraint itself consists of a class like the following:
class CityConstraint
def matches?(request)
# The condition would normally go through ActiveRecord to test the
# existence of town dynamically
towns = %w(london prague washington)
towns.include?(request.params[:slug])
end
end
The constraint runs (the method matches?) every time, when there is a hit in the routing table on the rule with the constraint. I believe that it is what you need. Probably some form of cache would be necessary not to kill the performance.
And by the way - this solution is only theoretical and I've never used it in production. It would be great if someone will test it :-)