Skip to content
This repository
tree: 48e1f8ba4c
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 1117 lines (765 sloc) 49.001 kb

The Rails Initialization Process

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

  • Using rails server
  • Using Passenger

endprologue.

This guide goes through every single file, class and method call that is required to boot up the Ruby on Rails stack for a default Rails 3.1 application, explaining each part in detail along the way. For this guide, we will be focusing on how the two most common methods (rails server and Passenger) boot a Rails application.

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

Launch!

As of Rails 3, script/server has become rails server. This was done to centralize all rails related commands to one common file.

bin/rails

The actual rails command is kept in bin/rails at the and goes like this:

#!/usr/bin/env ruby

begin
require “rails/cli”
rescue LoadError
railties_path = File.expand_path(‘../../railties/lib’, FILE)
$:.unshift(railties_path)
require “rails/cli”
end

This file will attempt to load rails/cli and if it cannot find it then add the railties/lib path to the load path ($:) and will then try to require it again.

railties/lib/rails/cli.rb

This file looks like this:

require ‘rbconfig’
require ‘rails/script_rails_loader’

  1. If we are inside a Rails application this method performs an exec and thus
  2. the rest of this script is not run.
    Rails::ScriptRailsLoader.exec_script_rails!

require ‘rails/ruby_version_check’
Signal.trap(“INT”) { puts; exit }

if ARGV.first == ‘plugin’
ARGV.shift
require ‘rails/commands/plugin_new’
else
require ‘rails/commands/application’
end

The rbconfig file here is out of Ruby’s standard library and provides us with the RbConfig class which contains useful information dependent on how Ruby was compiled. We’ll see this in use in railties/lib/rails/script_rails_loader.

require ‘pathname’

module Rails
module ScriptRailsLoader
RUBY = File.join(*RbConfig::CONFIG.values_at(“bindir”, “ruby_install_name”)) + RbConfig::CONFIG[“EXEEXT”]
SCRIPT_RAILS = File.join(‘script’, ‘rails’)

end

end

The rails/script_rails_loader file uses RbConfig::Config to gather up the bin_dir and ruby_install_name values for the configuration which will result in a path such as /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby, which is the default path on Mac OS X. If you’re running Windows the path may be something such as C:/Ruby192/bin/ruby. Anyway, the path on your system may be different, but the point of this is that it will point at the known ruby executable location for your install. 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 like this:

def self.exec_script_rails!
cwd = Dir.pwd
return unless in_rails_application? || in_rails_application_subdirectory?
exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application?
Dir.chdir(“..”) do

  1. Recurse in a chdir block: if the search fails we want to be sure
  2. the application is generated in the original working directory.
    exec_script_rails! unless cwd == Dir.pwd
    end
    rescue SystemCallError
  3. could not chdir, no problem just return
    end

This method will first check if the current working directory (cwd) is a Rails application or is a subdirectory of one. The way to determine this is defined in the in_rails_application? method like this:

def self.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:

def self.in_rails_application_subdirectory?(path = Pathname.new(Dir.pwd))
File.exists?(File.join(path, SCRIPT_RAILS)) || !path.root? && in_rails_application_subdirectory?(path.parent)
end

This climbs the directory tree until it reaches a path which contains a script/rails file. If a directory is reached which contains this file then this line will run:

exec RUBY, SCRIPT_RAILS, *ARGV if in_rails_application?

This is effectively the same as doing ruby script/rails [arguments]. Where [arguments] at this point in time is simply “server”.

script/rails

This file looks like this:

APP_PATH = File.expand_path(‘../../config/application’, FILE)
require File.expand_path(‘../../config/boot’, FILE)
require ‘rails/commands’

The APP_PATH constant here will be used later in rails/commands. The config/boot file that script/rails references 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 this:

require ‘rubygems’

  1. Set up gems listed in the Gemfile.
    gemfile = File.expand_path(‘../../Gemfile’, FILE)
    begin
    ENV[‘BUNDLE_GEMFILE’] = gemfile
    require ‘bundler’
    Bundler.setup
    rescue Bundler::GemNotFound => e
    STDERR.puts e.message
    STDERR.puts “Try running `bundle install`.”
    exit!
    end if File.exist?(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, then requires Bundler and calls Bundler.setup which adds the dependencies of the application (including all the Rails parts) to the load path, making them available for the application to load. The gems that a Rails 3.1 application depends on are as follows:

  • abstract (1.0.0)
  • actionmailer (3.1.0.beta)
  • actionpack (3.1.0.beta)
  • activemodel (3.1.0.beta)
  • activerecord (3.1.0.beta)
  • activeresource (3.1.0.beta)
  • activesupport (3.1.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 (3.1.0.beta)
  • railties (3.1.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:

aliases = {
“g” => “generate”,
“c” => “console”,
“s” => “server”,
“db” => “dbconsole”,
“r” => “runner”
}

command = ARGV.shift
command = aliases[command] || command

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’

  1. Change to the application’s path if there is no config.ru file in current dir.
  2. This allows us to run script/rails server from other directories, but still get
  3. the main config.ru and properly set the tmp directory.
    Dir.chdir(File.expand_path(‘../../’, APP_PATH)) unless File.exists?(File.expand_path(“config.ru”))
require ‘rails/commands/server’ Rails::Server.new.tap { |server|
  1. We need to require application after the server sets environment,
  2. otherwise the —environment option given to the server won’t propagate.
    require APP_PATH
    Dir.chdir(Rails.application.root)
    server.start
    }

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 requires action_dispatch and sets up the Rails::Server class.

actionpack/lib/action_dispatch.rb

Action Dispatch is the routing component of the Rails framework. It depends on Active Support, actionpack/lib/action_pack.rb and Rack being available. The first thing required here is active_support.

activesupport/lib/active_support.rb

This file begins with requiring active_support/lib/active_support/dependencies/autoload.rb which redefines Ruby’s autoload method to have a little more extra behaviour especially in regards to eager autoloading. Eager autoloading is the loading of all required classes and will happen when the config.cache_classes setting is true. The required file also requires another file: active_support/lazy_load_hooks

activesupport/lib/active_support/lazy_load_hooks.rb

This file defines the ActiveSupport.on_load hook which is used to execute code when specific parts are loaded. We’ll see this in use a little later on.

This file begins with requiring active_support/inflector/methods.

activesupport/lib/active_support/inflector/methods.rb

The methods.rb file is responsible for defining methods such as camelize, underscore and dasherize as well as a slew of others. The ActiveSupport::Inflector documentation covers them all pretty decently.

In this file there are a lot of lines such as this inside the ActiveSupport module:

autoload :Inflector

Due to the overriding of the autoload method, Ruby will know to look for this file at activesupport/lib/active_support/inflector.rb when the Inflector class is first referenced.

The active_support/lib/active_support/version.rb that is also required here simply defines an ActiveSupport::VERSION constant which defines a couple of constants inside this module, the main constant of this is ActiveSupport::VERSION::STRING which returns the current version of ActiveSupport.

The active_support/lib/active_support.rb file simply defines the ActiveSupport module and some autoloads (eager and of the normal variety) for it.

actionpack/lib/action_dispatch.rb cont’d.

Now back to action_pack/lib/action_dispatch.rb. The next require in this file is one for action_pack, which simply calls action_pack/version.rb which defines ActionPack::VERSION and the constants, much like ActiveSpport does.

After this line, there’s a require to active_model which simply defines autoloads for the ActiveModel part of Rails and sets up the ActiveModel module which is used later on.

The last of the requires is to rack, which like the active_model and active_support requires before it, sets up the Rack module as well as the autoloads for constants within it.

Finally in action_dispatch.rb the ActionDispatch module and its autoloads are declared.

rails/commands/server.rb

The Rails::Server class is defined in this file as inheriting from Rack::Server. When Rails::Server.new is called, this calls the initialize method in rails/commands/server.rb:

def initialize(*)
super
set_environment
end

Firstly, super is called which calls the initialize method on Rack::Server.

Rack: lib/rack/server.rb

Rack::Server is responsible for providing a common server interface for all Rack-based applications, which Rails is now a part of.

The initialize method in Rack::Server simply sets a couple of variables:

def initialize(options = nil)
@options = options
@app = options[:app] if options && options[:app]
end

In this case, options will be nil so nothing happens in this method.

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:

def set_environment
ENV[“RAILS_ENV”] ||= options[:environment]
end

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

def options
@options ||= parse_options(ARGV)
end

Then parse_options is defined like this:

def parse_options(args)
options = default_options

  1. Don’t evaluate CGI ISINDEX parameters.
  2. http://hoohoo.ncsa.uiuc.edu/cgi/cl.html
    args.clear if ENV.include?(“REQUEST_METHOD”)
options.merge! opt_parser.parse! args options[:config] = ::File.expand_path(options[:config]) ENV[“RACK_ENV”] = options[:environment] options

end

With the default_options set to this:

def default_options
{
:environment => ENV[‘RACK_ENV’] || “development”,
:pid => nil,
:Port => 9292,
:Host => “0.0.0.0”,
:AccessLog => [],
:config => “config.ru”
}
end

There is no REQUEST_METHOD key in ENV so we can skip over that line. The next line merges in the options from opt_parser which is defined plainly in Rack::Server

def opt_parser
Options.new
end

The class is defined in Rack::Server, but is overwritten in Rails::Server to take different arguments. Its parse! method begins like this:

def parse!(args)
args, options = args.dup, {}

opt_parser = OptionParser.new do |opts| opts.banner = “Usage: rails server [mongrel, thin, etc] [options]” opts.on(“-p”, “—port=port”, Integer, “Runs Rails on the specified port.”, “Default: 3000”) { |v| options[:Port] = v } …

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, then the start method will launch the server.

Rails::Server#start

This method is defined like this:

def start
puts “=> Booting #{ActiveSupport::Inflector.demodulize(server)}”
puts “=> Rails #{Rails.version} application starting in #{Rails.env} on http://#{options[:Host]}:#{options[:Port]}”
puts “=> Call with -d to detach” unless options[:daemonize]
trap(:INT) { exit }
puts “=> Ctrl-C to shutdown server” unless options[:daemonize]

#Create required tmp directories if not found %w(cache pids sessions sockets).each do |dir_to_make| FileUtils.mkdir_p(Rails.root.join(‘tmp’, dir_to_make)) end super

ensure

  1. The ‘-h’ option calls exit before @options is set.
  2. If we call ‘options’ with it unset, we get double help banners.
    puts ‘Exiting’ unless @options && options[:daemonize]
    end

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 if they don’t already exist prior to calling super. The super method will call Rack::Server.start which begins its definition like this:

def start
if options[:warn]
$-w = true
end

if includes = options[:include] $LOAD_PATH.unshift(*includes) end if library = options[:require] require library end if options[:debug] $DEBUG = true require ‘pp’ p options[:server] pp wrapped_app pp app end

end

In a Rails application, these options are not set at all and therefore aren’t used at all. The first line of code that’s executed in this method is a call to this method:

wrapped_app

This method calls another method:

@wrapped_app ||= build_app app

Then the app method here is defined like so:

def app
@app ||= begin
if !::File.exist? options[:config]
abort “configuration #{options[:config]} not found”
end

app, options = Rack::Builder.parse_file(self.options[:config], opt_parser) self.options.merge! options app end

end

The options[:config] value defaults to config.ru which contains this:

  1. This file is used by Rack-based servers to start the application.

require ::File.expand_path(‘../config/environment’, FILE)
run YourApp::Application

The Rack::Builder.parse_file method here takes the content from this config.ru file and parses it using this code:

app = eval "Rack::Builder.new {( " cfgfile “\n )}.to_app”,
TOPLEVEL_BINDING, config

The initialize method 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 chain of events that this simple line sets off will be the focus of a large majority of this guide. 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 parts of Rails like so:

require “rails”

%w(
active_record
action_controller
action_mailer
active_resource
rails/test_unit
).each do |framework|
begin
require “#{framework}/railtie”
rescue LoadError
end
end

First off the line is the rails require itself.

railties/lib/rails.rb

This file is responsible for the initial definition of the Rails module and, rather than defining the autoloads like ActiveSupport, ActionDispatch and so on, it actually defines other functionality. Such as the root, env and application methods which are extremely useful in Rails 3 applications.

However, before all that takes place the rails/ruby_version_check file is required first.

railties/lib/rails/ruby_version_check.rb

This file simply checks if the Ruby version is less than 1.8.7 or is 1.9.1 and raises an error if that is the case. Rails 3 simply will not run on earlier versions of Ruby than 1.8.7 or 1.9.1.

NOTE: You should always endeavor to run the latest version of Ruby with your Rails applications. The benefits are many, including security fixes and the like, and very often there is a speed increase associated with it. The caveat is that you could have code that potentially breaks on the latest version, which should be fixed to work on the latest version rather than kept around as an excuse not to upgrade.

active_support/core_ext/kernel/reporting.rb

This is the first of the many Active Support core extensions that come with Rails. This one in particular defines methods in the Kernel module which is mixed in to the Object class so the methods are available on main and can therefore be called like this:

silence_warnings do

  1. some code
    end

These methods can be used to silence STDERR responses and the silence_stream allows you to also silence other streams. Additionally, this mixin allows you to suppress exceptions and capture streams. For more information see the Silencing Warnings, Streams, and Exceptions section from the Active Support Core Extensions Guide.

active_support/core_ext/logger.rb

The next file that is required is another Active Support core extension, this time to the Logger class. This begins by defining the around_[level] helpers for the Logger class as well as other methods such as a datetime_format getter and setter for the formatter object tied to a Logger object.

For more information see the Extensions to Logger section from the Active Support Core Extensions Guide.

railties/lib/rails/application.rb

The next file required by railties/lib/rails.rb is application.rb. This file defines the Rails::Application constant which the application’s class defined in config/application.rb in a standard Rails application depends on. Before the Rails::Application class is defined however, there’s some other files that get required first.

The first of these is active_support/core_ext/hash/reverse_merge which can be read about in the Active Support Core Extensions guide under the “Merging” section.

active_support/file_update_checker.rb

The ActiveSupport::FileUpdateChecker class defined within this file is responsible for checking if a file has been updated since it was last checked. This is used for monitoring the routes file for changes during development environment runs.

railties/lib/rails/plugin.rb

This file defines Rails::Plugin which inherits from Rails::Engine. Unlike Rails::Engine and Rails::Railtie however, this class is not designed to be inherited from. Instead, this is used simply for loading plugins from within an application and an engine.

This file begins by requiring rails/engine.rb

railties/lib/rails/engine.rb

The rails/engine.rb file defines the Rails::Engine class which inherits from Rails::Railtie. The Rails::Engine class defines much of the functionality found within a standard application class such as the routes and config methods.

The API documentation for Rails::Engine explains the function of this class pretty well.

This file’s first line requires rails/railtie.rb.

railties/lib/rails/railtie.rb

The rails/railtie.rb file is responsible for defining Rails::Railtie, the underlying class for all ties to Rails now. Gems that want to have their own initializers or rake tasks and hook into Rails should have a GemName::Railtie class that inherits from Rails::Railtie.

The API documentation for Rails::Railtie, much like Rails::Engine, explains this class exceptionally well.

The first require in this file is rails/initializable.rb.

railties/lib/rails/initializable.rb

Now we reach the end of this particular rabbit hole as rails/initializable.rb doesn’t require any more Rails files, only tsort from the Ruby standard library.

This file defines the Rails::Initializable module which contains the Initializer class, the basis for all initializers in Rails. This module also contains a ClassMethods class which will be included into the Rails::Railtie class when these requires have finished.

Now that rails/initializable.rb has finished being required from rails/railtie.rb, the next require is for rails/configuration.

railties/lib/rails/configuration.rb

This file defines the Rails::Configuration module, containing the MiddlewareStackProxy class as well as the Generators class. The MiddlewareStackProxy class is used for managing the middleware stack for an application, which we’ll see later on. The Generators class provides the functionality used for configuring what generators an application uses through the config.generators option.

The first file required in this file is activesupport/deprecation.

activesupport/lib/active_support/deprecation.rb

This file, and the files it requires, define the basic deprecation warning features found in Rails. This file is responsible for setting defaults in the ActiveSupport::Deprecation module for the deprecation_horizon, silenced and debug values. The files that are required before this happens are:

  • active_support/deprecation/behaviors
  • active_support/deprecation/reporting
  • active_support/deprecation/method_wrappers
  • active_support/deprecation/proxy_wrappers

activesupport/lib/active_support/deprecation/behaviors.rb

This file defines the behavior of the ActiveSupport::Deprecation module, setting up the DEFAULT_BEHAVIORS hash constant which contains the three defaults to outputting deprecation warnings: :stderr, :log and :notify. This file begins by requiring activesupport/notifications and activesupport/core_ext/array/wrap.

activesupport/lib/active_support/notifications.rb

This file defines the ActiveSupport::Notifications module. Notifications provides an instrumentation API for Ruby, shipping with a queue implementation that consumes and publish events to log subscribers in a thread.

The API documentation for ActiveSupport::Notifications explains the usage of this module, including the methods that it defines.

The file required in active_support/notifications.rb is active_support/core_ext/module/delegation which is documented in the Active Support Core Extensions Guide.

activesupport/core_ext/array/wrap

As this file comprises of a core extension, it is covered exclusively in the Active Support Core Extensions guide

activesupport/lib/active_support/deprecation/reporting.rb

This file is responsible for defining the warn and silence methods for ActiveSupport::Deprecation as well as additional private methods for this module.

activesupport/lib/active_support/deprecation/method_wrappers.rb

This file defines a deprecate_methods which is primarily used by the module/deprecation core extension required by the first line of this file. Other core extensions required by this file are the module/aliasing and array/extract_options files.

activesupport/lib/active_support/deprecation/proxy_wrappers.rb

proxy_wrappers.rb defines deprecation wrappers for methods, instance variables and constants. Previously, this was used for the RAILS_ENV and RAILS_ROOT constants for 3.0 but since then these constants have been removed. The deprecation message that would be raised from these would be something like:

BadConstant is deprecated! Use GoodConstant instead.

active_support/ordered_options

This file is the next file required from rails/configuration.rb is the file that defines ActiveSupport::OrderedOptions which is used for configuration options such as config.active_support and the like.

The next file required is active_support/core_ext/hash/deep_dup which is covered in Active Support Core Extensions guide

The file that is required next from is rails/paths

railties/lib/rails/paths.rb

This file defines the Rails::Paths module which allows paths to be configured for a Rails application or engine. Later on in this guide when we cover Rails configuration during the initialization process we’ll see this used to set up some default paths for Rails and some of them will be configured to be eager loaded.

railties/lib/rails/rack.rb

The final file to be loaded by railties/lib/rails/configuration.rb is rails/rack which defines some simple autoloads:

module Rails
module Rack
autoload :Debugger, “rails/rack/debugger”
autoload :Logger, “rails/rack/logger”
autoload :LogTailer, “rails/rack/log_tailer”
autoload :Static, “rails/rack/static”
end
end

Once this file is finished loading, then the Rails::Configuration class is initialized. This completes the loading of railties/lib/rails/configuration.rb and now we jump back to the loading of railties/lib/rails/railtie.rb, where the next file loaded is active_support/inflector.

activesupport/lib/active_support/inflector.rb

active_support/inflector.rb requires a series of file which are responsible for setting up the basics for knowing how to pluralize and singularize words. These files are:

require ‘active_support/inflector/inflections’
require ‘active_support/inflector/transliterate’
require ‘active_support/inflector/methods’

require ‘active_support/inflections’
require ‘active_support/core_ext/string/inflections’

The active_support/inflector/methods file has already been required by active_support/autoload and so won’t be loaded again here. The activesupport/lib/active_support/inflector/inflections.rb is required by active_support/inflector/methods.

active_support/inflections

This file references the ActiveSupport::Inflector constant which isn’t loaded by this point. But there were autoloads set up in activesupport/lib/active_support.rb which will load the file which loads this constant and so then it will be defined. Then this file defines pluralization and singularization rules for words in Rails. This is how Rails knows how to pluralize “tomato” to “tomatoes”.

activesupport/lib/active_support/inflector/transliterate.rb

In this file is where the transliterate and parameterize:http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize methods are defined. The documentation for both of these methods is very much worth reading.

Back to railties/lib/rails/railtie.rb

Once the inflector files have been loaded, the Rails::Railtie class is defined. This class includes a module called Initializable, which is actually Rails::Initializable. This module includes the initializer method which is used later on for setting up initializers, amongst other methods.

railties/lib/rails/initializable.rb

When the module from this file (Rails::Initializable) is included, it extends the class it’s included into with the ClassMethods module inside of it. This module defines the initializer method which is used to define initializers throughout all of the railties. This file completes the loading of railties/lib/rails/railtie.rb. Now we go back to rails/engine.rb.

railties/lib/rails/engine.rb

The next file required in rails/engine.rb is active_support/core_ext/module/delegation which is documented in the Active Support Core Extensions Guide.

The next two files after this are Ruby standard library files: pathname and rbconfig. The file after these is rails/engine/railties.

railties/lib/rails/engine/railties.rb

This file defines the Rails::Engine::Railties class which provides the engines and railties methods which are used later on for defining rake tasks and other functionality for engines and railties.

Back to railties/lib/rails/engine.rb

Once rails/engine/railties.rb has finished loading the Rails::Engine class gets its basic functionality defined, such as the inherited method which will be called when this class is inherited from.

Once this file has finished loading we jump back to railties/lib/rails/plugin.rb

Back to railties/lib/rails/plugin.rb

The next file required in this is a core extension from Active Support called array/conversions which is covered in this section of the Active Support Core Extensions Guide.

Once that file has finished loading, the Rails::Plugin class is defined.

Back to railties/lib/rails/application.rb

Jumping back to rails/application.rb now. This file defines the Rails::Application class where the application’s class inherits from. This class (and its superclasses) define the basic behaviour on the application’s constant such as the config method used for configuring the application.

Once this file’s done then we go back to the railties/lib/rails.rb file, which next requires rails/version.

railties/lib/rails/version.rb

Much like active_support/version, this file defines the VERSION constant which has a STRING constant on it which returns the current version of Rails.

Once this file has finished loading we go back to railties/lib/rails.rb which then requires active_support/railtie.rb.

activesupport/lib/active_support/railtie.rb

This file requires active_support and rails which have already been required so these two lines are effectively ignored. The third require in this file is to active_support/i18n_railtie.rb.

activesupport/lib/active_support/i18n_railtie.rb

This file is the first file that sets up configuration with these lines inside the class:

class Railtie < Rails::Railtie
config.i18n = ActiveSupport::OrderedOptions.new
config.i18n.railties_load_path = []
config.i18n.load_path = []
config.i18n.fallbacks = ActiveSupport::OrderedOptions.new

By inheriting from Rails::Railtie the Rails::Railtie#inherited method is called:

def inherited(base)
unless base.abstract_railtie?
base.send(:include, Railtie::Configurable)
subclasses << base
end
end

This first checks if the Railtie that’s inheriting it is a component of Rails itself:

ABSTRACT_RAILTIES = %w(Rails::Railtie Rails::Plugin Rails::Engine Rails::Application)

def abstract_railtie?
ABSTRACT_RAILTIES.include?(name)
end

Because I18n::Railtie isn’t in this list, abstract_railtie? returns false. Therefore the Railtie::Configurable module is included into this class and the subclasses method is called and I18n::Railtie is added to this new array.

def subclasses
@subclasses ||= []
end

The config method used at the top of I18n::Railtie is defined on Rails::Railtie and is defined like this:

def config
@config ||= Railtie::Configuration.new
end

At this point, that Railtie::Configuration constant is automatically loaded which causes the rails/railties/configuration file to be loaded. The line for this is this particular line in railties/lib/rails/railtie.rb:

autoload :Configuration, “rails/railtie/configuration”

railties/lib/rails/railtie/configuration.rb

This file begins with a require out to rails/configuration which has already been required earlier in the process and so isn’t required again.

This file defines the Rails::Railtie::Configuration class which is responsible for providing a way to easily configure railties and it’s the initialize method here which is called by the config method back in the i18n_railtie.rb file. The methods on this object don’t exist, and so are rescued by the method_missing defined further down in configuration.rb:

def method_missing(name, *args, &blk)
if name.to_s =~ /=$/
@options[$`.to_sym] = args.first elsif @options.key?(name)
@@options[name]
else
super
end
end

So therefore when an option is referred to it simply stores the value as the key if it’s used in a setter context, or retrieves it if used in a getter context. Nothing fancy going on there.

Back to activesupport/lib/active_support/i18n_railtie.rb

After the configuration method the reloader method is defined, and then the first of of Railties’ initializers is defined: i18n.callbacks.

initializer “i18n.callbacks” do
ActionDispatch::Reloader.to_prepare do
I18n::Railtie.reloader.execute_if_updated
end
end

The initializer method (from the Rails::Initializable module) here doesn’t run the block, but rather stores it to be run later on:

def initializer(name, opts = {}, &blk)
raise ArgumentError, “A block must be passed when defining an initializer” unless blk
opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
initializers << Initializer.new(name, nil, opts, &blk)
end

An initializer can be configured to run before or after another initializer, which we’ll see a couple of times throughout this initialization process. Anything that inherits from Rails::Railtie may also make use of the initializer method, something which is covered in the Configuration guide].

The Initializer class here is defined within the Rails::Initializable module and its initialize method is defined to just set up a couple of variables:

def initialize(name, context, options, &block)
@name, @context, @options, @block = name, context, options, block
end

Once this initialize method is finished, the object is added to the object the initializers method returns:

def initializers
@initializers ||= self.class.initializers_for(self)
end

If @initializers isn’t set (which it won’t be at this point), the intializers_for method will be called for this class.

def initializers_for(binding)
Collection.new(initializers_chain.map { |i| i.bind(binding) })
end

The Collection class in railties/lib/rails/initializable.rb inherits from Array and includes the TSort module which is used to sort out the order of the initializers based on the order they are placed in.

The initializers_chain method referenced in the initializers_for method is defined like this:

def initializers_chain
initializers = Collection.new
ancestors.reverse_each do | klass |
next unless klass.respond_to?(:initializers)
initializers = initializers + klass.initializers
end
initializers
end

This method collects the initializers from the ancestors of this class and adds them to a new Collection object using the + method which is defined like this for the Collection class:

def +(other)
Collection.new(to_a + other.to_a)
end

So this method is overridden to return a new collection comprising of the existing collection as an array and then using the Array# method combines these two collections, returning a “super” Collection object. In this case, the only initializer that’s going to be in this new Collection object is the i18n.callbacks initializer.

The next method to be called after this initializer method is the after_initialize method on the config object, which is defined like this:

def after_initialize(&block)
ActiveSupport.on_load(:after_initialize, :yield => true, &block)
end

The on_load method here is provided by the active_support/lazy_load_hooks file which was required earlier and is defined like this:

def self.on_load(name, options = {}, &block)
if base = @loaded[name]
execute_hook(base, options, block)
else
@load_hooks[name] << [block, options]
end
end

The @loaded variable here is a hash containing elements representing the different components of Rails that have been loaded at this stage. Currently, this hash is empty. So the else is executed here, using the @load_hooks variable defined in active_support/lazy_load_hooks:

@load_hooks = Hash.new {|h,k| h[k] = [] }

This defines a new hash which has keys that default to empty arrays. This saves Rails from having to do something like this instead:

@load_hooks[name] = []
@load_hooks[name] << [block, options]

The value added to this array here consists of the block and options passed to after_initialize.

We’ll see these @load_hooks used later on in the initialization process.

This rest of i18n_railtie.rb defines the protected class methods include_fallback_modules, init_fallbacks and validate_fallbacks.

Back to activesupport/lib/active_support/railtie.rb

This file defines the ActiveSupport::Railtie constant which like the I18n::Railtie constant just defined, inherits from Rails::Railtie meaning the inherited method would be called again here, including Rails::Configurable into this class. This class makes use of Rails::Railtie’s config method again, setting up the configuration options for Active Support.

Then this Railtie sets up three more initializers:

  • active_support.initialize_whiny_nils
  • active_support.deprecation_behavior
  • active_support.initialize_time_zone

We will cover what each of these initializers do when they run.

Once the active_support/railtie file has finished loading the next file required from railties/lib/rails.rb is the action_dispatch/railtie.

activesupport/lib/action_dispatch/railtie.rb

This file defines the ActionDispatch::Railtie class, but not before requiring action_dispatch.

activesupport/lib/action_dispatch.rb

This file attempts to locate the active_support and active_model libraries by looking a couple of directories back from the current file and then adds the active_support and active_model lib directories to the load path, but only if they aren’t already, which they are.

activesupport_path = File.expand_path(‘../../../activesupport/lib’, FILE)
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)

activemodel_path = File.expand_path(‘../../../activemodel/lib’, FILE)
$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path)

In effect, these lines only define the activesupport_path and activemodel_path variables and nothing more.

The next two requires in this file are already done, so they are not run:

require ‘active_support’
require ‘active_support/dependencies/autoload’

The following require is to action_pack (activesupport/lib/action_pack.rb) which has a 22-line copyright notice at the top of it and ends in a simple require to action_pack/version. This file, like other version.rb files before it, defines the ActionPack::VERSION constant:

module ActionPack
module VERSION #:nodoc:
MAJOR = 3
MINOR = 1
TINY = 0
PRE = “beta”

STRING = [MAJOR, MINOR, TINY, PRE].compact.join(‘.’) end

end

Once action_pack is finished, then active_model is required.

activemodel/lib/active_model.rb

This file makes a require to active_model/version which defines the version for Active Model:

module ActiveModel
module VERSION #:nodoc:
MAJOR = 3
MINOR = 1
TINY = 0
PRE = “beta”

STRING = [MAJOR, MINOR, TINY, PRE].compact.join(‘.’) end

end

Once the version.rb file is loaded, the ActiveModel module has its autoloaded constants defined as well as a sub-module called ActiveModel::Serializers which has autoloads of its own. When the ActiveModel module is closed the active_support/i18n file is required.

activesupport/lib/active_support/i18n.rb

This is where the i18n gem is required and first configured:

begin
require ‘i18n’
require ‘active_support/lazy_load_hooks’
rescue LoadError => e
$stderr.puts “You don’t have i18n installed in your application. Please add it to your Gemfile and run bundle install”
raise e
end

I18n.load_path << “#{File.dirname(FILE)}/locale/en.yml”

In effect, the I18n module first defined by i18n_railtie is extended by the i18n gem, rather than the other way around. This has no ill effect. They both work on the same way.

This is another spot where active_support/lazy_load_hooks is required, but it has already been required so it’s not loaded again.

If i18n cannot be loaded, the user is presented with an error which says that it cannot be loaded and recommends that it’s added to the Gemfile. However, in a normal Rails application this gem would be loaded.

Once it has finished loading, the I18n.load_path method is used to add the activesupport/lib/active_support/locale/en.yml file to I18n’s load path. When the translations are loaded in the initialization process, this is one of the files where they will be sourced from.

The loading of this file finishes the loading of active_model and so we go back to action_dispatch.

Back to activesupport/lib/action_dispatch.rb

The remainder of this file requires the rack file from the Rack gem which defines the Rack module. After rack, there’s autoloads defined for the Rack, ActionDispatch, ActionDispatch::Http, ActionDispatch::Session. A new method called autoload_under is used here, and this simply prefixes the files where the modules are autoloaded from with the path specified. For example here:

autoload_under ‘testing’ do
autoload :Assertions

The Assertions module is in the action_dispatch/testing folder rather than simply action_dispatch.

Finally, this file defines a top-level autoload, the Mime constant.

Back to activesupport/lib/action_dispatch/railtie.rb

After action_dispatch is required in this file, the ActionDispatch::Railtie class is defined and is yet another class that inherits from Rails::Railtie. This class defines some initial configuration option defaults for config.action_dispatch before setting up a single initializer called action_dispatch.configure.

With action_dispatch/railtie now complete, we go back to railties/lib/rails.rb.

Back to railties/lib/rails.rb

With the Active Support and Action Dispatch railties now both loaded, the rest of this file deals with setting up UTF-8 to be the default encoding for Rails and then finally setting up the Rails module. This module defines useful methods such as Rails.logger, Rails.application, Rails.env, and Rails.root.

Back to railties/lib/rails/all.rb

Now that rails.rb is required, the remaining railties are loaded next, beginning with active_record/railtie.

activerecord/lib/active_record/railtie.rb

Before this file gets into the swing of defining the ActiveRecord::Railtie class, there are a couple of files that are required first. The first one of these is active_record.

activerecord/lib/active_record.rb

This file begins by detecting if the lib directories of active_support and active_model are not in the load path and if they aren’t then adds them. As we saw back in action_dispatch.rb, these directories are already there.

The first three requires have already been done by other files and so aren’t loaded here, but the 4th require, the one to arel will require the file provided by the Arel gem, which defines the Arel module.

require ‘active_support’
require ‘active_support/i18n’
require ‘active_model’
require ‘arel’

The 5th require in this file is one to active_record/version which defines the ActiveRecord::VERSION constant:

module ActiveRecord
module VERSION #:nodoc:
MAJOR = 3
MINOR = 1
TINY = 0
PRE = “beta”

STRING = [MAJOR, MINOR, TINY, PRE].compact.join(‘.’) end

end

Once these requires are finished, the base for the ActiveRecord module is defined along with its autoloads.

Near the end of the file, we see this line:

ActiveSupport.on_load(:active_record) do
Arel::Table.engine = self
end

This will set the engine for Arel::Table to be ActiveRecord::Base.

The file then finishes with this line:

I18n.load_path << File.dirname(FILE) + ‘/active_record/locale/en.yml’

This will add the translations from activerecord/lib/active_record/locale/en.yml to the load path for I18n, with this file being parsed when all the translations are loaded.

Back to activerecord/lib/active_record/railtie.rb

The next two requires in this file aren’t run because their files are already required, with rails being required by rails/all and active_model/railtie being required from action_dispatch.

require “rails”
require “active_model/railtie”

The next require in this file is to action_controller/railtie.

actionpack/lib/action_controller/railtie.rb

This file begins with a couple more requires to files that have already been loaded:

require “rails”
require “action_controller”
require “action_dispatch/railtie”

However the require after these is to a file that hasn’t yet been loaded, action_view/railtie, which begins by requiring action_view.

actionpack/lib/action_view.rb

action_view.rb

Something went wrong with that request. Please try again.