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:
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
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:
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.
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_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:
Another example involves applications that needs authentication. You can have a
SessionsController that handles authentication, with the
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
logout_path in your application where necessary. The hyperlinks show:
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
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
scope method, as we saw above, accepts a
: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:
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
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
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.
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 :)