= Sinatra 注) 本文書は英語から翻訳したものであり、その内容が最新でない場合もあります。最新の情報はオリジナルの英語版を参照して下さい。 SinatraはRubyで下記のような最小労力で手早くウェブアプリケーションを作成するためのDSLです。 # myapp.rb require 'sinatra' get '/' do 'Hello world!' end gemをインストールして動かしてみる。 gem install sinatra ruby -rubygems 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.csshttp://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-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options] は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 builder :index end ./views/index.builderを表示します。 === 鋸 テンプレート 鋸を使うには鋸ライブラリが必要です: ## 鋸を読み込みます require 'nokogiri' get '/' do nokogiri :index end ./views/index.nokogiriを表示します。 === Sass テンプレート Sassテンプレートを使うにはsassライブラリが必要です: ## hamlかsassを読み込みます require 'sass' get '/stylesheet.css' do sass :stylesheet end ./views/stylesheet.sassを表示します。 {Sass' options}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options] はSinatraの設定でグローバルに設定することができます。 see {Options and Configurations}[http://www.sinatrarb.com/configuration.html], を参照してそれぞれ設定を上書きして下さい。 set :sass, {:style => :compact } # デフォルトのSass styleは :nested get '/stylesheet.css' do 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が返されます。 == リクエストオブジェクトへのアクセス 受信するリクエストオブジェクトは、`request`メソッドを通じてリクエストレベル(フィルタ、ルート、エラーハンドラ)からアクセスすることができます: # アプリケーションが http://example.com/example で動作している場合 get '/foo' do request.body # クライアントによって送信されたリクエストボディ(下記参照) request.scheme # "http" request.script_name # "/example" request.path_info # "/foo" request.port # 80 request.request_method # "GET" request.query_string # "" request.content_length # request.bodyの長さ request.media_type # request.bodyのメディアタイプ request.host # "example.com" request.get? # true (他の動詞についても同様のメソッドあり) request.form_data? # false request["SOME_HEADER"] # SOME_HEADERヘッダの値 request.referer # クライアントのリファラまたは'/' request.user_agent # ユーザエージェント (:agent 条件によって使用される) request.cookies # ブラウザクッキーのハッシュ request.xhr? # Ajaxリクエストかどうか request.url # "http://example.com/example/foo" request.path # "/example/foo" request.ip # クライアントのIPアドレス request.secure? # false request.env # Rackによって渡された生のenvハッシュ end script_namepath_infoなどのオプションは次のように利用することもできます: before { request.path_info = "/" } get "/" do "全てのリクエストはここに来る" end request.bodyはIOまたはStringIOのオブジェクトです: post "/api" do request.body.rewind # 既に読まれているときのため data = JSON.parse request.body.read "Hello #{data['name']}!" end == 設定 どの環境でも起動時に1回だけ実行されます。 configure do ... end 環境変数:production(RACK_ENV環境変数) がセットされている時だけ実行する方法: configure :production do ... end 環境変数:production:testの場合に設定する方法: configure :production, :test do ... end == エラーハンドリング エラーハンドラーはルートコンテキストとbeforeフィルタ内で実行します。 hamlerbhaltなどを使うこともできます。 === 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_founderrorハンドラーを インストールしています。 == 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_PATHsinatra/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] - バグレポート パッチの送信、サポートなど * {Issue tracker}[http://github.com/sinatra/sinatra/issues] - チケット管理とリリース計画 * {Twitter}[http://twitter.com/sinatra] * {メーリングリスト}[http://groups.google.com/group/sinatrarb] * {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] on http://freenode.net