SinatraをCGIでもThin等のアプリサーバでも使えるようにする書き方
個人的にSinatraとLimonadeがマイブームなのですが、Sinatraに関して言えば、CGIで動かす方法は(本家のドキュメントとかでは)あまりフォローされていないようです。国内ではそれなりに記事もあるようなのですが、結局ソースに手を入れてしまう(Sinatraのソースに「Rack::Handler::CGI.run」をベタに書く)か、rackupで実行する方法かが多そう。でも、前者はCGI専用になってしまうのでいまいちだし、後者はシェルが2個実行されるのがどうにも美しくありません(現実的にはあまり問題にならないのかもしれませんが)。
そこでいろいろいじってみたところ、何とか良さげな方法ができました。
まずSinatraのソース。適当です。
require 'rubygems' require 'sinatra' set :run, true get '/?' do "hello" end get '/test' do "hello" end
ポイントは、getの引数の「'/?'」です。普通は「'/'」と書くところですが、CGIで下記のrewriteを使うと、引数が「''」しかマッチングしなくなります。そこで「'/?'」と書いておくと、''でも'/'でもマッチングするのでした。同様に、'/test/'でも'/test'でもマッチングさせたいときには「'/test/?'」と書けばよいようです。
さて、このままではCGIでは動きません。:runがtrueになっているからです。これはMongrelやThinやWEBrickなどのアプリケーションサーバで動かす場合の設定です。アプリケーションサーバを使う場合はこのままで動きます。
CGIを使う場合のために、以下のようなdispatch.cgiを書きます。
#!/usr/bin/ruby require 'rubygems' load 'start.rb' set :run, false Rack::Handler::CGI.run Sinatra::Application
なんとなくrubygemsをrequireしていますが、本来はstart.rbで呼び出してくれるので不要なはず。
start.rbをrequireじゃなくてloadしているのは深い意味はありません。
そして:runをfalseに上書きしたあと、RackのCGIハンドラーを呼び出します。これでCGIとして動作するようになります。
ちなみに.htaccessはこんな感じです。
Options +ExecCGI AddHandler cgi-script cgi DirectoryIndex dispatch.cgi <Files start.rb> deny from all </Files> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*) dispatch.cgi/$1 [L]
start.rbは直接叩けないようdeny from allにしてあります。また、DirectoryIndexとRewriteRuleで、存在するファイル以外へのアクセスを全てdispatch.cgiで動かすようにしています。
こんな感じで書けば、Sinatraのソース自体はいじらずに、CGIでもアプリサーバでも動かせるようになるはずです。
ただ、本当はSinatraのソースをSinatra::Baseのサブクラスとして書く方が今っぽいのかもしれません。それなら同一プロセスで複数のSinatraアプリを共存させることもできるそうですし。まあでも、classレスですっきりしたソースがSinatraの魅力でもあるので、この辺りは好きずきでどうぞ。