yukata

日々出会ったIT技術関連の所感やら紹介やら

rails3.2.8の起動を追いかける その2

前回の続き

それでは、sampleアプリケーション内の、「sample/script/rails」ファイルで読み込まれた、「rails/commands」以降の内容を確認していきたいと思います。

rails/commands」の中身

「/Library/Ruby/Gems/1.8/gems/railties-3.2.8/lib/rails/commands.rb 」

ちょっと長くなってしまうので、実行される部分のみ抜粋しました。また、行数が追いにくくなってしまうので2つにわけています。

require 'active_support/core_ext/object/inclusion'

ARGV << '--help' if ARGV.empty?

aliases = {
  "g"  => "generate",
  "d"  => "destroy",
  "c"  => "console",
  "s"  => "server",
  "db" => "dbconsole",
  "r"  => "runner"
}

command = ARGV.shift
command = aliases[command] || command
1行目:

ActiveSupport関連の読み込みですね。
ActiveSupportは、Rubyに便利な機能を付け加えるものです。Railsだけをやっている人は、ActiveSupportの機能なのかRubyの機能なのかわからずに使っている機能も多いかと思います。例えば、"blank?"っていうメソッドをよく使いますが、これはActiveSupportが提供するもので、irbとかでいきなり実行しても使えません。
これに関しては今後の記事で取り上げたいと思います。ここでは読み飛ばします。

3行目:

引数がなかった場合"--help"を付けたのと同じようにしてくれるんですね!親切ですね!!

5~15行目:

引数の別名をハッシュで持っておき、変数"command"に対応する正式名を格納しています。今回の引数は"s"なので、"server"が"command"に格納されます。

case command
  # ~省略~
when 'server'
  Dir.chdir(File.expand_path('../../', APP_PATH)) unless
File.exists?(File.expand_path("config.ru"))

  require 'rails/commands/server'
  Rails::Server.new.tap { |server|
    require APP_PATH
    Dir.chdir(Rails.application.root)
    server.start
  }

  # ~省略~
end
1行目:

先ほどの変数"command"に格納された値は、"server"なので、上記コードの部分が実行されます。

4、5行目:

"config.ru"が存在しなければ、"APP_PATH"に格納されてあるディレクトリへ移動します。"config.ru"は存在しますので、これは実行されません。
ちなみに"APP_PATH"には、前回の記事で説明した箇所で、"config/application"が格納されています。

7、8行目:

"::Rack::Server"クラスを継承する、"Rails::Server"クラスを読み込みます。
"Rails::Server"クラスをインスタンス化して、"tap"メソッドにブロックを指定して実行しています。
"tap"メソッドは、Objectクラスのインスタンスメソッド(拡張)で、受け取ったブロックに自身のインスタンスを引数として渡して実行するだけです。

9~11行目:

"APP_PATH"、つまり「sample/config/application.rb 」を読み込み、railsアプリケーションのルートディレクトリへ移動しています。
そして、"server.start"が実行されます。


"server.start"で実行されるコード

"::Rack::Server"の持っている"start"メソッドを、"Rails::Server"クラスがオーバーライドしていますので、"Rails::Server"クラスの"start"メソッドが実行されます。
以下、「/Library/Ruby/Gems/1.8/gems/railties-3.2.8/lib/rails/commands/server.rb」のstartメソッド

def start
  url = "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}"
  puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"
  puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}"
  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
  # The '-h' option calls exit before @options is set.
  # If we call 'options' with it unset, we get double help banners.
  puts 'Exiting' unless @options && options[:daemonize]
end
2~7行目:

SSLが有効であれば"https://host:port"、有効でなければ"http://host:prot"を変数"url"へ格納します。
そして、railsの起動情報を標準出力へ表示します。"Ctrl-C"でのサーバ停止も設定されています。

10~12行目:

Railsのルートディレクトリ直下の"tmp"ディレクトリ(sample/tmp)に、"cache", "pids", "sessions", "sockets"ディレクトリを作成します。

14行目:

"super"メソッドが最後に実行され、親クラスである"::Rack::Server"の"start"メソッドが実行されます。ここで実行されるstartメソッドにより、Rackの仕様に従ってWebサーバが起動します。今回はオプションを指定していませんので、Webrickサーバが起動します。
Rackとは、Webアプリケーションサーバとアプリケーションフレームワーク間のインターフェスの仕様で、以下の決まりがあります。

  • 「call」というメソッドを実装している
  • 「call」メソッドの引数としてWebサーバからのリクエストを受け取る
  • 「call」メソッドは,下記をレスポンスとして返す


上記を満たしていることによって、ユーザからのリクエストを受け付けたときに、Rackの仕様でHTTPリクエストを受け取り、インスタンス毎にRailsで書いたコードの処理が行われ、ユーザへHTTPレスポンスを返します。

細かいところはだいぶ省きましたが、だいたいの流れはこんな感じだと思います。今後はもう少し細かい部分を個別に追っていきたいと思います。