Rack 概要
Rackイメージ図
Rackイメージ図 pic.twitter.com/cIep0K0Z6n
— 小高 あたる (@ataru_kodaka) July 17, 2014
rackup, config.ru イメージ図
rackup, config.ru イメージ図 pic.twitter.com/oLojnzsVPn
— 小高 あたる (@ataru_kodaka) July 17, 2014
基本は、 1 「call(env)を受けて[code, {header}, [body]]を返すオブジェクト」を 2 「server に渡す」
ということをやっている。
前者をappと呼ぶと、これを生成するにはいくつかやり方があって、
- def call(env) を実装したクラスを作り、new すること。
- Rack::Builder をnewし、そのオブジェクトをrun()で渡してあげる。Builderクラスは call()関数を持ち、run で渡されたオブジェクトに call() を移譲する。
- config.ru を使う方法。*.ru はDSLだが実質的にruby スクリプトで、これをeval or requireする関数が用意されている:app = Rack::Builder.parse_file('config.ru')
1.は、
class App def call (env) [200, {"content-type" => 'text/html'}, ['Hello World']] end end app = App.new
2.は、
app = Rack::Builder.new { run App.new } # あるいは、 # app = Rack::Builder.new; app.run(App.new);
rakeup は内部的に 3 をやっている。
こうして app オブジェクトを作ったら、server に渡してあげる: server.run(app, options)。使うサーバーによってハンドラーで切り分けて云々やってるけど、そこはお任せでいいでしょう。
app 内で、いちいちENV['REQUEST_METHOD'] とかやるのは面倒なので、Rack::Request, Response などが用意されている。
- Rack::Request.new(env).request_method
- res.write(str) で body への追加と content-length まで面倒みてくれる
んで、r.finish とすると、[code, {header}, [body]] と配列化してくれるので call() の最後に置く。
path_info のマッピングは、Rack::Builder の map() を使う。builder = Rack::Builder.new して、builder.map '/hoge' do; run App.new; end; と登録する。 builder.use Rack::Reloader すると動くかなと思ったらうまくいかない模様
んで、ここらへんの処理をいろいろ楽にしてくれるのが、sinatra なわけだ。