トップレベルスタイルSinatraの起動プロセス

Sinatraのトップレベルスタイルで書かれたWebサーバーが,どのように起動するのかを解説します.

トップレベルスタイル とは、以下のような書き方のことを指します:

require 'sinatra'

get '/hello' do
  'hogehoge'
end

このスタイルで書かれたSinatraのWebサーバーには,サーバーを起動するような記述が一切ありません.どのようにWebサーバーが起動しているのでしょうか?

TL;DR

以下のようなトップレベルスタイルのSinatraアプリケーションは、

require 'sinatra'

get '/' do
  'hoge'
end

以下のようなコードと概ね等価ということです:

# 空のモジュラーアプリの定義
require 'sinatra/base'

class Application < Sinatra::Base
end

# DSLの定義
[:get, :post, :put, :delete].each do |method_name|
  define_method method_name do |*args, &blk|
    Application.send(method_name, *args, &blk)
  end
end

# Webサーバー起動処理の登録
at_exit { Application.run! }

# アプリケーションの定義
get '/' do
  'hoge'
end

解説

モジュラースタイル

Sinatraでは、上記のようなトップレベルスタイルを用いる以外にも、 モジュラースタイル を用いる方法があります。

モジュラースタイルというのは、以下のような書き方を指します:

require 'sinatra/base'

class MySinatraApplication < Sinatra::Base
  get '/hello' do
    'hogehoge'
  end
end

MySinatraApplication.run!

この用に定義されたWebアプリケーションのことを モジュラーアプリ と呼びます。

空のモジュラーアプリの定義

require 'sinatra' と書くと、 Sinatra::Application という名前の空のモジュラーアプリが定義されます。

DSLの定義

また、require 'sinatra' が実行されることで、 Sinatra::Delegator というモジュールがグローバルに extend され、get とか post などの、SinatraDSLで使われるメソッドが定義されます。

これらのメソッドは Sinatra::Application.getSinatra::Application.post に”リダイレクト”されます。

つまり、以下のコードは、

require 'sinatra'

get '/' do
  'hoge'
end

以下のコードと等価です:

require 'sinatra'

Sinatra::Application.get '/' do
  'hoge'
end

詳しい実装が気になる人は sinatra/base.rb at master · sinatra/sinatra · GitHub を読んで下さい。 メタプログラミングのお手本のような使い方だと思います。

サーバーの起動

Rubyには Kernel.#at_exit というメソッドがあります。 このメソッドにブロックを与えると、インタプリタ終了時にそのブロックが実行されます。

Application::Sinatra の定義の中で at_exit が呼び出されている部分があり、Webサーバーの起動処理が登録されています。

これによって、スクリプトの実行終了時にWebサーバーが起動します。

まとめ

つまり、以下のようなトップレベルスタイルのSinatraアプリケーションは、

require 'sinatra'

get '/' do
  'hoge'
end

以下のようなコードと概ね等価ということです:

# 空のモジュラーアプリの定義
require 'sinatra/base'

class Application < Sinatra::Base
end

# DSLの定義
[:get, :post, :put, :delete].each do |method_name|
  define_method method_name do |*args, &blk|
    Application.send(method_name, *args, &blk)
  end
end

# Webサーバー起動処理の登録
at_exit { Application.run! }

# アプリケーションの定義
get '/' do
  'hoge'
end