The Rails Initialization Process

This guide explains the internals of the initialization process in Rails
as of Rails 4. It is an extremely in-depth guide and recommended for advanced Rails developers.

After reading this guide, you will know:

How to use rails server.

This guide goes through every method call that is
required to boot up the Ruby on Rails stack for a default Rails 4
application, explaining each part in detail along the way. For this
guide, we will be focusing on what happens when you execute +rails
server+ to boot your app.

NOTE: Paths in this guide are relative to Rails or a Rails application unless otherwise specified.

TIP: If you want to follow along while browsing the Rails source
code, we recommend that you use the t
key binding to open the file finder inside GitHub and find files
quickly.

This file will first attempt to push the railties/lib directory if
present, and then requires rails/cli.

railties/lib/rails/cli.rb

This file looks like this:

require'rbconfig'require'rails/script_rails_loader'# If we are inside a Rails application this method performs an exec and thus# the rest of this script is not run.Rails::ScriptRailsLoader.exec_script_rails!
require'rails/ruby_version_check'Signal.trap("INT") { puts; exit(1) }
ifARGV.first =='plugin'ARGV.shift
require'rails/commands/plugin_new'elserequire'rails/commands/application'end

The rbconfig file from the Ruby standard library provides us with the RbConfig class which contains detailed information about the Ruby environment, including how Ruby was compiled. We can see this in use in railties/lib/rails/script_rails_loader.

The rails/script_rails_loader file uses RbConfig::Config to obtain the bin_dir and ruby_install_name values for the configuration which together form the path to the Ruby interpreter. The RbConfig::CONFIG["EXEEXT"] will suffix this path with ".exe" if the script is running on Windows. This constant is used later on in exec_script_rails!. As for the SCRIPT_RAILS constant, we'll see that when we get to the in_rails_application? method.

Back in rails/cli, the next line is this:

Rails::ScriptRailsLoader.exec_script_rails!

This method is defined in rails/script_rails_loader:

defself.exec_script_rails!
cwd =Dir.pwd
returnunless in_rails_application? || in_rails_application_subdirectory?
exec RUBY, SCRIPT_RAILS, *ARGVif in_rails_application?
Dir.chdir("..") do# Recurse in a chdir block: if the search fails we want to be sure# the application is generated in the original working directory.
exec_script_rails! unless cwd ==Dir.pwd
endrescueSystemCallError# could not chdir, no problem just returnend

This method will first check if the current working directory (cwd) is a Rails application or a subdirectory of one. This is determined by the in_rails_application? method:

defself.in_rails_application?File.exists?(SCRIPT_RAILS)
end

The SCRIPT_RAILS constant defined earlier is used here, with File.exists? checking for its presence in the current directory. If this method returns false then in_rails_application_subdirectory? will be used:

script/rails

The APP_PATH constant will be used later in rails/commands. The config/boot file referenced here is the config/boot.rb file in our application which is responsible for loading Bundler and setting it up.

config/boot.rb

config/boot.rb contains:

# Set up gems listed in the Gemfile.ENV['BUNDLE_GEMFILE'] ||=File.expand_path('../../Gemfile', __FILE__)
require'bundler/setup'ifFile.exists?(ENV['BUNDLE_GEMFILE'])

In a standard Rails application, there's a Gemfile which declares all
dependencies of the application. config/boot.rb sets
ENV['BUNDLE_GEMFILE'] to the location of this file. If the Gemfile
exists, bundler/setup is then required.

The gems that a Rails 4 application depends on are as follows:

TODO: change these when the Rails 4 release is near.

abstract (1.0.0)

actionmailer (4.0.0.beta)

actionpack (4.0.0.beta)

activemodel (4.0.0.beta)

activerecord (4.0.0.beta)

activesupport (4.0.0.beta)

arel (2.0.7)

builder (3.0.0)

bundler (1.0.6)

erubis (2.6.6)

i18n (0.5.0)

mail (2.2.12)

mime-types (1.16)

polyglot (0.3.1)

rack (1.2.1)

rack-cache (0.5.3)

rack-mount (0.6.13)

rack-test (0.5.6)

rails (4.0.0.beta)

railties (4.0.0.beta)

rake (0.8.7)

sqlite3-ruby (1.3.2)

thor (0.14.6)

treetop (1.4.9)

tzinfo (0.3.23)

rails/commands.rb

Once config/boot.rb has finished, the next file that is required is rails/commands which will execute a command based on the arguments passed in. In this case, the ARGV array simply contains server which is extracted into the command variable using these lines:

TIP: As you can see, an empty ARGV list will make Rails show the help
snippet.

If we used s rather than server, Rails will use the aliases defined in the file and match them to their respective commands. With the server command, Rails will run this code:

when'server'# Change to the application's path if there is no config.ru file in current dir.# This allows us to run script/rails server from other directories, but still get# the main config.ru and properly set the tmp directory.Dir.chdir(File.expand_path('../../', APP_PATH)) unlessFile.exists?(File.expand_path("config.ru"))
require'rails/commands/server'Rails::Server.new.tap do |server|
# We need to require application after the server sets environment,# otherwise the --environment option given to the server won't propagate.requireAPP_PATHDir.chdir(Rails.application.root)
server.start
end

This file will change into the root of the directory (a path two directories back from APP_PATH which points at config/application.rb), but only if the config.ru file isn't found. This then requires rails/commands/server which sets up the Rails::Server class.

After super has finished in Rack::Server, we jump back to rails/commands/server.rb. At this point, set_environment is called within the context of the Rails::Server object and this method doesn't appear to do much at first glance:

defset_environmentENV["RAILS_ENV"] ||= options[:environment]
end

In fact, the options method here does quite a lot. This method is defined in Rack::Server like this:

This method will set up keys for the options which Rails will then be
able to use to determine how its server should run. After initialize
has finished, we jump back into rails/server where APP_PATH (which was
set earlier) is required.

config/application

When require APP_PATH is executed, config/application.rb is loaded.
This file exists in your app and it's free for you to change based
on your needs.

Rails::Server#start

After config/application is loaded, server.start is called. This method is defined like this:

This is where the first output of the Rails initialization happens. This
method creates a trap for INT signals, so if you CTRL-C the server,
it will exit the process. As we can see from the code here, it will
create the tmp/cache, tmp/pids, tmp/sessions and tmp/sockets
directories. It then calls wrapped_app which is responsible for
creating the Rack app, before creating and assigning an
instance of ActiveSupport::Logger.

The super method will call Rack::Server.start which begins its definition like this:

The interesting part for a Rails app is the last line, server.run. Here we encounter the wrapped_app method again, which this time
we're going to explore more (even though it was executed before, and
thus memorized by now).

The initialize method of Rack::Builder will take the block here and execute it within an instance of Rack::Builder. This is where the majority of the initialization process of Rails happens. The require line for config/environment.rb in config.ru is the first to run:

require ::File.expand_path('../config/environment', __FILE__)

config/environment.rb

This file is the common file required by config.ru (rails server) and Passenger. This is where these two ways to run the server meet; everything before this point has been Rack and Rails setup.

This file begins with requiring config/application.rb.

config/application.rb

This file requires config/boot.rb, but only if it hasn't been required before, which would be the case in rails server but wouldn't be the case with Passenger.

Then the fun begins!

Loading Rails

The next line in config/application.rb is:

require'rails/all'

railties/lib/rails/all.rb

This file is responsible for requiring all the individual frameworks of Rails:

This is where all the Rails frameworks are loaded and thus made
available to the application. We won't go into detail of what happens
inside each of those frameworks, but you're encouraged to try and
explore them on your own.

For now, just keep in mind that common functionality like Rails engines,
I18n and Rails configuration is all being defined here.

Back to config/environment.rb

When config/application.rb has finished loading Rails, and defined
your application namespace, you go back to config/environment.rb,
where your application is initialized. For example, if you application was called
Blog, here you would find Blog::Application.initialize!, which is
defined in rails/application.rb

railties/lib/rails/application.rb

As you can see, you can only initialize an app once. This is also where the initializers are run.

TODO: review this

The initializers code itself is tricky. What Rails is doing here is it
traverses all the class ancestors looking for an initializers method,
sorting them and running them. For example, the Engine class will make
all the engines available by providing the initializers method.

We won't dig into the server configuration itself, but this is
the last piece of our journey in the Rails initialization process.

This high level overview will help you understand when your code is
executed and how, and overall become a better Rails developer. If you
still want to know more, the Rails source code itself is probably the
best place to go next.