# Learn-Rails-by-Reading-Source-Code
[](https://github.com/996icu/996.ICU/blob/master/LICENSE)
[](https://996.icu)
## Table of Contents
* [Part 0: Before reading Rails 5 source code](#part-0-before-reading-rails-5-source-code)
* [What will you learn from this tutorial?](#what-will-you-learn-from-this-tutorial)
* [Part 1: Your app: an instance of YourProject::Application](#part-1-your-app-an-instance-of-yourprojectapplication)
* [Part 2: config](#part-2-config)
* [Part 3: Every request and response](#part-3-every-request-and-response)
* [Puma](#puma)
* [Rack apps](#rack-apps)
* [The core app: ActionDispatch::Routing::RouteSet instance](#the-core-app-actiondispatchroutingrouteset-instance)
* [Render view](#render-view)
* [How can instance variables defined in Controller be accessed in view file?](#how-can-instance-variables-defined-in-controller-be-accessed-in-view-file)
* [Part 4: What does `$ rails server` do?](#part-4-what-does--rails-server-do)
* [Thor](#thor)
* [Rails::Server#start](#railsserverstart)
* [Starting Puma](#starting-puma)
* [Conclusion](#conclusion)
* [Exiting Puma](#exiting-puma)
* [Process and Thread](#process-and-thread)
* [Send `SIGTERM` to Puma](#send-sigterm-to-puma)
## Part 0: Before reading Rails 5 source code
1) I suggest you to learn Rack [http://rack.github.io/](http://rack.github.io/) first.
In Rack, an object with `call` method is a Rack app.
So what is the object with `call` method in Rails? I will answer this question in Part 1.
2) You need a good IDE which can help for debugging. I use [RubyMine](https://www.jetbrains.com/).
### What will you learn from this tutorial?
* How does Rails start your application?
* How does Rails process every request?
* How does Rails combine ActionController, ActionView and Routes together?
* How does Puma, Rack, Rails work together?
* What's Puma's multiple threads?
I should start with the command `$ rails server`, but I put this to Part 4. Because it's a little bit complex.
## Part 1: Your app: an instance of YourProject::Application
Assume your Rails app's class name is `YourProject::Application` (defined in `./config/application.rb`).
First, I will give you a piece of important code.
```ruby
# ./gems/railties-5.2.2/lib/rails/commands/server/server_command.rb
module Rails
module Command
class ServerCommand < Base
def perform
# ...
Rails::Server.new(server_options).tap do |server|
# APP_PATH is '/path/to/your_project/config/application'.
# require APP_PATH will create the 'Rails.application' object.
# Actually, 'Rails.application' is an instance of `YourProject::Application`.
# Rack server will start 'Rails.application'.
require APP_PATH
Dir.chdir(Rails.application.root)
server.start
end
end
end
end
class Server < ::Rack::Server
def start
#...
# 'wrapped_app' will get an well prepared app from `./config.ru` file.
# 'wrapped_app' will return an instance of `YourProject::Application`.
# But the instance of `YourProject::Application` returned is not created in 'wrapped_app'.
# It has been created when `require APP_PATH` in previous code:
# in Rails::Command::ServerCommand#perform
wrapped_app
super # Will invoke ::Rack::Server#start.
end
end
end
```
A Rack server need to start with an `App` object. The `App` object should have a `call` method.
`config.ru` is the conventional entry file for Rack app. So let's look at it.
```ruby
# ./config.ru
require_relative 'config/environment'
run Rails.application # It seems that this is the app.
```
Let's test it by `Rails.application.respond_to?(:call)`, it returns `true`.
Let's step into `Rails.application`.
```ruby
# ./gems/railties-5.2.2/lib/rails.rb
module Rails
class << self
@application = @app_class = nil
attr_accessor :app_class
# Oh, 'application' is a class method for module 'Rails'. It is not an object.
# But it returns an object which is an instance of 'app_class'.
# So it is important for us to know what class 'app_class' is.
def application
@application ||= (app_class.instance if app_class)
end
end
end
```
Because `Rails.application.respond_to?(:call)` returns `true`, `app_class.instance` has a `call` method.
When was `app_class` set?
```ruby
module Rails
class Application < Engine
class << self
def inherited(base) # This is a hooked method.
Rails.app_class = base # This line set the 'app_class'.
end
end
end
end
```
`Rails::Application` is inherited by `YourProject`,
```ruby
# ./config/application.rb
module YourProject
# The hooked method `inherited` will be invoked here.
class Application < Rails::Application
end
end
```
So `YourProject::Application` is the `Rails.app_class` here.
You may have a question: When does Rails execute the code in `./config/application.rb`?
To answer this question, we need to look back to `config.ru`.
```ruby
# ./config.ru
require_relative 'config/environment' # Let's step into this line.
run Rails.application # It seems that this is the app.
```
```ruby
# ./config/environment.rb
# Load the Rails application.
require_relative 'application' # Let's step into this line.
# Initialize the Rails application.
Rails.application.initialize!
```
```ruby
# ./config/application.rb
require_relative 'boot'
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module YourProject
# The hooked method `inherited` will be invoked here.
class Application < Rails::Application
config.load_defaults 5.2
config.i18n.default_locale = :zh
end
end
```
Because `YourProject::Application` is `Rails.app_class`, `app_class.instance` is `YourProject::Application.instance`.
But where is the `call` method?
`call` method should be a method of `YourProject::Application.instance`.
The `call` method processes every request. Here it is.
```ruby
# ./gems/railties/lib/rails/engine.rb
module Rails
class Engine < Railtie
def call(env) # This method will process every request. It is invoked by Rack. So it is very important.
req = build_request env
app.call req.env # We will discuss the 'app' object later.
end
end
end
# ./gems/railties/lib/rails/application.rb
module Rails
class Application < Engine
end
end
# ./config/application.rb
module YourProject
class Application < Rails::Application
end
end
```
Ancestor's chain is `YourProject::Application < Rails::Application < Rails::Engine < Rails::Railtie`.
So `YourProject::Application.new.respond_to?(:call)` returns `true`.
But what does `app_class.instance` really do?
`instance` is just a method name. What we really expects is something like `app_class.new`.
Let's look at the definition of `instance`.
```ruby
# ./gems/railties/lib/rails/application.rb
module Rails
class Application < Engine
def instance
super.run_load_hooks! # This line confused me at the beginning.
end
end
end
```
After a deep research, I realized that this code is equal to:
```ruby
def instance
return_value = super # Keyword 'super' means call the ancestor's same name method: 'instance'.
return_value.run_load_hooks!
end
```
```ruby
# ./gems/railties/lib/rails/railtie.rb
module Rails
class Railtie
def instance
# 'Rails::Railtie' is the top ancestor class.
# Now 'app_class.instance' is 'YourProject::Application.new'.
@instance ||= new
end
end
end
```
```ruby
module Rails
def application
@application ||= (app_class.instance if app_class)
end
end
```
So `YourProject::Application.new` is `Rails.application`.
Rack server will start `Rails.application` in the end.
`Rails.application` is an important object in Rails.
And you'll only have one `Rails.application` in one Puma process.
Multiple threads in a Puma process shares the `Rails.application`.
## Part 2: config
The first time we see the `config` is in `./config/application.rb`.
```ruby
# ./config/application.rb
#...
module YourProject
class Application < Rails::Application
# Actually, `config` is a method of `YourProject::Application`.
# It is defined in it's grandfather's father: `Rails::Railtie`
config.load_defaults 5.2 # Let's step into this line to see what is config.
config.i18n.default_locale = :zh
end
end
```
```ruby
module Rails
class Railtie
class << self
# Method `:config` is defined here.
# Actually, method `:config` is delegated to another object `:instance`.
delegate :config, to: :instance
# Call `YourProject::Application.config` will actually call `YourProject::Application.instance.config`
def instance
# return an instance of `YourProject::Application`.
# Call `YourProject::Application.config` will actually call `YourProject::Application.new.config`
@instance ||= new
end
end
end
class Engine < Railtie
end
class Application < Engine
class << self
def instance
# 'super' will call `:instance` method in `Railtie`,
# which will return an instance of `YourProject::Application`.
return_value = super
return_value.run_load_hooks!
end
end
def run_load_hooks!
return self if @ran_load_hooks
@ran_load_hooks = true
# ...
self # `self` is an instance of `YourProject::Application`, and `self` is `Rails.application`.
end
# This is the method `config`.
def config
# It is an instance of class `Rails::Application::Configuration`.
# Please notice that `Rails::Application` is superclass of `YourProject::Application` (self's class).
@config ||= Application::Configuration.new(self.class.find_root(self.class.called_from))
end
end
end
```
In the end, `YourProject::Application.config === Rails.application.config` returns `true`.
Invoke Class's `config` method become invoke the class's instance's `config` method.
```ruby
module Rails
class << self
def configuration
application.config
end
end
end
```
So `Rails.configuration === Rails.application.config` returns `true`.
FYI:
```ruby
module Rails
class Application
class Configuration < ::Rails::Engine::Configuration
end
end
class Engine
class Configuration < ::Rails::Railtie::Configuration
attr_accessor :middleware
def initialize(root = nil)
super()
#...
@middleware = Rails::Configuration::MiddlewareStackProxy.new
end
end
end
class Railtie
class Configuration
end
end
end
```
## Part 3: Every request and response
Imagine we have this route for the home page.
```ruby
# ./config/routes.rb
Rails.application.routes.draw do
root 'home#index' # HomeController#index
end
```
### Puma
When a request is made from client, Puma will process the request in `Puma::Server#process_client`.
If you want to know how Puma enter the method `Puma::Server#process_client`, please read part 4 or just search 'process_client' in this document.
```ruby
# ./gems/puma-3.12.0/lib/puma/server.rb
require 'socket'
module Puma
# The HTTP Server itself. Serves out a single Rack app.
#
# This class is used by the `Puma::Single` and `Puma::Cluster` classes
# to generate one or more `Puma::Server` instances capable of handling requests.
# Each Puma process will contain one `Puma::Server` instacne.
#
# The `Puma::Server` instance pulls requests from the socket, adds them to a
# `Puma::Reactor` where they get eventually passed to a `Puma::ThreadPool`.
#
# Each `Puma::Server` will have one reactor and one thread pool.
class Server
def initialize(app, events=Events.stdio, options={})
# app: #<:configuration::configmiddleware:0x00007fcf1612c338>
# @config = #<:configuration:0x00007fcf169a6c98>
# >
@app = app
#...
end
# Given a connection on +client+, handle the incoming requests.
#
# This method support HTTP Keep-Alive so it may, depending on if the client
# indicates that it supports keep alive, wait for another request before
# returning.
#
def process_client(client, buffer)
begin
# ...
while true
# Let's step into this line.
case handle_request(client, buffer) # Will return true in this example.
when true
return unless @queue_requests
buffer.reset
ThreadPool.clean_thread_locals if clean_thread_locals
unless client.reset(@status == :run)
close_socket = false
client.set_timeout @persistent_timeout
@reactor.add client
return
end
end
end
# ...
ensure
buffer.reset
client.close if close_socket
#...
end
end
# Given the request +env+ from +client+ and a partial request body
# in +body+, finish reading the body if there is one and invoke
# the Rack app. Then construct the response and write it back to
# +client+
#
def handle_request(req, lines)
env = req.env
# ...
# app: #<:configuration::configmiddleware:0x00007fcf1612c338>
# @config = #<:configuration:0x00007fcf169a6c98>
# >
status, headers, res_body = @app.call(env) # Let's step into this line.
# ...
return keep_alive
end
end
end
```
```ruby
# ./gems/puma-3.12.0/lib/puma/configuration.rb
module Puma
class Configuration
class ConfigMiddleware
def initialize(config, app)
@config = config
@app = app
end
def call(env)
env[Const::PUMA_CONFIG] = @config
# @app: #<:application:0x00007fb4b1b4bcf8>
@app.call(env)
end
end
end
end
```
### Rack apps
As we see when Ruby enter `Puma::Configuration::ConfigMiddleware#call`, the `@app` is `YourProject::Application` instance.
It is just the `Rails.application`.
Rack need a `call` method to process request.
Rails defined this `call` method in `Rails::Engine#call`, so that `YourProject::Application` instance will have a `call` method.
```ruby
# ./gems/railties/lib/rails/engine.rb
module Rails
class Engine < Railtie
def call(env) # This method will process every request. It is invoked by Rack.
req = build_request env
app.call req.env # The 'app' method is blow.
end
def app
# FYI,
# caller: [
# "../gems/railties-5.2.2/lib/rails/application/finisher.rb:47:in `block in