# Request Lifecycle

  • Introduction

    When using any tool in the "real world", you feel more confident if you understand how that tool works. Application development is no different. When you understand how your development tools function, you feel more comfortable and confident using them.

    The goal of this document is to give you a good, high-level overview of how the Rails framework works. By getting to know the overall framework better, everything feels less "magical" and you will be more confident building your applications. If you don't understand all of the terms right away, don't lose heart! Just try to get a basic grasp of what is going on, and your knowledge will grow as you explore other sections of the documentation.

  • Rails application’s entry point

    The config.ru consists of the two lines of code:

    require_relative 'config/environment'
    
    run Rails.application
    
    1
    2
    3

    The first line loads the config/environment.rb file, and in the second line, we are passing the instance of our application to the run method that is available out of the box. If you named your application as Guestbook, then the instance of your application would come as Guestbook::Application.

    Then the run method is provided by Rack - API for Ruby frameworks to communicate with web servers. Rack informs the web application about the request using the call method.

    The call method implemented by the web application has to accept one argument, the env hash and has to return an array with three elements - status, headers, and response body.

  • In the middle of the request process

    We are in the middle of the request process - the server accepted the request and passed it to the application. It’s a perfect moment to mention the middleware.

    The middleware, as the name suggests, is a code that is executed in the middle.

    You can see the list of middlewares used by Rails by using the following command:

    bin/rails middleware
    
    1

    The list is quite long. One of the middlewares that are shipped with Rails by the default is ActionDispatch::RequestId. If we would look into the source of that class, we would see that it implements the call method that accepts the env hash - just like Rack expects the middleware class to implement.

    The initializer of the class also accepts the header argument. If this argument is passed, it looks for this header values as a request id; otherwise, it generates a unique string using SecureRandom.uuid method.

    The last position on the middlewares list is Guestbook::Application.routes (if you named your application as Guestbook) - this is where our application is triggered.

  • Routes

    Because the run method is invoked on Guest::Application.routes instance, this class implements the call method. Let’s take a closer look at it.

    Guest::Application.routes returns instance of ActionDispatch::Routing::RouteSet class. This class is responsible for matching the request path with routes defined in the config/routes.rb file.

    When the route is matched, the new instance of the controller is created, and the action method is invoked. Before it happens, the request and response objects are saved because they must be available in any place of the controller.

    After the controller’s action is executed, the flash message is set if needed, and the response array is returned.

  • Controller’s middleware

    We already discussed the middleware, but since we are talking about the controller structure, it is worth mentioning that each controller can have its list of middlewares.

    The same rules apply as before - our middleware class has to implement the call method that takes the env hash as an argument, and the initialize method has to accept the app argument and optional arguments for configuration purposes

  • A moment before rendering

    I mentioned that before the action is rendered, response and request object are saved to be accessible in the controller. It happens in the dispatch method from the ActionController::Metal class

    The most exciting part of this method is the execution of the process method. This method is also defined in multiple ancestors from our controller class.

    If you would like to list all ancestors for the given controller, you can do it via UsersController.ancestors.

  • Rendering

    The final step in the request cycle is the rendering of the view for the action. The result of rendering is stored in the response.body variable.

    As you may remember, the response array contains three objects: response code, response body, and headers. The content-type header depends on the kind of view, and the response code depends on whether the given action was found and the request was accepted. The rendering itself is a larger topic and deserves its article so that I won’t go more in-depth right now.