Member and Collection Routes in Ruby on Rails
In Ruby on Rails Jan 18, 2022
Updated on May 13, 2023
The routes of a Ruby on Rails application live in the config/routes.rb file. The Rails router matches a request in the browser to a controller action. The Rails default for routing is the resource routing. A resource route will map HTTP verbs like GET, POST, PATCH, PUT and DELETE to a controller action and also to a certain CRUD operation in the database. More precisely, a resource route will create seven routes in an application corresponding to the seven standard controller actions: index, show, new, edit, create, update and destroy.
Generate the Application
Let's generate a new application. In the terminal, we type:
rails new Blog
After we create the application, we change the directory to its folder:
cd Blog
Generate an Article Scaffold
Let's generate a scaffold for an Article
resource:
bin/rails g scaffold Article title:string content:text
And then we run the migration:
bin/rails db:migrate
Set the Root Route
This application needs a root
route, so let's set it to the articles
index
action. In the config/routes.rb
file, put this code at the top:
root 'articles#index'
Add a Status to Articles
Now we want to add a status attribute to the article
model. The articles have two possible statuses:
draft and published. For this status attribute, we'll use enums. In Rails, an enum
is an attribute that maps to integers in the database but can be queried by name.
Let's generate the migration. In the terminal we type:
bin/rails g migration AddStatusToArticles status:integer
Then we run the migration:
bin/rails db:migrate
Let's declare the two statuses in the app/models/article.rb
file:
enum status: {draft: 0, published: 1}, _default: "draft"
The default status is draft. When the article is ready to be published, we want to set its status to published.
We want to add a Publish button in the views. When the article is ready, we want to change its status to published by hitting this button.
To do that, we first define a custom action in the articles
controller. We will call this action
publish
.
def publish
@article.published!
redirect_to root_path
flash[:notice] = 'Article was successfully published.'
end
With this method, we change the status to published and then redirect to the
root_path
.
We add this action to the list of actions to which we will apply the set_article
filter:
before_action :set_article, only: %i[ show edit update destroy publish ]
For this action, we will add a member route in the config/routes.rb
file. We can add a block like this:
resources :articles do
member do
get 'publish'
end
end
Or, if you have only one member route, you can eliminate the block like this:
resources :articles do
get 'publish', on: :member
end
Since our application has only one member route, we can eliminate the block.
Defining this member route enables the Rails router to recognize paths like /articles/:id/publish
with
GET. Also, it creates the publish_article_path
helper and the corresponding
publish_article_url
helper.
In the index
view, we only want to display a list of published articles and put the drafts on a separate
view. Let's open the articles
controller and edit the index
method:
def index
@articles = Article.published.all
end
Next, we will add the drafts
custom action to our controller:
def drafts
@articles = Article.draft.all
end
And in the config/routes.rb
file, we add a collection route to it:
resources :articles do
get 'publish', on: :member
get 'drafts', on: :collection
end
This will enable the Rails router to recognize the /articles/drafts
URL with GET. Also,
it will create the drafts_articles_path
helper and its corresponding drafts_articles_url
helper.
Then we create the corresponding drafts.html.erb
view in the app/views/articles/
folder.
Here, we add the following code:
<h1> Drafts</h1>
<% @articles.each do |article| %>
<%= render article %>
<p>
<%= link_to "Publish this article", publish_article_path(article) %>
</p>
<% end %>
We display a list of draft articles, and for every article, we add a Publish button linked to the
publish_article_path
.
We also add a Publish button in the show
article
view:
<p>
<%= link_to "Publish this article", publish_article_path(@article) %>
</p>
Finally, we add a link to the drafts in the index
articles
view using the
drafts_articles_path
. At the bottom, we add this code:
<p>
<%= link_to 'Drafts', drafts_articles_path %>
</p>
Conclusion
Using the member and the collection routes, we easily added additional routes to the seven default routes created using the resource routing.