Named Routes
So far, we have seen only one responsibility of the router: to match an incoming HTTP request to a route defined in the routes.rb
file and route it to the piece of code that handles it, which is a corresponding action method on a controller class.
- Input: HTTP Request URL
- Output: Controller and Action
However, the Rails router also works in the other way. That is, given the controller and action, it can generate the URL for that route.
- Input: Controller and Action
- Output: HTTP Request URL
In addition, you can also name a route and use it to generate the matching URL. Given the route name (along with the corresponding data or model), the router can generate the resulting URL.
But, why do you need Named Routes?
Consider the following route.
get 'post/:id', to: 'posts#show'
When you make a request to post/10
, the router invokes the show
action on the PostsController
class.
What if you want to link to a post? How would you go about creating the URL?
Well, the simplest solution is to interpolate it, passing the post id.
link_to post.title, "/post/#{post.id}"
But hard-coding URLs into your application is not always a good idea. When your URL changes, you'll have to search the whole codebase to find and replace the old URLs. If you miss one, your code won't complain until the users click the link, only to be shown an error page.
To fix this, you could provide the controller and action name along with the post id, so Rails could infer the URL.
link_to post.title, controller: 'posts', action: 'show', id: post.id
However, there's an even better way with the named routes, where you can name a route and use that name to generate the URL.
How to Name a Route in Rails?
You can name a route by passing the :as
option. Naming a route creates two methods in your Rails application. The method names follow the convention of {name}_path
and {name}_url
, where name
is the name of the route.
For example, consider the following route.
get "/posts/:id", to: "posts#show", as: "article"
When you name a route, Rails will automatically generate helper methods to generate URLs for that route. These methods are called name_url
or name_path
, where name
is the value you gave to the :as
option. For the above route, the router will automatically generate the methods article_path
and article_url
.
The difference between the two methods is that article_path
returns only the path portion of the URL, whereas article_url
returns the complete URL, which includes the protocol and domain.
article_path(4) -> `/posts/4`
article_url(4) -> `https://blog.com/posts/4` or `https://localhost:3000/posts/4`
Once you have named a route, you can use these helper methods in any place where you need a URL or a path, for example, the helpers such as link_to
or button_to
:
link_to post.title, article_path(id: post.id)
It's less code to write, and it also avoids the problem of hard-coding the URL.
In fact, we can skip the id
key, and just pass its value.
link_to post.title, article_path(post.id)
We can go even further. Rails allows you to pass any object to the named helper methods as long as those objects have a to_param
method on it.
class Question
def to_param
'100'
end
end
article_path(Question.new) # '/post/100'
What's more, all the active record objects have a to_param
method out of box, which returns the object's id
, allowing you to do this:
link_to post.title, article_path(post)
This version is more readable, concise, and also avoids the problem of hard-coded URLs.
Pretty cool, right?
How to Access URL Helper Methods in Rails?
You can use the helper methods directly in following places:
- Views
button_to user_account_path
- Controller Classes
class PagesController < ApplicationController
def profile
user_account_url
# ...
end
end
- Controller / Integration tests
require "test_helper"
class BookNotesControllerTest < ActionDispatch::IntegrationTest
test "should get index" do
get book_notes_url
assert_response :success
end
end
Additionally, you can access the helper methods in the Rails console using the app
object available in the console.
>> app.root_path
"/"
If the route expects any dynamic segments, you have to pass them via a hash to the named methods. Consider the following route:
get "/portal/:product", to: "pages#portal", as: "portal"
To generate the URL corresponding to this route, you'll pass the product value as follows:
portal_url(product: "ipad") # "http://www.example.com/portal/ipad",
portal_path(product: "iphone") # "/portal/iphone",
In the next chapter, we'll learn how you can override the named route parameter to another name.
If you have any questions or feedback, didn't understand something, or noticed a mistake, please leave a comment below or send me an email. You can also subscribe to my blog to receive future posts via email.