Ruby on Railsの初期化プロセスを解説!railsコマンドからアプリケーションの起動まで
Railsの初期化プロセスは複雑で、数多くのステップが絡み合っています。その複雑さをステップバイステップで確認していくことで、Railsの初期化順序を詳細に解説しています。Ruby on Railsのバージョンは7.0.5で確認しています。
目次
- Railsの初期化プロセスの全体的な流れ
- Step 1. railsコマンドの実行
- Step 2.config/application.rbの読み込み
- Step 2.1. config/application.rb内でRailsの初期化処理の登録
- Step 2.2. config/application.rb内でGemのロード
- Step 2.3. config/application.rb内でRails::Applicationクラスのロード
- Step 3. サーバーを起動させる
- Step 4. config.ruの実行
- Step 4.1. config.ruからRailsの初期化処理の実行
- Step 4.2. config.ruからアプリケーションサーバの起動
- 参考: bin/rails s 時のファイルの読み込み順序ログ
Railsの初期化プロセスの全体的な流れ
Ruby on Railsの初期化時の「bin/rails serverからアプリケーションサーバの起動まで」のステップは次のようになっています。重要な処理の部分に絞ることで、厳密性よりも理解のしやすさを重視しています。
Step 1. railsコマンドの実行
まずは、ターミナルからrailsコマンドを実行します。
$ bin/rails server
bin/rails
ファイル内は次のようになってます。
config/boot.rb
ではrequire "bundler/setup"
とrequire "bootsnap/setup"
が呼ばれ、それぞれGemfile
内のgemのセットアップ(※gemのロードはされない)とbootsnap
(ブート時間を短くするためgem)のセットアップがされます。rails/commands
ではserver
やconsole
など引数に応じたコマンドクラスが実行されるようになっています。
#!/usr/bin/env ruby
APP_PATH = File.expand_path("../config/application", __dir__)
#config/boot.rbを読み込む
#https://github.com/rails/rails/blob/v7.0.5/railties/lib/rails/generators/rails/app/templates/config/boot.rb.tt
require_relative "../config/boot"
#引数に応じてrailsコマンドを実行させる
#https://github.com/rails/rails/blob/v7.0.5/railties/lib/rails/commands.rb
require "rails/commands"
Step 2.config/application.rbの読み込み
rails/commands
の処理をたどっていくと、bin/rails
コマンドの引数にserver
を指定しているので、Rails::Command::ServerCommand#perform
が実行されます。
そして、perform
メソッド内のrequire APP_PATH
によりconfig/application.rb
が読み込まれます。
#https://github.com/rails/rails/blob/v7.0.5/railties/lib/rails/commands/server/server_command.rb#L129-L148
module Rails
module Command
class ServerCommand < Base # :nodoc:
...
def perform
extract_environment_option_from_argument
set_application_directory!
prepare_restart
Rails::Server.new(server_options).tap do |server|
# NOTE: APP_PATH には /path/to/config/application が設定されている
# そのため、 config/application.rb が読み込まれる
require APP_PATH
Dir.chdir(Rails.application.root)
if server.serveable?
print_boot_information(server.server, server.served_url)
after_stop_callback = -> { say "Exiting" unless options[:daemon] }
server.start(after_stop_callback)
else
say rack_server_suggestion(options[:using])
end
end
end
Step 2.1. config/application.rb内でRailsの初期化処理の登録
config/application.rb
の先頭部分でrequire "rails/all"
が読み込まれます。
require_relative "boot"
require "rails/all"
# ...
rails/all.rb
のファイルは各railtieやengineが読み込まれます。
各railtieやengineのファイル内ではinitializer
メソッドで初期化処理の登録が行われます。初期化処理の実行自体はRails.application.initialize!
時に行われるのでここではあくまで初期化処理の登録にとどまっています。
#https://github.com/rails/rails/blob/v7.0.5/railties/lib/rails/all.rb
require "rails"
%w(
active_record/railtie
active_storage/engine
action_controller/railtie
action_view/railtie
action_mailer/railtie
active_job/railtie
action_cable/engine
action_mailbox/engine
action_text/engine
rails/test_unit/railtie
).each do |railtie|
begin
require railtie
rescue LoadError
end
end
initializer
のサンプルとしてactive_record/railtie.rb
内では次のように初期化処理を登録しています。
# https://github.com/rails/rails/blob/v7.0.5/activerecord/lib/active_record/railtie.rb#L301-L316
initializer "active_record.clear_active_connections" do
config.after_initialize do
# ActiveRecord::Baseが読み込まれたときに実行される
ActiveSupport.on_load(:active_record) do
# アクティブなコネクションを切断する
clear_active_connections!
flush_idle_connections!
end
end
end
Step 2.2. config/application.rb内でGemのロード
次にconfig/application.rb
を読み進めていくと、Bundler.require(*Rails.groups)
が実行されます。ここでGemfile
内のgemがロードされます。
require_relative "boot"
require "rails/all"
# ここまで読んだ
# :test, :development, :production のグループに応じて
# Gemfileのgemがロードされる
Bundler.require(*Rails.groups)
# ...
Step 2.3. config/application.rb内でRails::Applicationクラスのロード
そして、config/application.rb
を最後まで読み進めると Rails::Application
を継承した自分のApplication
クラスがロードされます。
require_relative "boot"
require "rails/all"
Bundler.require(*Rails.groups)
# ここまで読んだ
# Rails::Applicationを継承した自分のApplicationクラスがロードされる
module RailsInitializatoinTest
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
end
end
Step 3. サーバーを起動させる
config/application.rb
をロードできたので、Rails::Command::ServerCommand#perform
に戻り、サーバーを起動させます。
# https://github.com/rails/rails/blob/v7.0.5/railties/lib/rails/commands/server/server_command.rb#L129-L148
module Rails
module Command
class ServerCommand < Base # :nodoc:
...
def perform
extract_environment_option_from_argument
set_application_directory!
prepare_restart
Rails::Server.new(server_options).tap do |server|
require APP_PATH
Dir.chdir(Rails.application.root)
if server.serveable?
# 「Booting Puma...」がコンソールに表示される
print_boot_information(server.server, server.served_url)
after_stop_callback = -> { say "Exiting" unless options[:daemon] }
# サーバーを起動させる
server.start(after_stop_callback)
else
say rack_server_suggestion(options[:using])
end
end
end
Step 4. config.ruの実行
サーバー起動の中ではconfig.ru
ファイルを読み込み、Rackアプリケーションとして実行します。
config.ru
の中身は次のようになっており、config/environment.rb
を読み込んでRailsの初期化処理を実行し、サーバーを起動させています。
# アプリケーションを起動させるためにRackベースのサバーとして利用されるファイル
# config/environment.rbを読み込む
require_relative "config/environment"
# サーバーを起動させる
run Rails.application
Rails.application.load_server
Step 4.1. config.ruからRailsの初期化処理の実行
config.ru
から呼ばれるconfig/enviromnent.rb
ファイル内では、Railsアプリケーションの読み込みとRailsアプリケーションの初期化をします。
Railsアプリケーションの読み込みは、既に前の方の処理で読み込んでいるためスルーされます。
Railsアプリケーションの初期化は Rails.application.initialize!
が呼ばれ、各railtieやengineのファイル内で登録された初期化処理が実行されます。
# Railsアプリケーションを読み込む
# 前の方の処理で既に読み込んでいるためスルー
require_relative "application"
# Railsアプリケーションを初期化する
Rails.application.initialize!
また、この初期化処理の実行時に次のようなことが起こります。
- 各railtieやengineファイル内の
initializer
メソッドの実行 config/initializers/*.rb
のファイルの読み込みActiveSupport.run_load_hooks(:active_record, Base)
などの登録されてい登録されていた(※production環境の場合)config/routes.rb
ファイルの読み込み- などなど
Step 4.2. config.ruからアプリケーションサーバの起動
最後に、config.ru
のrun Rails.application
とRails.application.load_server
によりアプリケーションサーバ(ex Pumaなど)の設定ファイルを読み込み、サーバーが起動します。
# ...
# サーバーを起動させる
run Rails.application
Rails.application.load_server
参考: bin/rails s 時のファイルの読み込み順序ログ
bin/rails server
コマンドを実行したときの流れは次のようになっています。
$ RAILS_ENV=production bin/rails s
# bin/rails ファイルの読み込み
# L require_relative "../config/boot"
# | L require "bundler/setup"
# | L require "bootsnap/setup"
# Lrequire "rails/commands"
# L config/application.rb ファイル
# | L require_relative "boot"
# | L require "rails/all" # */railtieが読み込まれてinitializerが登録される、active_record.rb などの読み込まれる。Baseは呼ばれないのでActiveSupport.run_load_hooksは実行されない
# L run Bundler.require(*Rails.groups) # Gemが読み込まれる
# L 「Booting Puma...」がコンソールに表示される
# L config.ru ファイル
# L require_relative "config/environment"
# | L require_relative "application" # 既に読み込み済みなのでスルー
# | L Rails.application.initialize! # 登録済みのinitializerを実行する、各gemのhookが登録される
# | L load config/initializers/*.rb files
# | L ActiveSupport.run_load_hooks(:active_record, Base) ※development環境の場合は呼ばれない
# | L load config/routes.rb file
# L run Rails.application # Railsアプリケーションがrackとして起動
# L Rails.application.load_server # Rack::Handler::Puma
# L load config/puma.rb file
# L 「Puma starting in single mode...」がコンソールに表示される