dira ⋅ geek ⋅ girl

Friendly URLs - reverberations

dira

Friendly URLs contain human-parseable meaning. Like contributors/dira instead of users/2 (ok, for some humans the second route conveys a lot of meaning too :D).

They are desirable in many situations, but what is the cost? The change is  facilitated by Rails; however, depending on the amplitude of the change, there can be more or less reverberations throughout your application.

Change route name

posts/1  becomes   articles/1. Reverberations: none

# change name for generated routes
map.resources :posts, :as => 'articles'

# manually update any custom routes that use the old name
map.post_draft '/article/:id/:token', :controller => 'posts',
        :action => 'show', :conditions => { :method => :get }

The controller/integration tests still work, the application works, everybody go home.

Change route parameters

users/1 becomes   users/dira. Reverberations: more

First, decide what field from your model to show in the URL. Then:

  1. if you already have a property perfectly suitable to be an URL (contains only alpha-numerical characters, no slashes or dots), and that is already unique among existing records. Assuming it's the login field of the User model:

    # model:
    def to_param
     login.parameterize
    end
    
    # for controllers (the User controller and all the controllers that are
    # nested under it, or in other namespaces): replace User.find(params[:id]) with
    User.find_by_login(params[:id])
    

    If using make_resourceful or another plugin for resourceful controllers, add a method that retrieves the needed object by login instead of by id:

    def current_object
      User.find_by_login(params[:id])
    end
    
  2. otherwise, use a plugin like permalink_fu or find_by_param to take care of transforming your data into a URL-compatible value, assuring that it is unique etc. For using the login field again, the changes are:

    • define a column in the database (default name: permalink)
    • create a migration in which you set the permalink value (using the functions offered by the plugin) for the data already created
    • define the permalink in the model
    • adjust the controllers to retrieve resources based on the permalink, not the id
  3. there is a quick and dirty option, for resources whose desired names are generally URL-compatible, but they could contain dots. The code prevents the dot to be interpreted as the separator in /controller/resource.format. But I considering solution 2.

What about controller/integration tests?

# if you used the more general
get :edit, :id => @user.to_param

# instead of the specific
# get :edit, :id => @user.id

your tests still work as nothing changed.

After that...

Using Ajax? check your Javascript files for connections to the old route name / parameters! Ack is your friend here.

Not sure you got all the reverberations covered? You can use a tool like tarantula to spider your site and check that all the links are still working.

  • to benefit from its brute power, you should fill all your models with data before letting it crawl over it.
    If you're using fixtures, just load them in the tests as shown in tarantula's readme.
    If using factories, instantiate them so that you have content in the pages and tarantula can browse happily
  • are you using Javascript without progressive enhancement? tarantula will detect it and kill u :D
  • using namespaces? you can do separate tests in which you tell tarantula to crawl in each namespace (for example one test for public, one for admin). The report will contain a section for each test method
  • The one thing I didn't figure out with tarantula is how to prevent deleting a resource and then trying other actions on it.

In conclusion

Humanizing routes is not difficult but still there are some points to pay attention to. Using a permalink plugin and having well-written tests helps.