Dec 16, 2015
Table of contents:
Last week we looked at defining routes in Ruby on Rails.
When a request enters your application, the router will parse the URL path and dispatch the request to the relevant Controller.
The Controller forms an important part of the MVC (Model-View-Controller) design pattern and so Controllers are an integral part of a Ruby on Rails application.
In today’s tutorial we are going to be looking at the role of the Controller in a typical Ruby on Rails project.
The Controller has a very simple, yet very important role in the MVC design pattern.
It is the Controller’s responsibility to accept a request and dispatch a response from the application.
This will usually involve talking to the model layer to gather the required data for the request, and then returning an appropriate response, typically HTML or JSON in most Rails applications.
The Controller has a very narrow set of responsibilities and so as a developer you should stick to these conventions and not let extra responsibility leak into your Controllers.
As with just about every other aspect of Rails, there are already a lot of conventions around how you should structure your Controllers.
Firstly, your Controllers should live under the controllers
directory.
As we saw in last week’s tutorial, if you have nested routes you will need to create nested directories to house these controllers.
Your Controller should be named by pluralising the last word, such as UsersController
or ProductCommentsController
.
Although as with most other things in Rails, you can override this convention, your life will be a lot easier if you just follow it.
By default your routes will automatically know which Controller to dispatch the request to if you have named it according to the convention.
Finally, each of your Controllers should inherit from ApplicationController
:
class UsersController < ApplicationController
end
In last week’s tutorial we created routes for the :articles
resource. When an article request enters your application, Rails will create a new instance of the ArticlesController
and call the appropriate method.
This means we need to define those methods in order for the request to work.
class ArticlesController < ApplicationController
def index
# Return all articles
end
end
Your Controllers are basically just normal Ruby objects but with inherited functionality from the ApplicationController
.
Whilst resource routes will have predefined method names, you are free to create any other public methods that can be dispatched to from a HTTP request
If you have any helper methods on your Controllers, it’s best practice to have them as private
or protected
so they can’t be dispatched to.
One of the main responsibilities of your Controllers is to accept requests. An HTTP request will usually have parameters in one form or another.
For example, a request like users/123
will pass 123
as the id
parameter.
A request like users?sort=name
will pass the sort
column as the parameter.
Or a POST
request will send a payload of parameters in the body of the request.
Rails allows you to get access to these parameters using the params
hash.
For example here is a Controller method to display a user by their id:
def show
@user = User.find(params[:id])
end
And here is an example of a Controller method to create a new user:
def create
@user = User.new(params[:user])
if @user.save
redirect_to @user
else
render 'new'
end
end
A second important responsibility of the Controller is to work with the application’s session.
By default, HTTP requests are stateless. In order to have functionality such as the ability to be authenticated, we need a way to persist data between requests.
Each session has a unique id so we can store data in the session for that user.
Rails has a whole load of configuration options and different storage mechanisms for dealing with Sessions, but we will look deeper into that area of the framework in a future tutorial.
To access the session, you can use the session
instance method that can be handled like a hash.
For example, when a user authenticates with your application, you will want to store the user’s id between requests. To do this we can put it in the session:
class SessionsController < ApplicationController
def create
if user = User.authenticate(params[:username], params[:password])
session[:current_user_id] = user.id
redirect_to root_url
end
end
end
Now whenever a request enters your application, you can ensure that the user is authenticated by accessing the user’s id from the session and retrieving the user from the database:
class ApplicationController < ActionController::Base
private
def current_user
@_current_user ||=
session[:current_user_id] && User.find_by(id: session[:current_user_id])
end
end
Finally, when it’s time to log the user out, you can send a DELETE
request to your SessionsController
:
class SessionsController < ApplicationController
def destroy
@_current_user = session[:current_user_id] = nil
redirect_to root_url
end
end
In this example I’m simply removing the value from the session by setting it to nil
.
A flash message is a message that is displayed to the user for one request.
For example, you might want to display a message to say they successful created a new Article, logged out successfully, or that they should be expecting an email soon.
Flash messages are stored in the session for one request only. You don’t need to clear the flash message after it has been displayed as Rails will automatically take care of this for you:
class SessionsController < ApplicationController
def destroy
@_current_user = session[:current_user_id] = nil
flash[:notice] = 'You have successfully logged out.'
redirect_to root_url
end
end
Now you can display this flash message in your View like this:
<% if flash[:notice] %>
<p class="notice"><% flash[:notice] %></p>
<% end %>
Controllers form an important part of the MVC pattern. The Controller’s responsibility is very narrow, but extremely important in the grand scheme of a web application.
The Controller should accept requests, send the appropriate messages, and then return a response.
In today’s tutorial we’ve mostly looked at how Controllers can be used to accept requests and return responses for traditional web applications.
But Controllers are also an important part of accepting and return API requests and responses.
In a future tutorial we will be looking at how to deal with JSON, or XML requests as responses, but it’s basically the same!