Target: remove the :controller/:action(/:id)
routes from config/route.rb
.
Problem: make sure we have complete route coverage.
Well, the problem can be solved in two ways:
- Go into every controller and translate each action to a routing spec.
- Parse last week's logs and check whether each url is routable.
The first solution sounds easy but the second one sounds more fun. :-)
Recipe
-
Remove the wildcard routes from
config/routes.rb
:The offending lines look something like this:
match ':controller(/:action(/:id(.:format)))'
. Get rid of them. -
Get a few days' worth of
haproxy.log
(or some other log):Just ask your admin for that.
-
Take a random sample out of it:
Usually
cat ~/Logs/haproxy.log | shuf -n 1000 >> test/fixtures/logs/requests.log
will suffice. -
Translate each url into a routing spec:
Use the following sample spec file and edit it to skip known failing urls and fix subdomains.
-
Run the routing specs:
bundle exec rspec spec/routing/from_log_routing_spec.rb
-
Write some proper routing specs for each logically failing url:
from_log_routeg_spec.rb
only tests that the urls are recognized. You need to write meaningful routing specs that test if the route links to the correct controller and action. -
Fix routing errors in
config/routes.rb
:After you have your proper routing specs in place, go and edit your
routes.rb
. The reason we edit after we write specs is to make sure that the specs work as expected and the edit solved the failure. Get a new random sample and repeat the rest of the recipe until happy.
Important note
Remember that Rails performs DELETE
and PUT
requests with an actual POST
with some _method=put
or _method=delete
. This means that the log will not always have the correct HTTP verb.
After deploy, what?
As always, things go wrong and your routing specs won't get you 100% coverage. If you use an exception_notification-type gem you might be fine, 404s will come flocking to your inbox. But...
If you are on Rails 3.0, the RoutingError
exceptions can't be rescued from inside of the application. A sane workaround I found looks like this:
match '*a', :to => 'statics#render_404'
Put this at the bottom of your routes (but inside the route.draw
block) and link it to an appropriate controller. The controller action should look something like this:
Resources
Good luck and good routings!