Let's say that we have a library full of books and we need to build an
application to manage these books. Typically we need to perform the following
seven operations.
- GET the list of all books.
- GET detailed information about a particular book.
- Click on "Add a new book" and GET a form in response.
- POST the values from the form to create a new book.
- GET the form with an existing book's data prepopulated so that the user can
edit the information about the book.
- UPDATE the system with the edited data that was just submitted.
- DELETE the book.
These seven actions collectively are known as RESTful representations of the
book. We can see that in all the above seven cases, sentences contain the HTTP
verbs GET
or POST
except for operation numbers 6 and 7.
In operation number 6, we are trying to update a particular book. It is possible
to use the POST
method for updating the records. But we will be following the
Rails' standards and will use
PUT or
PATCH.
The routes for all seven actions will look like this:
Rails resources
Writing all those seven lines of code in the routes file time and again for each
item gets repetitive. Rails has put all that under the umbrella term of
resources
.
If we use resources
instead of manually specifying all routes, this is how the
routes definition will look like:
Think of resources
as a shortcut for not typing all that code. Looking into
the
mapper.rb
file from Rails codebase, we can see that all the seven methods are listed
there.
Rails provides a Rake task to list all the routes.
As we discussed that one single resources :books
is responsible for generating
these seven routes.
Prefix | VERB | URI Pattern | Controller#Action | Used for |
---|
books | GET | /books | books#index | List books |
| POST | /books | books#create | Create a book |
new_book | GET | /books/new | books#new | Form for a new book |
edit_book | GET | /books/:id/edit | books#edit | Form for editing book |
book | GET | /books/:id | books#show | Show info about book |
| PATCH | /books/:id | books#update | Update info about book |
| PUT | /books/:id | books#update | Update info about book |
| DELETE | /books/:id | books#destroy | Delete info about book |
If we visit http://localhost:3000/rails/info/routes then also we can see all the
routes.
Using only and except to be selective
Sometimes we do not need all the seven routes. For example, let's say that in
our application, we don't need to allow users to delete any book. In that case,
even if a user sends a DELETE request, we don't want the destroy
action to be
called.
To do that, we need to tell Rails Routing not to have any routing for DELETE
verb:
In this case, only index
and show
actions are added to routing. That is, a
user can see the list of books and can get details about a book. But they can't
create, edit or delete a book.
Difference between singular resource and resources in Rails routes
So far, we have been using resources
to declare a resource. Rails also lets us
declare a singular version of it using resource
.
Rails recommends we use singular resource when we do not have an identifier. For
example, the URL for the profile page is, typically, /profile
and not
/profile/495
. That is because when we visit /profile
we need to show the
profile of the currently logged in user.
Let's see an example with singular resource.
The routes generated by singular resource will be:
Prefix | VERB | URI Pattern | Controller#Action |
---|
new_profile | GET | /profile/new | profiles#new |
edit_profile | GET | /profile/edit | profiles#edit |
profile | GET | /profile | profiles#show |
| PATCH | /profile | profiles#update |
| PUT | /profile | profiles#update |
| DELETE | /profile | profiles#destroy |
| POST | /profile | profiles#create |
Notice that the URL pattern is using the singular style. URLs are /profile/new
and not /profiles/new
.
Singular resources are always named singularly whereas plural resources have a
plural name.
However, the controller name is still plural. This was a subject of great debate
within the Rails community. The community decided to keep the controller name
plural for both singular and multiple resources.
Here is an explanation from the
Rails official guide:
Because you might want to use the same controller for a singular route
(/account
) and a plural route (/accounts/45
), singular resources map to
plural controllers. So that, for example, resource :photo
and resources
:photos
creates both singular and plural routes that map to the same
controller (PhotosController
).
Notice that for the show
action, the corresponding URL is just /profile
. No
identifier is needed. The same is true for creating, updating, and deleting the
profile.
In this case, for all these actions, we will be performing the operations on the
currently logged-in user's data. Therefore we don't need an identifier for those
actions.
Finally, there is no index
action since here we are talking about a singular
resource.
Another common usage of the singular resource is when we are dealing with login
operations. When a user logs in, we need to maintain a session. For each
logged-in user, there could be only one session.
In that case, our routes definition will look like this:
Controller could look like this:
Adding more RESTful actions
You are not limited to the seven routes that RESTful routing creates by default.
If the need arises, Rails allows you to add additional routes that apply to the
collection or individual members of the collection.
You can add two types of routes to a RESTful resource. These are called
collection and member routes.
Collection routes
Collection routes apply to the whole collection. For example, if a RESTful
resource called books represents a collection of book objects then a collection
route called "bulk_delete" will create the books/bulk_delete
path.
A collection route can be added like this.
The above code will add a bulk_delete
RESTful action in the
books_controller
and DELETE requests on books/bulk_delete
will be routed
to the bulk_delete
action.
The above code could also be written like this.
Member routes
A member route applies to a member of the collection.
Let's say that a user has many accounts and you want to list all the accounts of a user.
In this case the path would look like users/:id/accounts
path.
A member route can be added like this.
The above code will add a accounts
RESTful action in the UsersController
and
GET requests on users/:id/accounts
will be routed to the accounts
action.
Just as with member routes, you can pass :on
to eliminate the block like this.
Collection routes should be added when a CRUD operation needs to be performed on
a collection of objects whereas a member route should be used when the CRUD
operation concerns a single object from a collection of objects.
To learn about routing in depth, you can refer to the in-depth
chapter about Rails routing.
We haven't made any changes in our project. So there is nothing to commit in
this chapter.
Let's clean up any accidental changes.