2008-08-17
■[ruby] rubyの軽いWebアプリケーションフレームワークを試してみた
さくらのレンタルサーバで、CGI+rubyで簡単なアプリケーションを作っているのだけど、ライブラリはrubyに標準で添付している cgi ライブラリを使っている。1画面で完結するアプリなら、cgiライブラリでも良いのだけど、もう少し複雑なアプリを簡単に作るには、フレームワークを使いたいところ。
かといって、DBを使う程でもないアプリには、Ruby on Railsは少し大げさな気がするし、CGIしか使えないさくらだと、結構重そうだ。
というわけで、もう少し軽いWebアプリケーションフレームワークをネット上で探してみた。Camping、Rack、Ramazeがなかなか良さそうだったので、この3つのフレームワークを試してみた。
Camping
ソースが4kb以下という軽量フレームワーク。
1つのファイルに、アプリの全ての構成要素(MVC)を入れるというポリシーが面白い。
お決まりのHello Worldアプリはこんな感じ。Controllerだけの単純な構造なので、あまりCampingらしさがないかも。
#!/usr/bin/ruby require 'rubygems' require 'camping' Camping.goes :HelloWorldApp module HelloWorldApp::Controllers class Index < R '/' def get "Hello, World." end end end puts HelloWorld.run
でもソースが短いからと言って、CGI起動が速いわけでない。
「Camping.goes :HelloWorldApp」で、HelloWorldAppクラスの動的評価をしているせいか、今回試したフレームワークの中では、起動が一番遅いようだ。詳細は、後述の起動性能比較を参照のこと。CGIでない環境なら、問題にならないと思うけどね。
Rack
Rack: a Ruby Webserver Interface
正確には、Webアプリケーションフレームワークというより、フレームワークを作るためのライブラリで、Webサーバとフレームワークをつなぐためのものなのだけど、最小限の機能を持ったフレームワークとしても利用できる。
WebサーバとのI/Fは、メソッド呼出の引数1つ(Hash)と戻り値(Array)のみというシンプルさが良い。
Hello Worldアプリ(CGI用)はこんな感じ。
#!/usr/bin/ruby require 'rubygems' require 'rack' class HelloWorld def call(env) [200, {'Content-Type'=>'text/html'}, ["Hello, World."]] end end Rack::Handler::CGI.run HelloWorld.new
戻り値が、ステータスコードとヘッダ、コンテンツのArrayになっているメソッドになっている。上の例では使っていないけど、引数envはHashになっていて、パスやクエリとかが入っている。Rack::Requestを使えば、リクエストパラメータを簡単に取得する事もできる。
難点は、やはりフレームワークとして使うには機能が不足気味な点か。このまま使っても良いんだけど、結局、自作フレームワークをRackの上に作る事になってしまいそう。
例えば、HTML生成用にErubis等のテンプレートを呼び出す場合、以下のようになるのだけど、Erubisを呼び出すコードをいちいち書かなくて済むように、フレームワークっぽいものを作ることになってしまいそう。
#!/usr/bin/ruby require 'rubygems' require 'rack' require 'erubis' class HelloWorld def call(env) [ 200, {'Content-Type'=>'text/html'}, [Erubis::EscapedEruby.load_file("hello.eruby").evaluate] ] end end Rack::Handler::CGI.run HelloWorld.new
Ramaze
Ramaze • The Web Framework for Rubyists
利用できるテンプレートやORM等の自由度が高いフレームワーク。
公式サイトへ行くと、ドキュメントやサンプルが豊富なのも好印象。
Hello Worldアプリはこんな感じになる。
#!/usr/bin/ruby require 'rubygems' require 'ramaze' class MainController < Ramaze::Controller map '/' def index "Hello, World" end end Ramaze.start :adapter => :cgi
テンプレートを使うのも簡単。メソッド名と同じ名前のテンプレートファイルを view ディレクトリの下に置くだけ。Erubisを使うなら、以下のような view/index.rhtml を置くだけ。
<html><body><%= @content %></body></html>
アプリのソースの方は、以下のようになる。
#!/usr/bin/ruby require 'rubygems' require 'ramaze' class MainController < Ramaze::Controller map '/' engine :Erubis def index @content = "Hello, World" end end Ramaze.start :adapter => :cgi
MainControllerのindexを実行したあと、テンプレート view/index.rhtmlが実行される。このとき、インスタンス変数 @content がテンプレートに渡される。
ちなみにテンプレートエンジンは拡張子で判断するので、「engine :Erubis」は省略可能です。
起動性能比較
CGI起動でも軽いWebアプリケーションフレームワークを探すのが目的だったので、起動性能比較もしてみた。起動性能比較といっても、Hello WorldアプリにHTTPリクエストを100回投げて、その所要時間を比較するという単純なもの。
HTTPリクエストを投げるrubyスクリプトは、benchmarkライブラリを利用し、以下のものを用意した。
require 'benchmark' require 'open-uri' count = 100 count = ARGV[0].to_i if ARGV[0] class Loader def initialize(n = 1, prefix = '') @prefix = prefix @n = n end def load(path) contents = '' @n.times do open(@prefix + path) do |f| contents = f.read end end # puts contents end end loader = Loader.new(count, "http://localhost/~NAT/cgi/") # prefixは環境に応じて適当に書き換える Benchmark.bmbm do |r| r.report("cgi") { loader.load("hello_cgi.cgi") } r.report("cgi+erubis") { loader.load("hello_erubis.cgi") } r.report("rack") { loader.load("hello_rack.cgi") } r.report("rack+erubis") { loader.load("hello_rack_erubis.cgi") } r.report("ramaze") { loader.load("hello_rack_erubis.cgi") } r.report("ramaze+erubis") { loader.load("hello_ramaze_erubis.cgi") } r.report("camping") { loader.load("hello_camping.cgi") } end
Hello Worldアプリは、ここまでフレームワーク毎に紹介したものを使った。
なおcgiを使ったHello Worldアプリは、下記のものを使った。
#!/usr/bin/ruby require 'cgi' cgi = CGI.new cgi.out() do "Hello, World." end
cgi+erubisは下記の通り。
#!/usr/bin/ruby require 'rubygems' require 'cgi' require 'erubis' cgi = CGI.new cgi.out() do Erubis::EscapedEruby.load_file("view/index.rhtml"). evaluate({:content => "Hello, World"}) end
手元のMacBook Pro(CPU:Intel Core 2 Duo 2.2Ghz, メモリ:2GB, OS:Mac OS X 10.5.4)で実行したところ、下記のような結果になった。
user system total real
cgi 0.100000 0.030000 0.130000 ( 1.850957)
cgi+erubis 0.100000 0.040000 0.140000 ( 10.470253)
rack 0.120000 0.050000 0.170000 ( 8.563580)
rack+erubis 0.130000 0.050000 0.180000 ( 9.949786)
ramaze 0.130000 0.050000 0.180000 ( 9.896040)
ramaze+erubis 0.150000 0.070000 0.220000 ( 27.786989)
camping 0.150000 0.100000 0.250000 ( 53.961681)
totalの差がそれほどなくとも、realの差が大きい場合があるのだけど、この違いはなんなのだろうか・・・?
realに着目すると、erubisを使う場合だと、rack+erubisが速い。cgiとcgi+erubisの差と、rackとrack+erubisの差を比べると、rackの方が小さいのだけど、この違いもよく分からない。totalに着目すると、cgi+erubisとrack+erubisの速さが逆転するし・・・。
なんとなくだけど、realの結果が一番体感速度に近い気がする。体感的には、cgi+erubis ≒ rack+erubis < (性能2倍の壁) < ramaze+erubis < (性能2倍の壁) < camping という感じ。
分からないことだらけだが、とりあえずtotalとrealのいずれでも、cgiが一番速く、campingが一番遅い、といったところは言える。
私的総合評価
フレームワークの評価は、色んな側面から評価すべきだと思うので、簡単にどっちが良いとは言えないのだけど、下記に示すような個人的な好みと、私のやりたいことと照らし合わせると、ramazeが良さそう。
- 個人的な好み
- コード記述量は少ない方が良い
- テンプレートには、使い慣れたErubisを使いたい
- 新しく覚える事が少ない方が良い
- 私のやりたいこと
というわけで、しばらくramazeを使ってみようかと思う。
- 48 http://www.google.co.jp/search?hl=ja&client=firefox-a&rls=org.mozilla:ja:unofficial&hs=7Qh&q=html+ruby+フレームワーク&btnG=検索&lr=
- 25 http://www.google.co.jp/search?q=ruby+cgi+フレームワーク&btnG=検索&hl=ja&client=safari&sa=2
- 23 http://clip.livedoor.com/page/3282428/ さくらのレンタルサーバでramazeを動かしてみた - NAT’s Programming
- 23 http://d.hatena.ne.jp/Denma/20090701
- 20 http://d.hatena.ne.jp/Denma/20541231
- 19 http://www.google.co.jp/search?hl=ja&safe=off&client=firefox-a&rls=org.mozilla:ja:official&hs=4d8&lr=lang_ja&sa=X&oi=spell&resnum=0&ct=result&cd=1&q=dispatch.cgi+ruby&spell=1
- 17 http://clip.livedoor.com/page/3282375/ rubyの軽いWebアプリケーションフレームワークを試してみ
- 15 http://www.google.co.jp/search?hl=ja&q=ruby+cgi+フレームワーク&btnG=Google+検索&lr=&aq=f&oq=
- 15 http://www.google.co.jp/search?q=ramaze+cgi&lr=lang_ja&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:ja:official&client=firefox-a
- 13 http://www.google.com/search?hl=ja&lr=lang_ja&ie=UTF-8&oe=UTF-8&q=CGI+フレームワーク&num=50