initialize! does not exist on Rails::Application but thanks to a method_missing on the Railtie class (which is one of the ancestors of Rails::Application), calling initialize! will create an instance of the application and forward the method invokation to it.

This is the middleware stack, this object is composed of middlewares, each middleware is instantiated with the one that follows him in the stack. The last middleware is instantiated with the actual rack app.

Invoking call on this object will go through all the middlewares and the app.

Indeed, Rails is composed of several middlewares used to handle the request.

Here is the middleware stack of a pristine rails 4.1 app :

use Rack::Sendfile
use ActionDispatch::Static
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007fbaa3b9d718>
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
run MyApp::Application.routes

What’s important here is the last line : run MyApp::Application.routes

# Defines the routes for this engine. If a block is given to
# routes, it is appended to the engine.
def routes
@routes ||= ActionDispatch::Routing::RouteSet.new
@routes.append(&Proc.new) if block_given?
@routes
end

Ok, let’s look at the call method of ActionDispatch::Routing::RouteSet.new, are we done yet? I don’t think so …

Rails then normalizes the params, and tries to get a controller from them. The goal is to retrieve the controller class from params[:controller]

If the controller is not found, Rails will return an empty response with the HTTP status code 404. The X-Cascade header is a Rack convention to signify that another middleware can go ahead and tries to render a webpage. In our case it will most likely be ActionDispatch::ShowExceptions, go ahead and read its code, it’s pretty straight forward.

At this point, the controller variable contains a controller class, HomeController for example.

Rails controllers are instances of ActionController::Base, which itself inherits from ActionController::Metal.

ActionController::Metal Is the simplest way of using controllers in rails, you just have to create a class that inherits from it, define actions, each action can set the response body with self.response_body = 'foo', and it’s ready to roll.

The cool thing is that the action method of ActionController::Metal returns a rack endpoint for the current controller and a given action :

# Returns a Rack endpoint for the given action name.
def self.action(name, klass = ActionDispatch::Request)
middleware_stack.build(name.to_s) do |env|
new.dispatch(name, klass.new(env))
end
end

This way, the dispatch method takes an action as its parameter and has to return our well-deserved array of status code, headers, and response body !

def process(action, *args)
@_action_name = action.to_s
unless action_name = _find_action_name(@_action_name)
raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
end
@_response_body = nil
process_action(action_name, *args)
end
def process_action(method_name, *args)
send_action(method_name, *args)
end
# Actually call the method associated with the action. Override
# this method if you wish to change how action methods are called,
# not to add additional behavior around it. For example, you would
# override #send_action if you want to inject arguments into the
# method.
alias send_action send

First, Rails will check the action name is correct, set its response body to nil, and call the method corresponding to the action (that’s what process_action does basically)

Yes !

We did it ! We followed the request from the beginning to the final action call.

Wait a minute … This won’t render anything if we don’t explicitly use self.response_body =, how come my rails app works ?

That’s because our controllers are instances of ActionController::Base, that adds tons and tons of goodies over the simple and poor ActionController::Metal.

ActionController::Base includes lots of modules, I’ll just talk about two of them that are important in our case.

The first one is ActionController::Rendering (and its sibling AbstractController::Rendering) I won’t go over the details but their roles is to provide the render method that will search the proper templates and render them.

This module overrides the send_action method, used when calling the action, to call render if it has not been called already.

This way, even if you don’t call render, Rails catches up and calls it for you.

Conclusion

Rails is automagical, but Rails is complex as well, part of it is because each part of Rails is supposed to be usable standalone. That’s nice, but it gives headaches when reading the code. You have to expect methods are overridden in one of the dozens of modules included in the class you’re reading.