Refactoring Rails Routes

Today I want to share a quick Ruby on Rails tip that I have used in the past and just used again in a recently inherited codebase.

The general problem under consideration is when you have route specifications where you want to change the syntax, but you didn’t want to change how the application works. I had done this in the past when upgrading a Rails app from version 2 to version 3, and yesterday I wanted to make some route changes to reduce deprecation warnings at the beginning of test runs.

In this case, I was getting some deprecation warnings about the syntax of the route. For example:

DEPRECATION WARNING: Defining a route where `to` is a controller without an action is deprecated. ...

So I wanted to fix the underlying issue but leave the routes otherwise unchanged.

General approach

To make sure that I do route refactorings correctly, I first write out the output of rake routes to a file before making any changes. Then, when I make changes, I can write out the result of rake routes to another file and then diff the two. If I just want to see if they changed, diff -q <file1> <file2> is sufficient. If I want to see the specific changes, doing a normal diff or unified diff is helpful to see what changed. For the deprecation warnings, this was enough to ensure that I made the changes correctly.

For the Rails 3 conversion, I ended up doing this very slowly and committing whenever I had a valid change. I would change the name of each output file to match the git commit that it was generated at to help keep track of the changes. There were some changes that were inconsequential but would affect the diff result, so it was easier to check them only once and then have them filtered out of subsequent diffs.

To make that point a bit clearer:

A -> B - 1 change (manually verified)
B -> C - 1 change (manually verified)
C -> D - 1 change (manually verified)

If I diffed D against A, I would have to reverify (or be distracted by) the previous changes that B and C introduced. By diffing against the last known good copy (C -> D), I can save effort.

Why not just run the tests?

We can’t rely solely on the test suite to catch regressions because most apps do not exhaustively test their routes. If yours does, consider yourself the lucky exception! I think there is some merit in punting on testing routes. It seems like a low return on investment–at least until you are trying to upgrade major versions and need to overhaul the syntax. For the most part the routing syntax has probably stabilized, so there might be more value in doing it now. Still, I feel like testing DSL logic is hard to justify since they are already so readable, and in this case we have a good way to expand them (rake routes).

However, you might as well run the test suite just in case it picks something up. You might choose to run the subset of it that might contain things that test the routes. Routing tests, controller tests, and end-to-end tests are tests that generally hit the routes.

Categories: main

« How to Actually Publish More Things Commuting Probably Costs More Than You Think »