Table of Contents
Open Table of Contents
Rack とは
Rack とは、(Web) アプリケーションと Web サーバーを繋ぐインターフェイスになる。
この Rack だが、ほぅ、そんな考え方もあるね、というレベルでの認知だったが、Rails が v2.3 から Rack 対応となったことも受け、どんなものなのか見てみることにした。
本家である、Rack: a Ruby Webserver Interface のサイトの言葉を借りると、
Rack provides a minimal interface between webservers supporting Ruby and Ruby frameworks.
Rack は Ruby をサポートする Web サーバ(達)と Ruby のフレームワーク(達)の間の最低限のインターフェイスを提供する。
とある。
現在、Ruby で書かれている Web アプリ用のフレームワークは有名な Rails を筆頭に結構な数が出てきている。また、Ruby のための Web サーバも結構な数が出てきている。フレームワークとそれを動作させる Web サーバの組み合わせは増え続けており、それぞれが相手先に合わせた対応を行っていくというのは、結構シンドイことになる。
そこで、フレームワークとそれを動作させる Web サーバの間を取り持つインターフェイスとして、Rack が登場した。お互いに間を取り持つ Rack を意識して開発をしていれば、事はスムーズに進むはず、ということのようだ。
Web でのエンドユーザと Web サーバのやり取り自体は単純である。 Web サーバは、リクエストを受けとり、レスポンスを返す、つまり、*「Web アプリって、要するにリクエストをレスポンスに変換するだけの関数だよね」*というのが Rack の基本思想のようだ。
以下のサイトにて 5 分でわかるように親切に説明を行ってくれている。
Rack プロトコル
では、具体的にどのようにフレームワークと Web サーバの間の取り持ちを Rack は行おうとしているのだろうか。
先程の*「Web アプリって、要するにリクエストをレスポンスに変換するだけの関数だよね」*という Rack の基本思想にあるように、
- 環境変数(のハッシュ)を受け取って(リクエスト)
- ステータスコードと HTTP ヘッダと HTTP ボディを返す(レスポンス)
というインターフェイスの実装になっている。
前者の環境変数は、String オジェクトをキーとする Hash オブジェクトになっている。直接操作もできるが、Rack::Request
でラップしてあげると扱い易い。
req = Rack::Request.new(env)
後者のレスポンスは、3 つの要素からなる配列となっている。
- 第 1 要素
- HTTP ステータスコード
- 第 2 要素
- HTTP レスポンスヘッダー
- 第 3 要素
- HTTP ボディ
Rack::Response
で扱えるようになっている。
Rack を使用して作成したアプリケーションが Rack のプロトコルに正しく従っているかどうかは、Rack::Lint
というミドルウェアをRack(棚) に組み込むことでチェックすることができるようだ。
use Rack::Lint
Rack を試してみる
実際に試してみないと何のことかよくわからない。。試してみる。
まずは、事前準備として、Rack のインストールから。
$ sudo gem install rack
$ gem list rack
*** LOCAL GEMS ***
rack (1.0.0)
お決まりの “Hello Wolrd!” を表示する Web アプリケーションを Rack で書いてみる。
- アプリケーションの処理は、
hello.rb
に記載 - そのアプリケーションを動作させるための記載は、
hello.ru
に行う
ということにする。
hello.rb:
require "rubygems"
require "rack"
class HelloApp
def call(env)
[200, { 'Content-Type' => 'text/plain' }, ['Hello World!']]
end
end
hello.ru:
require "hello"
run HelloApp.new
これを動作させるためは、rackup
コマンドを使用する。Rack をインストールした際にこのコマンドはインストールされている。Rack アプリケーションを動作させるためのツールで、これで Web サーバを起動する。
上記 2 つのファイルがあるディレクトリで、
$ rackup hello.ru
をタイプする。そして、やみくもに、ブラウザを起動し、http://localhost:9292/ をリクエストすると、“Hello World!” が表示されていることを確認できる。
rackup
には指定可能なオプションが幾つかあり、help で確認できる。
$ 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
先の Hello World Web アプリケーションだが、環境変数(env)を引数にとる call メソッドで実装されている。
このように、Rack を使用した実装では、call メソッドを定義し、アプリケーションの処理を記述しておけばよい。call メソッドは、リクエスト発生時に、環境変数(env)を引数に呼び出されるので、最後にレスポンスとなるインスタンスを結果として返す処理を書くだけとなる。
また、Proc オブジェクトとしても記載することができる。先の Hello World Web アプリケーションを以下のように書くこともできる。(1 ファイルにまとめている)
hello_proc.ru
require "rubygems"
require "rack"
hello_app = Proc.new do |env|
[200, { 'Content-Type' => 'text/plain' }, ['Hello World!']]
end
run hello_app
サーバの起動は、
$ rackup hello_proc.ru
となる。
Hello World Web アプリケーションだけでは寂しいので、環境変数を表示する ShowEnv アプリケーションも書いてみる。
show_evn.rb:
require "rubygems"
require "rack"
class ShowEnvApp
def call(env)
[
200,
{ 'Content-Type' => 'text/html; charset=UTF-8' },
[
env.keys.sort.map do |key|
Rack::Utils.escape_html("#{key} => #{env[key]}")
end.join("<br />\n")
]
]
end
end
show_env.ru:
require "show_env"
run ShowEnvApp.new
起動する。
$ rackup show_env.ru
以下の内容が画面に表示される。
GATEWAY_INTERFACE => CGI/1.2
HTTP_ACCEPT => text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HTTP_ACCEPT_CHARSET => Shift_JIS,utf-8;q=0.7,*;q=0.7
HTTP_ACCEPT_ENCODING => gzip,deflate
HTTP_ACCEPT_LANGUAGE => ja,en-us;q=0.7,en;q=0.3
HTTP_CACHE_CONTROL => max-age=0
HTTP_CONNECTION => keep-alive
HTTP_COOKIE => _radiant_080_test_session=abcd
HTTP_HOST => localhost:9292
HTTP_KEEP_ALIVE => 300
HTTP_USER_AGENT => Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ja-JP-mac; rv:1.9.0.11) Gecko/2009060214 Firefox/3.0.11
HTTP_VERSION => HTTP/1.1
PATH_INFO => /
QUERY_STRING =>
REMOTE_ADDR => 127.0.0.1
REQUEST_METHOD => GET
REQUEST_PATH => /
REQUEST_URI => /
SCRIPT_NAME =>
SERVER_NAME => localhost
SERVER_PORT => 9292
SERVER_PROTOCOL => HTTP/1.1
SERVER_SOFTWARE => Mongrel 1.1.5
rack.errors => #<Rack::Lint::ErrorWrapper:0x114fe00>
rack.input => #<Rack::Lint::InputWrapper:0x114fe3c>
rack.multiprocess => false
rack.multithread => true
rack.run_once => false
rack.url_scheme => http
rack.version => 01
Web サーバには、rackup
のデフォルトである webrick/mongrel のうちの Mongrel が使用されていることがわかる。
また、Rack 独自の環境変数も含まれており、rack.
という表記になっている。
Rack のミドルウェア
Rack には幾つかのミドルウェアが用意されている。
ここで言うミドルウェアとは、「Response を別の Response に変換する処理」 を行ってくれるもののようだ。
以下は、Ruby Rack Middleware Tutorial から。
Your application is called, and then middleware is invoked in the order that you specify. These middlewares call each other, acting as a set of ‘filters’ for the response, so it is important to note that the ordering to which you ‘use’ them can be important, and have an effect on the results.
- アプリケーションが呼ばれると、ミドルウェアも定義した(
use
)順番で呼び出される。 - ミドルウェアは、レスポンスに対するフィルターのように動作する。
use
を使用してミドルウェアを指定する順番というのは重要。その順番がレスポンスの結果に影響を与える。
とある。上記サイトにあるサンプルソースがその動作を理解するのにわかり易い。
module Rack
class Upcase
def initialize app
@app = app
end
def call env
puts 'upcase'
p @app
puts
status, headers, body = @app.call env
[status, headers, [body.first.upcase]]
end
end
end
module Rack
class Reverse
def initialize app
@app = app
end
def call env
puts 'reverse'
p @app
puts
status, headers, body = @app.call env
[status, headers, [body.first.reverse]]
end
end
end
use Rack::Upcase
use Rack::Reverse
use Rack::ContentLength
app = lambda { |env| [200, { 'Content-Type' => 'text/html' }, 'Hello World'] }
run app
コンソールには以下の結果が残る。
upcase
#<Rack::Reverse:0x5574e0 @app=#<Rack::ContentLength:0x557620 @app=#<Proc:0x00561210@rack.ru:38>>>
reverse
#<Rack::ContentLength:0x557620 @app=#<Proc:0x00561210@rack.ru:38>>
Rack のその他の特徴
全て見きれていないのだが、URL マッピングなども当然用意されている。
手軽なものからそこそこ凝ったものまで、既に用意されているミドルウェアなどを利用して、Rack を使用した Web アプリケーションを構築する下地はできあがってきているようだ。
Rack のドキュメントは以下にまとまっている。
参考サイト
以下のサイトを参考にさせて頂いた。