"小さい"Rackアプリケーション
ちょっと Rack について見てたりした(Rackまわり - Rubyとか Illustratorとか SFとか折紙とか)
で、この辺(第23回 Rackとは何か(1)Rackの生まれた背景:Ruby Freaks Lounge|gihyo.jp … 技術評論社)なんかも参考に最低限の Webアプリケーションを作ってみる。
(適宜自分ツイッターからの再録を挿みます)
Rack、config.ru
「rackup」コマンドで Rackを起動するとき、オプションで指定しないと設定ファイル「config.ru」を見に行く。アプリケーションのクラスを定義する Rubyスクリプトを require するのが定例の様だけど、そこでクラス定義してもいいわけだ。
class App def call(env) [200, {'Content-type' => 'text/plain'}, ['hello']] end # def call(env) end # class App rub App.new
これを「config.ru」という名前で保存し、「rackup」コマンドを実行すれば、取り敢えずどんなリクエストにも「hello」と返すだけの Webアプリケーションが起動する。
この辺を出発点に記述を少なくしていこうと思った。
なんかクラス定義を終わらせたら即座に .new してもいいよね、そしてそのまま runメソッドに渡す
run (class App def call(env) [200, {'Content-type' => 'text/plain'}, ['hello']] end # def call(env) App end.new)
class 定義はそのままではそのクラスを返すわけじゃなくて、最後の実行文の結果を返す、ので定義最終行に「App」と書いておく、ちょっと無様。
なら、無名のクラスを作ってそれでいいじゃん
run(Class.new do def call(env) [200, {'Content-type' => 'text/plain'}, ['hello']] end # def call(env) end.new)
config.ru 一行
メソッド定義も短くする、改行も自然に減らすようにメソッドで定義しよう
. connfig.ru「run Class.new{define_method(:call, lambda{|env| [200, {'Content-type' => 'text/plain'}, ['hello']]})}.new」 一行Rackアプリ #railstokyo
5:01 PM Nov 13th webから
run Class.new{define_method(:call, lambda{|env| [200, {'Content-type' => 'text/plain'}, ['hello']]})}.new
「config.ru」ファイル、一行になった。
さて、「run」に渡すのは、いちいちクラス作って .new しなくても、要するに「.call(env)」メソッドを持つオブジェクトならいいわけだ、lambda とか
. 一行Rackアプリ、config.ru「run lambda{|env| [200, {'Content-type' => 'text/plain'}, ['hello']]}」
7:39 PM Nov 14th webから
run lambda{|env| [200, {'Content-type' => 'text/plain'}, ['hello']]}
さて、先の技評記事にもあったが、callメソッドの返り値を作るには「Racc::Response」も使える
. 一行Rackアプリ「run lambda{|env| Rack::Response.new('hello').finish}」 もう少し短く出来たが、これでは text/html になる
7:40 PM Nov 14th webから
run lambda{|env| Rack::Response.new('hello').finish}
ステータスやヘッダの指定とか省略出来るのでそれなりに短くなったが、コメントにある通り、Content-typeの指定が html になってしまう。
あと、「.finish」注意、これを呼ぶ事で Rack::Responseオブジェクトが .callメソッドの返り値として期待される配列になる。
さて Sinatra では
一方、簡潔な Webアプリケーションにいいというシナトラでは、ここまで単純なのはどうなるんだろう。
. 一行Rackアプリ、一方シナトラ sinatra では、「ruby -rubygems -rsinatra -e 'get("/"){"hello"}'」(これは trxt/html になる)
8:10 PM Nov 14th webから
ruby -rubygems -rsinatra -e 'get("/"){"hello"}'
コマンドラインからの一行で、ルート「/」への GETリクエストに対して「hello」と返す Webアプリケーションとなる。
ここでちょっと、「ruby -rubygems -rsinatra」が効くのは Ruby 1.9以降かもしれないの注意。
Rackだってコマンドラインから
. 一行Rackアプリ、いや Rackアプリだってコマンドライン一行で書けた、140字ぎりぎり
9:09 PM Nov 14th webから
. 一行「ruby -rtempfile -e '`rackup #{Tempfile.open(["",".ru"]){|f| %Q[run lambda{|e| Rack::Response.new("hello").finish}].display f; f.path}}`'」
9:08 PM Nov 14th webから
ruby -rtempfile -e '`rackup #{Tempfile.open(["",".ru"]){|f| %Q[run lambda{|e| Rack::Response.new("hello").finish}].display f; f.path}}`'
「rackup」コマンドは設定「.ru」ファイルを要求する、それを tempfileで作ってみた。
Rack::Server を使ってコマンドラインから
http://rack.rubyforge.org/doc/ を見るに、「rackup」コマンドを使わなくても直接 Rack::Serverオブジェクトをたてることもできる様だ(http://rack.rubyforge.org/doc/classes/Rack/Server.html#M000205)。
そこに書いてあったサンプルスクリプト
Rack::Server.start( :app => lambda do |e| [200, {'Content-Type' => 'text/html'}, ['hello world']] end, :server => 'cgi' )
は動かなかった(rack (1.2.1))。どうもイニシャライズのときの :appオプションと @appインスタンス変数の割り当てが微妙な感じ。なのでインスタンス変数を手で設定してやる。
. 一行Rackアプリ、コマンドライン一行で書く Rackアプリ、140字ぎりぎり
3:19 PM Nov 15th webから
. 一行「ruby -rubygems -r rack -e "Rack::Server.new.tap{|s| s.instance_variable_set :@app, proc{Rack::Response.new('hello').finish}}.start"」
3:18 PM Nov 15th webから
ruby -rubygems -r rack -e "Rack::Server.new.tap{|s| s.instance_variable_set :@app, proc{Rack::Response.new('hello').finish}}.start"
ちなみに「env」引数なんか見てないから省略した、そうなると lambda では引数の数のチェックが厳しいので proc を使う事にした。
ここでちょっと、コマンドラインの場合の引用符、Windowsでは二重引用符が外側の方が良いかもしれないの注意。