Help us understand the problem. What is going on with this article?

Rackとは何か

More than 5 years have passed since last update.

僕はRackミドルウェアを何個か書いたことがあるけど、この前Rubyを始めたばかりの人に「Rackって何?」って聞かれた時、ちゃんと答えられなかった。
なので、rack/rackは何を実装していて、RailsやSinatraはどのようにRackを利用しているのかについてRack 1.6.4のコードを読みつつ調べてみた。

Rackとは何か

Rackは、指定したファイルを独自のRuby DSLとして読み込み、DSLで指定した様々なミドルウェア、アプリケーションを組み合わせてWebサーバを立ち上げることができるrackupというコマンドを提供するライブラリである。

Rack::Serverの仕組み

rackupコマンドはRack::Server.startしているだけであり、rackupによって立ち上がるWebサーバの挙動を理解するにはRack::Serverの仕組みを知る必要がある。

Rack::Server.startは以下の手順でWebサーバを立ち上げる。

  • config.ruをRack::Builder DSLとして評価し、Rackアプリケーションを生成
  • RACK_ENVに応じてRackアプリケーションにRackミドルウェアをラップする
  • RackミドルウェアをラップしたRackアプリケーションをRackハンドラに渡す

以下で各用語の意味を説明する。

Rack::Builder DSL

RackはRack::Builder DSLという独自のDSLを提供している。rackup [rackup config]を叩くと[rackup config]に指定したファイルをRack::Builder DSLとして読み込み、そのDSLによって設定されたWebサーバーが立ち上がる。rackup configの指定を省略するとカレントディレクトリのconfig.ruが読み込まれる。

Rack::Builder DSLはrack/builder.rb#L62-L142に実装されていて、以下の4つを含む。

DSL 意味
run 後述のRackアプリケーションを指定する。これがベースの挙動になる。runだけは指定が必須。
use 後述のRackミドルウェアを指定する。
warmup Procオブジェクトを渡すとサーバが立ち上がる前に評価される。
map リクエストパスに応じてミドルウェアやアプリケーションを切り替えるために使う。

Rackアプリケーション

Rackアプリケーションは、引数を1つ取り3つの値を返すcallを呼び出すことができるオブジェクトであると定義されておりRack protocolと呼ばれる仕様を満たすことが期待されている。
RailsやSinatraで書くアプリケーションがこれに属する。

Rack protocolに定義されているcallの引数や返り値の具体的な内容は以下の通りである。

種別 名前 内容
引数 environment HTTPリクエストヘッダなどを含むHash
返り値 status to_iした結果が100以上になるオブジェクト。HTTPステータスコードになる。
headers eachを呼ぶとキーと値をyieldするオブジェクト。HTTPレスポンスヘッダに使われる。
body eachを呼ぶとStringyieldするオブジェクト。HTTPレスポンスボディに使われる。

Rackミドルウェア

Rackアプリケーションのcallの前後に行いたい処理を記述するクラス。Rackアプリケーションを引数にinitializeすると、そのアプリケーションをcallするRackアプリケーションを返すことが期待される。
rack-mini-profilerなど、rack-のつくgemは大体Rackミドルウェアとして実装されている。

Rack::Builder DSL内で複数のRackミドルウェアをuseすることでミドルウェアスタックが作られる。useした順にリクエストが処理されてベースのRackアプリケーションに渡され、そのレスポンスをuseの逆順に処理されることになる。

Rackハンドラ

RackミドルウェアにラップされたRackアプリケーションと、portやhostなどのオプションを受け取って実際にWebサーバを立ち上げるクラス。HTTPリクエストをパースしたりHTTPレスポンスを組み立てるのはRackハンドラの役割である。
WEBrick, Thin, Pumaなどがこれに属する。

Rack::Handler.defaultでは、環境変数RACK_HANDLERが定義されていればそれを使い、そうでなければThin > Puma > WEBrickの優先順位で定義されているものを使用する。

Rails::Serverの仕組み

railsの開発でrackupを叩く人はほとんどいないと思う。通常叩くrails sRack::ServerのサブクラスであるRails::Serverstartしている

Rails::Serverでは、opt_parserをオーバーライドしてrails s特有のオプションを定義したり、デフォルトポートを3000にしたり、RAILS_ENVに応じた処理が定義されている。
Railsアプリにもconfig.ruはおいてあるが、rackupを叩いてしまうとRails::ServerではなくRack::Serverがstartされるので、これらの恩恵を受けることができなくなる。

Sinatra::Applicationの仕組み

require "sinatra"するとat_exitSinatra::Application.run!フックされる

Rails::Serverと違ってSinatra::ApplicationRack::Serverのサブクラスではないので、newの際にRack::Builder DSLを呼んだりrun!の際にRack::Handlerを探したりする実装が含まれている。

まとめ

RackはRubyでWebサーバーを立ち上げるためのインターフェースである。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした