An in-Depth Look at Basic Rails Routing
The purpose of tutorial is to explain, in a beginner tone, how URL mapping happens in a Rails application. I will not be able to cover all the information about Rails Routing, but I’ll do my best to talk about the basic aspects. This part of the tutorial will focus on Simple Routes, the next part we will talk on Resourceful Routes.
The Ruby on Rails routing system handles requests in a unique way. It examines the URL of incoming requests and determines the controller action responsible for handling each request. It makes use of the special syntax specified in the routes file, config/routes.rb, in doing this mapping. This routes file controls every URL aspect of your web application, thus it is important we look into it.
Rails Routes File
The generated file contains instructions on how it can be used. Let’s leave that and go dig into creation of routes.
Creating a Regular Route
The basic means of creating a route involves mapping a URL to a controller and an action. When the Rails router sees a request it dispatches it to a controller’s action matching the URL.
So a URL looking like this:
/books/2
Will be mapped to a controller’s action assuming the route is defined as:
get 'books/:id' => 'books#show'
That is the shorthand for:
get 'books/:id' to: 'books/show'
Here the controller will be BooksController
and there will be a show
method. The next line of action is carried out by the controller. You should note the pattern we used in calling the controller and action method
books#show
This is the pattern you will be using in this kind of scenario. The controller is in its plural form and there is a #
in front of the method which is the Ruby way of saying that you are talking about an instance method. You might be confused about :id
in the examples above, you can be sure I will not leave you in the dark on that :)
There is no magic on how this is handled. When a request is made to your application, Rails grabs all the parameters that comes with it and makes them available in a special hash called params
that you can use in your controller. If a request URL looks like this: http://sitepointbooks.com/books/15`, the routes that handles this will look like this:
get 'books/:id' => 'books#show'
The controller and action method will look like this:
class BooksController < ApplicationController
def show
@books = Book.find(params[:id])
end
end
This is obviously not the only way this is done, just for necessity sake I’ll drop another method.
class BooksController < ApplicationController
before_action :set_book, only: [:show]
def show
end
private
def set_book
@books = Book.find(params[:id])
end
end
This second pattern helps you share the common setup among other actions. If you see something like the above in future, you should understand it. With that established, let’s look into something interesting – the root route. Do not forget the things we talked about above, you will need that knowledge in the next section. You dig?
The Root Route
The root route gets called upon when a request is made to the domain name of your web application depending on the rule you have stated in your routes file. Let’s say your domain name is http://test.com
, when someone tries to connect to it, this rule gets executed. Now what is the rule?
The rule in your routes file can look like this:
#config/routes.rb
root to: 'welcome#index'
Like we saw above, it means that there is a WelcomesController
and an index
method action. Here are some other examples:
root to: 'pages#index'
root to: 'welcome#show'
root to: 'pages#home'
You just need to take note of the convention used in naming root routes and you are good to go. There is a shorthand format for naming root routes and it looks like this:
root 'pages#index'
When you generate a new Rails application and point your browser to the root, you will be presented with a root page even though you have not defined a root route. How is that possible? If you have not seen this give it a shot before you continue reading.
Open up your terminal and generate a new Rails application. bundle install
your gems and start your rails server
. Fire up your browser and point to http://localhost:3000
and you will see a root page. No, that was not magic :) Let me explain what happens.
When you have not defined a root for your application, Rails, by default, routes to an internal Rails::WelcomeController
and renders a welcome page. Did that sink in?
You might be asking; “where the heck is this welcome page in my machine?” You can find the code for Rails::WelcomeController
on GitHub – Rails default welcome page.
Rails automatically stops calling this controller (Rails::WelcomeController
) the moment you define a root route in the routes file.
With that said, let us look into Named Routes.
Named Routes
Rails allow you to name a route using the :as
parameter. Here is one way of doing this:
get 'contact', to: 'pages#show', as: 'contact'
This provides you with contact_url
and contact_path
helpers that you can call in your application where needed. For example in your views like this:
When the link gets clicked, the show
action of the PagesController
gets invoked. The path shown in the hyperlink is:
/contact
Another example involves applications that needs authentication. You can have a SessionsController
that handles authentication, with the new
and destroy
actions handling logging in and logging out, respectively. Named routes can come in handy in this scenario:
get 'login', to: 'sessions#new', as: 'login'
get 'logout', to: 'sessions#destroy', as: 'logout'
Now you can use login_path
or logout_path
in your application where necessary. The hyperlinks show:
/login
/logout
Rails Routes Scoping
Rails provides you with the ability to group related routes and set default options for them. To do this, we make use of the scope
method. Let’s see how this works, suppose you have this in your routes file:
get 'articles/new', to: 'articles#new'
get 'articles/edit/:id', to: 'articles#edit'
post 'articles/draft/:id', to: 'articles#draft'
In the code above, all routes map to ArticlesController
. Using scope
you can tidy things up and make it look like this
scope controller: :articles do
get 'articles/new' => :new
get 'articles/edit/:id' => :edit
post 'articles/draft/:id' => :draft
end
From the above we we are giving the routing system of our application instructions to map the routes in the block to the ArticlesController
. We do not have to state this individually for each of the routes. We can also tell it the path to use by modifying the code to look like this:
scope path: '/articles', controller: :articles do
get 'new' => :new
get 'edit/:id' => :edit
post 'post/:id' => :post
end
The scope
method, as we saw above, accepts a :controller
and :path
option. It also accepts an :as
option, so we can have
scope :articles, as: 'posts' do
get 'new' => :new
end
To read more on how scope works, check the following links:
Rake Routes
How do you know what routes exist in your application? Or I should have asked first; is it possible to know the existing routes in your application? When you point your browser to a route that has not been added in your routes file, you’ll get a Rails Routing error page that looks like the image below:
That is the default 404 error
page that you’ll see in development environment. It lists all of the routes already defined in your application. As indicated in the image, you can switch between path_hlpers
and url_helpers
which you can make use of in your application like this
<%= link_to "Login", login_path %>
You can also find the routes defined in your application by making use of a standard Rake task that comes in all Rails application. To see how this works, open up the routes file of the Rails application you generated earlier and paste in the following codes:
#config/routes.rb
Rails.application.routes.draw do
resources :books
end
Type this command into your terminal rake routes
and hit ENTER. This should produce what I have below:
Prefix Verb URI Pattern Controller#Action
books GET /books(.:format) books#index
POST /books(.:format) books#create
new_book GET /books/new(.:format) books#new
edit_book GET /books/:id/edit(.:format) books#edit
book GET /books/:id(.:format) books#show
PATCH /books/:id(.:format) books#update
PUT /books/:id(.:format) books#update
DELETE /books/:id(.:format) books#destroy
Let’s take it further, edit your routes file to look like this:
#config/routes.rb
Rails.application.routes.draw do
resources :books
root "welcome#home"
get 'login', to: 'sessions#new', as: 'login'
get 'logout', to: 'sessions#destroy', as: 'logout'
scope path: '/articles', controller: :articles do
get 'new' => :new
get 'edit/:id' => :edit
post 'post/:id' => :post
end
end
Run rake routes
and check out the output. It should look like this:
Prefix Verb URI Pattern Controller#Action
books GET /books(.:format) books#index
POST /books(.:format) books#create
new_book GET /books/new(.:format) books#new
edit_book GET /books/:id/edit(.:format) books#edit
book GET /books/:id(.:format) books#show
PATCH /books/:id(.:format) books#update
PUT /books/:id(.:format) books#update
DELETE /books/:id(.:format) books#destroy
root GET / welcome#home
login GET /login(.:format) sessions#new
logout GET /logout(.:format) sessions#destroy
new GET /articles/new(.:format) articles#new
GET /articles/edit/:id(.:format) articles#edit
POST /articles/post/:id(.:format) articles#post
The results are always listed in this format:
- The route name.
- The HTTP Verb used.
- The URL pattern to match.
- The routing parameters for the route.
You can restrict the result of rake routes
to routes of a given controller. Run this command in your terminal: CONTROLLER=books rake routes
and see the result.
Conclusion
At this point, you understand the basic aspects of the Rails routing system. In the next part we will talk about Resourceful Routes. Thanks for reading :)