Rails URL Helpers
Rails is meant to be flexible. As a result, there are typically a number of ways to accomplish the same goals. Routes are a great example of how this principle operates in a Rails app. In this section, we will review how to leverage built-in URL helper methods instead of hard coding route paths into an application (along with why this is a good idea).
Paths vs Route Helpers
What's a real-world difference between using hard-coded paths compared with route helper methods? Let's imagine that you have a meeting in NYC, and you want to get from one side of the city to the other. You have a couple of different options:
Traverse the streets on foot
Take a taxi
Walking is like hard coding your route's path. Technically, it can work. However, it's slow, potentially error-prone (one small mistake can lead to the wrong part of town), and, if the meeting location changes, it will require quite a bit of manual work to adjust and walk to the new destination.
Taking a taxi is like using a route helper: you can simply provide the address to the driver and let them navigate the city streets for you. It is faster than walking, and, if the address for the meeting changes while you're en route, it's not as difficult or slow to adjust.
Don't worry if it's still a little fuzzy. Here's an example of what it looks like in code:
Hard-coded path:
"/posts/#{@post.id}"
Here you're saying: "I know exactly the GPS coordinates of my meeting, driver. Do exactly as I say."
Route helper:
post_path(@post)
Here you're saying: "Can you find the best way to a controller that knows how to work with this thing called a Post
based on looking at this instance called @post
.
We want to use route helper methods as opposed to hard coding because:
Route helpers are more dynamic since they are methods and not simply strings. This means that if something changes with the route there are many cases where the code itself won't need to be changed at all
Route helper methods help clean up the view and controller code and assist with readability. On a side note, you cannot use these helper methods in your model files
It's more natural to be able to pass arguments into a method as opposed to using string interpolation. For example,
post_path(post, opt_in: true)
is more readable than"posts/<%= post.id %>?opt_in=true"
Route helpers translate directly into HTML-friendly paths. In other words, if you have any weird characters in your URLs, the route helpers will convert them so they can be read properly by browsers. This includes items such as spaces or characters such as
&
,%
, etc.
Implementing Route Helpers
To begin, we're going to start with an application that has the MVC set up for posts
, with index
and show
actions currently in place. The route call looks like this:
This will create routing methods for posts that we can utilize in our views and controllers. Running rails routes
in the terminal will give the following output:
These four columns tell us everything that we'll need to use the route helper methods. The breakdown is below:
Column 1 - This column gives the prefix for the route helper methods. In the current application,
posts
andpost
are the prefixes for the methods that you can use throughout your applications. The two most popular method types are_path
and_url
. So if we want to render a link to our posts' index page, the method would beposts_path
orposts_url
. The difference between_path
and_url
is that_path
gives the relative path and_url
renders the full URL. If you open up the rails console, by runningrails console
, you can test these route helpers out. Runapp.posts_path
and see what the output is. You can also runapp.posts_url
and see how it prints out the full path instead of the relative path. In general, it's best to use the_path
version so that nothing breaks if your server domain changesColumn 2 - This is the HTTP verb
Column 3 - This column shows what the path for the route will be and what parameters need to be passed to the route. As you may notice, the second row for the show route calls for an ID. When you pass the
:show
argument to theresources
method, it will automatically create this route and assume that you will need to pass theid
into the URL string. Whenever you haveid
parameters listed in the path like this, you will need to pass the route helper method an ID, so an example of what our show route code would look like ispost_path(@post)
. Notice how this is different than theindex
route ofposts_path
. Also, you can ignore the(.:format)
text for now. If you open up the Rails console again, you can call the route helpers. If you have aPost
with anid
of3
, you can runapp.post_path(3)
and see what the resulting output is. Running route helpers in the rails console is a great way of testing out routes to see what their exact output will beColumn 4 - This column shows the controller and action with a syntax of
controller#action
One of the other nice things about utilizing route helper methods is that they create predictable names for the methods. Once you get into day-to-day Rails development, you will only need to run rails routes
to find custom paths.
Let's imagine that you take over a legacy Rails application that was built with traditional routing conventions. If you see CRUD controllers for newsletters, students, sales, offers, and coupons, you don't have to look up the routes to know that you could call the index URLs for each resource below:
Newsletters -
newsletters_path
Students -
students_path
Sales -
sales_path
Offers -
offers_path
Coupons -
coupons_path
This is an example of the Rails design goal: "convention over configuration." Rails' convention is that resources are accessible through their pluralized name with _path
tacked on. Since all Rails developers honor these conventions, Rails developers rapidly come to feel at home in other Rails developers' codebases.
The link_to
Method
link_to
MethodOur first three tests are currently passing; let's take a look at the lone failure. The failing test ensures that a link from the index page will point to that post's respective show page view template:
This matcher is currently failing since our index page doesn't link to the show page. To fix this, let's update the index page like so:
That is some bossy code. Let's use a link_to
method to clean this up and get rid of multiple ERB
calls on the same line.
This works and gets the tests passing, however, it can be refactored. Instead of hard-coding the path and using string interpolation, let's use post_path
and pass in the post
argument.
This is much better, but to be thorough, let's make one last refactor: Rails is smart enough to know that if you pass in the post
object as an argument, it will automatically use the ID attribute, so we'll use this implementation code:
If you run the tests now, you'll see that they're all still passing.
We're using the link_to
method to automatically create an HTML a
tag. If you open the browser and inspect the HTML element of the link, you would see the following:
(If your browser loads a blank page, add Post.create(title: 'A lovely title', description: 'A superb description') to db/seeds.rb
, run rake db:seed
, and then restart your server.) As you can see, even though we never added HTML code for the link –– e.g., <a href="..."></a>
–– the link_to
method rendered the correct tag for us.)
Using the :as option
If for any reason you don't like the naming structure for the methods or paths, you can customize them quite easily. A common change is updating the path users go to in order to register for a site.
If we had a User
model/controller, in routes.rb
file, you would add the following line:
Now the application lets programmers use register_path
when creating links with link_to
. Rails leverages routes and these "helper route" names in many places to help you keep your code flexible and brief.
Summary
Hopefully, this lesson shed some light on the beauty of using route helper methods. If you run the tests again after making the above changes, you'll notice something interesting: all of the tests are still passing! If we had hardcoded the URLs in the links in our views, we would have had a major issue: all of our links to the show pages would have broken, along with our Capybara tests. However, by using the built-in helper methods, the links all updated automatically.
Last updated