Open-Source Internship opportunity by OpenGenus for programmers. Apply now.
In Ruby on Rail, routing is how an application handles HTTP requests and ultimately decides what will be provided to the user through controller actions. In this article, we will go through some routing conventions to follow when building a route.
We will create a project that shows a list of posts, and each post has a link to pdf document and an image which users can view.
Setup Sample Project
First thing we will need to do is to store images in app/assets/images and pdf documents in app/assets/docs The next thing we will do is to create post model and post_file model. Post model will contain the articles for a post and PostFile model will contain the images and pdf docs for the article at OpenGenus.
rails g model post article:text
The model will generate a migration that creates a table posts with article as column for the table.
rails g model PostFile image:text pdf:text
The model will generate a migration that creates a table post_files with image and pdf as columns for the table. Let's add a foreign key for the table;
class CreatePostFiles < ActiveRecord::Migration[7.0]
def change
create_table :post_files do |t|
t.text :image
t.text :pdf
t.references :posts, null: false, foreign_key: true
t.timestamps
end
end
end
Lets now run the migration so that our tables are created in the db/schema.rb file.
rails db:migrate
Once our table is created we wil now insert data to the tables using rails console, run the following command to open your console;
rails console
The above command will give you a ruby console which we will use to insert data to our table;
Post.create([{article: "This is my First Post"},{article: "Tis is my second post"},{article: "This is my third post"}])
The code above will insert 3 rows in our table posts. let's insert image and pdf name in our post_files table;
PostFile.create([{image: "sports.png", pdf: "sports.pdf", posts_id: 1},{image: "dancer.png", pdf: "dancer.pdf", posts_id: 2},{image: "hospital.png", pdf: "hospital.pdf", posts_id: 3}])
Then we will need to create controllers and views that we will use for this project. let's create the controllers;
rails g controller posts index show
rails g controller post_files index show
Creating RESTful Resources
There are two ways of creating routes; explicitly creating each route or using resources.
Routes file
Routing file is located in the config/routes.rb file.
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Defines the root path route ("/")
# root "articles#index"
end
Routes declared inside this file will be used to match against specific controller actions.
The root route
When a user first visits an application, they need to be taken to a default page which is the root page. To do this we simply need to declare the root of our application:
Rails.application.routes.draw do
# Defines the root path route ("/")
root to: 'posts#index'
end
Let's talk about the syntax on the route. We use root to: to declare that the following route is the root of our application. Next we have "posts#index", this specific syntax helps us understand what the action is and the specific controller being called. For this route we are calling the posts controller and the index action inside that controller. A great way to visualize this syntax is like so: "controller#action".
Explicitly creating routes
In our project's routes.rb we should have the following;
Rails.application.routes.draw do
root to: 'posts#index'
get 'posts/index'
get "/posts/:id", to: "posts#show", as: 'post_show'
end
The code provided above sets up three routes in a Rails application. The first route maps the root URL of the application (/) to the index action of the PostsController, meaning that when a user visits the root URL, they will see a list of all posts. The second route maps the GET request for /posts/index to the same index action of the PostsController, allowing users to access the same list of posts by visiting that URL directly. The third route maps the GET request for /posts/:id to the show action of the PostsController, where :id is a dynamic segment that represents the id of a specific post.
This route generates a named route with the prefix post_show, which can be used in the application's code to generate URLs for individual posts. For example, calling post_show_path(1) would generate the URL /posts/1, which would display the details of the post with an id of 1.
Nested Routes
in your routes.rb file we will add routes for post_files;
Rails.application.routes.draw do
root to: 'posts#index'
get 'posts/index'
get "/posts/:id", to: "posts#show", as: 'post_show'
get 'posts/:post_id/post_files', to: 'post_files#index', as: 'post_files'
get 'posts/:post_id/post_files/:id', to: 'post_files#show', as: 'post_file'
end
Focus on the last two code of line we just added to route our post_files
The code sets up two nested routes in our application. The first route maps the GET request for /posts/:post_id/post_files to the index action of the PostFilesController, where :post_id is a dynamic segment that represents the id of a specific post. This means that when a user visits a URL like /posts/1/post_files, they will see a list of all files associated with the post that has an id of 1. The second route maps the GET request for /posts/:post_id/post_files/:id to the show action of the PostFilesController, where both :post_id and :id are dynamic segments that represent the id of a specific post and file, respectively. This means that when a user visits a URL like /posts/1/post_files/2, they will see the details of the file with an id of 2 that is associated with the post that has an id of 1. Both routes generate named routes with the prefixes post_files and post_file, respectively, which can be used in the application's code to generate URLs for these nested resources. For example, calling post_files_path(1) would generate the URL /posts/1/post_files, and calling post_file_path(1, 2) would generate the URL /posts/1/post_files/2.
Using Resources to create routes
Rails provides us with route helpers that can help us avoid explicitly declaring the routes like we did above.
resources :posts
The above line of code does exactly the same thing as our previous routes and is the most common way to buld routes in rails. resources is a method provided by Rails that builds the seven necessary routes to perform our CRUD actions on our post resource. If you want to checkout all the routes that you have, on the terminal;
rails routes
Rails will print out all the routes as shown below;
edit_post GET /posts/:id/edit(.:format) posts#edit
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#update
new_post GET /post/new posts#new
POST /post(.:format) posts#create
posts GET /post(.:format) posts#index
posts GET /post/:id(.:format) posts#show
Specify the routes needed in resources
We can now specify the paths we need in our project as we only use show and index action and we are also going to make our routes nested for use in our project. Replace your explicit routes for resources as shown in the code below as it is the most common way to declare routes. We will use the resources URL path in this project so take note of that.
Rails.application.routes.draw do
root to: 'posts#index'
resources :posts, only: [:index, :show] do
resources :post_files, only: [:index, :show]
end
end
This code sets up nested resources for posts and post_files, with only the index and show actions defined for each resource. The only option limits the generated routes to only the specified actions. This means that the application will only respond to requests for the index and show actions of both posts and post_files. Any other action (such as new, create, edit, update, or destroy) will return a 404 Not Found error.
Go ahead and run the command rails routes on your terminal, it will give you a list of paths but we are only intrested in this;
Prefix Verb URI Pattern
Controller#Action
root GET /
posts#index
post_post_files GET /posts/:post_id/post_files(.:format)
post_files#index
post_post_file GET /posts/:post_id/post_files/:id(.:format)
post_files#show
posts GET /posts(.:format)
posts#index
post GET /posts/:id(.:format)
posts#show
I need you to take note of the URL for resources routes, it is generated automatically by resources and not explicitly defined as in the explicit routes.
Edit the posts controller file;
class PostsController < ApplicationController
def index
@posts = Post.all
end
def show
@post = Post.find(params[:id])
end
end
Edit controllers/post_files_controller.rb
class PostFilesController < ApplicationController
def index
@post_files = PostFile.find(params[:post_id])
@post = Post.find(params[:post_id])
end
def show
pdf = find_pdf(params[:id])
send_file pdf, type: 'application/pdf', disposition: 'inline'
end
private
def find_pdf(id)
Rails.root.join('app', 'assets','docs', "#{id}.pdf")
end
end
Next open views/posts/index.html.rb;
<% @posts.each do |post|%>
<h3><%= link_to post.article, post_path(post.id) %></h3>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
</p>
<% end %>
We loop through the Posts variable in order to get individual post. From the code you can see we have link_to post.article, post_path(post.id)
The "post.article" specifies the text that will be displayed as the hyperlink, while "post_path(post.id)" is a Rails helper method that generates the URL for the specific post. The "post.id" specifies the unique identifier for the post, which is used to generate the URL. Run your server;
Next let's make some changes in our views/posts/show.html.erb;
<h1><%= @post.article %></h1>
<%= @post.id %>
<p><%= link_to 'View Files', post_post_files_path(@post.id) %> </p>
If you click on "This is my first post" on the index page it should take you to the /posts/1, it will display the contents of the show action;
Let's also make changes to our views/post_files/index.html.erb
<h1>Post Files</h1>
<%= image_tag @post_files.image, alt: "user profile" %>
<p><%= link_to 'View pdf file', post_post_file_path(@post.id, @post_files.pdf) %></p>
Refresh your page then click on the View Files link, it should take you to /posts/1/post_files which is the index action of PostFilesController ;
It will display an image and a link to open the pdf file.
If you click View pdf file it should open your pdf file on this path /posts/1/post_files/sports.pdf
Conclusion
Always use resources when working with routes as it is the most common used and also easy to setup.I hope this helped make rails routes syntax a bit clearer.