Rack解説を試みて失敗しつつも晒す、の巻

後先考えず、空気も読まず、気分がのったので、晒してみます。記事を書いた後で、意外と日本語のRack情報があった事に気づいて愕然としたのは、内緒です。以降、基本的には公式ドキュメント等の劣化コピーなので、注意してください。

Rack

Rackとは、Rubyで実装したアプリケーションとウェブサーバとを繋ぐインターフェースです。Rackプロトコルに従って実装したアプリケーションは、利用するウェブサーバを自由に選択する事が可能となります。

Rackの開発元へは、以下のリンクから。
Rack: a Ruby Webserver Interface

Rackプロトコル概要

Rackプロトコルについて、簡単に説明します。

Rackプロトコルに従ったアプリケーションとは、環境変数envを引数にとるメソッドcallを持つRubyオブジェクト(インスタンス)を意味します。callは、3つの要素を持つArrayオブジェクトを返します。これは順に、『HTTPステータスコード』『HTTPレスポンスヘッダ』『HTTPボディ』となります。

環境変数

アプリケーションは、環境変数を読み書きする事で、入出力操作等を行える様になります。環境変数は、いわゆるCGI環境変数や、Rack独自の環境変数、アプリケーション独自の変数を格納します。環境変数はStringオブジェクトをキーに持つHashオブジェクトです。

ここでCGI環境変数と呼んでいるのは、PEP333に定められているCGI環境変数と同様です。CGI環境変数はすべてStringオブジェクトとして値を保持します。

Rack独自の環境変数のキーは、"rack."で始まります。入力ストリーム(rack.input)およびエラーストリーム(rack.errors)も、これに含まれます。

アプリケーション独自の環境変数のキーは、アプリケーション固有のプレフィックスで始まります。キーは、Rack環境変数同様に必ずドットを含まなければなりません。つまり、『プレフィックス.名前』というキーを利用しなければならないという事になります。アプリケーション独自と書きましたが、アプリケーションのみならず、ウェブサーバが利用する事もあり得ます。

レスポンス

先述の通り、レスポンスは3つの要素からなるArrayオブジェクトです。それぞれについて、簡単に説明します。

第1要素は、HTTPステータスコードになります。これは、必ず100以上でなければなりません。また、to_iメソッドに応答可能である必要があります。整数でなくとも、to_iによって整数に変換可能であればかまいません。

第2要素は、HTTPレスポンスヘッダです。これは、eachに応答し、ヘッダフィールドを意味するキーとその値をyieldしなければなりません。簡単にいえば、Hash#eachと同様の振る舞いをするという事です。
特に注意すべき制約として、ステータスコードが204か304の場合を除いて、必ずContent-Typeを含まなければならないという点が挙げられます。

第3要素は、HTTPボディです。これは、eachに応答し、Stringオブジェクトをyieldしなければなりません。一般に、StringオブジェクトからなるArrayオブジェクトか、アプリケーションインスタンス自身、もしくはFileの振る舞いをするオブジェクトが使われるでしょう。

検査

Rackプロトコルに従えているかどうかについて、検査するための手段が提供されています。Rack::Lintというミドルウェアを有効にする事で、アプリケーション実行時に検査し、異常があれば例外をスローします。

補足

Rackプロトコルの詳細については、以下の公式ドキュメントをご覧ください。
File: SPEC

参考までに自身による和訳へのリンクを紹介しておきます。
KOSHIGOE学習帳 - [Ruby] Rackプロトコル仕様

対応環境

ここで、Rackの対応環境について紹介したいと思います。

ウェブサーバ

Rackが同梱するハンドラ(Rack::Handler::*)か、ウェブサーバが同梱するハンドラによって、以下の様なウェブサーバがRack対応を果たしています。

Mongrel, EventedMongrel, WEBrick, FCGI, CGI, SCGI, LiteSpeed, Ebb, Fuzed, Phusion Passenger, Thin

ウェブフレームワーク

Rackが同梱するアダプタ(Rack::Adapter::*)か、ウェブフレームワークが同梱するアダプタによって、以下の様なウェブフレームワークがRack対応を果たしています。

Camping, Coset, Halcyon, Mack, Maveric, Merb, Racktools::SimpleApplication, Ramaze, Sinatra, Vintage, Waves

Ruby on Railsは、Thinに同梱されるアダプタによって稼働させる事ができ、これは今後のバージョンでRackにマージされる予定です。

補足

詳細については、以下の公式ドキュメントをご覧ください。
Rack Documentation

チュートリアル

Rackについて、基本的な説明をしてきましたが、ここで実際のアプリケーションを動かしてみましょう。参考までに、手元の環境を掲載しておきます。

$ uname -a
Darwin koshigoe.local 9.4.0 Darwin Kernel Version 9.4.0: Mon Jun  9 19:30:53 PDT 2008; root:xnu-1228.5.20~1/RELEASE_I386 i386 i386 MacBookPro3,1 Darwin
$ ruby --version
ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9.4.0]
$ gem --version
1.2.0
  

Rack導入

まずは、Rackをインストールします。

$ sudo gem install rack
$ rackup --version
Rack 0.1
    

2008/09/09現在、RubyGemsでインストール可能なRackのバージョンは0.4.0です("rackup --version"の結果は、Rackプロトコルとしては、バージョン0.1という事だと思います)。

最新の開発版に興味がある方は、GitHubのリポジトリをご覧ください。
chneukirchen's rack at master — GitHub

簡易アプリケーション

以下を"config.ru"として適当な場所に保存します。

run Proc.new { |env|
  [
    200,
    {'Content-Type' => 'text/html; charset=UTF-8'},
    [env.map { |key, value| Rack::Utils.escape_html("#{key}=#{value}")}.join("<br />\n")]
  ]
}
 

ここでは、手軽にアプリケーションインスタンスをProcオブジェクトで実装しました。環境変数をダンプするだけのアプリケーションとなります。

このファイルを、rackupを利用して任意のウェブサーバ上で動かしてみます。

$ rackup config.ru
    

ここではウェブサーバを指定していないため、WEBrickが起動するはずです。早速、ウェブブラウザから結果を見てみましょう。オプションを省略したので、"http://localhost:9292/"にリクエストすれば結果が見られるはずです。

環境変数の内容が表示されたでしょうか。

rackup

話が前後しますが、rackupについても簡単に説明しておきます。rackupは、rackup configファイルを読み込み、任意のウェブサーバでアプリケーションを実行するためのユーティリティです。rackup configとは、Rackの独自DSLで記述する設定ファイルです。DSLはRack::Builderで実装されています。

基本的に、『アプリケーションライブラリの読み込み』『ミドルウェアの選択』『アプリケーションの実行およびマッピング』を記述する事になります。

Rackには、実行環境があり、rackup実行時に指定する事ができます。Rackのソースを見た限りでは、development, deployment, noneの3種類が定義されているようです。rackupは指定された実行環境に応じて、自動的にミドルウェアを選択します。
bin/rackup at master from chneukirchen's rack — GitHub

以下は、rackupのヘルプになります。

$ rackup -h
Usage: rackup [ruby options] [rack options] [rackup config]
Ruby options:
  -e, --eval LINE          evaluate a LINE of code
  -d, --debug              set debugging flags (set $DEBUG to true)
  -w, --warn               turn warnings on for your script
  -I, --include PATH       specify $LOAD_PATH (may be used more than once)
  -r, --require LIBRARY    require the library, before executing your script
Rack options:
  -s, --server SERVER      serve using SERVER (webrick/mongrel)
  -o, --host HOST          listen on HOST (default: 0.0.0.0)
  -p, --port PORT          use PORT (default: 9292)
  -E, --env ENVIRONMENT    use ENVIRONMENT for defaults (default: development)
  -D, --daemonize          run daemonized in the background
  -P, --pid FILE           file to store PID (default: rack.pid)
Common options:
  -h, --help               Show this message
      --version            Show version
    

設定ファイルは、.ruで終わる任意のファイル名で良いはずです。設定ファイルの指定を省略した場合、カレントディレクトリのconfig.ruを読み込もうとします。

ミドルウェア

Rackは、いくつかのミドルウェアを提供しています。

基本構造

ミドルウェアは、基本的にはアプリケーションを包むラッパーです。設定ファイル内で"use Hoo"とする事で、アプリケーションがミドルウェアに包まれ、処理を連鎖させるわけです。少々、強引な解釈かと思いますが、おおむねこのような理解で問題ないかと思います。

ミドルウェアは、以下の規約に従います(実装から独断で書いています)。

  • initializeメソッドの第1引数にアプリケーションインスタンスをとる
  • 実行時にアプリケーション同様に扱うためにcallメソッドを実装している
  • ほか、Rackプロトコルに従う

ラッピングするためにinitializerの点が特徴的ですが、基本的にはアプリケーションのような振る舞いをするオブジェクトです。

一覧

バージョン0.4.0時点で利用可能なミドルウェアの一覧です。中には、正確にはアプリケーションというべきものも含まれていますが、便利だという事で含めます。

Rack::Auth::Basic
Basic認証を行うミドルウェア。全体もしくはアプリケーションごとに保護可能。
Rack::Auth::Digest::MD5
MD5によるダイジェスト認証を行うミドルウェア。アプリケーションごとの保護のみ確認。
Rack::Auth::OpenID
OpenIDによる認証を行うミドルウェア。動作確認はとれていない。
Rack::Cascade
レスポンスコードによるアプリケーションのカスケーディングを行うミドルウェア。ファイルが存在しなければ(404 Not Foundなら)任意のアプリケーションを実行する、など。
Rack::CommonLogger
ロギングを行うミドルウェア。development環境とdeployment環境では強制される。
Rack::Deflater
圧縮転送に対応するミドルウェア。
Rack::Directory
ディレクトリインデックスの表示か指定アプリケーションの実行を行うミドルウェア(アプリケーション?)。
Rack::File
ファイルの表示を行うアプリケーション。
Rack::Lint
プロトコルを守れているか検査するミドルウェア。development環境では強制される。
Rack::Lobster
サンプルアプリケーション。ロブスターが表示される。
Rack::URLMap
アプリケーションのロケーションを設定できる。DSLのmapに対応する。
Rack::Rcursive
Rack::ForwardRequest.new(location)がraiseされた時に、locationに対応するアプリケーションに転送するミドルウェア。
Rack::Reloader
Rubyスクリプトが変更された時に、スクリプトをリロードするミドルウェア。リロードの最短間隔を指定できる。
Rack::Session::Cookie
Cookieを利用した単純なセッション管理を行うミドルウェア。env['rack.session']などからHashでセッションデータを操作可能。
Rack::Session::MemCache
memcachedを利用した並列管理可能なセッション管理を行うミドルウェア。セッションキーはCookieで行う。env['rack.session']などからHashでセッションデータを操作可能。
Rack::Session::Pool
Cookieを利用した並列管理可能なセッション管理を行うミドルウェア。env['rack.session']などからHashでセッションデータを操作可能。
Rack::ShowExceptions
例外発生時に見栄えの良いページとして表示するミドルウェア。development環境では強制される。
Rack::ShowStatus
エラーレスポンス(400以上)の際、Rackが提供するテンプレートを使ってページを表示するミドルウェア。env['rack.showstatus.detail']に文字列(HTML)を入れておくと、これも表示される。
Rack::Static
URLに対応する静的ファイルを表示するミドルウェア。特定のディレクトリ(URL)のみを静的ファイルとして扱う様な場合に便利。

サンプル

ミドルウェアの利用例をGitHubに置いておきました。興味のある方は、ご覧ください。サンプル中の英語については、気にしないようお願いします。
koshigoe's rackup_examples at master — GitHub

一部のミドルウェアでは、その実行に別途RubyGemsを必要とするものがあります。必要に応じてインストールしてください。

まとめ

以上、怪しげなところが多々ありますが、簡単にRackについて説明しました。

基礎として、後は、Rack::RequestRack::Response、およびRack::Utilsの紹介もするべきですが、今回はここまで。まだ、アプリケーション実装の詳細までは経験していないため、この程度にとどめようかと思います。

この記事の内容は、基本的には公式ドキュメントとウェブに公開されている情報をまとめ直した様なものです。より詳細に興味がある場合は、Rackのソースや、以下の参考情報をご覧ください。いくつかはまだ読んでいなかったりしますが、興味深そうなだったので含めておきます。

機会があれば、省略したユーティリティクラスや、利用レポートなどについても書いてみようかと思っています。利用レポートといっても、ssbをRack化してmod_passengerで動かしているデータしかとれませんが。しかも、作業用マシンで動かしていて、毎日電源を落としているので、稼働データは取れません。

会社の同僚がこれを読まない事を祈っています。おそらく、社内勉強会では、Rackネタを披露します。この内容から大きく変わる事はないでしょう。

それでは、長文失礼しました。

プロフィール

このブログ記事について

このページは、koshigoeが2008年9月 9日 23:48に書いたブログ記事です。

ひとつ前のブログ記事は「Rackのミドルウェア一通り(Rack::Auth::OpenID除く)」です。

次のブログ記事は「Rack::Adapterを調べてみる」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。