Recently I’m reading the Rails source code and I wanna create a series of blogs to talk about it. Today I’m going to introduct the initialization process of Rails. Just notice that I use the latest Rails 4.2.0 beta1 code for analysis. If you use Rails 4.1 or earlier, the code may be different (actually it is).
bin/rails
When we run rails s in a Rails application folder, it will call into the bin/rails script. This script has following contents,
1 2 3 4 5 6 7 8 |
|
Firstly it tries to call the spring script which is an application preloader to speed up development by keeping our application in the background. And then it defines the APP_PATH constant, which points to the <app_root>/config/application, and next it requires the config/boot.rb.
config/boot.rb
The config/boot.rb has following contents,
1 2 3 |
|
Since Rails uses Bundler to manage the rubygems, it defines a Gemfile which declares all dependencies of the application. The ENV['BUNDLE_GEMFILE']
is set to the path of this Gemfile, and require 'bundler/setup'
is calling Bundler to resolve the load paths of your Gem dependencies.
rails/command.rb
The rails next requires ‘rails/commands’, and let’s have a look at that file,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
We can see that when we pass s or server to the rails script, it will set command to server and then create an instance of Rails::CommandsTasks and call its run_command!
instance method.
rails/commands/commands_tasks.rb
The rails/commands/commands_tasks defines the Rails::CommandsTasks
class, let’s check the important part of this class,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
In the run_command!
instance method, it checks that if the COMMAND_WHITELIST includes the command passed, then it will just invoke an instance method whose method name matches the command parameter. So in our case, if we passed server it will invoke the server
method, which we show below,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
We can see that it creates a Rails::Server instance and then require our application file which is already defined in APP_PATH.
Let’s first have a look at the config/application.rb
config/application.rb
The config/application.rb has following contents,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
Firstly it requires the config/boot.rb, since above this file is already required by rails script, the require has no effect here.
Next it requires the ‘rails/all’, this file has following contents, it just load the railtie of each module. Each module has its own railtie, which is to manage the initialization and configuration. We will talk about railties in another blog so I’ll not explain here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
And then next it requires the gems we defined in Gemfile by calling Bundler.require
. And next our application class is defined. Since I named my application as sample, the class is in Sample
Module.
The Sample::Application extends from Rails::Application. After this class is defined the Rails.application
will be set to an instance of Sample::Application. Why? Let’s have a look at Rails
module.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
We can see that it defines two instance variables in Rails’s singleton class, @application and @app_class. And after the app_class is defined, the Rails.application returns the @application if it’s already defined, if not, it will call app_class.instance to create an instance.
So when does the Rails.app_class is defined? Let’s have a look at the Rails::Application class.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
We can see that if there is a class inherits it, it will set the Rails.app_class to the inherited class, in our case Sample::Application. So after class Sample::Application is defined, the Rails.app_class is set, and then if we call Rails.application
, the application instance will be initialized.
Notice that the new
method in Rails::Application is set to private, so we can only call instance
to generate an instance. And this instance is a singleton, which is reasonable since one Rails application should have only one application instance.
Rails::Server
After the application instance is instantiated, we go back to Rails::CommandsTasks#server method, it will call Dir.chdir(Rails.application.root)
to go to application root folder, and then call server.start
. Let’s have a look how the Rails::Server is defined.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
We can see that Rails::Server extends from ::Rack::Server. Rack is a specification which defines an interface between the web server and application. To support Rack, you need to define an app object, it has a call
method, which takes the environment hash as a parameter, and returning an Array with three elements:
- The HTTP response code
- A Hash of headers
- The response body, which must respond to each
Although this interface is very simple, you can configure middleware around the app and intercept the request and response.
Rack is also a rubygem. It provides a set of middleware such as logging, set content length in HTTP response, etc. And it also defines a simple DSL to configure the middleware and app, the config file has ru extension and Rack will load a file called config.ru by default.
Let’s have a look at the ::Rack::Server class,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
I only leave the relevant code here and omitted some parts such as option parsing. We can see that the start
method creates a @_server
instance variable which is a handler represents the web server interface, it has a run
method which accepts the app. The app is the Rack app object we mentioned above, it’s an object which reponds to call
method which accepts an environment hash and returns an array with 3 elements: the HTTP status code, response HTTP headers and the body.
The wrapped_app
calls the build_app
method which just wraps some default middleware around the app. And the app
method actually sets the @app
instance variable, which is actually read from the config.ru file (by build_app_from_string
method).
config.ru
Every rails application has a config.ru file at root folder. Let’s have a look at that file,
1 2 3 4 |
|
This file is actually a ruby file, it first requires the config/environment.rb file and then calls run Rails.application
. The run
method is defined in Rack DSL, and it accepts the app object. So we can imagine that if we call Rails.application.respond_to?(:call)
it should return true.
config/environment.rb
Let’s have a look what’s inside config/environment.rb.
1 2 3 4 5 |
|
It just requires config/application.rb (which we already required) and then initialize the application by calling Rails.application.initialize!
. After that the initialization process is finished and the Rails.application
could be used as Rack application.
In this part we had a detailed look at the Rails initialization process and we will dig into Railties and Rails.application.initialize!
in following series.