Ruby Freaks Lounge

第24回 Rackとは何か(2)Rackの使い方

この記事を読むのに必要な時間:およそ 3 分

前回の記事では,Rackとは何かについてを,Rackが生まれた背景を交えてご紹介しました。今回は,Rackを実際に使ってみるにはどうすればいいのかを実際に作りながら解説します。

rackupとRack::Builder

前回の記事の最後で,アプリケーションの他にconfig.ruというファイルを用意し,rackupといういうコマンドを使ってアプリケーションを起動しましたが,これについてもう少し詳しく説明したいと思います。

実は,単にRackアプリケーションを起動するためだけであれば,config.ru(rackupファイルと呼びます。拡張子のruはおそらくrackupの略でしょう)は必要ありません。Rackの入門記事等で目にしたことのある方もいるかもしれませんが,以下のようなコードをファイルの末尾に書き加えるとsimple_app.rb単体でアプリケーションを起動することができます。

if __FILE__ == $0
  require 'rack/handler/webrick'
  Rack::Handler::WEBrick.run SimpleApp.new, :Port => 9292
end

rackupから起動した場合と特にかわらない結果になったと思います。前回の構成でrackupコマンドを実行した場合,コマンドの内部でこのような処理をしているからです。このほうが「サーバを起動してアプリケーションを実行している」のがよくわかりますね。では,なぜ最初からこの形ではなく,rackupを使うやり方を紹介したのでしょうか。

一つは,この形式ではサーバ依存のコードが残ってしまっているからです。前回,サーバとアプリケーションの間にRackが入ることによって,自由に組み合わせられるようになったことをRackの利点として説明しました。実際アプリケーションにはサーバ依存の実装はありませんし,rackupを使って簡単にアプリケーションサーバを切り換えられることも見ていただきました。しかし,残念なことに上記のコードでは「どのサーバによってどのように実行されるか」を明示的に記述してしまっているため,(些細なことではありますが)サーバを変更する度にアプリケーションのコードを修正する必要があります。これではRackのメリットが活かし切れていません。

また,ThinやMongrelのようにRack本体がハンドラを持っているアプリケーションサーバならこの方法で起動できますし,CGIなどの環境ではむしろこう記述する他はありません。ですが,PassengerのようにWebサーバのモジュールとして動作するタイプのサーバではこの方法で起動することはできませんし,Rack側ではなくサーバ側でRackに対応している場合も同じように書けるとは限りません。MongrelやWEBrickであればrackupコマンドが,ThinやPassengerであればサーバ側で,それぞれ「rackupファイルを読んでアプリケーションを初期化して,適切なハンドラで起動する」ところまではやってくれます。であれば,そちらに任せてしまえばそれぞれの差異を考慮する必要がないので,そうするべきでしょう。

もう一つは,rackupファイルでは,Rack::Builderを通してDSLでRackアプリケーションの構成を記述する仕組みが使えるということです。上記のコードのアプリケーションを起動する部分をRack::Builderを使うように書き換えると,以下のようになります。

if __FILE__ == $0
  require 'rack/builder'
  require 'rack/handler/webrick'
  app = Rack::Builder.new {
      run SimpleApp.new
  }
  Rack::Handler::WEBrick.run app, :Port => 9292
end

Rack::Builderのブロックの中身がrackupファイルと同じになっているのにお気付きでしょうか。rackupコマンドは,rackupファイルの中身をRack::Builderに渡しているわけです。この例ではあまり有難味はありませんが,後述するRack::URLMapやミドルウェアを使うようになると,Rack::Builderを使ってDSLで構成を記述する方がスマートです。

つまり,「基本的にはconfig.ruに色々書いて,アプリケーション側にはサーバ依存のコードは書かない」ものだと覚えてください。

Rack::Request/Rack::Response

前回の記事では,「リクエストはHashで受けとり,レスポンスはArrayで返す」と言及しました。これはRackの仕様であるため,もちろん従う必要があるのですが,リクエストの扱いなどは概ねどのアプリケーションでも同じなわりに意外と煩雑ですし,レスポンスの形式もちょっとしたミスで上手く動かなかったりしそうですね。

そのため,Rack::RequestRack::Responseというラッパーが用意されています。

前回使ったアプリケーションを上記のクラスを使って書き変えると,以下のようになります。

# coding: utf-8

require 'rack/request'
require 'rack/response'

class SimpleApp
  def call(env)
    req = Rack::Request.new(env)

    body = case req.request_method
           when 'GET'
             '<html><body><form method="POST"><input type="submit" value="見たい?" /></form></body></html>'
           when 'POST'
             '<html><body>何見てんだよ。</body></html>'
           end

    res = Rack::Response.new { |r|
      r.status = 200
      r['Content-Type'] = 'text/html;charset=utf-8'
      r.write body
    }
    res.finish
  end
end

HashやArrayよりは少し扱いやすくなりました。上記のコードでは使っていませんが,クエリの扱いやPOSTデータの受けとり,リダイレクトの制御などもRack::RequestとRack::Responseを使うと簡単にできるようになります。

著者プロフィール

佐孝太郎(さこうたろう)

株式会社ライブドア所属。仕事では主にPerlとObjCを使っているものの, MacRubyでCocoaアプリのテストを書くなど,隙を見付けてはRubyを使おうと目論んでいる。

bloghttp://blog.livedoor.jp/faulist/
twitter:http://twitter.com/faultier

コメント

  • ありがとうございます

    Ruby/CGI のアプリをいじっている者です。有益な記事をありがとうございます。この記事で Rack の概要をつかめました。
    ところで

    require 'rack/handler/webrick'
    の前に
    require 'rubygems'
    が必要ですね。エラーメッセージを見れば分かることではありますけど。

    Commented : #1  小波秀雄 (2010/05/09, 10:54)

コメントの記入