# Sinatra *注) 本文書は英語から翻訳したものであり、その内容が最新でない場合もあります。最新の情報はオリジナルの英語版を参照して下さい。* Sinatraは最小の労力でRubyによるWebアプリケーションを手早く作るための[DSL](http://ja.wikipedia.org/wiki/ドメイン固有言語)です。 ``` ruby # myapp.rb require 'sinatra' get '/' do 'Hello world!' end ``` gemをインストールし、 ``` shell gem install sinatra ``` 次のように実行します。 ``` shell ruby myapp.rb ``` [localhost:4567](http://localhost:4567) を開きます。 ThinがあればSinatraはこれを利用するので、`gem install thin`することをお薦めします。 ## 目次 * [Sinatra](#sinatra) * [目次](#目次) * [ルーティング(Routes)](#ルーティングroutes) * [条件(Conditions)](#条件conditions) * [戻り値(Return Values)](#戻り値return-values) * [カスタムルーティングマッチャー(Custom Route Matchers)](#カスタムルーティングマッチャーcustom-route-matchers) * [静的ファイル(Static Files)](#静的ファイルstatic-files) * [ビュー / テンプレート(Views / Templates)](#ビュー--テンプレートviews--templates) * [リテラルテンプレート(Literal Templates)](#リテラルテンプレートliteral-templates) * [利用可能なテンプレート言語](#利用可能なテンプレート言語) * [Haml テンプレート](#haml-テンプレート) * [Erb テンプレート](#erb-テンプレート) * [Builder テンプレート](#builder-テンプレート) * [Nokogiri テンプレート](#nokogiri-テンプレート) * [Sass テンプレート](#sass-テンプレート) * [SCSS テンプレート](#scss-テンプレート) * [Less テンプレート](#less-テンプレート) * [Liquid テンプレート](#liquid-テンプレート) * [Markdown テンプレート](#markdown-テンプレート) * [Textile テンプレート](#textile-テンプレート) * [RDoc テンプレート](#rdoc-テンプレート) * [AsciiDoc テンプレート](#asciidoc-テンプレート) * [Radius テンプレート](#radius-テンプレート) * [Markaby テンプレート](#markaby-テンプレート) * [RABL テンプレート](#rabl-テンプレート) * [Slim テンプレート](#slim-テンプレート) * [Creole テンプレート](#creole-テンプレート) * [MediaWiki テンプレート](#mediawiki-テンプレート) * [CoffeeScript テンプレート](#coffeescript-テンプレート) * [Stylus テンプレート](#stylus-テンプレート) * [Yajl テンプレート](#yajl-テンプレート) * [WLang テンプレート](#wlang-テンプレート) * [テンプレート内での変数へのアクセス](#テンプレート内での変数へのアクセス) * [`yield`を伴うテンプレートとネストしたレイアウト](#yieldを伴うテンプレートとネストしたレイアウト) * [インラインテンプレート(Inline Templates)](#インラインテンプレートinline-templates) * [名前付きテンプレート(Named Templates)](#名前付きテンプレートnamed-templates) * [ファイル拡張子の関連付け](#ファイル拡張子の関連付け) * [オリジナルテンプレートエンジンの追加](#オリジナルテンプレートエンジンの追加) * [フィルタ(Filters)](#フィルタfilters) * [ヘルパー(Helpers)](#ヘルパーhelpers) * [セッションの使用](#セッションの使用) * [停止(Halting)](#停止halting) * [パッシング(Passing)](#パッシングpassing) * [別ルーティングの誘発](#別ルーティングの誘発) * [ボディ、ステータスコードおよびヘッダの設定](#ボディステータスコードおよびヘッダの設定) * [ストリーミングレスポンス(Streaming Responses)](#ストリーミングレスポンスstreaming-responses) * [ロギング(Logging)](#ロギングlogging) * [MIMEタイプ(Mime Types)](#mimeタイプmime-types) * [URLの生成](#urlの生成) * [ブラウザリダイレクト(Browser Redirect)](#ブラウザリダイレクトbrowser-redirect) * [キャッシュ制御(Cache Control)](#キャッシュ制御cache-control) * [ファイルの送信](#ファイルの送信) * [リクエストオブジェクトへのアクセス](#リクエストオブジェクトへのアクセス) * [アタッチメント(Attachments)](#アタッチメントattachments) * [日付と時刻の取り扱い](#日付と時刻の取り扱い) * [テンプレートファイルの探索](#テンプレートファイルの探索) * [コンフィギュレーション(Configuration)](#コンフィギュレーションconfiguration) * [攻撃防御に対する設定](#攻撃防御に対する設定) * [利用可能な設定](#利用可能な設定) * [環境設定(Environments)](#環境設定environments) * [エラーハンドリング(Error Handling)](#エラーハンドリングerror-handling) * [Not Found](#not-found) * [エラー(Error)](#エラーerror) * [Rackミドルウェア(Rack Middleware)](#rackミドルウェアrack-middleware) * [テスト(Testing)](#テストtesting) * [Sinatra::Base - ミドルウェア、ライブラリおよびモジュラーアプリ](#sinatrabase---ミドルウェアライブラリおよびモジュラーアプリ) * [モジュラースタイル vs クラッシックスタイル](#モジュラースタイル-vs-クラッシックスタイル) * [モジュラーアプリケーションの提供](#モジュラーアプリケーションの提供) * [config.ruを用いたクラッシックスタイルアプリケーションの使用](#configruを用いたクラッシックスタイルアプリケーションの使用) * [config.ruはいつ使うのか?](#configruはいつ使うのか) * [Sinatraのミドルウェアとしての利用](#sinatraのミドルウェアとしての利用) * [動的なアプリケーションの生成](#動的なアプリケーションの生成) * [スコープとバインディング(Scopes and Binding)](#スコープとバインディングscopes-and-binding) * [アプリケーション/クラスのスコープ](#アプリケーションクラスのスコープ) * [リクエスト/インスタンスのスコープ](#リクエストインスタンスのスコープ) * [デリゲートスコープ](#デリゲートスコープ) * [コマンドライン](#コマンドライン) * [マルチスレッド](#マルチスレッド) * [必要環境](#必要環境) * [最新開発版](#最新開発版) * [Bundlerを使う場合](#bundlerを使う場合) * [直接組み込む場合](#直接組み込む場合) * [グローバル環境にインストールする場合](#グローバル環境にインストールする場合) * [バージョニング(Versioning)](#バージョニングversioning) * [参考文献](#参考文献) ## ルーティング(Routes) Sinatraでは、ルーティングはHTTPメソッドとURLマッチングパターンがペアになっています。 ルーティングはブロックに結び付けられています。 ``` ruby get '/' do .. 何か見せる .. end post '/' do .. 何か生成する .. end put '/' do .. 何か更新する .. end patch '/' do .. 何か修正する .. end delete '/' do .. 何か削除する .. end options '/' do .. 何か満たす .. end link '/' do .. 何かリンクを張る .. end unlink '/' do .. 何かアンリンクする .. end ``` ルーティングは定義された順番にマッチします。 リクエストに最初にマッチしたルーティングが呼び出されます。 ルーティングのパターンは名前付きパラメータを含むことができ、 `params`ハッシュで取得できます。 ``` ruby get '/hello/:name' do # "GET /hello/foo" と "GET /hello/bar" にマッチ # params['name'] は 'foo' か 'bar' "Hello #{params['name']}!" end ``` また、ブロックパラメータで名前付きパラメータにアクセスすることもできます。 ``` ruby get '/hello/:name' do |n| # "GET /hello/foo" と "GET /hello/bar" にマッチ # params['name'] は 'foo' か 'bar' # n が params['name'] を保持 "Hello #{n}!" end ``` ルーティングパターンはアスタリスク(すなわちワイルドカード)を含むこともでき、 `params['splat']` で取得できます。 ``` ruby get '/say/*/to/*' do # /say/hello/to/world にマッチ params['splat'] # => ["hello", "world"] end get '/download/*.*' do # /download/path/to/file.xml にマッチ params['splat'] # => ["path/to/file", "xml"] end ``` ここで、ブロックパラメータを使うこともできます。 ``` ruby get '/download/*.*' do |path, ext| [path, ext] # => ["path/to/file", "xml"] end ``` ルーティングを正規表現にマッチさせることもできます。 ``` ruby get /\A\/hello\/([\w]+)\z/ do "Hello, #{params['captures'].first}!" end ``` ここでも、ブロックパラメータが使えます。 ``` ruby get %r{/hello/([\w]+)} do |c| "Hello, #{c}!" end ``` ルーティングパターンは、オプショナルパラメータを取ることもできます。 ``` ruby get '/posts.?:format?' do # "GET /posts" と "GET /posts.json", "GET /posts.xml" の拡張子などにマッチ end ``` ところで、ディレクトリトラバーサル攻撃防御設定を無効にしないと(下記参照)、 ルーティングにマッチする前にリクエストパスが修正される可能性があります。 ## 条件(Conditions) ルーティングにはユーザエージェントのようなさまざまな条件を含めることができます。 ``` ruby get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do "Songbirdのバージョン #{params['agent'][0]}を使ってます。" end get '/foo' do # Songbird以外のブラウザにマッチ end ``` ほかに`host_name`と`provides`条件が利用可能です。 ``` ruby get '/', :host_name => /^admin\./ do "Adminエリアです。アクセスを拒否します!" end get '/', :provides => 'html' do haml :index end get '/', :provides => ['rss', 'atom', 'xml'] do builder :feed end ``` 独自の条件を定義することも簡単にできます。 ``` ruby set(:probability) { |value| condition { rand <= value } } get '/win_a_car', :probability => 0.1 do "あなたの勝ちです!" end get '/win_a_car' do "残念、あなたの負けです。" end ``` 複数の値を取る条件には、アスタリスクを使います。 ``` ruby set(:auth) do |*roles| # <- ここでアスタリスクを使う condition do unless logged_in? && roles.any? {|role| current_user.in_role? role } redirect "/login/", 303 end end end get "/my/account/", :auth => [:user, :admin] do "アカウントの詳細" end get "/only/admin/", :auth => :admin do "ここは管理者だけ!" end ``` ## 戻り値(Return Values) ルーティングブロックの戻り値は、HTTPクライアントまたはRackスタックでの次のミドルウェアに渡されるレスポンスボディを決定します。 これは大抵の場合、上の例のように文字列ですが、それ以外の値も使用することができます。 Rackレスポンス、Rackボディオブジェクト、HTTPステータスコードのいずれかとして妥当なオブジェクトであればどのようなオブジェクトでも返すことができます。 * 3つの要素を含む配列: `[ステータス(Fixnum), ヘッダ(Hash), レスポンスボディ(#eachに応答する)]` * 2つの要素を含む配列: `[ステータス(Fixnum), レスポンスボディ(#eachに応答する)]` * `#each`に応答するオブジェクト。通常はそのまま何も返さないが、 与えられたブロックに文字列を渡す。 * ステータスコードを表現する整数(Fixnum) これにより、例えばストリーミングを簡単に実装することができます。 ``` ruby class Stream def each 100.times { |i| yield "#{i}\n" } end end get('/') { Stream.new } ``` 後述する`stream`ヘルパーメソッドを使って、定型パターンを減らしつつストリーミングロジックをルーティングに埋め込むこともできます。 ## カスタムルーティングマッチャー(Custom Route Matchers) 先述のようにSinatraはルーティングマッチャーとして、文字列パターンと正規表現を使うことをビルトインでサポートしています。しかしこれに留まらず、独自のマッチャーを簡単に定義することもできるのです。 ``` ruby class AllButPattern Match = Struct.new(:captures) def initialize(except) @except = except @captures = Match.new([]) end def match(str) @captures unless @except === str end end def all_but(pattern) AllButPattern.new(pattern) end get all_but("/index") do # ... end ``` ノート: この例はオーバースペックであり、以下のようにも書くことができます。 ``` ruby get // do pass if request.path_info == "/index" # ... end ``` または、否定先読みを使って: ``` ruby get %r{^(?!/index$)} do # ... end ``` ## 静的ファイル(Static Files) 静的ファイルは`./public`ディレクトリから配信されます。 `:public_folder`オプションを指定することで別の場所を指定することができます。 ``` ruby set :public_folder, File.dirname(__FILE__) + '/static' ``` ノート: この静的ファイル用のディレクトリ名はURL中に含まれません。 例えば、`./public/css/style.css`は`http://example.com/css/style.css`でアクセスできます。 `Cache-Control`の設定をヘッダーへ追加するには`:static_cache_control`の設定(下記参照)を加えてください。 ## ビュー / テンプレート(Views / Templates) 各テンプレート言語はそれ自身のレンダリングメソッドを通して展開されます。それらのメソッドは単に文字列を返します。 ``` ruby get '/' do erb :index end ``` これは、`views/index.erb`をレンダリングします。 テンプレート名を渡す代わりに、直接そのテンプレートの中身を渡すこともできます。 ``` ruby get '/' do code = "<%= Time.now %>" erb code end ``` テンプレートのレイアウトは第2引数のハッシュ形式のオプションをもとに表示されます。 ``` ruby get '/' do erb :index, :layout => :post end ``` これは、`views/post.erb`内に埋め込まれた`views/index.erb`をレンダリングします(デフォルトは`views/layout.erb`があればそれになります)。 Sinatraが理解できないオプションは、テンプレートエンジンに渡されることになります。 ``` ruby get '/' do haml :index, :format => :html5 end ``` テンプレート言語ごとにオプションをセットすることもできます。 ``` ruby set :haml, :format => :html5 get '/' do haml :index end ``` レンダリングメソッドに渡されたオプションは`set`で設定されたオプションを上書きします。 利用可能なオプション:
locals
ドキュメントに渡されるローカルのリスト。パーシャルに便利。 例: erb "<%= foo %>", :locals => {:foo => "bar"}
default_encoding
文字エンコーディング(不確かな場合に使用される)。デフォルトは、settings.default_encoding
views
テンプレートを読み出すビューのディレクトリ。デフォルトは、settings.views
layout
レイアウトを使うかの指定(true または false)。値がシンボルの場合は、使用するテンプレートが指定される。例: erb :index, :layout => !request.xhr?
content_type
テンプレートが生成するContent-Type。デフォルトはテンプレート言語ごとに異なる。
scope
テンプレートをレンダリングするときのスコープ。デフォルトは、アプリケーションのインスタンス。これを変更した場合、インスタンス変数およびヘルパーメソッドが利用できなくなる。
layout_engine
レイアウトをレンダリングするために使用するテンプレートエンジン。レイアウトをサポートしない言語で有用。デフォルトはテンプレートに使われるエンジン。例: set :rdoc, :layout_engine => :erb
layout_options
レイアウトをレンダリングするときだけに使う特別なオプション。例: set :rdoc, :layout_options => { :views => 'views/layouts' }
テンプレートは`./views`ディレクトリ下に配置されています。 他のディレクトリを使用する場合の例: ``` ruby set :views, settings.root + '/templates' ``` テンプレートはシンボルを使用して参照させることを覚えておいて下さい。 サブディレクトリでもこの場合は`:'subdir/template'`のようにします。 レンダリングメソッドは文字列が渡されると、それをそのまま文字列として出力するので、シンボルを使ってください。 ### リテラルテンプレート(Literal Templates) ``` ruby get '/' do haml '%div.title Hello World' end ``` これはそのテンプレート文字列をレンダリングします。 ### 利用可能なテンプレート言語 いくつかの言語には複数の実装があります。使用する(そしてスレッドセーフにする)実装を指定するには、それを最初にrequireしてください。 ``` ruby require 'rdiscount' # または require 'bluecloth' get('/') { markdown :index } ``` #### Haml テンプレート
依存 haml
ファイル拡張子 .haml
haml :index, :format => :html5
#### Erb テンプレート
依存 erubis または erb (Rubyに同梱)
ファイル拡張子 .erb, .rhtml or .erubis (Erubisだけ)
erb :index
#### Builder テンプレート
依存 builder
ファイル拡張子 .builder
builder { |xml| xml.em "hi" }
インラインテンプレート用にブロックを取ることもできます(例を参照)。 #### Nokogiri テンプレート
依存 nokogiri
ファイル拡張子 .nokogiri
nokogiri { |xml| xml.em "hi" }
インラインテンプレート用にブロックを取ることもできます(例を参照)。 #### Sass テンプレート
依存 sass
ファイル拡張子 .sass
sass :stylesheet, :style => :expanded
#### Scss テンプレート
依存 sass
ファイル拡張子 .scss
scss :stylesheet, :style => :expanded
#### Less テンプレート
依存 less
ファイル拡張子 .less
less :stylesheet
#### Liquid テンプレート
依存 liquid
ファイル拡張子 .liquid
liquid :index, :locals => { :key => 'value' }
LiquidテンプレートからRubyのメソッド(`yield`を除く)を呼び出すことができないため、ほぼ全ての場合にlocalsを指定する必要があるでしょう。 #### Markdown テンプレート
依存 次の何れか: RDiscount, RedCarpet, BlueCloth, kramdown, maruku
ファイル拡張子 .markdown, .mkd and .md
markdown :index, :layout_engine => :erb
Markdownからメソッドを呼び出すことも、localsに変数を渡すこともできません。 それゆえ、他のレンダリングエンジンとの組み合わせで使うのが普通です。 ``` ruby erb :overview, :locals => { :text => markdown(:introduction) } ``` ノート: 他のテンプレート内で`markdown`メソッドを呼び出せます。 ``` ruby %h1 Hello From Haml! %p= markdown(:greetings) ``` MarkdownからはRubyを呼ぶことができないので、Markdownで書かれたレイアウトを使うことはできません。しかしながら、`:layout_engine`オプションを渡すことでテンプレートのものとは異なるレンダリングエンジンをレイアウトのために使うことができます。 #### Textile テンプレート
依存 RedCloth
ファイル拡張子 .textile
textile :index, :layout_engine => :erb
Textileからメソッドを呼び出すことも、localsに変数を渡すこともできません。 それゆえ、他のレンダリングエンジンとの組み合わせで使うのが普通です。 ``` ruby erb :overview, :locals => { :text => textile(:introduction) } ``` ノート: 他のテンプレート内で`textile`メソッドを呼び出せます。 ``` ruby %h1 Hello From Haml! %p= textile(:greetings) ``` TexttileからはRubyを呼ぶことができないので、Textileで書かれたレイアウトを使うことはできません。しかしながら、`:layout_engine`オプションを渡すことでテンプレートのものとは異なるレンダリングエンジンをレイアウトのために使うことができます。 #### RDoc テンプレート
依存 RDoc
ファイル拡張子 .rdoc
rdoc :README, :layout_engine => :erb
RDocからメソッドを呼び出すことも、localsに変数を渡すこともできません。 それゆえ、他のレンダリングエンジンとの組み合わせで使うのが普通です。 ``` ruby erb :overview, :locals => { :text => rdoc(:introduction) } ``` ノート: 他のテンプレート内で`rdoc`メソッドを呼び出せます。 ``` ruby %h1 Hello From Haml! %p= rdoc(:greetings) ``` RDocからはRubyを呼ぶことができないので、RDocで書かれたレイアウトを使うことはできません。しかしながら、`:layout_engine`オプションを渡すことでテンプレートのものとは異なるレンダリングエンジンをレイアウトのために使うことができます。 #### AsciiDoc テンプレート
依存 Asciidoctor
ファイル拡張子 .asciidoc, .adoc and .ad
asciidoc :README, :layout_engine => :erb
AsciiDocテンプレートからRubyのメソッドを直接呼び出すことができないため、ほぼ全ての場合にlocalsを指定する必要があるでしょう。 #### Radius テンプレート
依存 Radius
ファイル拡張子 .radius
radius :index, :locals => { :key => 'value' }
RadiusテンプレートからRubyのメソッドを直接呼び出すことができないため、ほぼ全ての場合にlocalsを指定する必要があるでしょう。 #### Markaby テンプレート
依存 Markaby
ファイル拡張子 .mab
markaby { h1 "Welcome!" }
インラインテンプレート用にブロックを取ることもできます(例を参照)。 #### RABL テンプレート
依存 Rabl
ファイル拡張子 .rabl
rabl :index
#### Slim テンプレート
依存 Slim Lang
ファイル拡張子 .slim
slim :index
#### Creole テンプレート
依存 Creole
ファイル拡張子 .creole
creole :wiki, :layout_engine => :erb
Creoleからメソッドを呼び出すことも、localsに変数を渡すこともできません。 それゆえ、他のレンダリングエンジンとの組み合わせで使うのが普通です。 ``` ruby erb :overview, :locals => { :text => creole(:introduction) } ``` ノート: 他のテンプレート内で`creole`メソッドを呼び出せます。 ``` ruby %h1 Hello From Haml! %p= creole(:greetings) ``` CreoleからはRubyを呼ぶことができないので、Creoleで書かれたレイアウトを使うことはできません。しかしながら、`:layout_engine`オプションを渡すことでテンプレートのものとは異なるレンダリングエンジンをレイアウトのために使うことができます。 #### MediaWiki テンプレート
依存 WikiCloth
ファイル拡張子 .mediawiki および .mw
mediawiki :wiki, :layout_engine => :erb
MediaWikiのテンプレートは直接メソッドから呼び出したり、ローカル変数を通すことはできません。それゆえに、通常は別のレンダリングエンジンと組み合わせて利用します。 ```ruby erb :overview, :locals => { :text => mediawiki(:introduction) } ``` ノート: 他のテンプレートから部分的に`mediawiki`メソッドを呼び出すことも可能です。 #### CoffeeScript テンプレート
依存 CoffeeScript および JavaScriptの起動方法
ファイル拡張子 .coffee
coffee :index
#### Stylus テンプレート
依存 Stylus および JavaScriptの起動方法
ファイル拡張子 .styl
stylus :index
Stylusテンプレートを使えるようにする前に、まず`stylus`と`stylus/tilt`を読み込む必要があります。 ``` ruby require 'sinatra' require 'stylus' require 'stylus/tilt' get '/' do stylus :example end ``` #### Yajl テンプレート
依存 yajl-ruby
ファイル拡張子 .yajl
yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource'
テンプレートのソースはRubyの文字列として評価され、その結果のJSON変数は`#to_json`を使って変換されます。 ``` ruby json = { :foo => 'bar' } json[:baz] = key ``` `:callback`および`:variable`オプションは、レンダリングされたオブジェクトを装飾するために使うことができます。 ``` ruby var resource = {"foo":"bar","baz":"qux"}; present(resource); ``` #### WLang テンプレート
依存 wlang
ファイル拡張子 .wlang
wlang :index, :locals => { :key => 'value' }
WLang内でのRubyメソッドの呼び出しは一般的ではないので、ほとんどの場合にlocalsを指定する必要があるでしょう。しかしながら、WLangで書かれたレイアウトは`yield`をサポートしています。 ### テンプレート内での変数へのアクセス テンプレートはルーティングハンドラと同じコンテキストの中で評価されます。ルーティングハンドラでセットされたインスタンス変数はテンプレート内で直接使うことができます。 ``` ruby get '/:id' do @foo = Foo.find(params['id']) haml '%h1= @foo.name' end ``` また、ローカル変数のハッシュで明示的に指定することもできます。 ``` ruby get '/:id' do foo = Foo.find(params['id']) haml '%h1= bar.name', :locals => { :bar => foo } end ``` このやり方は他のテンプレート内で部分テンプレートとして表示する時に典型的に使用されます。 ### `yield`を伴うテンプレートとネストしたレイアウト レイアウトは通常、`yield`を呼ぶ単なるテンプレートに過ぎません。 そのようなテンプレートは、既に説明した`:template`オプションを通して使われるか、または次のようなブロックを伴ってレンダリングされます。 ``` ruby erb :post, :layout => false do erb :index end ``` このコードは、`erb :index, :layout => :post`とほぼ等価です。 レンダリングメソッドにブロックを渡すスタイルは、ネストしたレイアウトを作るために最も役立ちます。 ``` ruby erb :main_layout, :layout => false do erb :admin_layout do erb :user end end ``` これはまた次のより短いコードでも達成できます。 ``` ruby erb :admin_layout, :layout => :main_layout do erb :user end ``` 現在、次のレンダリングメソッドがブロックを取れます: `erb`, `haml`, `liquid`, `slim `, `wlang`。 また汎用の`render`メソッドもブロックを取れます。 ### インラインテンプレート(Inline Templates) テンプレートはソースファイルの最後で定義することもできます。 ``` ruby require 'sinatra' get '/' do haml :index end __END__ @@ layout %html = yield @@ index %div.title Hello world!!!!! ``` ノート: Sinatraをrequireするソースファイル内で定義されたインラインテンプレートは自動的に読み込まれます。他のソースファイル内にインラインテンプレートがある場合には`enable :inline_templates`を明示的に呼んでください。 ### 名前付きテンプレート(Named Templates) テンプレートはトップレベルの`template`メソッドで定義することもできます。 ``` ruby template :layout do "%html\n =yield\n" end template :index do '%div.title Hello World!' end get '/' do haml :index end ``` 「layout」というテンプレートが存在する場合、そのテンプレートファイルは他のテンプレートがレンダリングされる度に使用されます。`:layout => false`で個別に、または`set :haml, :layout => false`でデフォルトとして、レイアウトを無効にすることができます。 ``` ruby get '/' do haml :index, :layout => !request.xhr? end ``` ### ファイル拡張子の関連付け 任意のテンプレートエンジンにファイル拡張子を関連付ける場合は、`Tilt.register`を使います。例えば、Textileテンプレートに`tt`というファイル拡張子を使いたい場合は、以下のようにします。 ``` ruby Tilt.register :tt, Tilt[:textile] ``` ### オリジナルテンプレートエンジンの追加 まず、Tiltでそのエンジンを登録し、次にレンダリングメソッドを作ります。 ``` ruby Tilt.register :myat, MyAwesomeTemplateEngine helpers do def myat(*args) render(:myat, *args) end end get '/' do myat :index end ``` これは、`./views/index.myat`をレンダリングします。Tiltについての詳細は、https://github.com/rtomayko/tilt を参照してください。 ## フィルタ(Filters) beforeフィルタは、リクエストのルーティングと同じコンテキストで各リクエストの前に評価され、それによってリクエストとレスポンスを変更可能にします。フィルタ内でセットされたインスタンス変数はルーティングとテンプレートからアクセスすることができます。 ``` ruby before do @note = 'Hi!' request.path_info = '/foo/bar/baz' end get '/foo/*' do @note #=> 'Hi!' params['splat'] #=> 'bar/baz' end ``` afterフィルタは、リクエストのルーティングと同じコンテキストで各リクエストの後に評価され、それによってこれもリクエストとレスポンスを変更可能にします。beforeフィルタとルーティング内でセットされたインスタンス変数はafterフィルタからアクセスすることができます。 ``` ruby after do puts response.status end ``` ノート: `body`メソッドを使わずにルーティングから文字列を返すだけの場合、その内容はafterフィルタでまだ利用できず、その後に生成されることになります。 フィルタにはオプションとしてパターンを渡すことができ、この場合はリクエストのパスがパターンにマッチした場合にのみフィルタが評価されるようになります。 ``` ruby before '/protected/*' do authenticate! end after '/create/:slug' do |slug| session[:last_slug] = slug end ``` ルーティング同様、フィルタもまた条件を取ることができます。 ``` ruby before :agent => /Songbird/ do # ... end after '/blog/*', :host_name => 'example.com' do # ... end ``` ## ヘルパー(Helpers) トップレベルの`helpers`メソッドを使用してルーティングハンドラやテンプレートで使うヘルパーメソッドを定義できます。 ``` ruby helpers do def bar(name) "#{name}bar" end end get '/:name' do bar(params['name']) end ``` あるいは、ヘルパーメソッドをモジュール内で個別に定義することもできます。 ``` ruby module FooUtils def foo(name) "#{name}foo" end end module BarUtils def bar(name) "#{name}bar" end end helpers FooUtils, BarUtils ``` その効果は、アプリケーションクラスにモジュールをインクルードするのと同じです。 ### セッションの使用 セッションはリクエスト間での状態維持のために使用されます。その起動により、ユーザセッションごとに一つのセッションハッシュが与えられます。 ``` ruby enable :sessions get '/' do "value = " << session[:value].inspect end get '/:value' do session[:value] = params['value'] end ``` ノート: `enable :sessions`は実際にはすべてのデータをクッキーに保持します。これは必ずしも期待通りのものにならないかもしれません(例えば、大量のデータを保持することでトラフィックが増大するなど)。Rackセッションミドルウェアの利用が可能であり、その場合は`enable :sessions`を呼ばずに、選択したミドルウェアを他のミドルウェアのときと同じようにして取り込んでください。 ``` ruby use Rack::Session::Pool, :expire_after => 2592000 get '/' do "value = " << session[:value].inspect end get '/:value' do session[:value] = params['value'] end ``` セキュリティ向上のため、クッキー内のセッションデータはセッション秘密鍵(session secret)で署名されます。Sinatraによりランダムな秘密鍵が個別に生成されます。しかし、この秘密鍵はアプリケーションの立ち上げごとに変わってしまうので、すべてのアプリケーションのインスタンスで共有できる秘密鍵をセットしたくなるかもしれません。 ``` ruby set :session_secret, 'super secret' ``` 更に、設定変更をしたい場合は、`sessions`の設定においてオプションハッシュを保持することもできます。 ``` ruby set :sessions, :domain => 'foo.com' ``` foo.comのサブドメイン上のアプリ間でセッションを共有化したいときは、代わりにドメインの前に *.* を付けます。 ``` ruby set :sessions, :domain => '.foo.com' ``` ### 停止(Halting) フィルタまたはルーティング内で直ちにリクエストを止める場合 ``` ruby halt ``` この際、ステータスを指定することもできます。 ``` ruby halt 410 ``` body部を指定することも、 ``` ruby halt 'ここにbodyを書く' ``` ステータスとbody部を指定することも、 ``` ruby halt 401, '立ち去れ!' ``` ヘッダを付けることもできます。 ``` ruby halt 402, {'Content-Type' => 'text/plain'}, 'リベンジ' ``` もちろん、テンプレートを`halt`に結びつけることも可能です。 ``` ruby halt erb(:error) ``` ### パッシング(Passing) ルーティングは`pass`を使って次のルーティングに飛ばすことができます。 ``` ruby get '/guess/:who' do pass unless params['who'] == 'Frank' "見つかっちゃった!" end get '/guess/*' do "はずれです!" end ``` ルーティングブロックからすぐに抜け出し、次にマッチするルーティングを実行します。マッチするルーティングが見当たらない場合は404が返されます。 ### 別ルーティングの誘発 `pass`を使ってルーティングを飛ばすのではなく、他のルーティングを呼んだ結果を得たいというときがあります。これを実現するには`call`を使えばいいです。 ``` ruby get '/foo' do status, headers, body = call env.merge("PATH_INFO" => '/bar') [status, headers, body.map(&:upcase)] end get '/bar' do "bar" end ``` ノート: 先の例において、テストを楽にしパフォーマンスを改善するには、`"bar"`を単にヘルパーに移し、`/foo`および`/bar`から使えるようにするのがいいです。 リクエストが、その複製物でない同じアプリケーションのインスタンスに送られるようにしたいときは、`call`に代えて`call!`を使ってください。 `call`についての詳細はRackの仕様書を参照してください。 ### ボディ、ステータスコードおよびヘッダの設定 ステータスコードおよびレスポンスボディを、ルーティングブロックの戻り値にセットすることが可能であり、これは推奨されています。しかし、あるケースでは実行フローの任意のタイミングでボディをセットしたくなるかもしれません。`body`ヘルパーメソッドを使えばそれができます。そうすると、それ以降、ボディにアクセスするためにそのメソッドを使うことができるようになります。 ``` ruby get '/foo' do body "bar" end after do puts body end ``` また、`body`にはブロックを渡すことができ、これはRackハンドラにより実行されることになります(これはストリーミングを実装するのに使われます。"戻り値"の項を参照してください。) ボディと同様に、ステータスコードおよびヘッダもセットできます。 ``` ruby get '/foo' do status 418 headers \ "Allow" => "BREW, POST, GET, PROPFIND, WHEN", "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt" body "I'm a tea pot!" end ``` 引数を伴わない`body`、`headers`、`status`などは、それらの現在の値にアクセスするために使えます。 ### ストリーミングレスポンス(Streaming Responses) レスポンスボディの部分を未だ生成している段階で、データを送り出したいということがあります。極端な例では、クライアントがコネクションを閉じるまでデータを送り続けたいことがあります。`stream`ヘルパーを使えば、独自ラッパーを作る必要はありません。 ``` ruby get '/' do stream do |out| out << "それは伝 -\n" sleep 0.5 out << " (少し待つ) \n" sleep 1 out << "- 説になる!\n" end end ``` これはストリーミングAPI、[Server Sent Events](http://dev.w3.org/html5/eventsource/)の実装を可能にし、[WebSockets](http://en.wikipedia.org/wiki/WebSocket)の土台に使うことができます。また、一部のコンテンツが遅いリソースに依存しているときに、スループットを上げるために使うこともできます。 ノート: ストリーミングの挙動、特に並行リクエスト(cuncurrent requests)の数は、アプリケーションを提供するのに使われるWebサーバに強く依存します。いくつかのサーバは、ストリーミングを全くサポートしません。サーバがストリーミングをサポートしない場合、ボディは`stream`に渡されたブロックの実行が終了した後、一度に全部送られることになります。ストリーミングは、Shotgunを使った場合は全く動作しません。 オプション引数が`keep_open`にセットされている場合、ストリームオブジェクト上で`close`は呼ばれず、実行フローの任意の遅れたタイミングでユーザがこれを閉じることを可能にします。これはThinやRainbowsのようなイベント型サーバ上でしか機能しません。他のサーバでは依然ストリームは閉じられます。 ``` ruby # ロングポーリング set :server, :thin connections = [] get '/subscribe' do # サーバイベントにおけるクライアントの関心を登録 stream(:keep_open) do |out| connections << out # 死んでいるコネクションを排除 connections.reject!(&:closed?) end end post '/message' do connections.each do |out| # クライアントへ新規メッセージ到着の通知 out << params['message'] << "\n" # クライアントへの再接続の指示 out.close end # 肯定応答 "message received" end ``` ### ロギング(Logging) リクエストスコープにおいて、`logger`ヘルパーは`Logger`インスタンスを作り出します。 ``` ruby get '/' do logger.info "loading data" # ... end ``` このロガーは、自動でRackハンドラのロギング設定を参照します。ロギングが無効(disabled)にされている場合、このメソッドはダミーオブジェクトを返すので、ルーティングやフィルタにおいて特に心配することはありません。 ノート: ロギングは、`Sinatra::Application`に対してのみデフォルトで有効にされているので、`Sinatra::Base`を継承している場合は、ユーザがこれを有効化する必要があります。 ``` ruby class MyApp < Sinatra::Base configure :production, :development do enable :logging end end ``` ロギングミドルウェアが設定されてしまうのを避けるには、`logging`設定を`nil`にセットします。しかしこの場合、`logger`が`nil`を返すことを忘れないように。よくあるユースケースは、オリジナルのロガーをセットしたいときです。Sinatraは、とにかく`env['rack.logger']`で見つかるものを使います。 ### MIMEタイプ(Mime Types) `send_file`か静的ファイルを使う時、SinatraがMIMEタイプを理解できない場合があります。その時は `mime_type` を使ってファイル拡張子毎に登録して下さい。 ``` ruby configure do mime_type :foo, 'text/foo' end ``` これは`content_type`ヘルパーで利用することができます: ``` ruby get '/' do content_type :foo "foo foo foo" end ``` ### URLの生成 URLを生成するためには`url`ヘルパーメソッドが使えます。Hamlではこのようにします。 ``` ruby %a{:href => url('/foo')} foo ``` これはリバースプロキシおよびRackルーティングを、それらがあれば考慮に入れます。 このメソッドには`to`というエイリアスがあります(以下の例を参照)。 ### ブラウザリダイレクト(Browser Redirect) `redirect` ヘルパーメソッドを使うことで、ブラウザをリダイレクトさせることができます。 ``` ruby get '/foo' do redirect to('/bar') end ``` 他に追加されるパラメータは、`halt`に渡される引数と同様に取り扱われます。 ``` ruby redirect to('/bar'), 303 redirect 'http://google.com', 'wrong place, buddy' ``` また、`redirect back`を使えば、簡単にユーザが来たページへ戻るリダイレクトを作れます。 ``` ruby get '/foo' do "do something" end get '/bar' do do_something redirect back end ``` redirectに引数を渡すには、それをクエリーに追加するか、 ``` ruby redirect to('/bar?sum=42') ``` または、セッションを使います。 ``` ruby enable :sessions get '/foo' do session[:secret] = 'foo' redirect to('/bar') end get '/bar' do session[:secret] end ``` ### キャッシュ制御(Cache Control) ヘッダを正しく設定することが、適切なHTTPキャッシングのための基礎となります。 キャッシュ制御ヘッダ(Cache-Control header)は、次のように簡単に設定できます。 ``` ruby get '/' do cache_control :public "キャッシュしました!" end ``` ヒント: キャッシングをbeforeフィルタ内で設定します。 ``` ruby before do cache_control :public, :must_revalidate, :max_age => 60 end ``` `expires`ヘルパーを対応するヘッダに使っている場合は、キャッシュ制御は自動で設定されます。 ``` ruby before do expires 500, :public, :must_revalidate end ``` キャッシュを適切に使うために、`etag`または`last_modified`を使うことを検討してください。これらのヘルパーを、重い仕事をさせる *前* に呼ぶことを推奨します。そうすれば、クライアントが既にキャッシュに最新版を持っている場合はレスポンスを直ちに破棄するようになります。 ``` ruby get '/article/:id' do @article = Article.find params['id'] last_modified @article.updated_at etag @article.sha1 erb :article end ``` また、[weak ETag](http://ja.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation)を使うこともできます。 ``` ruby etag @article.sha1, :weak ``` これらのヘルパーは、キャッシングをしてくれませんが、必要な情報をキャッシュに与えてくれます。もし手早いリバースプロキシキャッシングの解決策をお探しなら、 [rack-cache](https://github.com/rtomayko/rack-cache)を試してください。 ``` ruby require "rack/cache" require "sinatra" use Rack::Cache get '/' do cache_control :public, :max_age => 36000 sleep 5 "hello" end ``` `:static_cache_control`設定(以下を参照)を、キャッシュ制御ヘッダ情報を静的ファイルに追加するために使ってください。 RFC 2616によれば、アプリケーションは、If-MatchまたはIf-None-Matchヘッダが`*`に設定されている場合には、要求されたリソースが既に存在するか否かに応じて、異なる振る舞いをすべきとなっています。Sinatraは、getのような安全なリクエストおよびputのような冪等なリクエストは既に存在しているものとして仮定し、一方で、他のリソース(例えば、postリクエスト)は新たなリソースとして取り扱われるよう仮定します。この振る舞いは、`:new_resource`オプションを渡すことで変更できます。 ``` ruby get '/create' do etag '', :new_resource => true Article.create erb :new_article end ``` ここでもWeak ETagを使いたい場合は、`:kind`オプションを渡してください。 ``` ruby etag '', :new_resource => true, :kind => :weak ``` ### ファイルの送信 ファイルを送信するには、`send_file`ヘルパーメソッドを使います。 ``` ruby get '/' do send_file 'foo.png' end ``` これはオプションを取ることもできます。 ``` ruby send_file 'foo.png', :type => :jpg ``` オプション一覧
filename
ファイル名。デフォルトは実際のファイル名。
last_modified
Last-Modifiedヘッダの値。デフォルトはファイルのmtime。
type
コンテンツの種類。設定がない場合、ファイル拡張子から類推される。
disposition
Content-Dispositionに使われる。許容値: nil (デフォルト)、 :attachment および :inline
length
Content-Lengthヘッダ。デフォルトはファイルサイズ。
status
送られるステータスコード。静的ファイルをエラーページとして送るときに便利。 Rackハンドラでサポートされている場合は、Rubyプロセスからのストリーミング以外の手段が使われる。このヘルパーメソッドを使うと、Sinatraは自動で範囲リクエスト(range requests)を扱う。
### リクエストオブジェクトへのアクセス 受信するリクエストオブジェクトは、`request`メソッドを通じてリクエストレベル(フィルタ、ルーティング、エラーハンドラ)からアクセスすることができます。 ``` ruby # アプリケーションが http://example.com/example で動作している場合 get '/foo' do t = %w[text/css text/html application/javascript] request.accept # ['text/html', '*/*'] request.accept? 'text/xml' # true request.preferred_type(t) # 'text/html' 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_param"] # some_param変数の値。[]はパラメータハッシュのショートカット request.referrer # クライアントのリファラまたは'/' 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 (sslではtrueになる) request.forwarded? # true (リバースプロキシの裏で動いている場合) request.env # Rackによって渡された生のenvハッシュ end ``` `script_name`や`path_info`などのオプションは次のように利用することもできます。 ``` ruby before { request.path_info = "/" } get "/" do "全てのリクエストはここに来る" end ``` `request.body`はIOまたはStringIOのオブジェクトです。 ``` ruby post "/api" do request.body.rewind # 既に読まれているときのため data = JSON.parse request.body.read "Hello #{data['name']}!" end ``` ### アタッチメント(Attachments) `attachment`ヘルパーを使って、レスポンスがブラウザに表示されるのではなく、ディスクに保存されることをブラウザに対し通知することができます。 ``` ruby get '/' do attachment "保存しました!" end ``` ファイル名を渡すこともできます。 ``` ruby get '/' do attachment "info.txt" "保存しました!" end ``` ### 日付と時刻の取り扱い Sinatraは`time_for`ヘルパーメソッドを提供しており、それは与えられた値からTimeオブジェクトを生成します。これはまた`DateTime`、`Date`および類似のクラスを変換できます。 ``` ruby get '/' do pass if Time.now > time_for('Dec 23, 2012') "まだ時間がある" end ``` このメソッドは、`expires`、`last_modified`といった種類のものの内部で使われています。そのため、アプリケーションにおいて、`time_for`をオーバーライドすることでそれらのメソッドの挙動を簡単に拡張できます。 ``` ruby helpers do def time_for(value) case value when :yesterday then Time.now - 24*60*60 when :tomorrow then Time.now + 24*60*60 else super end end end get '/' do last_modified :yesterday expires :tomorrow "hello" end ``` ### テンプレートファイルの探索 `find_template`ヘルパーは、レンダリングのためのテンプレートファイルを見つけるために使われます。 ``` ruby find_template settings.views, 'foo', Tilt[:haml] do |file| puts "could be #{file}" end ``` この例はあまり有益ではありません。しかし、このメソッドを、独自の探索機構で働くようオーバーライドするなら有益になります。例えば、複数のビューディレクトリを使えるようにしたいときがあります。 ``` ruby set :views, ['views', 'templates'] helpers do def find_template(views, name, engine, &block) Array(views).each { |v| super(v, name, engine, &block) } end end ``` 他の例としては、異なるエンジン用の異なるディレクトリを使う場合です。 ``` ruby set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views' helpers do def find_template(views, name, engine, &block) _, folder = views.detect { |k,v| engine == Tilt[k] } folder ||= views[:default] super(folder, name, engine, &block) end end ``` これをエクステンションとして書いて、他の人と簡単に共有することもできます! ノート: `find_template`はファイルが実際に存在するかのチェックをしませんが、与えられたブロックをすべての可能なパスに対し呼び出します。これがパフォーマンス上の問題にはならないのは、`render`はファイルを見つけると直ちに`break`を使うからです。また、テンプレートの場所(および内容)は、developmentモードでの起動でない限りはキャッシュされます。このことは、複雑なメソッド(a really crazy method)を書いた場合は記憶しておく必要があります。 ## コンフィギュレーション(Configuration) どの環境でも起動時に1回だけ実行されます。 ``` ruby configure do # 1つのオプションをセット set :option, 'value' # 複数のオプションをセット set :a => 1, :b => 2 # `set :option, true`と同じ enable :option # `set :option, false`と同じ disable :option # ブロックを使って動的な設定をすることもできます。 set(:css_dir) { File.join(views, 'css') } end ``` 環境設定(`RACK_ENV`環境変数)が`:production`に設定されている時だけ実行する方法: ``` ruby configure :production do ... end ``` 環境設定が`:production`か`:test`に設定されている時だけ実行する方法: ``` ruby configure :production, :test do ... end ``` 設定したオプションには`settings`からアクセスできます: ``` ruby configure do set :foo, 'bar' end get '/' do settings.foo? # => true settings.foo # => 'bar' ... end ``` ### 攻撃防御に対する設定 Sinatraは、[Rack::Protection](https://github.com/rkh/rack-protection#readme)を使って、アプリケーションを多発する日和見的攻撃から守っています。この挙動は簡単に無効化できます(これはアプリケーションを大量の脆弱性攻撃に晒すことになります)。 ``` ruby disable :protection ``` 単一の防御層を外すためには、`protection`をオプションハッシュにセットします。 ``` ruby set :protection, :except => :path_traversal ``` また配列を渡して、複数の防御を無効にすることもできます。 ``` ruby set :protection, :except => [:path_traversal, :session_hijacking] ``` デフォルトでSinatraは、`:sessions`が有効になっている場合、セッションベースの防御だけを設定します。しかし、自身でセッションを設定したい場合があります。その場合は、`:session`オプションを渡すことにより、セッションベースの防御を設定することができます。 ``` ruby use Rack::Session::Pool set :protection, :session => true ``` ### 利用可能な設定
absolute_redirects
無効のとき、Sinatraは相対リダイレクトを許容するが、RFC 2616 (HTTP 1.1)は絶対リダイレクトのみを許容するので、これには準拠しなくなる。
アプリケーションが、適切に設定されていないリバースプロキシの裏で走っている場合は有効。ノート: urlヘルパーは、第2引数にfalseを渡さない限り、依然として絶対URLを生成する。
デフォルトは無効。
add_charset
Mimeタイプ content_typeヘルパーが自動的にキャラクタセット情報をここに追加する。このオプションは書き換えるのではなく、値を追加するようにすること。 settings.add_charset << "application/foobar"
app_file
メインのアプリケーションファイルのパスであり、プロジェクトのルート、viewsおよびpublicフォルダを見つけるために使われる。
bind
バインドするIPアドレス(デフォルト: `environment`がdevelopmentにセットされているときは、0.0.0.0 または localhost)。ビルトインサーバでのみ使われる。
default_encoding
不明なときに仮定されるエンコーディング(デフォルトは"utf-8")。
dump_errors
ログにおけるエラーの表示。
environment
現在の環境。デフォルトはENV['RACK_ENV']、それが無い場合は"development"
logging
ロガーの使用。
lock
各リクエスト周りのロックの配置で、Rubyプロセスごとにリクエスト処理を並行して走らせるようにする。
アプリケーションがスレッドセーフでなければ有効。デフォルトは無効。
method_override
put/deleteフォームを、それらをサポートしないブラウザで使えるように_methodのおまじないを使えるようにする。
port
待ち受けポート。ビルトインサーバのみで有効。
prefixed_redirects
絶対パスが与えられていないときに、リダイレクトにrequest.script_nameを挿入するか否かの設定。これによりredirect '/foo'は、redirect to('/foo')のように振る舞う。デフォルトは無効。
protection
Web攻撃防御を有効にするか否かの設定。上述の攻撃防御の項を参照。
public_dir
public_folderのエイリアス。以下を参照。
public_folder
publicファイルが提供されるディレクトリのパス。静的ファイルの提供が有効になっている場合にのみ使われる (以下のstatic設定を参照)。設定されていない場合、app_file設定から推定。
reload_templates
リクエスト間でテンプレートを再ロードするか否かの設定。developmentモードでは有効。
root
プロジェクトのルートディレクトリのパス。設定されていない場合、app_file設定から推定。
raise_errors
例外発生の設定(アプリケーションは止まる)。environment"test"に設定されているときはデフォルトは有効。それ以外は無効。
run
有効のとき、SinatraがWebサーバの起動を取り扱う。rackupまたは他の手段を使うときは有効にしないこと。
running
ビルトインサーバが稼働中か?この設定を変更しないこと!
server
ビルトインサーバとして使用するサーバまたはサーバ群の指定。指定順位は優先度を表し、デフォルトはRuby実装に依存。
sessions
Rack::Session::Cookieを使ったクッキーベースのセッションサポートの有効化。詳しくは、'セッションの使用'の項を参照のこと。
show_exceptions
例外発生時にブラウザにスタックトレースを表示する。environment"development"に設定されているときは、デフォルトで有効。それ以外は無効。
また、:after_handlerをセットすることができ、これにより、ブラウザにスタックトレースを表示する前に、アプリケーション固有のエラーハンドリングを起動させられる。
static
Sinatraが静的ファイルの提供を取り扱うかの設定。
その取り扱いができるサーバを使う場合は無効。
無効化でパフォーマンスは改善する
クラッシックスタイルではデフォルトで有効。モジュラースタイルでは無効。
static_cache_control
Sinatraが静的ファイルを提供するときこれをセットして、レスポンスにCache-Controlヘッダを追加するようにする。cache_controlヘルパーを使うこと。デフォルトは無効。
複数の値をセットするときは明示的に配列を使う: set :static_cache_control, [:public, :max_age => 300]
threaded
trueに設定されているときは、Thinにリクエストを処理するためにEventMachine.deferを使うことを通知する。
views
ビューディレクトリのパス。設定されていない場合、app_file設定から推定する。
x_cascade
マッチするルーティングが無い場合に、X-Cascadeヘッダをセットするか否かの設定。デフォルトはtrue
## 環境設定(Environments) 3種類の既定環境、`"development"`、`"production"`および`"test"`があります。環境は、`RACK_ENV`環境変数を通して設定できます。デフォルト値は、`"development"`です。`"development"`環境において、すべてのテンプレートは、各リクエスト間で再ロードされ、そして、特別の`not_found`および`error`ハンドラがブラウザにスタックトレースを表示します。`"production"`および`"test"`環境においては、テンプレートはデフォルトでキャッシュされます。 異なる環境を走らせるには、`RACK_ENV`環境変数を設定します。 ``` shell RACK_ENV=production ruby my_app.rb ``` 既定メソッド、`development?`、`test?`および`production?`を、現在の環境設定を確認するために使えます。 ``` ruby get '/' do if settings.development? "development!" else "not development!" end end ``` ## エラーハンドリング(Error Handling) エラーハンドラはルーティングおよびbeforeフィルタと同じコンテキストで実行されます。すなわちこれは、`haml`、`erb`、`halt`といった便利なものが全て使えることを意味します。 ### 未検出(Not Found) `Sinatra::NotFound`例外が発生したとき、またはレスポンスのステータスコードが404のときに、`not_found`ハンドラが発動します。 ``` ruby not_found do 'ファイルが存在しません' end ``` ### エラー(Error) `error`ハンドラはルーティングブロックまたはフィルタ内で例外が発生したときはいつでも発動します。例外オブジェクトはRack変数`sinatra.error`から取得できます。 ``` ruby error do 'エラーが発生しました。 - ' + env['sinatra.error'].message end ``` エラーをカスタマイズする場合は、 ``` ruby error MyCustomError do 'エラーメッセージ...' + env['sinatra.error'].message end ``` と書いておいて、下記のように呼び出します。 ``` ruby get '/' do raise MyCustomError, '何かがまずかったようです' end ``` そうするとこうなります。 ``` エラーメッセージ... 何かがまずかったようです ``` あるいは、ステータスコードに対応するエラーハンドラを設定することもできます。 ``` ruby error 403 do 'Access forbidden' end get '/secret' do 403 end ``` 範囲指定もできます。 ``` ruby error 400..510 do 'Boom' end ``` Sinatraを開発環境の下で実行している場合は、特別な`not_found`および`error`ハンドラが導入され、これは親切なスタックトレースと追加のデバッギング情報をブラウザに表示します。 ## Rackミドルウェア(Rack Middleware) SinatraはRuby製Webフレームワークのミニマルな標準的インタフェースである[Rack](http://rack.github.io/)上に構築されています。アプリケーションデベロッパーにとってRackにおける最も興味深い機能は、「ミドルウェア(middleware)」をサポートしていることであり、これは、サーバとアプリケーションとの間に置かれ、HTTPリクエスト/レスポンスを監視および/または操作することで、各種の汎用的機能を提供するコンポーネントです。 Sinatraはトップレベルの`use`メソッドを通して、Rackミドルウェアパイプラインの構築を楽にします。 ``` ruby require 'sinatra' require 'my_custom_middleware' use Rack::Lint use MyCustomMiddleware get '/hello' do 'Hello World' end ``` `use`の文法は、[Rack::Builder](http://rubydoc.info/github/rack/rack/master/Rack/Builder)DSLで定義されているそれ(rackupファイルで最もよく使われる)と同じです。例えば `use`メソッドは複数の引数、そしてブロックも取ることができます。 ``` ruby use Rack::Auth::Basic do |username, password| username == 'admin' && password == 'secret' end ``` Rackは、ロギング、デバッギング、URLルーティング、認証、セッション管理など、多様な標準的ミドルウェアを共に配布されています。Sinatraはその多くのコンポーネントを自動で使うよう基本設定されているため、通常、それらを`use`で明示的に指定する必要はありません。 便利なミドルウェアを以下で見つけられます。 [rack](https://github.com/rack/rack/tree/master/lib/rack)、 [rack-contrib](https://github.com/rack/rack-contrib#readm)、 または[Rack wiki](https://github.com/rack/rack/wiki/List-of-Middleware)。 ## テスト(Testing) SinatraでのテストはRackベースのテストライブラリまたはフレームワークを使って書くことができます。[Rack::Test](http://rdoc.info/github/brynary/rack-test/master/frames)をお薦めします。 ``` ruby require 'my_sinatra_app' require 'minitest/autorun' require 'rack/test' class MyAppTest < Minitest::Test 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を使う場合は、上記`Sinatra::Application`をアプリケーションのクラス名に置き換えてください。 ## Sinatra::Base - ミドルウェア、ライブラリおよびモジュラーアプリ 軽量なアプリケーションであれば、トップレベルでアプリケーションを定義していくことはうまくいきますが、再利用性可能なコンポーネント、例えばRackミドルウェア、RailsのMetal、サーバコンポーネントを含むシンプルなライブラリ、あるいはSinatraの拡張プログラムを構築するような場合、これは無視できない欠点を持つものとなります。トップレベルは、軽量なアプリケーションのスタイルにおける設定(例えば、単一のアプリケーションファイル、`./public`および`./views`ディレクトリ、ロギング、例外詳細ページなど)を仮定しています。そこで`Sinatra::Base`の出番です。 ``` ruby require 'sinatra/base' class MyApp < Sinatra::Base set :sessions, true set :foo, 'bar' get '/' do 'Hello world!' end end ``` `Sinatra::Base`のサブクラスで利用できるメソッドは、トップレベルDSLで利用できるものと全く同じです。ほとんどのトップレベルで記述されたアプリは、以下の2点を修正することで`Sinatra::Base`コンポーネントに変えることができます。 * `sinatra`の代わりに`sinatra/base`を読み込む (そうしない場合、SinatraのDSLメソッドの全てがmainの名前空間にインポートされます) * ルーティング、エラーハンドラ、フィルタ、オプションを`Sinatra::Base`のサブクラスに書く `Sinatra::Base`はまっさらです。ビルトインサーバを含む、ほとんどのオプションがデフォルトで無効になっています。利用可能なオプションとその挙動の詳細については[Configuring Settings](http://sinatra.github.com/configuration.html)(英語)をご覧下さい。 もしもクラシックスタイルと同じような挙動のアプリケーションをトップレベルで定義させる必要があれば、`Sinatra::Application`をサブクラス化させてください。 ```ruby require "sinatra/base" class MyApp < Sinatra::Application get "/" do 'Hello world!' end end ``` ### モジュラースタイル vs クラッシックスタイル 一般的認識と違って、クラッシックスタイルを使うことに問題はなにもありません。それがそのアプリケーションに合っているのであれば、モジュラーアプリケーションに移行する必要はありません。 モジュラースタイルを使わずにクラッシックスタイルを使った場合の一番の不利な点は、Rubyプロセスごとにただ一つのSinatraアプリケーションしか持てない点です。複数が必要な場合はモジュラースタイルに移行してください。モジュラースタイルとクラッシックスタイルを混合できないということはありません。 一方のスタイルから他方へ移行する場合、デフォルト設定がわずかに異なる点に注意が必要です。
設定 クラッシック モジュラー モジュラー
app_file sinatraを読み込むファイル Sinatra::Baseをサブクラス化したファイル Sinatra::Applicationをサブクラス化したファイル
run $0 == app_file false false
logging true false true
method_override true false true
inline_templates true false true
static true false true
### モジュラーアプリケーションの提供 モジュラーアプリケーションを開始、つまり`run!`を使って開始させる二種類のやり方があります。 ``` ruby # my_app.rb require 'sinatra/base' class MyApp < Sinatra::Base # ... アプリケーションのコードを書く ... # Rubyファイルが直接実行されたらサーバを立ち上げる run! if app_file == $0 end ``` として、次のように起動するか、 ``` shell ruby my_app.rb ``` または、Rackハンドラを使えるようにする`config.ru`ファイルを書いて、 ``` ruby # config.ru (rackupで起動) require './my_app' run MyApp ``` 起動します。 ``` shell rackup -p 4567 ``` ### config.ruを用いたクラッシックスタイルアプリケーションの使用 アプリケーションファイルと、 ``` ruby # app.rb require 'sinatra' get '/' do 'Hello world!' end ``` 対応する`config.ru`を書きます。 ``` ruby require './app' run Sinatra::Application ``` ### config.ruはいつ使うのか? `config.ru`ファイルは、以下の場合に適しています。 * 異なるRackハンドラ(Passenger, Unicorn, Herokuなど)でデプロイしたいとき * `Sinatra::Base`の複数のサブクラスを使いたいとき * Sinatraをミドルウェアとして利用し、エンドポイントとしては利用しないとき **モジュラースタイルに移行したという理由だけで、`config.ru`に移行する必要はなく、`config.ru`で起動するためにモジュラースタイルを使う必要はありません。** ### Sinatraのミドルウェアとしての利用 Sinatraは他のRackミドルウェアを利用することができるだけでなく、 全てのSinatraアプリケーションは、それ自体ミドルウェアとして別のRackエンドポイントの前に追加することが可能です。 このエンドポイントには、別のSinatraアプリケーションまたは他のRackベースのアプリケーション(Rails/Ramaze/Camping/…)が用いられるでしょう。 ``` ruby require 'sinatra/base' class LoginScreen < Sinatra::Base enable :sessions get('/login') { haml :login } post('/login') do if params['name'] = 'admin' and params['password'] = 'admin' session['user_name'] = params['name'] else redirect '/login' end end end class MyApp < Sinatra::Base # ミドルウェアはbeforeフィルタの前に実行される use LoginScreen before do unless session['user_name'] halt "アクセスは拒否されました。ログインしてください。" end end get('/') { "Hello #{session['user_name']}." } end ``` ### 動的なアプリケーションの生成 新しいアプリケーションを実行時に、定数に割り当てることなく生成したくなる場合があるでしょう。`Sinatra.new`を使えばそれができます。 ``` ruby require 'sinatra/base' my_app = Sinatra.new { get('/') { "hi" } } my_app.run! ``` これは省略できる引数として、それが継承するアプリケーションを取ります。 ```ruby # config.ru (rackupで起動) require 'sinatra/base' controller = Sinatra.new do enable :logging helpers MyHelpers end map('/a') do run Sinatra.new(controller) { get('/') { 'a' } } end map('/b') do run Sinatra.new(controller) { get('/') { 'b' } } end ``` これは特にSinatraのextensionをテストするときや、Sinatraを自身のライブラリで利用する場合に役立ちます。 これはまた、Sinatraをミドルウェアとして利用することを極めて簡単にします。 ``` ruby require 'sinatra/base' use Sinatra do get('/') { ... } end run RailsProject::Application ``` ## スコープとバインディング(Scopes and Binding) 現在のスコープはどのメソッドや変数が利用可能かを決定します。 ### アプリケーション/クラスのスコープ 全てのSinatraアプリケーションはSinatra::Baseのサブクラスに相当します。 もしトップレベルDSLを利用しているならば(`require 'sinatra'`)このクラスはSinatra::Applicationであり、 そうでなければ、あなたが明示的に作成したサブクラスです。 クラスレベルでは`get`や`before`のようなメソッドを持っています。 しかし`request`や`session`オブジェクトには、全てのリクエストに対する単一のアプリケーションクラスがあるだけなので、アクセスできません。 `set`によって作られたオプションはクラスレベルのメソッドです。 ``` ruby class MyApp < Sinatra::Base # アプリケーションスコープの中だよ! set :foo, 42 foo # => 42 get '/foo' do # もうアプリケーションスコープの中にいないよ! end end ``` 次の場所ではアプリケーションスコープバインディングを持ちます。 * アプリケーションクラス本体 * 拡張によって定義されたメソッド * `helpers`に渡されたブロック * `set`の値として使われるProcまたはブロック * `Sinatra.new`に渡されたブロック このスコープオブジェクト(クラス)は次のように利用できます。 * configureブロックに渡されたオブジェクト経由(`configure { |c| ... }`) * リクエストスコープの中での`settings` ### リクエスト/インスタンスのスコープ やってくるリクエストごとに、あなたのアプリケーションクラスの新しいインスタンスが作成され、全てのハンドラブロックがそのスコープで実行されます。 このスコープの内側からは`request`や`session`オブジェクトにアクセスすることができ、`erb`や`haml`のようなレンダリングメソッドを呼び出すことができます。 リクエストスコープの内側からは、`settings`ヘルパーによってアプリケーションスコープにアクセスすることができます。 ``` ruby class MyApp < Sinatra::Base # アプリケーションスコープの中だよ! get '/define_route/:name' do # '/define_route/:name'のためのリクエストスコープ @value = 42 settings.get("/#{params['name']}") do # "/#{params['name']}"のためのリクエストスコープ @value # => nil (not the same request) end "ルーティングが定義された!" end end ``` 次の場所ではリクエストスコープバインディングを持ちます。 * get/head/post/put/delete/options/patch/link/unlink ブロック * before/after フィルタ * helper メソッド * テンプレート/ビュー ### デリゲートスコープ デリゲートスコープは、単にクラススコープにメソッドを転送します。 しかしながら、クラスのバインディングを持っていないため、クラススコープと全く同じふるまいをするわけではありません。 委譲すると明示的に示されたメソッドのみが利用可能であり、またクラススコープと変数/状態を共有することはできません(注: 異なった`self`を持っています)。 `Sinatra::Delegator.delegate :method_name`を呼び出すことによってデリゲートするメソッドを明示的に追加することができます。 次の場所ではデリゲートスコープを持ちます。 * もし`require "sinatra"`しているならば、トップレベルバインディング * `Sinatra::Delegator` mixinでextendされたオブジェクト コードをご覧ください: ここでは [Sinatra::Delegator mixin](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633)は[mainオブジェクトにextendされています](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30)。 ## コマンドライン Sinatraアプリケーションは直接実行できます。 ``` shell ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER] ``` オプション: ``` -h # ヘルプ -p # ポート指定(デフォルトは4567) -o # ホスト指定(デフォルトは0.0.0.0) -e # 環境を指定 (デフォルトはdevelopment) -s # rackserver/handlerを指定 (デフォルトはthin) -x # mutex lockを付ける (デフォルトはoff) ``` ### マルチスレッド _この[StackOverflow][so-answer]でのKonstantinによる回答を言い換えています。_ Sinatraでは同時実行モデルを負わせることはできませんが、根本的な部分であるThinやPuma、WebrickのようなRackハンドラ(サーバー)部分に委ねることができます。 Sinatra自身はスレッドセーフであり、もしRackハンドラが同時実行モデルのスレッドを使用していても問題はありません。 つまり、これはサーバーを起動させる時、特定のRackハンドラに対して正しい起動処理を特定することが出来ます。 この例はThinサーバーをマルチスレッドで起動する方法のデモです。 ```ruby # app.rb require 'sinatra/base' class App < Sinatra::Base get '/' do "Hello, World" end end App.run! ``` サーバーを開始するコマンドです。 ``` thin --threaded start ``` [so-answer]: http://stackoverflow.com/questions/6278817/is-sinatra-multi-threaded/6282999#6282999) ## 必要環境 次のRubyバージョンが公式にサポートされています。
Ruby 1.8.7
1.8.7は完全にサポートされていますが、特にそれでなければならないという理由がないのであれば、アップグレードまたはJRubyまたはRubiniusへの移行を薦めます。1.8.7のサポートがSinatra 2.0の前に終わることはないでしょう。Ruby 1.8.6はサポート対象外です。
Ruby 1.9.2
1.9.2は完全にサポートされています。1.9.2p0は、Sinatraを起動したときにセグメントフォルトを引き起こすことが分かっているので、使わないでください。公式なサポートは、少なくともSinatra 1.5のリリースまでは続きます。
Ruby 1.9.3
1.9.3は完全にサポート、そして推奨されています。以前のバージョンからの1.9.3への移行は全セッションを無効にする点、覚えておいてください。
Ruby 2.0.0
2.0.0は完全にサポート、そして推奨されています。現在、その公式サポートを終了する計画はありません。
Rubinius
Rubiniusは公式にサポートされています(Rubinius >= 2.x)。 gem install pumaすることが推奨されています。
JRuby
JRubyの最新安定版が公式にサポートされています。JRubyでC拡張を使うことは推奨されていません。 gem install trinidadすることが推奨されています。
開発チームは常に最新となるRubyバージョンに注視しています。 次のRuby実装は公式にはサポートされていませんが、Sinatraが起動すると報告されています。 * JRubyとRubiniusの古いバージョン * Ruby Enterprise Edition * MacRuby, Maglev, IronRuby * Ruby 1.9.0と1.9.1 (これらの使用はお薦めしません) 公式サポートをしないという意味は、問題がそこだけで起こり、サポートされているプラットフォーム上では起きない場合に、開発チームはそれはこちら側の問題ではないとみなすということです。 開発チームはまた、ruby-head(最新となる2.1.0)に対しCIを実行していますが、それが一貫して動くようになるまで何も保証しません。2.1.0が完全にサポートされればその限りではありません。 Sinatraは、利用するRuby実装がサポートしているオペレーティングシステム上なら動作するはずです。 MacRubyを使う場合は、`gem install control_tower`してください。 Sinatraは現在、Cardinal、SmallRuby、BlueRubyまたは1.8.7以前のバージョンのRuby上では動作しません。 ## 最新開発版 Sinatraの最新開発版のコードを使いたい場合は、マスターブランチに対してアプリケーションを走らせて構いません。ある程度安定しています。また、適宜プレリリース版gemをpushしているので、 ``` shell gem install sinatra --pre ``` すれば、最新の機能のいくつかを利用できます。 ### Bundlerを使う場合 最新のSinatraでアプリケーションを動作させたい場合には、[Bundler](http://gembundler.com/)を使うのがお薦めのやり方です。 まず、Bundlerがなければそれをインストールします。 ``` shell gem install bundler ``` そして、プロジェクトのディレクトリで、`Gemfile`を作ります。 ```ruby source 'https://rubygems.org' gem 'sinatra', :github => "sinatra/sinatra" # 他の依存ライブラリ gem 'haml' # Hamlを使う場合 gem 'activerecord', '~> 3.0' # ActiveRecord 3.xが必要かもしれません ``` ノート: `Gemfile`にアプリケーションの依存ライブラリのすべてを並べる必要があります。しかし、Sinatraが直接依存するもの(RackおよびTile)はBundlerによって自動的に取り込まれ、追加されます。 これで、以下のようにしてアプリケーションを起動することができます。 ``` shell bundle exec ruby myapp.rb ``` ### 直接組み込む場合 ローカルにクローンを作って、`sinatra/lib`ディレクトリを`$LOAD_PATH`に追加してアプリケーションを起動します。 ``` shell cd myapp git clone git://github.com/sinatra/sinatra.git ruby -I sinatra/lib myapp.rb ``` 追ってSinatraのソースを更新する方法。 ``` shell cd myapp/sinatra git pull ``` ### グローバル環境にインストールする場合 Sinatraのgemを自身でビルドすることもできます。 ``` shell git clone git://github.com/sinatra/sinatra.git cd sinatra rake sinatra.gemspec rake install ``` gemをルートとしてインストールする場合は、最後のステップはこうなります。 ``` shell sudo rake install ``` ## バージョニング(Versioning) Sinatraは、[Semantic Versioning](http://semver.org/)におけるSemVerおよびSemVerTagの両方に準拠しています。 ## 参考文献 * [プロジェクトサイト](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/topics) * http://freenode.net上のIRC: [#sinatra](irc://chat.freenode.net/#sinatra) * [Sinatra Book](https://github.com/sinatra/sinatra-book/) クックブック、チュートリアル * [Sinatra Recipes](http://recipes.sinatrarb.com/) コミュニティによるレシピ集 * http://rubydoc.info上のAPIドキュメント: [最新版(latest release)用](http://rubydoc.info/gems/sinatra)または[現在のHEAD用](http://rubydoc.info/github/sinatra/sinatra) * [CIサーバ](http://travis-ci.org/sinatra/sinatra) * [Greenbear Laboratory Rack日本語マニュアル](http://route477.net/w/RackReferenceJa.html)