9780596521424
actioncontroller_id57640.html

Chapter 5. Action Controller

Action Controller is the C for Controller in MVC. It is through the controller that a user interacts with a Rails application. The controller is responsible for interpreting the client's request to your application, manipulating and loading the model data containing your business logic, and then rendering an appropriate view of the model data as a response for the client.

From the perspective of a Rails application developer, controllers are the entry point of an application. Requests URIs are mapped to the controller actions that will serve them by the Rails routing system. The Rails routing system uses a simple domain-specific language (DSL) in the config/routes.rb file of your application to map the URIs to controller actions. We'll take a closer look at routing in Chapter 6, Routing.

The controller is also responsible for deciding, based on the parameters of the client's request, what format to return the data to the client in. For example, a web browser is most likely interested in an HTML response, whereas an API client would rather receive the response XML or JSON. Rails makes it trivial to support multiple different response formats via a single controller action. This makes the addition of alternative response formats, such as XML or CSV, very easy to add cleanly to your application.

Controllers also support many more powerful and easy to use features including:

  • A callback system called filters that allow code to be executed before, after, or around a controller action.

  • Access to and manipulation of HTTP cookies which can also be cryptographically signed to prevent tampering.

  • A facility called the flash for passing objects and messages between requests, commonly used for passing an error or status message from one request to the next.

  • Manipulation and management of client session data with a variety of different sessions store back-ends.

  • A powerful caching system for caching content with different levels of granularity including page, action, and fragment caching.

  • Built-in HTTP Basic & HTTP Digest Authentication helpers.

Getting Started

A controller is comprised of controller actions. A controller action is a public instance method defined in the controller class that can be routed to by the Rails framework. A controller action's job is to interact with the model layer of the application and pass the appropriate model data to the view for rendering. Once rendered, the response is sent back to the requesting client.

It is very important to remember that the config/routes.rb file of a Rails application defines the publicly accessible dynamic URI paths of a Rails application. Without any routes, a Rails application's controller actions are inaccessible to requests. For more information on configuration of your applications routes see Chapter 6, Routing.

Rails provides a generator for creating new controllers. The generator is passed the controller name in underscored or camel case format followed by zero or more actions:

$ rails generate controller name [action...] [option...]

The following options are available:

--helper

Whether or not to generate a helper for the controller. The default is true.

-t NAME, --test-framework=NAME

Specify the testing framework to use for testing your controller. The default is test_unit.

-e NAME, --template-engine=NAME

Specify the template engine to use in the controller's views. The default is erb.

The generator creates the following files:

  • controller class in app/controllers directory.

  • view templates for each action in app/views/name directory.

  • view helper for the controller in the app/helpers directory.

  • functional test stub in test/functional directory.

  • helper test stub in test/unit/heleprs directory.

To get started, let's generate a controller class named OrdersController with a single action index:

$ rails generate controller Orders index
      create  app/controllers/orders_controller.rb
       route  get "orders/index"
      invoke  erb
      create    app/views/orders
      create    app/views/orders/index.html.erb
      invoke  test_unit
      create    test/functional/orders_controller_test.rb
      invoke  helper
      create    app/helpers/orders_helper.rb
      invoke    test_unit
      create      test/unit/helpers/orders_helper_test.rb

It is a convention in Rails to name the controller as the plural of the name of the resource it is managing. For an Order resource, which could be an Active Record model or other type of resource, the controller would be named OrdersController. Let's take a look at the generated controller:

Example 5.1. A simple Rails controller

class OrdersController < ApplicationController
  def index
  end

end

This controller contains a single action named index, which is the action we generated with the Rails generator. The body of the controller action is where the controller performs its function of generating a response for the client based on the application's model data and the view templates.

Controllers in a Rails application subclass the application's ApplicationController, which can be found in app/controllers/application_controller.rb. As the parent class of all controllers in a Rails application, ApplicationController is the ideal place to include common functionality that is needed by all controllers in your application.

In order to return some content from this controller action we'll need to render a response. The simplest thing we can do is render some text:

class OrdersController < ApplicationController
  def index
    render :text => "Welcome to Rails!"
  end

end

However, before the controller action will be accessible to a client, there needs to be a route that connects a URI path to it in config/routes.rb. Fortunately, the generator saved us a bit of work by automatically inserting the following route for us:

get "orders/index"

This route tells Rails to route the /orders/index URI path to the OrdersController#index action of the application. If we start up our application with:

$ rails server

We can load up the page in the browser by navigating a web browser to http://localhost:3000/orders/index to see the rendered text. (Yes, we know it’s invalid HTML, but bear with us.)

Figure 5.1. Simple Text Rendered in the Browser

Simple Text Rendered in the Browser

We can also use curl to request the page from the server:

$ curl http://localhost:3000/orders/index
Welcome to Rails!

Record Identification

Before we jump into how to actually render view templates and responses to requests we're going to take a quick look at a very important concept that is frequently used throughout both Action Controller and Action View. The concept is called record identification. Record identification works by using the class name and id of the object to automatically generate identifiers for an object. Here are some examples of using the helpers provided by the ActionController::RecordIdentifier module:

include ActionController::RecordIdentifier

user = User.find(1)
singular_class_name(user) # => "user"
plural_class_name(user)   # => "users"
dom_id(user)              # => "user_1"
dom_class(user)           # => "user"

By itself, record identification is useful, but isn't that exciting. However, when you build assumptions on top of record identification, then a lot of code can be greatly simplified.

You'll notice record identification is generally used in controllers and views to reduce method calls that once took many options down to method calls that just receive an instance of an object or a collection of objects. For example, rendering a partial view template for a collection of users:

render :partial => "users/user", :collection => @users

With record identification simplifies to:

render @users

Another good example is the redirection called after creating a new record:

redirect_to users_url(@user)

With record identification becomes:

redirect_to @user

As you can see, based on a few assumptions, record identification can really help simplify a lot of code used in common situations in controllers and views. You'll notice the use of record identification throughout the rest of the Action Controller and Action View chapters.

Responding to Requests

The response to an incoming request, unless the controller action decides to redirect, is generated by calling the controller's render method. Without exception, every controller action in your application will either render a response or issue a redirect. There are various ways of rendering a response.

Generally, you'll render a view template, but it can also useful to return content to the client that isn't generated by a view template. The most common case of not rendering a view template is generating the response to an API request in XML or JSON format. Active Record objects know how to serialize themselves both as XML and JSON, so it makes more sense and takes much less code, to rely on Active Record's serialization support than to create additional view templates.

When looking at idiomatic Rails controller actions you'll see a pattern emerge, which is the following two steps:

  1. Load any required model objects from the data store.

  2. Render a view template or issue a redirect.

The HEAD Response

The head method returns a response without an empty body. This is useful in situations where an HTTP status code and headers are sufficient information for the client. One example is a controller that responds to an uptime monitoring service:

class PingController < ApplicationController
  def index
    head :ok
  end
end

You can use any HTTP status code or symbol from the section called “HTTP Status Codes” as the first argument to the head method. You can also add arbitrary HTTP headers to the response as options:

class PingController < ApplicationController
  def index
    head :ok, 'Content-Type' => Mime::TEXT
  end
end

All passed in header values have to_s called on them, which is why we can pass in the Mime::TEXT constant as the value of the Content-Type header.

It is also smart enough to convert a symbolized header name like :content_type into the correct HTTP format Content-Type:

class PingController < ApplicationController
  def index
    head :ok, :content_type => Mime::TEXT
  end
end

Using curl we can verify that the response has been given the correct Content-Type header:

curl -I  http://localhost:3000/ping
HTTP/1.1 200 OK
Connection: close
Date: Mon, 24 May 2010 22:23:58 GMT
Content-Type: text/plain
X-Runtime: 0.036393
Content-Length: 0
Cache-Control: no-cache

Rendering a Response

In order to actually render content to be returned to the client you need to call render in your controller action. The render method has two different signatures:

render(options = {})

Renders a response based on the provided options hash. The options hash must contain a key specifying a renderer. The available renderers are: :action, :template, :file, :inline, :partial, :text, :json, :js, :xml, and :update. We'll be taking a closer look at each of the renderers throughout the rest of the chapter. An example of rendering an action:

class OrdersController < ApplicationController
  def index
    render :action => 'index'
  end
end
render(template, options = {})

This form of the render call evolved out of the fact that the vast majority of calls to render use the :action renderer. The way of calling render acts as an :action render if you pass in template as just the name of a template without any path separators. It acts as a :template render if you pass in template as a template path prefixed with one or more path segments. An example of rendering the index action using the convenience form of the render method:

class OrdersController < ApplicationController
  def index
    render 'index'
  end
end

If render is not called at all during a controller action, the controller implicitly renders an action with the same name as the current controller action that is being executed. Comparing our previous example that renders the index action,

class OrdersController < ApplicationController
  def index
    render :action => 'index'
  end
end

the equivalent implicit default render looks as follows:

class OrdersController < ApplicationController
  def index
  end
end

View Templates

The majority of the time you'll generate a response by rendering a view template. Here we'll discuss where to find a controller's view templates and how to pass variables from the controller to the view. We'll take a much closer look at view templates in ???. All of an application’s view templates are found underneath the app/views directory. The list of view paths is mutable, so to view the current list of view paths you can inspect the ActionController::Base.view_paths accessor. This would give you an up-to-date list of view paths in the event that your code or a plugin has modified the view paths for your application.

The controller's view templates directory is found in a subdirectory named after the controller under app/views. The name of the subdirectory is determined by underscoring the controller's class name after the Controller portion of the name has been removed. Therefore, the views for the OrdersController are located in app/views/orders. If you're ever in doubt you can call the controller's controller_path class method to get the name of the subdirectory:

OrdersController.controller_path # => "orders"

If a controller is namespaced then each namespace is added to the path as a subdirectory:

Admin::OrdersController.controller_path # => "admin/orders"

All instance variables of the instance of the controller that is handling the request are passed to the view template during rendering. These instance variables are called the controller's assigns and are available in the view template as instance variables of the same name.

Common Render Options

The render call takes a variety of options depending which type of rendering is being performed. Several of the options are common to all types of rendering, so we'll go over them right here:

:content_type

Sets the HTTP Content-Type header of the response. This can be set to any content type string, such as application/rss+xml or to any registered MIME type in the Rails application. See ??? for more information on MIME types and a complete list of the default MIME type symbols available.

:location

Sets the HTTP Location header of the response. This header indicates the URI location of the created resource. url_for is called on the value, so you can pass any values understood by url_for including: an absolute URI, a hash of url_for options or an object compatible with the polymorphic routes.

Note that setting the Location header only makes sense if you're also setting the HTTP status code to 201 Created.

:status

Sets HTTP status code for the response. Accepts either a numeric status code, such as 201, or a symbol representing the status code, such as :created. See the section called “HTTP Status Codes” for a complete listing. Defaults to 200 for 200 OK.

Rendering Nothing

Rendering nothing is a way to return a response that has no response body. The response only contains headers. Rendering nothing is the simplest form of all of the different methods of rendering and looks as follows:

class PingController < ApplicationController
  def index
    render :nothing => true
  end
end

This works fine, but we've already looked at a more powerful and flexible method called head that also returns an empty response body. The advantage of head is that it allows you to set arbitrary HTTP headers on the response with the passed in options. With the :nothing option of render you're limited to setting the Content-Type, Location, and Status headers and using the headers method available in the controller to set other arbitrary headers.

Rendering an Action

Rendering an action instructs the controller to render a view template with the name provided from the controller's view directory.

The generator we ran earlier automatically generated an index.html.erb view template for us. The template, app/views/orders/index.html.erb, looks like this:

<h1>Orders#index</h1>
<p>Find me in app/views/orders/index.html.erb</p>

We can render this view template as follows:

class OrdersController < ApplicationController
  def index
    render :action => 'index'
  end
end

As we mentioned earlier, the template could also be rendered using the convenience form of the render method:

class OrdersController < ApplicationController
  def index
    render 'index'
  end
end

Following is a portion of an idiomatic OrdersController controller with show, new and create actions:

class OrdersController < ApplicationController
  # GET /blogs/1
  def show
    @order = Order.find(params[:id])
  end
  
  # GET /orders/new
  def new
    @order = Order.new
  end
  
  # POST /orders
  def create
    @order = Order.new(params[:order])

    if @order.save
      redirect_to(@order, :notice => 'Order was successfully created.')
    else
      render :action => "new"
    end
  end
end

Notice how the create method renders the new.html.erb view template when validation fails. Calling render only renders a template and never executes the method body of another controller action. This means that you must correctly set up all of the context the view template requires in each controller action the view template will be rendered from.

Options

:layout

The name of the layout template to render from the app/views/layouts directory. For example, :layout => 'super_cool_thinger' Defaults to the current controller’s layout. You can disable rendering a layout by setting :layout => false

Rendering a Template

Rendering a template is exactly the same as rendering an action, but instead of looking for the view template in the controller’s view template directory, the view template is searched for using the application’s view template directory, app/views as the base. This is useful for sharing templates between different controllers, possibly with a app/views/shared view directory. Like rendering an action, you can use either the normal method of passing a hash of options to render or the more concise shortcut syntax. To render a template app/views/shared/index.html.erb with the regular hash syntax:

class OrdersController < ApplicationController
  def index
    render :template => 'shared/index'
  end
end

Or to render the very same template with the more concise shortcut syntax:

class OrdersController < ApplicationController
  def index
    render 'shared/index'
  end
end
Options

:layout

The name of the layout template to render from the app/views/layouts directory. Defaults to the current controller's default layout. You can disable rendering a layout by passing false.

Rendering a File

Rendering a file is the same as rendering an action or template, however, you can render any file on the file system. You pass the :file option an absolution filename to render. Here we render the /etc/hosts file from my system:

class HostsController < ApplicationController
  def list
    render :file => '/etc/hosts', :layout => false, :content_type => Mime::TEXT
  end
end

Warning

Never accept user input as the filename to be rendered when rendering a file. The :file option allows any accessible file on the file system to be read, such as /etc/passwd or sensitive files from your application's configuration.

Here we use curl to fetch the contents of the /etc/hosts file:

$ curl http://localhost:3000/hosts/list
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##

127.0.0.1	localhost
255.255.255.255	broadcasthost
::1             localhost 
fe80::1%lo0	localhost

As is apparent from the example, you need to be extremely careful when rendering with the :file option. You must avoid using user input for the filename or be very careful in validating and sanitizing the input.

Options

:layout

The name of the layout template to render from the app/views/layouts directory. Defaults to the current controller's default layout. You can disable rendering a layout by passing false.

Inline Rendering

Rendering inline templates is very uncommon as it means you're rendering a view fragment inline in the controller. This violates the principles of MVC and should generally be avoided, since it is trivial to extract the content into its own view template. If you do have a need for it, though, you just use the :inline option and provide the contents of the view template as a string:

class CustomersController < ApplicationController
  def show
    @customer = Customer.find(params[:id])
    render :inline => <<-ERB
      <%= div_for(@customer) do %>
        <p><%= @customer.name %></p>
      <% end %>
    ERB
  end
end

Here we render a div containing the customers name using the Ruby heredoc syntax. As you can clearly see, this would be better extracted to an ERB template. ERB is the default template type for inline templates. You can use the :type option to change the template handler used to render the template.

By default, inline templates are not rendered within a layout, so you'll have to pass in the layout name you want to use in the :layout option if you want your content rendered in a layout.

Options

:layout

The name of the layout template to render from the app/views/layouts directory. Defaults to false.

:type

The type of template handler to use to render the content. You can use any template handler registered in the application. Valid values in an application without additional template handlers installed include: :erb and :builder. Defaults to :erb.

Rendering Partials

If you’re displaying the same chunk of view code in multiple view templates, you need to refactor and use a partial instead. Partial templates, more commonly called partials, are reusable view snippets used to extract a portion of a view into a partial template when you need to use the same view fragment in two or more different templates. Partial templates are exactly the same as other templates except that they must be named with a leading _ character. If you had a partial ERB template for rendering a view snippet for an order you would name it _order.html.erb.

Rendering partials is typically done from within another view template. The only reason you would ever want to render a partial template in a controller is return an HTML fragment as the response to an AJAX call. The JavaScript library, jQuery by default in Rails 3, would make the request for the partial with an AJAX request and then update the page with the page fragment returned in the response. We’ll take a much closer look at partials in ???, but for now, we'll just stick to the basics of rendering partials.

Here we render the _user.html.erb from the controller's app/views/users view template directory:

class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
    render :partial => 'user'
  end
end

You can also let the rendering subsystem determine the name of the partial template for you by implicitly passing the object to the :partial option. Here’s what that looks like:

class UsersController < ApplicationController
  def index
    @user = User.find(params[:id])
    render @user
  end
end
Options

:collection

A collection of objects to render in partials. The partial template is rendered once for each object in the collection.

:layout

The name of the partial layout template to render from the app/views/layouts directory. Defaults to false.

:locals

A hash of local variables to be assigned to the partial template. A local variable is created in the partial template with the name of each key in the :locals hash.

Rendering Text

Rendering text is probably the most straightforward of the rendering options. The content passed in with the :text option is returned to the client as the response. By default, rendering text does not wrap the content in the controller's layout, so you'll need to manually specify a layout if that is desired. Let's look at a simple example for a PingController, which simply renders the text PONG. Something like this is useful to respond to monitoring services, such as Pingdom. Here is the controller:

class PingController < ApplicationController
  def index
    render :text => 'PONG'
  end
end

It is worth noting that the content type of the response is not automatically set to text/plain when rendering text. The content type defaults to text/html, but could be overridden either by the Accept header of the request or the :content_type option of the render call.

Options

:layout

The name of the layout template to render from the app/views/layouts directory. Defaults to false.

Rendering XML

Rendering an XML response couldn't be easier with Rails, especially with Active Model and Active Record’s object’s support for serializing themselves to XML. You can pass a string of XML markup to the :xml option or any object that defines a to_xml method. The XML renderer will automatically call the to_xml method for you if the object you pass in respond_to?(:to_xml).

Assuming a simple Customer model with name and birthdate attributes that can serialize itself to XML:

Customer.create(:name => "Mr. T", :birthdate => "1952-05-21")

Calling to_xml on the customer object returns the following XML string:

<customer>
  <name>Mr. T</name>
  <birthdate type="date">1952-05-21</birthdate>
  <id type="integer">1</id>
</customer>

You can explicitly call to_xml on the object to get the XML string:

class CustomersController < ApplicationController
  def show
    @customer = Customer.find(params[:id])
    render :xml => @customer.to_xml
  end
end

But it isn't required because the object implements a to_xml method:

class CustomersController < ApplicationController
  def show
    @customer = Customer.find(params[:id])
    render :xml => @customer
  end
end

When allowing the XML renderer to implicitly call to_xml for you, you can also pass any other options accepted by to_xml to the render call. For example, to change the root element from customer to person we would do the following:

class CustomersController < ApplicationController
  def show
    @customer = Customer.find(params[:id])
    render :xml => @customer, :root => 'person'
  end
end

Additionally, when rendering XML, the controller automatically sets the correct Content-Type header for the you, which we can see by viewing the headers with curl:

$ curl -I http://localhost:3000/customers/1.xml
HTTP/1.1 200 OK
Connection: close
Date: Sun, 23 May 2010 15:59:22 GMT
ETag: "d3d1de8feb536eb14aab247d613f44d6"
Content-Type: application/xml; charset=utf-8
X-Runtime: 0.038544
Content-Length: 0
Cache-Control: max-age=0, private, must-revalidate

Another handy way to generate XML is to create a hash of data and pass that to the :xml option. Hashes have a to_xml built-in thanks to Active Support, so the XML renderer does the right thing:

class CustomersController < ApplicationController
  def show
    render :xml => { :customer => "Jim" }
  end
end

For more information on XML serialization please see ???.

Rendering JSON

JSON, which stands for JavaScript Object Notation, is an object serialization format based on the JavaScript language that has recently gained a lot of momentum. The JSON format is a nice, human readable, lightweight data interchange format. Let's take a look at the same customer object in JSON format by calling to_json on it:

{"name":"Mr. T","birthdate":"1952-05-21","id":1}

Rendering JSON is more-or-less the same as rendering XML, as Rails provides excellent support for serializing objects to JSON. There is one difference in the way the JSON encoding works, though. Basically, any object can be encoded as JSON, so the JSON renderer return the passed in object as the response if the object responds to to_str. Otherwise the JSON encoder will encode the object and return the result. To return your object in JSON format simply pass it in to the render call as the :json option:

class CustomersController < ApplicationController
  def show
    @customer = Customer.find(params[:id])
    render :json => @customer
  end
end

As with the XML rendering, you can pass any of the options supported by the JSON encoder as options to the render call. For example, to only return the customer's name we could add the :only option:

class CustomersController < ApplicationController
  def show
    @customer = Customer.find(params[:id])
    render :json => @customer, :only => :name
  end
end

Again, from the HTTP headers, you can see that Action Controller does the right thing and sets the correct Content-Type headers for the response:

$ curl -I http://localhost:3000/customers/1.json
HTTP/1.1 200 OK
Connection: close
Date: Sun, 23 May 2010 20:33:20 GMT
ETag: "eb99282aedf4a9b4a22a6a99d7b21c64"
Content-Type: application/json; charset=utf-8
X-Runtime: 0.041876
Content-Length: 0
Cache-Control: max-age=0, private, must-revalidate

Just like with XML serialization, it can be convenient to pass the JSON renderer a hash of data, which it will automatically convert to a JSON string for you:

class CustomersController < ApplicationController
  def show
    render :json => { :customer => "Jim" }
  end
end

For more information on JSON serialization please see ???.

JSONP

The JSON renderer also supports adding a JSONP (JSON with Padding) callback to the returned JSON response. JSONP is a technique used to circumvent the same origin policy implemented by web browsers. The same origin policy ensures that client-side scripts can't make cross-domain requests. For more information see the

JSONP was created as a work around to the same origin policy because web browsers do permit the cross-domain inclusion of JavaScript files. JSONP works by including a JavaScript file with an associated query parameter that specifies the name of a callback function. The server then wraps the returned JSON data in a call to the specified function.

To support JSONP you simply have to pass along an additional :callback option to the render call:

class CustomersController < ApplicationController
  def show
    @customer = Customer.find(params[:id])
    render :json => @customer, :callback => params[:callback]
  end
end

We simply pass along provided callback from params[:callback] to the render call. You are not limited to naming the callback parameter :callback; you can choose any name you like. Let's use curl again to take a look at the returned JSON:

curl http://localhost:3000/customers/1.json?callback=showCustomer

This returns the following JSON representation of the object wrapped in the showCustomer function call, as we requested with the callback query parameter.

showCustomer({"name":"Mr. T","birthdate":"1952-05-21","id":1})

In a real-world implementation of JSONP, the client JavaScript would most likely dynamically inject a script element into the HTML page to make the JSONP request happen dynamically. The script tag would look something like this:

<script type="text/javascript" src="http://example.com/customers/1?callback=showCustomer"></script>

This is only a brief introduction into how to support JSONP callbacks in your application. For a more in-depth look at JSON and JSONP take a look at the Wikipedia JSON page.

Rendering JavaScript

Support for rendering JavaScript is similar to that for rendering XML and JSON. If the object passed in with the :js option responds to to_js then the result of the to_js call is used as the response body. If the object doesn't respond to to_js then the object is assumed to be a string containing JavaScript content. Where support for JavaScript differs than that for XML and JSON is that Rails does not automatically provide any predefined to_js methods. This means that you will have to define your own to_js method on the object if you want to take advantage of that functionality.

The most likely scenario is that you need to generate a bit of JavaScript on the server-side of your application to service an AJAX request from a link, such as a link at the bottom of the intro to an article to dynamically load the body of the article. The HTML representation of the show action is a view that shows an excerpt of the article with a link to load the rest of the content:

<%= truncate @article.body %>

<div id='read-more'>
<%= link_to "Read More", article_path(@article), :remote => true %>
</div>

We haven't covered linking yet, but the link_to call with the :remote => true option instructs the unobtrusive JavaScript library to issue an AJAX call to the show action of the ArticlesController when clicked. When the AJAX call arrives at the controller we make the call to render with the :js option:

class ArticlesController < ApplicationController  
  def show
    @article = Article.find(params[:id])
    render :js => "$('read-more').replace(#{@article.body.to_json})"
  end
end

The render call generates JavaScript code to replace the contents of the read-more div with the body of the article, which the Prototype library running in the browser will execute when it receives it. This will replace the div with the link with the body of the article. This is fully functional, but placing inline view code in the controller violates the MVC architecture. It would be recommended to create a view template for the JavaScript snippet and render that instead.

Using curl to request the JavaScript from the controller:

$ curl http://localhost:3000/articles/1
$('read-more').replace("Lorem ipsum dolor sit amet ...")

Inspecting the headers shows that Rails correctly set the Content-Type header of the response to text/javascript:

$ curl -I http://localhost:3000/articles/1
HTTP/1.1 200 OK
Connection: close
Date: Mon, 24 May 2010 03:40:23 GMT
ETag: "d58fcb69d89c7685861e4047f9658982"
Content-Type: text/javascript; charset=utf-8
X-Runtime: 0.048712
Content-Length: 0
Cache-Control: max-age=0, private, must-revalidate

Redirecting

HTTP redirects are a way to redirect the user's browser from one page to another. Browser redirection works by returning an HTTP status code in the Redirection 3xx range along with an URI in the Location response header. Specifically one of the following codes:

  • 301 Moved Permanently

  • 302 Found

  • 303 See Other

  • 307 Temporary Redirect

By default, the redirect_to method in Rails uses the 302 Found HTTP status code to indicate that the response to the request is found under a different URI and that the browser should issue a GET request to retrieve it. This is actually the definition of 303 See Other, but all browsers implement support for 302 Found as if it were 303 See Other.

It is a best practice in web applications to issue a redirect after a successful POST request. Having the browser request a new page with a GET request after a successful POST request prevents duplicate form submission, including annoying form resubmission warnings, stemming from use of the browser’s refresh and back buttons. If the browser fetches a new page with a GET request after the POST request then these problems are mitigated. In Rails the pattern is to redirect on success and render on failure.

The most common example you'll see in every Rails application is seen in RESTful controllers that respond to a POST request with a #create method that creates a resource and then redirect to #show for that resouce upon success. Likewise, PUT requests that hit an #update method redirect to the same #show method upon success. Here’s an example:

class BlogsController < ApplicationController

  # ...

  def create
    @blog = Blog.new(params[:blog])
    
    if @blog.save
      redirect_to(@blog, :notice => 'Blog was successfully created.')
    else
      render :action => "new"
    end
  end

  # ...
end

Redirecting Back

Redirecting back to the page that issued the request can sometimes be handy when there’s a form that’s rendered in multiple places. Rails makes it easy: redirect_to :back Note however that if there is no referrer, ActionController::RedirectBackError will be raised.

Response Headers

Action Controller goes to great lengths to do as much work as it can for you. This includes attempting to always set all of the correct HTTP headers in each response. However, there are times when you'll need to add arbitrary HTTP headers to a response. Rails provides full access to the HTTP response headers through the headers accessor, which is available in your controller actions. The headers accessor returns a hash-like object of HTTP headers that you can modify as see you see fit. To set or modify a header, simply access the headers as you would a hash where the key is the name of the HTTP header:

headers['Content-Type'] = 'text/plain'

Also, since the object is hash-like, feel free to use any other hash methods on it:

headers.keys # => ["Content-Type"]

Cookies

The HTTP protocol is stateless, meaning that from the perspective of the web server each request is completely independent of the request that preceded it. HTTP cookies are a solution to the HTTP protocol being stateless. Cookies can be set by a web application and delivered to the client's browser using the HTTP Set-Cookie header. Any cookies that are set by the application are then sent back to the application with each request by the client's web browser in the HTTP Cookie header. This allows the web application to act statefully. Maintaining state is possible because the web application can store information in the client's cookies and reference the data on subsequent requests.

One obvious example of using cookies to maintain state is session management. Sessions are implemented for you in Rails by storing the either the id of the session or the entire session in the cookie, depending on whether the application is using a client or server-side session store. We'll take a look a deeper look at sessions in the next section the section called “Sessions”.

Working with Cookies

Rails makes it quick and easy to access, modify, or delete cookies through a simple hash-like cookie jar accessible via the cookies accessor method. The cookies accessor method is available in both the controllers and views of an application. Let's take a look at storing and retrieving data from a cookie. In the simplest case, you just assign a value to a key in the cookie jar through the cookies accessor:

cookies[:timezone] = "Eastern Time (US & Canada)"

The key is the name of the cookie to be stored in the client's browser and the value is the value the cookie will have. More properties in point form:

  • When a cookie is set to a string value, the cookie's path is set to /, which means that the cookie is valid for the entire site of the application.

  • When set directly to a string value, the cookie is not given an expiry.

  • A cookie that does not have an expiry will automatically be expired by the browser when browser's session is finished.

  • A browser's session is finished which is when the browser is shut down.

Reading the value of a cookie is as simple as storing data in a cookie. Just access the cookie jar with key name of the cookie you want the value for:

cookies[:timezeone] # => "Eastern Time (US & Canada)"

Note

The cookies hash uses indifferent access, so symbols or strings as keys turn into the same thing: cookies[:timezone] and cookies['timezone'] are equivalent.

If you want to change any of the properties of the cookie being set then you need to assign the cookie as a hash instead of a string. For example, setting a login style preference cookie when a user accesses the /admin page of your application might look as follows:

cookies[:login_style] = { :value => 'openid', :path => '/admin', :expires => 10.years.from_now }

The cookie's value is set to openid, its path is set to /admin, which is the path of the currently requested page of the application, and the cookie is set to expire in 10 years.

Following is the complete list of properties that can be set on a cookie:

:domain

The domain the cookie is valid for. Useful for sharing a cookie between subdomains. Defaults to nil, which means that the client will use the requested domain as the domain for the cookie.

:expires

The expiration of the cookie as a Time object. Defaults to nil, which means the cookie is valid until the end of the current browser session.

:httponly

Whether or not client side scripts, such as JavaScript, have access to the cookie. Defaults to false, which means that client side scripts do have access to the cookie.

:path

The path the cookie is valid for. Defaults to /, which means that all paths are valid.

:secure

Instructs the client to only transmit the cookie over a secure HTTPS connection to the server. Defaults to false.

:value

The value to store in the cookie.

Deleting cookies is very simple as well. You call the delete method on the cookie jar object and pass in the key name of the cookie you want to delete:

cookies.delete(:login_style)

If you provided path or domain information when storing the cookie then you need to pass the same path or domain information as options to the delete call. For example, for the following cookie:

cookies[:login_style] = { :value => 'openid', :path => '/admin' }

You would have to delete this cookie with the associated path:

cookies.delete(:login_style, :path => '/admin')

Behind the scenes Rails instructs the browser to discard the cookie by setting the value of the cookie to an empty string and the expiration of the cookie to the long expired time of UNIX time 0, which is January 1, 1970.

Permanent Cookies

A permanent cookie is basically just a cookie that doesn't expire for a very, very long time. In Rails a permanent cookie is a cookie that doesn't expire for 20 years from the current time. Setting a permanent cookie using the permanent method of the cookie jar is just a clean way to set a 20 year in the future expiry date and it looks as follows:

>cookies.permanent[:login_style] = "openid"

This sets login_style cookie to have an expiry 20 years in the future. You access permanent cookies in the same way you access regular cookies:

cookies[:login_style] # => "openid"

Signed Cookies

By default, cookies stored by an application are stored in plain text and modifiable by the client of the application. By default, no effort is made to obfuscate or verify that the contents of the cookie have not been tampered with. This means that you definitely shouldn't store any sensitive information or anything that you wouldn't want the user of the application to be able to modify in a regular cookie. If you want to ensure that the contents of the cookie haven't been tampered with then you can use a signed cookie. Signed cookies are created in a similar way as permanent cookies, except that you use the signed method on the cookie jar.

Signing and verifying cookies relies upon a secret token that is assigned to Rails.application.config.secret_token. By default, Rails generates a 128 character token for you and assigns to the secret_token setting in config/initializers/secret_token.rb when you generate your application. Using the signed method on the cookie store instructs Rails to cryptographically sign the contents of the cookie with a Hash-based Message Authentication Code (HMAC). The Message Authentication Code (MAC) allows Rails to simultaneously verify both the data integrity and authenticity of the signed data. This prevents anyone without the secret key stored in config/initializers/secret_token.rb from tampering with the contents of the cookie. The contents of the cookie can still be viewed by the client, but not modified.

Warning

Just because you've signed a cookie doesn't mean that the signed cookie data can't be reused verbatim by an attacker that gets their hands on the contents of the cookie. The attacker can simply replay the stolen cookie data in an attempt to exploit your application.

Signing a cookie is very simple and looks as follows:

cookies.signed[:offer] = "Free Shipping"

Accessing signed cookies is the same as accessing regular cookies:

cookies[:offer] # => "Free Shipping

You can even combine the permanent and signed methods to get a permanent, signed cookie:

cookies.permanent.signed[:remember_me] = user.token

Sessions

Maintaining state between a user's requests is a requirement of most of today's web applications. Tracking the state of an application's user between HTTP transactions has many important uses including personalization of content, tracking desired items for purchase in a shopping cart, knowing which user is currently logged in, and many others.

Since HTTP is a stateless protocol by design, the server doesn't maintain any user information between requests. This means that it is up to the application framework or developer to provide session support.

Rails has complete support for sessions and makes maintaining user state easy through client or server based session stores. Rails supports several different session stores, allowing the developer to choose the type of session store that best meets the needs of the application.

Using the session

The session is used like a hash and is available through the session accessor in controllers and views. To store an object in the session assign a key in the session to the value to store:

session[:user_id] = @user.id

Above, just the user's id was stored in the session, but any object can be stored in the session:

session[:user] = @user

Any object can be stored in the session because Rails automatically serializes and deserializes session data to and from the session store using Marshal.dump and Marshal.load. However, in practice, you should only store simple objects like strings and numbers in the session for a few reasons:

  • Storing only lightweight, simple data in the session will help you avoid issues that might arise during deserialization of the stored objects from the session. Different Ruby versions have incompatible serialization approaches and Procs/lambdas for example have never played well with serialization.

  • Keeping a minimal amount of data in the session will also save on communication overhead for each request and response when you're using cookie session store.

Instead of storing entire user objects in the session, store just the user's id and load the user object on demand during the request.

Session data is retrieved by accessing the key in the session where the value was stored:

session[:user_id] # => 256700

To clear a particular key of the session you set the value to nil:

session[:user_id] = nil

To clear out the entire session you call:

reset_session

Note

Since there is an overhead required to maintaining a user's session, it can sometimes be desirable to disable sessions for the parts of an application where sessions aren't required or for clients, such as web crawlers, that don't use cookies. Fortunately in Rails 3, the session is lazy loaded. This means that the session won't be loaded unless it is accessed during a request.

Keep in mind that resetting the session will also wipe out any other data that Rails is storing in the session, such as the CSRF token or any flash messages that have been set.

Let's take a look at a more complete example of session use. The following methods are used to provide a current_user helper method to the application's controllers and views and also provides a method to facilitate loading the user from the session cookie:

class ApplicationController < ActionController::Base
  helper_method :current_user

  private
  def current_user
    @current_user
  end

  def current_user=(user)
    session[:user_id] = user ? user.id : nil
    @current_user = user
  end

  def login_from_session
    if session[:user_id]
      self.current_user = User.find_by_id(session[:user_id])
    end
  end
end

We're now going to take a look at the different session stores offered by Rails and how to configure them in your application.

Session Stores

A session store determines how and where session data is stored. Rails provides three different session stores that you can use in your application: the cookie store, the memcache store, and the Active Record store. By default, Rails uses the cookie session store. Let's take a closer look at each of the different session stores and why you might want to choose one over the other.

Cookie Store

The cookie session store keeps session data in a cookie in the client's browser. The cookie store is great because it requires no setup or configuration and is much faster than the other session stores. The session data is sent in a cookie with each request by the client and the server writes the session data in the cookie headers of each response, so it is a good idea to keep the session small to reduce the overhead of each request/response cycle.

Since the data is stored in a cookie, the maximum size of the encoded session data is limited to the maximum usable size of a cookie by all browsers, which is 4 KB. However, since the data is encoded and has a cryptographic signature attached to it, the most data you'll likely fit in the cookie store is around 2.5 KB. If you need to store more than around 2.5 KB of data you'd want to use one of the other session stores. If you do end up putting too much data in the session you'll end up triggering an exception when Rails processes the session data for the response.

Another thing to keep in mind is that the session data is readable by the client. The data is cryptographically signed by Rails using a Hash-based Message Authentication Code (HMAC) to prevent tampering, but the data is not encrypted. This means that you would never want to store sensitive private information in the session when using the cookie session store. If you must store private information in the session then you'd want to use one of the other session stores that maintains session data on the server.

Active Record Store

The Active Record session store keeps all session data in the database. There are two reasons you might want to use this session store. The first is if you need to store more data in the session than will fit in the cookie session store. The second is if you need to store confidential information in the session.

The maximum size of the data stored in the Active Record session store depends on the maximum size of the database column you choose to store the session data in. By default, Rails uses a :text column for storing the session data. When using MySQL, this stores around 64 KB of data.

Memcached Store

The Memcached session store is a server-side session store similar to the Active Record session store, but using Memcached for the back-end storage of the sessions. One major difference from the Active Record session store is that session data is not persistent in the Memcached session store. This is an inherent limitation of Memcached itself not being a persistent data store. The maximum size of session that can be stored in a Memcached server is 1 MB.

The reason you might want to use the Memcached session store is to leverage your existing Memcached infrastructure and to keep the load of sessions from impacting your database. You will still only want to use this session store if cookie session store is not adequate for your needs. Additionally, if anything happens to the Memcached process or the server that is running Memcached then the sessions that server contains will be lost. Unless your users won't notice losing their sessions then you'll probably not want to use the Memcached session store.

Configuration

Rails automatically configures your application with the cookie session store when you use the rails command to generate your Rails project. Customizing the configuration of the session store is very straightforward if you do need to stray from the default settings. The configuration of the session store is done in config/initializers/session_store.rb. There are two accessors on ActionController::Base for configuring a session store:

ActionController::Base.session_store

The session store to use. One of :cookie_store, :mem_cache_store, or :active_record_store. Defaults to :cookie_store.

ActionController::Base.session

The hash of session options for customization of the behavior of the session store. Options common to each session store are:

:key

The name of the cookie to store the session data in. Defaults to _session_id.

:domain

The domain the session cookie is valid for. Useful for sharing a session cookie between subdomains. Defaults to nil, which means that the client will use the requested domain as the domain for the session cookie.

:path

The path the session cookie is valid for. Defaults to /. Setting :path to nil instructs the client to use the requested path as the path for the session cookie.

:expire_after

How long from the time of the request to expire the session cookie in. The number of seconds provided, such as 1.hour, will be added to the current time at the time of the request. Defaults to nil, which means that the browser will discard the session cookie when it closes.

:secure

Instructs the client to only transmit the session cookie when it has an HTTPS connection to the server. Defaults to false.

:httponly

Prevents client side scripts from accessing the session cookie. This helps prevent cross site scripting (XSS) session hijacking attacks. Defaults to true.

:cookie_only

Helps to prevent session hijacking by only setting the id of the session from the value of the session id stored in the session cookie when using server side session stores. Defaults to true.

Following is an example of a config/initializers/session_store.rb file that configures a Rails application to use the Active Record session store with a couple of custom settings:

ActionController::Base.session_store = :active_record_store

ActionController::Base.session = {
  :key          => '_blog_session',
  :domain       => '.example.com',
  :expire_after => 1.week
}

Next we'll look at configuring each of the different session stores individually.

Cookie Store

The cookie store is the default session store used by Rails. The default configuration of the cookie store generated by the Rails application generator in the config/initializers/session_store.rb file looks like the following:

ActionController::Base.session = {
  :key         => '_blog_session',
  :secret      => 'fdd1077479a7a13a8f3346c952047494b37a2fb6203b9a4a0ec03'
}

Rails names the session :key based on the name of your application. In the example the name of the application is blog. A cryptographically secure :secret is also generated. The :secret option is required when using the cookie store. Also, the :secret was shortened in order to fit it on a single line, but is normally 128 characters. To generate a new cryptographically secure secret key you can use the rake secret task provided by Rails. Changing the :secret will invalidate all of your existing sessions, so use with caution.

There are two additional session options specific to the cookie session store:

:secret

The secret token used to cryptographically sign the session data with to prevent tampering. The secret needs to be random and greater than 30 characters in order to prevent dictionary attacks. The best way to generate a new cryptographically secure secret is to use the rake secret task provided by Rails.

Caution

Changing the session secret will invalidate all of your existing sessions.

The secret can also be a Proc object, for example: :secret => Proc.new { Account.current.secret_key }.

:digest

The cryptographic digest algorithm used to verify the integrity of the session data. Available options include: DSS, DSS1, MD2, MD4, MD5, MDC2, RIPEMD160, SHA, and SHA1. If the version of OpenSSL your installation of Ruby was compiled against is sufficient you may also have access to: SHA224, SHA256, SHA384, and SHA512. The default is SHA1.

Active Record Store

To configure your application to use the Active Record session store place the following line in config/initializers/session_store.rb:

ActionController::Base.session_store = :active_record_store

Before you can use the Active Record session store you'll need to generate and run a database migration. Run the following rake task to generate the migration:

$ rake db:sessions:create

The migration creates a sessions table with session_id, data, created_at, and updated_at columns. Run the migration to create the sessions table in your database:

$ rake db:migrate

There is also a rake task for clearing all sessions in the table:

$ rake db:sessions:clear

When using the Active Record session store each session will be stored in your database. If your application gets a lot of traffic there will end up being a substantial number of session stored in the database, which can negatively affect your application's performance. The simplest way to clear out old sessions would be to use rails runner from a cron task. The following command run from the root of your Rails project will delete all sessions in the production database that haven't been updated in the past 30 days:

$ rails runner -e production \
"ActiveRecord::SessionStore::Session.delete_all(
  [ 'updated_at < ?', 30.days.ago ]
)"

Memcached Store

To configure your applications to use the Memcached session store place the following line in config/initializers/session_store.rb:

ActionController::Base.session_store = :mem_cache_store

You'll need to have Memcached servers accessible to your Rails application in order to use the Memcached store. See the section called “Memcached” for instructions on setting up Memcached on your machine.

There are several additional session options specific to the Memcached session store:

:cache

The MemCache instance to use instead of creating a new connection to the servers defined by :memcache_server. This allows you to reuse an existing connection to Memcached from elsewhere in your application.

:failover

Whether or not the client should fail over to another Memcached server if the first server is unavailable. Defaults to true.

:logger

The logger to use for logging debug and info output. Defaults to nil. Set to Rails.logger to use the same logger as is configured for the rest of your Rails application.

:memcache_server

A server or list of Memcached servers to use for storing sessions. Each server is specified either by its hostname or its hostname and port in the format of hostname:port. Defaults to localhost:11211.

:namespace

The Memcached namespace to store the sessions under. Defaults to rack.session.

:timeout

The time-out to use for socket reads. Defaults to 0.5 seconds. You can set the time-out to nil to disable time-outs, however this is not recommended.

Flash

The flash in Rails is a hash-like object for passing objects from one request to the next. The flash is most commonly used to pass notice of success or failure of an operation to the user during the next request. Since an instance variable wouldn't survive from one request to the next, the flash utilizes the session to temporarily store the objects. Objects placed in the flash are available to the next requested action and are then cleared by Rails.

The most common use of the flash is displaying status messages in an application, either in the same controller action or after a redirect. Rails assumes that you'll be using the :notice key of the flash for notices and success type messages and the :alert key for warnings and errors. If you stick with this convention you get to use a few additional helpers that are built into Rails, this is just the default. You are free to use whatever keys are suitable to your application in the flash.

Storing Objects in the Flash

Rails assumes that you'll be using the :notice key in the flash for neutral or positive status messages and assumes you'll be using the :alert key for warnings, alerts, or other errors. As mentioned earlier, you are not restricted in any way as to which flash keys you use in your application. Let's take a look at a few simple examples of setting the flash.

Note

Unlike some other hash-like objects in Rails that allow for indifferent symbol or string access to keys, the flash is not one of them. In the flash :notice and "notice" are two distinct keys. In practice most Rails applications use symbols for keys when accessing the flash.

The following example shows idiomatic use of using the flash to display a status message to the user after successfully creating an order:

class OrdersController < ApplicationController
  
  def create
    @order = Order.new(params[:order])
    if @order.save
      redirect_to @order, :notice => "Successfully created the order"
    else
      render :action => "new"
    end
  end

end

In the example the flash was set using the :notice option in the redirect_to call. This is equivalent to setting the :notice key in the flash directly:

flash[:notice] = "Successfully created the order."

You can also set and access the flash using the notice and alert convenience accessors provided by Rails:

flash.notice = "Successfully created the order."
flash.notice # => "Successfully created the order."

flash.alert = "An error occurred processing your request"
flash.alert # => "An error occurred processing your request"

The redirect_to method was given the ability to modify the flash itself because the majority of the calls to the flash in a Rails application occur on the line directly preceding the redirection.

The reason that only the flash :notice is being set in the example is because the else condition of the action doesn't issue a redirect. This means that the view will have full access to the @order instance variable and will likely be displaying the errors generated by the failed Order#save call.

Displaying notices with flash[:notice] is by far the most common use of the flash in a Rails application. Displaying errors an warnings is also necessary in an application, but it is less common when using Active Record because Active Record objects support validations and error messages that can be directly shown in the view template. This makes setting a flash[:alert] message redundant in most cases. Setting a flash[:alert] message is more common for handling unexpected errors that could occur in an action when dealing with code that could raise exceptions or fail unexpectedly. For example:

class DiscountsController < ApplicationController
  rescue_from DiscountService::Error, :with => :unavailable

  def index
  end
  
  def show
    @discount = DiscountService.lookup(params[:id])
  end

  protected
  def unavailable
    redirect_to discounts_url, :alert => "The discount service is temporarily unavailable"
  end
end

Note

The flash is stored in the session. This means that if you delete or reset the user's session then you'll also be wiping out any stored flash messages.

In this example the DiscountEngine.lookup in the show method connects to an external discount web service. In the event that the service is not available at the time of the request and a DiscountEngine::Error is raised then the rescue_from declaration handles the exception and invokes the unavailable method. unavailable redirects the user back to the index action and sets flash[:alert] to the error message, which can then be displayed to the user in the following request.

As mentioned earlier, if you want to use keys other than :notice and :alert in your application then feel free to do so. You can set any key you want in the flash:

flash["warning"] = "Your account is running low on resources"
flash[:error] = "Could not connect to the search server"

You can also use any flash key with redirect_to by passing in a :flash hash. This is best illustrated with an example:

redirect_to account_url, :flash => { :warning => "Your account has been suspended" }

Now that we've taken a look at storing objects in the flash we can move on to how to display the flash in your view templates.

Accessing the Flash from View Templates

The flash is accessible within your views through the flash method, which returns an instance of the flash. You can display the contents of the notice placed in the flash in ??? like follows:

<% if flash[:notice] %>
  <p class="notice"><%= flash[:notice] %></p>
<% end %>

Rails provides the notice and alert methods as convenience accessors to flash[:notice] and flash[:alert] respectively. The following is equivalent to the previous example, but uses the convenience accessor:

<% if notice %>
  <p class="notice"><%= notice %></p>
<% end %>

Rails assumes that you'll be using flash[:notice] for success or other messages and flash[:alert] for failure or alert messages. You're free to use whatever scheme you choose, but if you follow the Rails idioms then you get the convenience of the notice and alert convenience options and methods in redirect_to and your views respectively.

Limiting an Object to the Current Request

Sometimes you want to use the flash to pass a message or object within the current request, but not have the message available to the next request. This is possible using the now method of the flash. Using flash.now is exactly the same as using flash, but with the call to now tacked on:

flash.now[:failure] = "Oh no, something horrible happened!"

Objects placed in the flash using flash.now are accessed in the same way as other objects in the flash, but are cleared out of the flash at the end of the current request:

flash[:failure] #=> "Oh no, something horrible happened!"

Keeping the Flash for an Extra Request

Once in a while you may want to keep the contents of the flash for an extra request. This is possible using the keep method. You can keep the entire flash or just a single key in the flash. To keep the entire flash, just call keep on the flash with no arguments:

flash.keep

Or you can pass in the key of the flash that you want to keep for the next request:

flash.keep(:notice)

Discarding the Flash at the End of the Current Request

You can also discard the entire flash or certain keys from the flash. This means that the entire flash or specific keys from the flash will be removed from the flash at the end of the current request. To discard the entire flash you call discard with no arguments:

flash.discard

You can also pass the key you want to discard to only get rid of a particular object in the flash:

flash.discard(:notice)

Controller Callbacks

Action Controller supports the execution of callbacks, known as filters, before, after, or both before and after a controller action. The filters are called before filters, after filters, and around filters respectively. Filters are excellent for executing code or methods before some or all of a controllers actions. This prevents you from having to repeat yourself by placing redundant code at the start or end of many controller actions.

Tip

If you see common code or methods called in multiple controller actions in a controller you should consider using filters to do the work for you. This will reduce the duplication and improve the quality of your controller actions.

Filters are commonly used for tasks such as authentication, setting user specific settings, such as the user's timezone, and other initialization or finalization tasks. In this section we'll take a look at the types of filters available, the different ways of defining filters, as well as some more advanced use of filters.

Filters

Before filters are by far the most common type of controller callback in Rails applications. The are executed before a controller action.

Let's take a look at a simple “before filter” that ensures that there is a logged in user to the application:

class ApplicationController < ActionController::Base
  before_filter :login_required

  private
  def login_required
    redirect_to new_session_url unless logged_in?
  end
end

This code instructs the controller to call login_required before every controller action in the application. Since we put the code in ApplicationController, which is the parent class of all controllers in a Rails application and filters are inherited to their subclasses, we've effectively required a logged in user for all controller action in the application.

Unfortunately, if every single controller action in the application is protected by the filter then there will be no way for the user to actually log in to the application. Fortunately, you can tell a controller not to run a before filter using the skip_before_filter method. Let's take a look at the SessionsController, which we'll use for logging the user into and out of the application:

class SessionsController < ApplicationController
  skip_before_filter :login_required

  def new
    # display the login form
  end

  def create
    # authenticate the user
  end

  def destroy
    # log the user out of the application
  end
end

SessionsController is a subclass of ApplicationController, which means that, by default, the before filter we previously defined to execute login_required will still execute before each controller action. This would prevent the user from ever logging into the application and, the way we've written it, would actually cause a redirection loop. The solution is the skip_before_filter call on line 2 of the SessionsController. This instructs the controller to not execute the login_required before filter at all in this controller. This allows the user to log in and log out of the application without triggering the login_required filter.

After and Around Filters

More filters are available to you beyond just before_filter; in particular: after_filter and around_filter.

After filters are executed after a controller action. After filters are less commonly used in a Rails application, but could be used for tasks such as compressing the response body, cleaning up resources, or logging of the generated response body. Here’s an example:

class ApplicationController < ActionController::Base
  after_filter :log_activity

  private
  def log_activity
    # ...
  end
end

Around filters are executed both before and after a controller action. You might need this when loading something transactionally and need to ensure that some activity happens after the action. Here’s an example:

class ApplicationController < ActionController::Base
  around_filter :load_something_special

  private
  def load_something_special
    # ...
    begin
      # load something special
      yield
    ensure
      # reset special things
    end
  end
end

Defining Filters

Filters can be defined in three different ways: by passing a symbol as a method reference, by passing an external object, or by passing the filter a block. The most commonly used method is passing a symbol as a method reference:

class AccountsController < ApplicationController
  before_filter :ensure_account_owner

  private
  def ensure_account_owner
    redirect_to login_url unless current_user.account_owner?
  end
end

As you can see from the example, the symbol passed to before_filter is the name of the method to execute. The method name can be any instance method defined for the controller. Making the method private indicates that the method is not a public controller action and prevents Rails from routing requests to it.

The second method of filtering involves passing an instance of an object to the filter definition that responds to a method named filter. Most commonly, you'd pass in a class constant:

class AccountsController < ApplicationController
  before_filter AccessControlFilter
end 

Then you'd have an AccessControlFilter class that has a static filter method:

class AccessControlFilter
  def self.filter(controller)
    redirect_to login_url unless current_user.account_owner?
  end
end

You could also pass in an instance of an object with a filter instance method:

class AccountsController < ApplicationController
  before_filter AccessControlFilter.new
end 
class AccessControlFilter
  def filter(controller)
    # verify the user is the account owner 
  end
end

Lastly, you can pass the filter a block to the filter call:

class AccountsController < ApplicationController
  before_filter {|controller| # stuff stuff stuff }
end

As you can see from the example, the block receives the instance of the controller as its first block argument. The block format is useful for simpler cases where the code required is compact and doesn't necessary need to be factored out into its own method.

Limiting Filters to Particular Actions

It can be very useful to limit when a filter runs to a subset of the actions in a controller. This is easily done using the :only and :except options in the call to before_filter, after_filter, or around_filter.

:only

A symbol or array of symbols representing the only controller action or actions that should execute the filter.

:except

A symbol or array of symbols representing the controller action or actions that should not execute the filter.

Sending Files

Sending files through Rails because they can’t be hosted on a public, static URL is a common requirement for web applications. Here’s how to do it:

send_file '/path/to/your_awesome_special_file.zip'

In the context of the controller, it would look like this:

class ProductsController < ApplicationController
  before_filter :authenticate

  def download_the_thing
    send_file '/path/to/your_awesome_special_file.zip'
  end

  ...

end

Pretty straightforward stuff. Note that if your web server supports the X-Sendfile header, Rails will use it by default, allowing the file-sending Rails process to serve other requests, and not be stuck just sending the file.

The send_file method also lets you manually specify an HTTP content type (Rails will guess based on the file-extension otherwise), whether to display the file inline or have it be downloaded, the status code, and whether you want the browser to guess the filename from the URL. See the Rails API docs for more detail.

Warning

Sanitize the filepath if you’re taking it from user-input/a params variable, or it becomes really easy for a malcious user to access anything on the filesystem the Rails app can access.

Site last updated on: December 11, 2012 at 10:21:28 PM PST
Cover for Rails 3 in a Nutshell

View 1 comment

  1. espinet – Posted June 12, 2010

    "but it can also useful to return" should be "but it can also be useful to return"?

Add a comment

View 1 comment

  1. fxn – Posted Aug. 18, 2010

    s/without/with/

Add a comment

View 1 comment

  1. Shripad – Posted Aug. 27, 2010

    Duplicate entry.

Add a comment

View 1 comment

  1. OdorcicD – Posted Dec. 13, 2010

    JSON includes the root by default

Add a comment

View 1 comment

  1. fxn – Posted Aug. 18, 2010

    This has changed, in Rails 3 if "/" appears anywhere in the Accept header, and there's no format segment, you'll go through format.html. There's no content negotiation in tha case.

Add a comment

View 1 comment

  1. Shripad – Posted Aug. 27, 2010

    "Sessions are implemented in Rails by storing the either the id..." should be "Sessions are implemented in Rails by storing either the id..."

Add a comment

View 1 comment

  1. Shripad – Posted Aug. 27, 2010

    Should be: cookies[:timezone]

Add a comment

View 3 comments

  1. davesailer – Posted Oct. 20, 2009

    "unless you use access it" -> "unless you access it" or "unless you use it"?

  2. Special_Dragonfly – Posted Nov. 9, 2009

    Or perhaps "unless you use/access it..."

  3. codyfauser – Posted Jan. 8, 2010

    Thanks, typo fixed.

Add a comment

View 1 comment

  1. davesailer – Posted Oct. 20, 2009

    Undefined: "CSRF token" Undefined: "flash messages"

Add a comment

View 2 comments

  1. cube – Posted Nov. 6, 2009

    I have a feeling that this code works but it's design is broken, especially the 'trick' with setting @current_user to false. And using .find_by_id instead of just .find can lead users reading this code to bad habits.

  2. codyfauser – Posted Jan. 8, 2010

    The code is actually very clever, as it prevents the SQL query from being executed more than once. Perhaps it is too clever a code sample for this section, though.

Add a comment

View 3 comments

  1. davesailer – Posted Oct. 20, 2009

    "and HMAC" Did you mean "an HMAC", and if so, what is it?

  2. Special_Dragonfly – Posted Nov. 9, 2009

    an alternative: "The data is cryptographically signed by Rails using ... and HMAC to prevent tampering". In which case, what is the other thing AND what's HMAC?

  3. codyfauser – Posted Jan. 8, 2010

    Clarified and fixed, thank you.

Add a comment