Lesson Weekend

In a Rails application, all of our routes are stored in config/routes.rb. For the most part, we won't be using the config much other than this file.

Routing with Rails

Let's make an update to routes.rb to include full CRUD routing for albums:

Rails.application.routes.draw do
  resources :albums

The resources keyword is the Rails convention for adding a group of routes. When we specify resources :<class name here>, Rails will automatically create all the routes we need to add CRUD functionality for that class.

Note that we must use resources, not resource. If we were to say resource :albums instead, we will get only six of the routes included below. albums#index would be omitted, which we don't want. This typo is a common error for beginners.

We can take a look at the routes our application has by typing in rake routes in the terminal:

Prefix Verb   URI Pattern                                                                              Controller#Action
albums GET    /albums(.:format)                                                                        albums#index
       POST   /albums(.:format)                                                                        albums#create
new_album GET    /albums/new(.:format)                                                                 albums#new
edit_album GET    /albums/:id/edit(.:format)                                                           albums#edit
album GET    /albums/:id(.:format)                                                                     albums#show
       PATCH  /albums/:id(.:format)                                                                    albums#update
       PUT    /albums/:id(.:format)                                                                    albums#update
       DELETE /albums/:id(.:format)                                                                    albums#destroy

# ActiveStorage routes omitted for now.

Rails has automatically generated seven RESTfully named routes for us. They correspond to all of the routes we need for CRUD:

  1. Get a list of all albums.
  2. Post a new album to the database.
  3. Go to a form page for adding a new album.
  4. Go to a form page for editing an album.
  5. Get a specific album by id.
  6. Update an album.
  7. Delete album.

Each route has a prefix, verb, URI pattern and controller action.

The prefix is a "helper path" that determines how we can link to the page in a Rails application. For example, if we wanted to create a link for the new album page, we could use the new_album_path, which combines the prefix new_album with _path.

The verb is the HTTP verb associated with the route's action. We covered this in detail in the last course section.

The URI pattern is the path for the route. We covered this in the last course section as well. The (:format) at the end of the route just tells us that the route can return different data formats such as images.

The controller action specifies which controller action will handle that particular route. We will cover controllers in more detail in the next lesson.

Let's break this down into an actual example. Let's say a user clicks on a link for creating a new album. Our view would utilize the new_album_path (which includes the prefix). Our application would then GET (the route action) the new album form page. This page is at /albums/new (the URI pattern), which is RESTfully named. Finally, the controller at albums#new (the controller action) will run any necessary code and determine what content will be served to the user.

rake routes provides a lot of helpful information, especially as our routes become more complicated.

One last note: we've omitted the last five routes that are provided for ActiveStorage. ActiveStorage allows us to associate file uploads with a record but we won't worry about that until the next course section.

Nested Routing with Rails

We can create any number of grouped CRUD routes using the resources keyword. We can also nest routes, which we will want to do for our association between Songs and Albums. Let's update routes.rb to handle this relationship:

Rails.application.routes.draw do
  resources :albums do
    resources :songs

We add do ... end to resources :albums to create a block and then we nest resources :songs inside of that.

Let's run rake routes again:

Prefix Verb   URI Pattern                                                                             Controller#Action
album_songs GET    /albums/:album_id/songs(.:format)                                                  songs#index
       POST   /albums/:album_id/songs(.:format)                                                       songs#create
new_album_song GET    /albums/:album_id/songs/new(.:format)                                           songs#new
edit_album_song GET    /albums/:album_id/songs/:id/edit(.:format)                                     songs#edit
album_song GET    /albums/:album_id/songs/:id(.:format)                                               songs#show
       PATCH  /albums/:album_id/songs/:id(.:format)                                                   songs#update
       PUT    /albums/:album_id/songs/:id(.:format)                                                   songs#update
       DELETE /albums/:album_id/songs/:id(.:format)                                                   songs#destroy
albums GET    /albums(.:format)                                                                       albums#index
       POST   /albums(.:format)                                                                       albums#create
new_album GET    /albums/new(.:format)                                                                albums#new
edit_album GET    /albums/:id/edit(.:format)                                                          albums#edit
album GET    /albums/:id(.:format)                                                                    albums#show
      PATCH  /albums/:id(.:format)                                                                    albums#update
      PUT    /albums/:id(.:format)                                                                    albums#update
      DELETE /albums/:id(.:format)   

# ActiveStorage routes excluded.

Now we have fourteen routes (not including ActiveStorage routes). Because songs are nested within albums, all routes related to songs reflect that nesting. For instance, to get all songs belonging to an album, we'd have the following prefix: album_songs. That would route to /albums/:album_id/songs, which would return the index action in a songs controller.

It's common to nest routes in one-to-many relationships. However, it's a bad practice to nest our routes any more deeply than this. For instance, the deep nesting below would be a code smell:

resources :resource1 do
  resources :resource2 do
    resources :resource3

Creating a Root Route

Creating a root route is also very simple. For instance, if we wanted our application's home page to be the list of all albums, we could do this:

Rails.application.routes.draw do
  root to: 'albums#index'

Cleaning Up Routes

We can also specify that a group of resources should only have certain actions by using either :except or :only. For example, if we didn't want users to destroy Songs, we might do this:

    resources :songs, except: [:destroy]

If we wanted users only to be able to see a list of Songs but not be able to do any other CRUD functionality, we could do this:

    resources :songs, only: [:index]

We would then see the corresponding changes in rake routes as well.

We will be using all seven routes for both albums and songs. However, if you find that you don't need all the routes in an application, they should be cleaned up. This will help communicate your intentions better to other developers.

We can also do more complex routing as well, including creating non-resourceful routes, redirects, and customizing just about every part of a route. Check out the following Rails documentation for more information. At the very least, skim through this document to get a better sense of how you can customize routes in your application. And while it is possible to create custom routes, avoid doing so for now. The conventional routes included in our application should be able to handle everything we need.

Now that we've covered the basics of routing, we're ready to take a closer look at controllers.

Lesson 9 of 34
Last updated July 14, 2022