= Sinatra
SinatraはRubyで下記のような最小労力で手早くウェブアプリケーションを作成するためのDSLです。
# myapp.rb
require 'rubygems'
require 'sinatra'
get '/' do
'Hello world!'
end
gemをインストールして動かしてみる。
sudo gem install sinatra
ruby myapp.rb
http://localhost:4567 を見る。
== ルート
Sinatraでは、ルートはHTTPメソッドとURLマッチングパターンがペアになっています。
ルートはブロックに結び付けられています。
get '/' do
.. 何か見せる ..
end
post '/' do
.. 何か生成する ..
end
put '/' do
.. 何か更新する ..
end
delete '/' do
.. 何か削除する ..
end
ルートは定義された順番にマッチします。 リクエストに最初にマッチしたルートが呼び出されます。
ルートのパターンは名前付きパラメータを含むことができ、
paramsハッシュで取得できます。
get '/hello/:name' do
# matches "GET /hello/foo" and "GET /hello/bar"
# params[:name] is 'foo' or 'bar'
"Hello #{params[:name]}!"
end
また、ブロックパラメータで名前付きパラメータにアクセスすることもできます。
get '/hello/:name' do |n|
"Hello #{n}!"
end
ルートパターンはsplat(またはワイルドカード)を含むこともでき、
params[:splat] で取得できます。
get '/say/*/to/*' do
# matches /say/hello/to/world
params[:splat] # => ["hello", "world"]
end
get '/download/*.*' do
# matches /download/path/to/file.xml
params[:splat] # => ["path/to/file", "xml"]
end
正規表現を使ったルート:
get %r{/hello/([\w]+)} do
"Hello, #{params[:captures].first}!"
end
ブロックパラーメータを使用した場合:
get %r{/hello/([\w]+)} do |c|
"Hello, #{c}!"
end
ルートにはユーザエージェントのようなさまざまな条件を含めることができます。
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
"You're using Songbird version #{params[:agent][0]}"
end
get '/foo' do
# Matches non-songbird browsers
end
== 静的ファイル
静的ファイルは./publicディレクトリから配信されます。
:publicオプションを指定することで別の場所を指定することができます。
set :public, File.dirname(__FILE__) + '/static'
注意: この静的ファイル用のディレクトリ名はURL中に含まれません。
例えば、./public/css/style.cssはhttp://example.com/css/style.cssでアクセスできます。
== ビュー / テンプレート
テンプレートは./viewsディレクトリ下に配置されています。
他のディレクトリを使用する場合の例:
set :views, File.dirname(__FILE__) + '/templates'
テンプレートはシンボルを使用して参照させることを覚えておいて下さい。
サブデレクトリでもこの場合は:'subdir/template'のようにします。
レンダリングメソッドは文字列が渡されると、そのまま文字列を出力します。
=== Haml テンプレート
hamlを使うにはhamlライブラリが必要です:
## hamlを読み込みます
require 'haml'
get '/' do
haml :index
end
./views/index.hamlを表示します。
{Haml's options}[http://haml.hamptoncatlin.com/docs/rdoc/classes/Haml.html]
はSinatraの設定でグローバルに設定することができます。
{Options and Configurations}[http://www.sinatrarb.com/configuration.html],
を参照してそれぞれ設定を上書きして下さい。
set :haml, {:format => :html5 } # デフォルトのフォーマットは:xhtml
get '/' do
haml :index, :haml_options => {:format => :html4 } # 上書き
end
=== Erb テンプレート
## erbを読み込みます
require 'erb'
get '/' do
erb :index
end
./views/index.erbを表示します。
=== Builder テンプレート
builderを使うにはbuilderライブラリが必要です:
## builderを読み込みます
require 'builder'
get '/' do
content_type 'application/xml', :charset => 'utf-8'
builder :index
end
./views/index.builderを表示します。
=== Sass テンプレート
Sassテンプレートを使うにはsassライブラリが必要です:
## hamlかsassを読み込みます
require 'sass'
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
sass :stylesheet
end
./views/stylesheet.sassを表示します。
{Sass' options}[http://haml.hamptoncatlin.com/docs/rdoc/classes/Sass.html]
はSinatraの設定でグローバルに設定することができます。
see {Options and Configurations}[http://www.sinatrarb.com/configuration.html],
を参照してそれぞれ設定を上書きして下さい。
set :sass, {:style => :compact } # デフォルトのSass styleは :nested
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
sass :stylesheet, :sass_options => {:style => :expanded } # 上書き
end
=== インラインテンプレート
get '/' do
haml '%div.title Hello World'
end
文字列をテンプレートとして表示します。
=== テンプレート内で変数にアクセスする
テンプレートはルートハンドラと同じコンテキストの中で評価されます。. ルートハンドラでセットされたインスタンス変数は
テンプレート内で直接使うことができます。
get '/:id' do
@foo = Foo.find(params[:id])
haml '%h1= @foo.name'
end
ローカル変数を明示的に定義することもできます。
get '/:id' do
foo = Foo.find(params[:id])
haml '%h1= foo.name', :locals => { :foo => foo }
end
このやり方は他のテンプレート内で部分テンプレートとして表示する時に典型的に使用されます。
=== ファイル内テンプレート
テンプレートはソースファイルの最後で定義することもできます。
require 'rubygems'
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
= yield
@@ index
%div.title Hello world!!!!!
注意: sinatraをrequireするファイル内で定義されたファイル内テンプレートは自動的に読み込まれます。
他のファイルで定義されているテンプレートを使うには use_in_file_templates!メソッドで指定します。
=== 名前付きテンプレート
テンプレートはトップレベルのtemplateメソッドで定義することができます。
template :layout do
"%html\n =yield\n"
end
template :index do
'%div.title Hello World!'
end
get '/' do
haml :index
end
「layout」というテンプレートが存在する場合、そのテンプレートファイルは他のテンプレートが
表示される度に使用されます。:layout => false.することでlayoutsを無効にできます。
get '/' do
haml :index, :layout => !request.xhr?
end
== ヘルパー
トップレベルのhelpersを使用してルートハンドラやテンプレートで使うヘルパメソッドを
定義できます。
helpers do
def bar(name)
"#{name}bar"
end
end
get '/:name' do
bar(params[:name])
end
== フィルタ
beforeフィルタはリクエストされたコンテキストを実行する前に評価され、
リクエストとレスポンスを変更することができます。フィルタ内でセットされた
インスタンス変数はルーティングとテンプレートで使用できます。
before do
@note = 'Hi!'
request.path_info = '/foo/bar/baz'
end
get '/foo/*' do
@note #=> 'Hi!'
params[:splat] #=> 'bar/baz'
end
== 強制終了
ルートかbeforeフィルタ内で直ちに実行を終了する方法:
halt
body部を指定することもできます ...
halt 'ここにbodyを書く'
ステータスとbody部を指定する ...
halt 401, '立ち去れ!'
== パッシング(Passing)
ルートはpassを使って次のルートに飛ばすことができます:
get '/guess/:who' do
pass unless params[:who] == 'Frank'
"見つかっちゃった!"
end
get '/guess/*' do
"はずれです!"
end
ルートブロックからすぐに抜け出し、次にマッチするルートを実行します。
マッチするルートが見当たらない場合は404が返されます。
== 設定
どの環境でも起動時に1回だけ実行されます。
configure do
...
end
環境変数:production(RACK_ENV環境変数) がセットされている時だけ実行する方法:
configure :production do
...
end
環境変数:production か:testの場合に設定する方法:
configure :production, :test do
...
end
== エラーハンドリング
エラーハンドラーはルートコンテキストとbeforeフィルタ内で実行します。
haml、erb、haltなどを使うこともできます。
=== Not Found
Sinatra::NotFoundが起きた時か レスポンスのステータスコードが
404の時にnot_foundハンドラーが発動します。
not_found do
'ファイルが存在しません'
end
=== エラー
+error+ ハンドラーはルートブロックかbeforeフィルタ内で例外が発生した時はいつでも発動します。
block or before filter. 例外オブジェクトはRack変数sinatra.errorから取得されます。
error do
'エラーが発生しました。 - ' + env['sinatra.error'].name
end
エラーをカスタマイズする場合は、
error MyCustomError do
'エラーメッセージ...' + request.env['sinatra.error'].message
end
と書いておいて,下記のように呼び出します。
get '/' do
raise MyCustomError, '何かがまずかったようです'
end
そうするとこうなります:
エラーメッセージ... 何かがまずかったようです
開発環境として実行している場合、Sinatraは特別なnot_foundとerrorハンドラーを
インストールしています。
== MIMEタイプ
send_fileか静的ファイルを使う時、Sinatraが理解でいないMIMEタイプがある場合があります。
その時は +mime_type+ を使ってファイル拡張子毎に登録して下さい。
mime_type :foo, 'text/foo'
== Rackミドルウェア
SinatraはRack[http://rack.rubyforge.org/]というRubyのWEBフレームワーク用の
最小限の標準インターフェース 上で動作しています。Rack中でもアプリケーションデベロッパー
向けに一番興味深い機能はミドルウェア(サーバとアプリケーション間に介在し、モニタリング、HTTPリクエストとレスポンス
の手動操作ができるなど、一般的な機能のいろいろなことを提供するもの)をサポートすることです。
Sinatraではトップレベルの+user+ メソッドを使ってRackにパイプラインを構築します。
require 'sinatra'
require 'my_custom_middleware'
use Rack::Lint
use MyCustomMiddleware
get '/hello' do
'Hello World'
end
use の意味は{Rack::Builder}[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSLで定義されていることと全て一致します。
例えば +use+ メソッドはブロック構文のように複数の引数を受け取ることができます。
use Rack::Auth::Basic do |username, password|
username == 'admin' && password == 'secret'
end
Rackはログ、デバッギング、URLルーティング、認証、セッションなどいろいろな機能を備えた標準的ミドルウェアです。
Sinatraはその多くのコンポーネントを自動で使うよう基本設定されているため、+use+で明示的に指定する必要はありません。
== テスト
SinatraでのテストはRack-basedのテストライブラリかフレームワークを使って書くことができます。
{Rack::Test}[http://gitrdoc.com/brynary/rack-test] をおすすめします。やり方:
require 'my_sinatra_app'
require 'rack/test'
class MyAppTest < Test::Unit::TestCase
include Rack::Test::Methods
def app
Sinatra::Application
end
def test_my_default
get '/'
assert_equal 'Hello World!', last_response.body
end
def test_with_params
get '/meet', :name => 'Frank'
assert_equal 'Hello Frank!', last_response.body
end
def test_with_rack_env
get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
assert_equal "あなたはSongbirdを使ってますね!", last_response.body
end
end
注意: ビルトインのSinatra::TestモジュールとSinatra::TestHarnessクラスは
0.9.2リリース以降、廃止予定になっています。
== Sinatra::Base - ミドルウェア、ライブラリ、 モジュラーアプリ
トップレベル(グローバル領域)上でいろいろ定義していくのは軽量アプリならうまくいきますが、
RackミドルウェアやRails metal、サーバのコンポーネントを含んだシンプルな
ライブラリやSinatraの拡張プログラムを考慮するような場合はそうとは限りません。
トップレベルのDSLがネームスペースを汚染したり、設定を変えてしまうこと(例:./publicや./view)がありえます。
そこでSinatra::Baseの出番です。
require 'sinatra/base'
class MyApp < Sinatra::Base
set :sessions, true
set :foo, 'bar'
get '/' do
'Hello world!'
end
end
このMyAppは独立したRackコンポーネントで、RackミドルウェアやRackアプリケーション
Rails metalとして使用することができます。config.ruファイル内で +use+ か、または
+run+ でこのクラスを指定するか、ライブラリとしてサーバコンポーネントをコントロールします。
MyApp.run! :host => 'localhost', :port => 9090
Sinatra::Baseのサブクラスで使えるメソッドはトップレベルのDSLを経由して確実に使うことができます。
ほとんどのトップレベルで記述されたアプリは、以下の2点を修正することでSinatra::Baseコンポーネントに変えることができます。
* +sinatra+の代わりにsinatra/baseを読み込む
(そうしない場合、SinatraのDSLメソッドの全てがメインネームスペースにインポートされます)
* ルート、エラーハンドラー、フィルター、オプションをSinatra::Baseのサブクラスに書く
Sinatra::Base はまっさらです。ビルトインサーバを含む、ほとんどのオプションがデフォルト
で無効になっています。オプション詳細については{Options and Configuration}[http://sinatra.github.com/configuration.html]
をご覧下さい。
補足: SinatraのトップレベルDSLはシンプルな委譲(delgation)システムで実装されています。
Sinatra::Applicationクラス(Sinatra::Baseの特別なサブクラス)は、トップレベルに送られる
:get、 :put、 :post、:delete、 :before、:error、:not_found、 :configure、:set messagesのこれら
全てを受け取ります。 詳細を閲覧されたい方はこちら(英語):
{Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1064]
{included into the main namespace}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb#L25].
== コマンドライン
Sinatraアプリケーションは直接実行できます。
ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-s HANDLER]
オプション:
-h # ヘルプ
-p # ポート指定(デフォルトは4567)
-e # 環境を指定 (デフォルトはdevelopment)
-s # rackserver/handlerを指定 (デフォルトはthin)
-x # mutex lockを付ける (デフォルトはoff)
== 最新開発版について
Sinatraの開発版を使いたい場合は、ローカルに開発版を落として、
LOAD_PATHのsinatra/libディレクトリを指定して実行して下さい。
cd myapp
git clone git://github.com/sinatra/sinatra.git
ruby -Isinatra/lib myapp.rb
sinatra/libディレクトリをto theLOAD_PATHに追加する方法もあります。
$LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib'
require 'rubygems'
require 'sinatra'
get '/about' do
"今使ってるバージョンは" + Sinatra::VERSION
end
Sinatraのソースを更新する方法:
cd myproject/sinatra
git pull
== その他
日本語サイト
* {Greenbear Laboratory Rack日本語マニュアル}[http://mono.kmc.gr.jp/~yhara/w/?RackReferenceJa] - Rackの日本語マニュアル
英語サイト
* {プロジェクトサイト}[http://sinatra.github.com/] - ドキュメント、
ニュース、他のリソースへのリンクがあります。
* {プロジェクトに参加(貢献)する}[http://sinatra.github.com/contributing.html] - バグレポート
パッチの送信、サポートなど
* {Lighthouse}[http://sinatra.lighthouseapp.com] - チケット管理とリリース計画
* {Twitter}[http://twitter.com/sinatra]
* {メーリングリスト}[http://groups.google.com/group/sinatrarb]
* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] on http://freenode.net