Rails Metal の URIマッピング2010/02/26

発端

passenger + Rack 環境にアプリを置こうと思ったのだけど、 いろいろあって、「ActiveRecord があった方が良くね?」という意見が発生。

「それだったら、Rails Metal で良くね?」という結論が、脳内会議で決議された訳だけど、そこで悩んだのが、URIマッピングのルールが良く判らない、という点。

ソースはあんまり見てない(特にroutes関係)なので、あまり信用しない方が良いけど、何かの役に立つかも知れないので、晒しておく。

テンプレート

$ ./script/generate metal foobar

の結果作成される app/metal/foobar.rb のテンプレートは

# Allow the metal piece to run in isolation
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)

class Foobar
  def self.call(env)
    if env["PATH_INFO"] =~ /^\/foobar/
      [200, {"Content-Type" => "text/html"}, ["Hello, World!"]]
    else
      [404, {"Content-Type" => "text/html"}, ["Not Found"]]
    end
  end
end

こんな感じになる。 これは(require行とその作用を除けば)、非常に単純なRackアプリケーションそのものなので、 詳細は省略する。

これで、

$ ./script/server &

$ curl http://localhost:3000/foobar
Hello, World!

foobar 以下のURIがこの metal で処理されていることが判る。

routes.rb ?

Rails アプリケーションのURI情報の設定なら、config/routes.rb が正道だ、と思ったが、 どうも routes.rb には metal 関係のキーワードは定義されていない。

ルーティング情報はどこに?

となると、怪しいのは

    if env["PATH_INFO"] =~ /^\/foobar/

この if文だという訳で、regexpを書き換えたりしてみたところ、どうもこいつがルーティング情報らしいことが判明。

# Allow the metal piece to run in isolation
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)

class Foobar
  def self.call(env)
    if env["PATH_INFO"] =~ /^\/hogehoge/
      [200, {"Content-Type" => "text/html"}, ["Hello, World!"]]
    else
      [404, {"Content-Type" => "text/html"}, ["Not Found"]]
    end
  end
end

だと

$ curl http://localhost:3000/foobar
(略)
Routing Error
(略)
$ curl http://localhost:3000/hogehoge
Hello, World!

になる。

まとめ

  • metal は routes.rb に書いた規則よりも前に実行される。
  • metal は全てのURIに対して実行される。
  • metal が、404のレスポンスを返した場合は、他の metal(あれば)、routes.rb の規則の適用の順にリクエストが取り回される。
  • 複数の metal が存在する場合の実行順は未定義(ファイルのiノード順っぽい)

ということみたいだ。

演繹できる事実として、同名の metal と controller が居た場合は metal が勝つのと、 controller に落ちるまでには metal が全て実行されるので、極端に metal のクラスが多い場合は要注意かも知れない。

あと、passenger と共に使用する場合は、metal の中で、request.body() とかやってしまった後に、404で次の metal や controller に処理を 回すと大変な事になるような気がする。

(passenger の Rackインターフェイスに入ってくる request.body()はrewindableなオブジェクトではないため)

追記

長々と書いた後で、config/config.ru に rackup用のconfigが書けることに気がついた。

ここなら、 マッピングルールを自在に書ける。

コメント

コメントをどうぞ

※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。

※なお、送られたコメントはブログの管理者が確認するまで公開されません。

名前:
メールアドレス:
URL:
コメント:

トラックバック

このエントリのトラックバックURL: http://dragonstar.asablo.jp/blog/2010/02/26/4909634/tb

※なお、送られたトラックバックはブログの管理者が確認するまで公開されません。