ActionPack, Rails

4 Reasons Why Rails Routes Are Driving You Insane

The Rails router is a constant source of headaches. Your specs are all passing, but you can’t seem to get the Rails error page to stop popping up when you’re clicking through. It’s really easy to throw up your hands in frustration; it’s even easier to want to strangle the forum poster who tells you to “just read the Rails Guide”.

It’s not just you. Here are 4 common issues with the router. I hope you feel a little less maddened after checking them out.

4. Nested resources and the complexities therein

It’s really easy for Rails experts to talk about REST and how wonderfully simple it is. But stacking and nesting simple things can often lead to a big mess. It’s going to be very difficult indeed to generate a route to a user’s post’s comment’s likes. You’re very often going to simply be told “no” by the router in that most wonderfully helpful of error messages: ActionController::RoutingError in User#show No route matches ...

The best advice I can give, above and beyond what I said here, would be to limit nesting resources to one level. A user’s posts: /users/1/posts. A post’s comments: /posts/2/comments. Et cetera.

3. Parameter matching, Or: The Dance of Guessing and Hoping

This is best illustrated with an example.

Given this route setup:

resources :users do
resources :posts

All these:

link_to @post.title, [@user,@post]
link_to @post.title, user_post_path(@user,@post)
link_to @post.title, user_post_path(@post,user_id: @user)
link_to @post.title, user_post_path(user_id: @user, id: @post)
link_to @post.title, controller: "posts", action: "show", user_id: @user, id: @post

Produce this URL:


But you have to make sure the parameters you’re passing in match appropriately. In the first two, the user needs to come before the post. Since we’re looking for a Post, its parameter is :id, not :post_id. These:

link_to @post.title, [@post,@user]
link_to @post.title, user_post_path(@post,@user)
link_to @post.title, user_post_path(user_id: @user, post_id: @post)
link_to @post.title, user_post_path(user_id: @user, post_id: @post)

produce this:

No route matches ...

and nobody wants that.

Rails 4 has made leaps and bounds in this arena. It will tell you the name of the parameter you’re missing, which is a great clue to what’s wrong. This is one of the arguments for always upgrading Rails whenever possible.

2. PUT/DELETE named routes

This one is super confusing and bites everyone at some point, but fortunately it’s easy to explain: Given a Post resource, the route to either update or destroy it is the same as the route to show it: post_path(@post). It depends on the helper you’re using, but you just have to specify the :method to be "put" (or, as is more fashionable now, "patch") or "delete".

1. Evil done in the name of backward compatibility

There’s a long, storied, and bloody history to routes. On a Rails 1.2 project, lo those many years ago, our team was consistently using hashes for all our route generation. So many instances of link_to("Show", :controller => "users", :action => "show", :id =>! Hash-formed routes were really hard to maintain and adapt, so they fell by the wayside in favor of named routes, so the above turned into link_to("Show", user_show_path(@user)). Then came REST with its resources :users and linking directly to instances, like link_to("Show",@user). Add in concerns, formats, methods and you’ve got yourself a hulking, bloated library.

Since there are apps that have been upgraded from Rails version 1.x to 4.x, Rails has seen fit to deprecate very few of the features glommed onto the router over the years. Unlike ActiveRecord syntax, which is often changed irreversibly over time, support for these old route features continues, so knowing the right and modern way of routing is elusive.

My current pattern is to link directly to a resource where possible (link_to "Show", @user) and to use named routes otherwise (link_to "My posts", user_posts_path(@user)).

I hope this has helped take some of the venom out of routes for you. If you’ve got any questions, leave a comment below!

Like this post? Join my mailing list here and I can send more goodness your way.

ActionPack, Rails

link_to with array

Recently someone asked me about passing an array as the second parameter to link_to. Hashes, named routes — those are familiar and easy to use. What is the meaning of the array?

The Rails routing system is many things. “Confusing” narrowly beats out “powerful” as the top-ranked adjective.

The short answer to the question is “an array containing an Article of id 1 and a comment of id 2 will translate into this URL: /articles/1/comments/2”.

But given the confusing nature of the router, let’s walk stepwise through what the routing system is doing here, to get a little more illumination on the subject.

link_to 'Destroy Comment', [comment.article, comment], method: :delete, data: { confirm: 'Are you sure?' }

The array [comment.article, comment] indicates a nested resource. So you’d expect this kind of setup:


resources :articles do
  resources :comments


class Article < ActiveRecord::Base
  has_many :comments


class Comment < ActiveRecord::Base
  belongs_to :article

The first element of the array is an Article, with an id, let’s say, of 1. So the router searches for and finds an :articles resource, which it translates as the URL “/articles/1”. The second element of the array is a Comment, let’s say with an id of 2. The router now searches for a :comments resource that is nested under an :articles resource and finds it, so the URL becomes “/articles/1/comments/2”.