#395 Action Controller Walkthrough pro

Do you ever wonder what goes on behind the scenes when a Rails controller handles a request? What do the "render" and "redirect_to" methods do exactly? This and more covered as we walk through the code of ActionController::Base.

Whenever we use a Ruby gem in one of our applications it’s a good idea to take a look at its source code so that we can get an understanding of how it works. Doing this can really help when we’re debugging problems in our applications. Reading other people’s source code is also a good way of improving your own Ruby code and the chances are that you’ll pick up some interesting tricks along the way. In this episode we’ll be diving into the Rails source code to get a better understanding of how controllers work and how they handle requests. We’ll be using a blogging application to help us with this and focussing on its ArticlesController which is a fairly standard RESTful controller with the standard seven actions.

Looking at ActionController

To start we’ll find the find the relevant source code. As with most controllers ours inherits from ApplicationController which in turn inherits from ActionController::Base and this base class is defined in the Rails source code. To get this we can clone its Git repository by running this command:

terminal

$ git clone https://github.com/rails/rails.git

Once this command has finished we can move into the rails directory. This will be the master branch which is currently the upcoming Rails 4 release. We want to browse the version of Rails that we’re using in our application so we’ll use git checkout to get the right branch. We can then open the actionpack/lib directory which is where the controller-related code is.

terminal

$ cd rails
$ git checkout v3.2.9
$ cd actionpack/lib/

In the action_controller/base.rb file we’ll find the source code for the class that our app’s ApplicationController inherits from. This class inherits from another class called Metal, defined in a metal.rb file, and Metal inherits from AbstractController::Base. It’s these three classes that we’ll be focussing on in this episode.

These three files don’t contain all the methods that we have available to us in the ArticlesController. If we call ancestors on it we’ll see that there are a large number of modules that are included in to our controller and these have many methods defined in them. If we subtract the included modules we’ll see the inheritance chain.

How ActionController Handles Requests

Now that we’ve located the relevant source code we can start to walk through what happens when our Rails application receives a request. Before it reaches a controller the request goes through a number of pieces of Rack Middleware. (We walked through how a request is handled by Middleware in episode 319.) After this it’s handed off to our application’s routes and this was covered in more detail in episodes 231 and 232. Rails’ routing behaviour is handled by ActionDispatch. If we look at the RouteSet class we’ll see its dispatch method and it’s here that the request is handed off to the appropriate controller.

This is done by calling the controller’s action method, passing in the action’s name and calling call on that. Back in our example application’s console we can demonstrate this. If we call action on our ArticlesController and pass in the name of an action we’ll get a proc object returned. This object acts as a Rack application and we can process the request through it.

This method takes a second argument so that we can customize the class that’s used to represent the request. The first thing it does is call middleware_stack.build, passing in the action’s name and a block. The build method loops through all the middleware defined on the controller, checks if they apply to the action being requested then builds our Rack app through it.

This means that each controller has its own mini middleware stack that we can apply to a given action and which is completely separate from the middleware stack that applies to our entire application. This stack is empty by default but we can easily add to it by calling use in a controller and passing in a middleware class. We can also restrict the actions that this is applied to.

If we try to visit a record that doesn’t exist now the error that’s raised will be handled by Rack::ShowExceptions middleware instead of the Rails one.

If we visit the edit action for the same non-existent article we’ll see the standard Rails error page as the middleware only applies to show. This is really useful: we could use it with Rack::SSL or some authentication or authorization middleware when we need to add that middleware to a specific set of actions.

After our request has passed through this middleware it makes a new instance of our controller and calls dispatch on it, passing in the action’s name and an instance of the request which is generated by passing in the Rack environment. The dispatch method is defined in the same file and it stores some details in instance variables, processes the action then calls to_a on it which returns the response.

The method_for_action method checks to see if an action exists or if a method called action_missing exists. For the latter case a _handle_action_missing method is called which calls action_missing passing in the action name. This means that if we define action_missing in our controller we can use it like method_missing so that a controller can handle any action.

Back in the process method if we have a valid action process_action will be called. This in turn calls send_action which is an alias to the send method. This means that the action will be called on the controller.

The Included Modules

We now know how an action in our controller is called but this isn’t the full story. There’s a lot of additional behaviour that Rails is mixing in through modules. Let’s take another look at ActionController::Base. This file includes a large number of modules which means that our app’s controllers will inherit all the modules’ behaviour. Some of these are in AbstractController, but most of them are in action_controller/metal. We’ll take a look at some of these modules, starting with RackDelegation. This is a small module but it’s an important one. It overrides the dispatch method that we saw earlier in the ActionController::Metal class and adds a third argument that accepts a response object.

The response is set to an instance variable so that it’s remembered and whenever we set the response body in our controller this response object is set. If we look back in the Metal class at the to_a method this checks for that response object. If it wasn’t for the RackDelegation module this response would be nil. RackDelegation delegates several methods to @_response. This ties in nicely to the Redirect module which we’ll look at next.

The Redirect module defines the redirect_to method and when we call redirect_to in a controller action this is the code that’s called.

This method sets the status, location and response_body which will all be delegated to the response object thanks to RackDelegation. One method that’s often used alongside redirect_to in a controller action is render. This is defined in the Rendering module.

There isn’t much in this method. Most of the logic is done in the call to super which calls the same method in the AbstractController class. This has its own Rendering module with a render method that look like this:

This method sets the response_body to the output of a render_to_body method. In turn render_to_body calls a _render_template method and this delegates to a view renderer. This is defined in the same class an it instantiates a new instance of ActionView::Renderer. This will delegate all the rendering behaviour to ActionView which we won’t cover in this episode but which will be covered in a future one.

We now know that calling redirect_to or render simply sets some details on the response object that’s returned from a Rack application at the end of a controller action. What about actions that don’t call either of these methods, such as this new action?

/app/controllers/articles_controller.rb

defnew@article = Article.new
end

Actions like these that don’t explicitly render a view are handled by the ImplicitRender module. This overrides the send_action method in the AbstractController::Base class and this calls default_render unless we already have a response_body. This causes the view matching the action to be rendered. This module also handles the behaviour for rendering an action when no method is defined and only a template exists.

That’s it for the modules that we’ll cover in this episode but there are a lot more that you’re encouraged to explore on your own. Most of them are self-explanatory based on their names and they handle things such as cookies, flash messages, caching, HTTP authentication. There are other modules in the abstract_controller directory. For example callbacks.rb here is used to for before and after filters.