diff --git a/README.jp.md b/README.jp.md index 356565ad..60f0ca60 100644 --- a/README.jp.md +++ b/README.jp.md @@ -3,27 +3,122 @@ *注) 本文書は英語から翻訳したものであり、その内容が最新でない場合もあります。最新の情報はオリジナルの英語版を参照して下さい。* -[DSL](http://ja.wikipedia.org/wiki/ドメイン固有言語)です。 +Sinatraは最小の労力でRubyによるWebアプリケーションを手早く作るための[DSL](http://ja.wikipedia.org/wiki/ドメイン固有言語)です。 ``` ruby # myapp.rb require 'sinatra' + get '/' do 'Hello world!' end ``` -gemをインストールして動かしてみる。 +gemをインストールし、 - gem install sinatra - ruby myapp.rb +``` shell +gem install sinatra +``` -[localhost:4567](http://localhost:4567) を見る。 +次のように実行します。 -## ルート +``` shell +ruby myapp.rb +``` -Sinatraでは、ルートはHTTPメソッドとURLマッチングパターンがペアになっています。 -ルートはブロックに結び付けられています。 +[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-テンプレート) + * [Radius テンプレート](#radius-テンプレート) + * [Markaby テンプレート](#markaby-テンプレート) + * [RABL テンプレート](#rabl-テンプレート) + * [Slim テンプレート](#slim-テンプレート) + * [Creole テンプレート](#creole-テンプレート) + * [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 @@ -59,10 +154,10 @@ unlink '/' do end ``` -ルートは定義された順番にマッチします。 -リクエストに最初にマッチしたルートが呼び出されます。 +ルーティングは定義された順番にマッチします。 +リクエストに最初にマッチしたルーティングが呼び出されます。 -ルートのパターンは名前付きパラメータを含むことができ、 +ルーティングのパターンは名前付きパラメータを含むことができ、 `params`ハッシュで取得できます。 ``` ruby @@ -84,7 +179,7 @@ get '/hello/:name' do |n| end ``` -ルートパターンはsplat(またはワイルドカード)を含むこともでき、 +ルーティングパターンはアスタリスク(すなわちワイルドカード)を含むこともでき、 `params[:splat]` で取得できます。 ``` ruby @@ -131,28 +226,28 @@ get '/posts.?:format?' do end ``` -ところで、ディレクトリトラバーサル保護機能を無効にしないと(下記参照)、 -ルートにマッチする前にリクエストパスが修正される可能性があります。 +ところで、ディレクトリトラバーサル攻撃防御設定を無効にしないと(下記参照)、 +ルーティングにマッチする前にリクエストパスが修正される可能性があります。 -### 条件 +### 条件(Conditions) -ルートにはユーザエージェントのようなさまざまな条件を含めることができます。 +ルーティングにはユーザエージェントのようなさまざまな条件を含めることができます。 ``` ruby get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do - "You're using Songbird version #{params[:agent][0]}" + "Songbirdのバージョン #{params[:agent][0]}を使ってます。" end get '/foo' do - # Matches non-songbird browsers + # Songbird以外のブラウザにマッチ end ``` -ほかに`host_name`と`provides`条件が利用可能です: +ほかに`host_name`と`provides`条件が利用可能です。 ``` ruby get '/', :host_name => /^admin\./ do - "Admin Area, Access denied!" + "Adminエリアです。アクセスを拒否します!" end get '/', :provides => 'html' do @@ -164,28 +259,27 @@ get '/', :provides => ['rss', 'atom', 'xml'] do end ``` -独自の条件を定義することも簡単にできます: +独自の条件を定義することも簡単にできます。 ``` ruby set(:probability) { |value| condition { rand <= value } } get '/win_a_car', :probability => 0.1 do - "You won!" + "あなたの勝ちです!" end get '/win_a_car' do - "Sorry, you lost." + "残念、あなたの負けです。" end ``` -### 戻り値 +### 戻り値(Return Values) -ルートブロックの戻り値は、HTTPクライアントまたはRackスタックでの次のミドルウェアに渡されるレスポンスボディを決定します。 +ルーティングブロックの戻り値は、HTTPクライアントまたはRackスタックでの次のミドルウェアに渡されるレスポンスボディを決定します。 これは大抵の場合、上の例のように文字列ですが、それ以外の値も使用することができます。 -Rackレスポンス、Rackボディオブジェクト、HTTPステータスコードのいずれかとして -妥当なオブジェクトであればどのようなオブジェクトでも返すことができます: +Rackレスポンス、Rackボディオブジェクト、HTTPステータスコードのいずれかとして妥当なオブジェクトであればどのようなオブジェクトでも返すことができます。 - 3要素の配列: `[ステータス(Fixnum), ヘッダ(Hash), レスポンスボディ(#eachに応答する)]` @@ -197,7 +291,7 @@ Rackレスポンス、Rackボディオブジェクト、HTTPステータスコ - ステータスコードを表現するFixnum -そのように、例えばストリーミングの例を簡単に実装することができます: +これにより、例えばストリーミングの例を簡単に実装することができます。 ``` ruby class Stream @@ -209,7 +303,54 @@ 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`オプションを指定することで別の場所を指定することができます。 @@ -218,10 +359,106 @@ get('/') { Stream.new } set :public_folder, File.dirname(__FILE__) + '/static' ``` -注意: この静的ファイル用のディレクトリ名はURL中に含まれません。 +ノート: この静的ファイル用のディレクトリ名はURL中に含まれません。 例えば、`./public/css/style.css`は`http://example.com/css/style.css`でアクセスできます。 -## ビュー / テンプレート +## ビュー / テンプレート(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`ディレクトリ下に配置されています。 他のディレクトリを使用する場合の例: @@ -232,374 +469,9 @@ set :views, File.dirname(__FILE__) + '/templates' テンプレートはシンボルを使用して参照させることを覚えておいて下さい。 サブデレクトリでもこの場合は`:'subdir/template'`のようにします。 -レンダリングメソッドは文字列が渡されると、そのまま文字列を出力します。 +レンダリングメソッドは文字列が渡されると、それをそのまま文字列として出力するので、シンボルを使ってください。 -### Haml テンプレート - -hamlを使うにはhamlライブラリが必要です: - -``` ruby -# hamlを読み込みます -require 'haml' - -get '/' do - haml :index -end -``` - -`./views/index.haml`を表示します。 - -[Haml’s -options](http://haml.info/docs/yardoc/file.HAML_REFERENCE.html#options) -はSinatraの設定でグローバルに設定することができます。 [Options and -Configurations](http://www.sinatrarb.com/configuration.html), -を参照してそれぞれ設定を上書きして下さい。 - -``` ruby -set :haml, {:format => :html5 } # デフォルトのフォーマットは:xhtml - -get '/' do - haml :index, :haml_options => {:format => :html4 } # 上書き -end -``` - -### Erb テンプレート - -``` ruby -# erbを読み込みます -require 'erb' - -get '/' do - erb :index -end -``` - -`./views/index.erb`を表示します。 - -### Erubis - -erubisテンプレートを表示するには、erubisライブラリが必要です: - -``` ruby -# erubisを読み込みます -require 'erubis' - -get '/' do - erubis :index -end -``` - -`./views/index.erubis`を表示します。 - -### Builder テンプレート - -builderを使うにはbuilderライブラリが必要です: - - # builderを読み込みます - require 'builder' - - get '/' do - builder :index - end - -`./views/index.builder`を表示します。 - -### Nokogiri テンプレート - -Nokogiriを使うにはNokogiriライブラリが必要です: - -``` -# Nokogiriを読み込みます -require 'nokogiri' - -get '/' do - nokogiri :index -end -``` - -`./views/index.nokogiri`を表示します。 - -### Sass テンプレート - -Sassテンプレートを使うにはsassライブラリが必要です: - -``` ruby -# 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), -を参照してそれぞれ設定を上書きして下さい。 - -``` ruby -set :sass, {:style => :compact } # デフォルトのSass styleは :nested - -get '/stylesheet.css' do - sass :stylesheet, :sass_options => {:style => :expanded } # 上書き -end -``` - -### Scss テンプレート - -Scssテンプレートを使うにはsassライブラリが必要です: - -``` ruby -# hamlかsassを読み込みます -require 'sass' - -get '/stylesheet.css' do - scss :stylesheet -end -``` - -`./views/stylesheet.scss`を表示します。 - -[Sass’ -options](http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options) -はSinatraの設定でグローバルに設定することができます。 see [Options and -Configurations](http://www.sinatrarb.com/configuration.html), -を参照してそれぞれ設定を上書きして下さい。 - -``` ruby -set :scss, :style => :compact # デフォルトのScss styleは:nested - -get '/stylesheet.css' do - scss :stylesheet, :style => :expanded # 上書き -end -``` - -### Less テンプレート - -Lessテンプレートを使うにはlessライブラリが必要です: - -``` ruby -# lessを読み込みます -require 'less' - -get '/stylesheet.css' do - less :stylesheet -end -``` - -`./views/stylesheet.less`を表示します。 - -### Liquid テンプレート - -Liquidテンプレートを使うにはliquidライブラリが必要です: - -``` ruby -# liquidを読み込みます -require 'liquid' - -get '/' do - liquid :index -end -``` - -`./views/index.liquid`を表示します。 - -LiquidテンプレートからRubyのメソッド(`yield`を除く)を呼び出すことができないため、 -ほぼ全ての場合にlocalsを指定する必要があるでしょう: - -``` ruby -liquid :index, :locals => { :key => 'value' } -``` - -### Markdown テンプレート - -Markdownテンプレートを使うにはrdiscountライブラリが必要です: - -``` ruby -# rdiscountを読み込みます -require "rdiscount" - -get '/' do - markdown :index -end -``` - -`./views/index.markdown`を表示します。(`md`と`mkd`も妥当な拡張子です) - -markdownからメソッドを呼び出すことも、localsに変数を渡すこともできません。 -それゆえ、他のレンダリングエンジンとの組み合わせで使うのが普通です: - -``` ruby -erb :overview, :locals => { :text => markdown(:introduction) } -``` - -他のテンプレートからmarkdownメソッドを呼び出してもよいことに注意してください: - -``` haml -%h1 Hello From Haml! -%p= markdown(:greetings) -``` - -### Textile テンプレート - -Textileテンプレートを使うにはRedClothライブラリが必要です: - -``` ruby -# redclothを読み込みます -require "redcloth" - -get '/' do - textile :index -end -``` - -`./views/index.textile`を表示します。 - -textileからメソッドを呼び出すことも、localsに変数を渡すこともできません。 -それゆえ、他のレンダリングエンジンとの組み合わせで使うのが普通です: - -``` ruby -erb :overview, :locals => { :text => textile(:introduction) } -``` - -他のテンプレートからtextileメソッドを呼び出してもよいことに注意してください: - -``` haml -%h1 Hello From Haml! -%p= textile(:greetings) -``` - -### RDoc テンプレート - -RDocテンプレートを使うにはRDocライブラリが必要です: - -``` ruby -# rdoc/markup/to_htmlを読み込みます -require "rdoc" -require "rdoc/markup/to_html" - -get '/' do - rdoc :index -end -``` - -`./views/index.rdoc`を表示します。 - -rdocからメソッドを呼び出すことも、localsに変数を渡すこともできません。 -それゆえ、他のレンダリングエンジンとの組み合わせで使うのが普通です: - -``` ruby -erb :overview, :locals => { :text => rdoc(:introduction) } -``` - -他のテンプレートからrdocメソッドを呼び出してもよいことに注意してください: - -``` haml -%h1 Hello From Haml! -%p= rdoc(:greetings) -``` - -### Radius テンプレート - -Radiusテンプレートを使うにはradiusライブラリが必要です: - -``` ruby -# radiusを読み込みます -require 'radius' - -get '/' do - radius :index -end -``` - -`./views/index.radius`を表示します。 - -RadiusテンプレートからRubyのメソッド(`yield`を除く)を呼び出すことができないため、 -ほぼ全ての場合にlocalsを指定する必要があるでしょう: - -``` ruby -radius :index, :locals => { :key => 'value' } -``` - -### Markaby テンプレート - -Markabyテンプレートを使うにはmarkabyライブラリが必要です: - -``` ruby -# markabyを読み込みます -require 'markaby' - -get '/' do - markaby :index -end -``` - -`./views/index.mab`を表示します。 - -### RABL テンプレート - -RABLテンプレートを使うにはrablライブラリが必要です: - -``` ruby -# rablを読み込みます -require 'rabl' - -get '/' do - rabl :index -end -``` - -`./views/index.rabl`を表示します。 - -### Slim テンプレート - -Slimテンプレートを使うにはslimライブラリが必要です: - -``` ruby -# slimを読み込みます -require 'slim' - -get '/' do - slim :index -end -``` - -`./views/index.slim`を表示します。 - -### Creole テンプレート - -Creoleテンプレートを使うにはcreoleライブラリが必要です: - -``` ruby -# creoleを読み込みます -require 'creole' - -get '/' do - creole :index -end -``` - -`./views/index.creole`を表示します。 - -### CoffeeScript テンプレート - -CoffeeScriptテンプレートを表示するにはcoffee-scriptライブラリと\`coffee\`バイナリが必要です: - -``` ruby -# coffee-scriptを読み込みます -require 'coffee-script' - -get '/application.js' do - coffee :application -end -``` - -`./views/application.coffee`を表示します。 - -### インラインテンプレート +### リテラルテンプレート(Literal Templates) ``` ruby get '/' do @@ -607,13 +479,506 @@ get '/' do 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`オプションを渡すことでテンプレートのものとは異なるレンダリングエンジンをレイアウトのために使うことができます。 + +``` ruby +erb :overview, :locals => { :text => textile(:introduction) } +``` + +#### 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`オプションを渡すことでテンプレートのものとは異なるレンダリングエンジンをレイアウトのために使うことができます。 + +#### 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`オプションを渡すことでテンプレートのものとは異なるレンダリングエンジンをレイアウトのために使うことができます。 + +#### 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 @@ -622,18 +987,54 @@ get '/:id' do end ``` -ローカル変数を明示的に定義することもできます。 +また、ローカル変数のハッシュで明示的に指定することもできます。 ``` ruby get '/:id' do foo = Foo.find(params[:id]) - haml '%h1= foo.name', :locals => { :foo => foo } + 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) テンプレートはソースファイルの最後で定義することもできます。 @@ -655,14 +1056,11 @@ __END__ %div.title Hello world!!!!! ``` -注意: -sinatraをrequireするファイル内で定義されたファイル内テンプレートは自動的に読み込まれます。 -他のファイルで定義されているテンプレートを使うには -`enable :inline_templates`を明示的に呼んでください。 +ノート: Sinatraをrequireするソースファイル内で定義されたインラインテンプレートは自動的に読み込まれます。他のソースファイル内にインラインテンプレートがある場合には`enable :inline_templates`を明示的に呼んでください。 -### 名前付きテンプレート +### 名前付きテンプレート(Named Templates) -テンプレートはトップレベルの`template`メソッドで定義することができます。 +テンプレートはトップレベルの`template`メソッドで定義することもできます。 ``` ruby template :layout do @@ -678,8 +1076,7 @@ get '/' do end ``` -「layout」というテンプレートが存在する場合、そのテンプレートファイルは他のテンプレートが -表示される度に使用されます。`:layout => false`することでlayoutsを無効にできます。 +「layout」というテンプレートが存在する場合、そのテンプレートファイルは他のテンプレートがレンダリングされる度に使用されます。`:layout => false`で個別に、または`set :haml, :layout => false`でデフォルトとして、レイアウトを無効にすることができます。 ``` ruby get '/' do @@ -687,28 +1084,35 @@ get '/' do end ``` -## ヘルパー +### ファイル拡張子の関連付け -トップレベルの`helpers`を使用してルートハンドラやテンプレートで使うヘルパメソッドを -定義できます。 +任意のテンプレートエンジンにファイル拡張子を関連付ける場合は、`Tilt.register`を使います。例えば、Textileテンプレートに`tt`というファイル拡張子を使いたい場合は、以下のようにします。 ``` ruby +Tilt.register :tt, Tilt[:textile] +``` + +### オリジナルテンプレートエンジンの追加 + +まず、Tiltでそのエンジンを登録し、次にレンダリングメソッドを作ります。 + +``` ruby +Tilt.register :myat, MyAwesomeTemplateEngine + helpers do - def bar(name) - "#{name}bar" - end + def myat(*args) render(:myat, *args) end end -get '/:name' do - bar(params[:name]) +get '/' do + myat :index end ``` -## フィルタ +これは、`./views/index.myat`をレンダリングします。Tiltについての詳細は、https://github.com/rtomayko/tilt を参照してください。 -beforeフィルタはリクエストされたコンテキストを実行する前に評価され、 -リクエストとレスポンスを変更することができます。フィルタ内でセットされた -インスタンス変数はルーティングとテンプレートで使用できます。 +## フィルタ(Filters) + +beforeフィルタは、リクエストのルーティングと同じコンテキストで各リクエストの前に評価され、それによってリクエストとレスポンスを変更可能にします。フィルタ内でセットされたインスタンス変数はルーティングとテンプレートからアクセスすることができます。 ``` ruby before do @@ -722,10 +1126,7 @@ get '/foo/*' do end ``` -afterフィルタは同じコンテキストにあるリクエストの後に評価され、 -同じくリクエストとレスポンスを変更することができます。 -beforeフィルタとルートで設定されたインスタンス変数は、 -afterフィルタからアクセスすることができます: +afterフィルタは、リクエストのルーティングと同じコンテキストで各リクエストの後に評価され、それによってこれもリクエストとレスポンスを変更可能にします。beforeフィルタとルーティング内でセットされたインスタンス変数はafterフィルタからアクセスすることができます。 ``` ruby after do @@ -733,8 +1134,9 @@ after do end ``` -フィルタにはオプションとしてパターンを渡すことができ、 -この場合はリクエストのパスがパターンにマッチした場合のみフィルタが評価されます: +ノート: `body`メソッドを使わずにルーティングから文字列を返すだけの場合、その内容はafterフィルタでまだ利用できず、その後に生成されることになります。 + +フィルタにはオプションとしてパターンを渡すことができ、この場合はリクエストのパスがパターンにマッチした場合にのみフィルタが評価されるようになります。 ``` ruby before '/protected/*' do @@ -746,41 +1148,142 @@ after '/create/:slug' do |slug| end ``` -## 強制終了 +ルーティング同様、フィルタもまた条件を取ることができます。 -ルートかbeforeフィルタ内で直ちに実行を終了する方法: +``` 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部を指定することもできます … +body部を指定することも、 ``` ruby halt 'ここにbodyを書く' ``` -ステータスとbody部を指定する … +ステータスとbody部を指定することも、 ``` ruby halt 401, '立ち去れ!' ``` -ヘッダを指定: +ヘッダを付けることもできます。 ``` ruby halt 402, {'Content-Type' => 'text/plain'}, 'リベンジ' ``` -## パッシング(Passing) +もちろん、テンプレートを`halt`に結びつけることも可能です。 -ルートは`pass`を使って次のルートに飛ばすことができます: +``` ruby +halt erb(:error) +``` + +### パッシング(Passing) + +ルーティングは`pass`を使って次のルーティングに飛ばすことができます。 ``` ruby get '/guess/:who' do @@ -793,42 +1296,384 @@ get '/guess/*' do end ``` -ルートブロックからすぐに抜け出し、次にマッチするルートを実行します。 -マッチするルートが見当たらない場合は404が返されます。 +ルーティングブロックからすぐに抜け出し、次にマッチするルーティングを実行します。マッチするルーティングが見当たらない場合は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サーバに強く依存します。WEBRickを含むいくつかのサーバは、ストリーミングを全くサポートしません。サーバがストリーミングをサポートしない場合、ボディは`stream`に渡されたブロックの実行が終了した後、一度に全部送られることになります。ストリーミングは、Shotgunを使った場合は全く動作しません。 + +オプション引数が`keep_open`にセットされている場合、ストリームオブジェクト上で`close`は呼ばれず、実行フローの任意の遅れたタイミングでユーザがこれを閉じることを可能にします。これはThinやRainbowsのようなイベント型サーバ上でしか機能しません。他のサーバでは依然ストリームは閉じられます。 + +``` ruby +# ロングポーリング + +set :server, :thin +connections = [] + +get '/subscribe' do + # サーバイベントにおけるクライアントの関心を登録 + stream(:keep_open) { |out| connections << out } + + # 死んでいるコネクションを排除 + connections.reject!(&:closed?) + + # 肯定応答 + "subscribed" +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 +mime_type :foo, 'text/foo' +``` + +これは`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://en.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\`メソッドを通じてリクエストレベル(フィルタ、ルート、エラーハンドラ)からアクセスすることができます: +受信するリクエストオブジェクトは、`request`メソッドを通じてリクエストレベル(フィルタ、ルーティング、エラーハンドラ)からアクセスすることができます。 ``` ruby # アプリケーションが 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ハッシュ + 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`などのオプションは次のように利用することもできます: +`script_name`や`path_info`などのオプションは次のように利用することもできます。 ``` ruby before { request.path_info = "/" } @@ -838,7 +1683,7 @@ get "/" do end ``` -`request.body`はIOまたはStringIOのオブジェクトです: +`request.body`はIOまたはStringIOのオブジェクトです。 ``` ruby post "/api" do @@ -848,17 +1693,124 @@ post "/api" do 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`に設定されている時だけ実行する方法: + +環境設定(`RACK_ENV`環境変数)が`:production`に設定されている時だけ実行する方法: ``` ruby configure :production do @@ -866,7 +1818,7 @@ configure :production do end ``` -環境が`:production`か`:test`の場合に設定する方法: +環境設定が`:production`か`:test`に設定されている時だけ実行する方法: ``` ruby configure :production, :test do @@ -874,15 +1826,220 @@ configure :production, :test do end ``` -## エラーハンドリング +設定したオプションには`settings`からアクセスできます: -エラーハンドラーはルートコンテキストとbeforeフィルタ内で実行します。 -`haml`、`erb`、`halt`などを使うこともできます。 +``` 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_charsets
+
+ Mimeタイプ content_typeヘルパーが自動的にキャラクタセット情報をここに追加する。このオプションは書き換えるのではなく、値を追加するようにすること。 + settings.add_charsets << "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`ハンドラーが発動します。 +`Sinatra::NotFound`例外が発生したとき、またはレスポンスのステータスコードが404のときに、`not_found`ハンドラーが発動します。 ``` ruby not_found do @@ -890,11 +2047,9 @@ not_found do end ``` -### エラー +### エラー(Error) -`error` -ハンドラーはルートブロックかbeforeフィルタ内で例外が発生した時はいつでも発動します。 -例外オブジェクトはRack変数`sinatra.error`から取得できます。 +`error`ハンドラーはルーティングブロックまたはフィルタ内で例外が発生したときはいつでも発動します。例外オブジェクトはRack変数`sinatra.error`から取得できます。 ``` ruby error do @@ -910,7 +2065,7 @@ error MyCustomError do end ``` -と書いておいて,下記のように呼び出します。 +と書いておいて、下記のように呼び出します。 ``` ruby get '/' do @@ -918,13 +2073,13 @@ get '/' do end ``` -そうするとこうなります: +そうするとこうなります。 ``` エラーメッセージ... 何かがまずかったようです ``` -あるいは、ステータスコードに対応するエラーハンドラを設定することもできます: +あるいは、ステータスコードに対応するエラーハンドラを設定することもできます。 ``` ruby error 403 do @@ -936,7 +2091,7 @@ get '/secret' do end ``` -範囲指定もできます: +範囲指定もできます。 ``` ruby error 400..510 do @@ -944,34 +2099,14 @@ error 400..510 do end ``` -開発環境として実行している場合、Sinatraは特別な`not_found`と`error`ハンドラーを -インストールしています。 +Sinatraを開発環境の下で実行している場合は、特別な`not_found`および`error`ハンドラーが導入され、これは親切なスタックトレースと追加のデバッギング情報をブラウザに表示します。 -## MIMEタイプ -`send_file`か静的ファイルを使う時、Sinatraが理解でいないMIMEタイプがある場合があります。 -その時は `mime_type` を使ってファイル拡張子毎に登録して下さい。 +## Rackミドルウェア(Rack Middleware) -``` ruby -mime_type :foo, 'text/foo' -``` +SinatraはRuby製Webフレームワークのミニマルな標準的インタフェースである[Rack](http://rack.rubyforge.org/)上に構築されています。アプリケーションデベロッパーにとってRackにおける最も興味深い機能は、「ミドルウェア(middleware)」をサポートしていることであり、これは、サーバとアプリケーションとの間に置かれ、HTTPリクエスト/レスポンスを監視および/または操作することで、各種の汎用的機能を提供するコンポーネントです。 -これはcontent\_typeヘルパで利用することができます: - -``` ruby -content_type :foo -``` - -## Rackミドルウェア - -[SinatraはRack](http://rack.rubyforge.org/)フレームワーク用の -最小限の標準インターフェース -上で動作しています。Rack中でもアプリケーションデベロッパー -向けに一番興味深い機能はミドルウェア(サーバとアプリケーション間に介在し、モニタリング、HTTPリクエストとレスポンス -の手動操作ができるなど、一般的な機能のいろいろなことを提供するもの)をサポートすることです。 - -Sinatraではトップレベルの`use` -メソッドを使ってRackにパイプラインを構築します。 +Sinatraはトップレベルの`use`メソッドを通して、Rackミドルウェアパイプラインの構築を楽にします。 ``` ruby require 'sinatra' @@ -985,10 +2120,7 @@ get '/hello' do end ``` -`use` -[Rack::Builder](http://rack.rubyforge.org/doc/classes/Rack/Builder.html) -DSLで定義されていることと全て一致します。 例えば `use` -メソッドはブロック構文のように複数の引数を受け取ることができます。 +`use`の文法は、[Rack::Builder](http://rack.rubyforge.org/doc/classes/Rack/Builder.html)DSLで定義されているそれ(rackupファイルで最もよく使われる)と同じです。例えば `use`メソッドは複数の引数、そしてブロックも取ることができます。 ``` ruby use Rack::Auth::Basic do |username, password| @@ -996,17 +2128,21 @@ use Rack::Auth::Basic do |username, password| end ``` -Rackはログ、デバッギング、URLルーティング、認証、セッションなどいろいろな機能を備えた標準的ミドルウェアです。 -Sinatraはその多くのコンポーネントを自動で使うよう基本設定されているため、`use`で明示的に指定する必要はありません。 +Rackは、ロギング、デバッギング、URLルーティング、認証、セッション管理など、多様な標準的ミドルウェアを共に配布されています。Sinatraはその多くのコンポーネントを自動で使うよう基本設定されているため、通常、それらを`use`で明示的に指定する必要はありません。 -## テスト +便利なミドルウェアを以下で見つけられます。 -SinatraでのテストはRack-basedのテストライブラリかフレームワークを使って書くことができます。 -[Rack::Test](http://gitrdoc.com/brynary/rack-test) -をおすすめします。やり方: +[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 'test/unit' require 'rack/test' class MyAppTest < Test::Unit::TestCase @@ -1028,21 +2164,16 @@ class MyAppTest < Test::Unit::TestCase def test_with_rack_env get '/', {}, 'HTTP_USER_AGENT' => 'Songbird' - assert_equal "あなたはSongbirdを使ってますね!", last_response.body + assert_equal "Songbirdを使ってます!", last_response.body end end ``` -注意: ビルトインのSinatra::TestモジュールとSinatra::TestHarnessクラスは -0.9.2リリース以降、廃止予定になっています。 +ノート: モジュラースタイルでSinatraを使う場合は、上記`Sinatra::Application`をアプリケーションのクラス名に置き換えてください。 -## Sinatra::Base - ミドルウェア、ライブラリ、 モジュラーアプリ +## Sinatra::Base - ミドルウェア、ライブラリおよびモジュラーアプリ -トップレベル(グローバル領域)上でいろいろ定義していくのは軽量アプリならうまくいきますが、 -RackミドルウェアやRails metal、サーバのコンポーネントを含んだシンプルな -ライブラリやSinatraの拡張プログラムを考慮するような場合はそうとは限りません。 -トップレベルのDSLがネームスペースを汚染したり、設定を変えてしまうこと(例:./publicや./view)がありえます。 -そこでSinatra::Baseの出番です。 +軽量なアプリケーションであれば、トップレベルでアプリケーションを定義していくことはうまくいきますが、再利用性可能なコンポーネント、例えばRackミドルウェア、RailsのMetal、サーバコンポーネントを含むシンプルなライブラリ、あるいはSinatraの拡張プログラムを構築するような場合、これは無視できない欠点を持つものとなります。トップレベルは、軽量なアプリケーションのスタイルにおける設定(例えば、単一のアプリケーションファイル、`./public`および`./views`ディレクトリ、ロギング、例外詳細ページなど)を仮定しています。そこで`Sinatra::Base`の出番です。 ``` ruby require 'sinatra/base' @@ -1057,41 +2188,138 @@ class MyApp < Sinatra::Base end ``` -このMyAppは独立したRackコンポーネントで、RackミドルウェアやRackアプリケーション -Rails metalとして使用することができます。`config.ru`ファイル内で `use` -か、または `run` -でこのクラスを指定するか、ライブラリとしてサーバコンポーネントをコントロールします。 +`Sinatra::Base`のサブクラスで利用できるメソッドは、トップレベルDSLで利用できるものと全く同じです。ほとんどのトップレベルで記述されたアプリは、以下の2点を修正することで`Sinatra::Base`コンポーネントに変えることができます。 + +* `sinatra`の代わりに`sinatra/base`を読み込む + +(そうしない場合、SinatraのDSLメソッドの全てがmainの名前空間にインポートされます) + +* ルーティング、エラーハンドラー、フィルター、オプションを`Sinatra::Base`のサブクラスに書く + + +`Sinatra::Base`はまっさらです。ビルトインサーバを含む、ほとんどのオプションがデフォルトで無効になっています。利用可能なオプションとその挙動の詳細については[Configuring Settings](http://sinatra.github.com/configuration.html)(英語)をご覧下さい。 + +### モジュラースタイル vs クラッシックスタイル + +一般的認識と違って、クラッシックスタイルを使うことに問題はなにもありません。それがそのアプリケーションに合っているのであれば、モジュラーアプリケーションに移行する必要はありません。 + +モジュラースタイルを使わずにクラッシックスタイルを使った場合の一番の不利な点は、Rubyプロセスごとにただ一つのSinatraアプリケーションしか持てない点です。複数が必要な場合はモジュラースタイルに移行してください。モジュラースタイルとクラッシックスタイルを混合できないということはありません。 + +一方のスタイルから他方へ移行する場合、デフォルト設定がわずかに異なる点に注意が必要です。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
設定クラッシックモジュラー
app_filesinatraを読み込むファイルSinatra::Baseをサブクラス化したファイル
run$0 == app_filefalse
loggingtruefalse
method_overridetruefalse
inline_templatestruefalse
statictruefalse
+ + +### モジュラーアプリケーションの提供 + +モジュラーアプリケーションを開始、つまり`run!`を使って開始させる二種類のやり方があります。 + ``` ruby -MyApp.run! :host => 'localhost', :port => 9090 +# my_app.rb +require 'sinatra/base' + +class MyApp < Sinatra::Base + # ... アプリケーションのコードを書く ... + + # Rubyファイルが直接実行されたらサーバを立ち上げる + run! if app_file == $0 +end ``` -Sinatra::Baseのサブクラスで使えるメソッドはトップレベルのDSLを経由して確実に使うことができます。 -ほとんどのトップレベルで記述されたアプリは、以下の2点を修正することでSinatra::Baseコンポーネントに変えることができます。 +として、次のように起動するか、 -- `sinatra`の代わりに`sinatra/base`を読み込む +``` shell +ruby my_app.rb +``` -(そうしない場合、SinatraのDSLメソッドの全てがメインネームスペースにインポートされます) +または、Rackハンドラを使えるようにする`config.ru`ファイルを書いて、 -- ルート、エラーハンドラー、フィルター、オプションをSinatra::Baseのサブクラスに書く +``` ruby +# config.ru (rackupで起動) +require './my_app' +run MyApp +``` -`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). +``` shell +rackup -p 4567 +``` -### Sinatraをミドルウェアとして利用する +### 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エンドポイントの前に追加することが可能です。 @@ -1116,12 +2344,12 @@ class LoginScreen < Sinatra::Base end class MyApp < Sinatra::Base - # middleware will run before filters + # ミドルウェアはbeforeフィルタの前に実行される use LoginScreen before do unless session['user_name'] - halt "Access denied, please login." + halt "アクセスは拒否されました。ログインしてください。" end end @@ -1129,7 +2357,51 @@ class MyApp < Sinatra::Base 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) 現在のスコープはどのメソッドや変数が利用可能かを決定します。 @@ -1138,65 +2410,67 @@ end 全てのSinatraアプリケーションはSinatra::Baseのサブクラスに相当します。 もしトップレベルDSLを利用しているならば(`require 'sinatra'`)このクラスはSinatra::Applicationであり、 そうでなければ、あなたが明示的に作成したサブクラスです。 -クラスレベルでは\`get\`や\`before\`のようなメソッドを持っています。 -しかし\`request\`オブジェクトや\`session\`には、全てのリクエストのために1つのアプリケーションクラスが存在するためアクセスできません。 +クラスレベルでは`get`や`before`のようなメソッドを持っています。 +しかし`request`や`session`オブジェクトには、全てのリクエストに対する単一のアプリケーションクラスがあるだけなので、アクセスできません。 -\`set\`によって作られたオプションはクラスレベルのメソッドです: +`set`によって作られたオプションはクラスレベルのメソッドです。 ``` ruby class MyApp < Sinatra::Base - # Hey, I'm in the application scope! + # アプリケーションスコープの中だよ! set :foo, 42 foo # => 42 get '/foo' do - # Hey, I'm no longer in the application scope! + # もうアプリケーションスコープの中にいないよ! end end ``` -次の場所ではアプリケーションスコープバインディングを持ちます: +次の場所ではアプリケーションスコープバインディングを持ちます。 -- アプリケーションのクラス本体 +- アプリケーションクラス本体 - 拡張によって定義されたメソッド -- \`helpers\`に渡されたブロック +- `helpers`に渡されたブロック -- \`set\`の値として使われるProcまたはブロック +- `set`の値として使われるProcまたはブロック -このスコープオブジェクト(クラス)は次のように利用できます: +- `Sinatra.new`に渡されたブロック + +このスコープオブジェクト(クラス)は次のように利用できます。 - configureブロックに渡されたオブジェクト経由(`configure { |c| ... }`) -- リクエストスコープの中での\`settings\` +- リクエストスコープの中での`settings` ### リクエスト/インスタンスのスコープ やってくるリクエストごとに、あなたのアプリケーションクラスの新しいインスタンスが作成され、全てのハンドラブロックがそのスコープで実行されます。 -このスコープの内側からは\`request\`や\`session\`オブジェクトにアクセスすることができ、\`erb\`や\`haml\`のような表示メソッドを呼び出すことができます。 -リクエストスコープの内側からは、\`settings\`ヘルパによってアプリケーションスコープにアクセスすることができます。 +このスコープの内側からは`request`や`session`オブジェクトにアクセスすることができ、`erb`や`haml`のようなレンダリングメソッドを呼び出すことができます。 +リクエストスコープの内側からは、`settings`ヘルパーによってアプリケーションスコープにアクセスすることができます。 ``` ruby class MyApp < Sinatra::Base - # Hey, I'm in the application scope! + # アプリケーションスコープの中だよ! get '/define_route/:name' do - # Request scope for '/define_route/:name' + # '/define_route/:name'のためのリクエストスコープ @value = 42 settings.get("/#{params[:name]}") do - # Request scope for "/#{params[:name]}" + # "/#{params[:name]}"のためのリクエストスコープ @value # => nil (not the same request) end - "Route defined!" + "ルーティングが定義された!" end end ``` -次の場所ではリクエストスコープバインディングを持ちます: +次の場所ではリクエストスコープバインディングを持ちます。 -- get/head/post/put/delete ブロック +- get/head/post/put/delete/options/patch/link/unlink ブロック - before/after フィルタ @@ -1207,21 +2481,19 @@ end ### デリゲートスコープ デリゲートスコープは、単にクラススコープにメソッドを転送します。 -しかしながら、クラスのバインディングを持っていないため、クラススコープと全く同じふるまいをするわけではありません: +しかしながら、クラスのバインディングを持っていないため、クラススコープと全く同じふるまいをするわけではありません。 委譲すると明示的に示されたメソッドのみが利用可能であり、またクラススコープと変数/状態を共有することはできません(注: -異なった\`self\`を持っています)。 +異なった`self`を持っています)。 `Sinatra::Delegator.delegate :method_name`を呼び出すことによってデリゲートするメソッドを明示的に追加することができます。 -次の場所ではデリゲートスコープを持ちます: +次の場所ではデリゲートスコープを持ちます。 - もし`require "sinatra"`しているならば、トップレベルバインディング -- \`Sinatra::Delegator\` mixinでextendされたオブジェクト +- `Sinatra::Delegator` mixinでextendされたオブジェクト コードをご覧ください: ここでは [Sinatra::Delegator -mixin](http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128) -は[main -名前空間にincludeされています](http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28). +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)。 ## コマンドライン @@ -1240,58 +2512,167 @@ ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER] -s # rackserver/handlerを指定 (デフォルトはthin) -x # mutex lockを付ける (デフォルトはoff) -## 最新開発版について +## 必要環境 + +次の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`に追加してアプリケーションを起動します。 -Sinatraの開発版を使いたい場合は、ローカルに開発版を落として、 -`LOAD_PATH`の`sinatra/lib`ディレクトリを指定して実行して下さい。 ``` shell cd myapp git clone git://github.com/sinatra/sinatra.git -ruby -Isinatra/lib myapp.rb +ruby -I sinatra/lib myapp.rb ``` -`sinatra/lib`ディレクトリをアプリケーションの`LOAD_PATH`に追加する方法もあります。 - -``` ruby -$LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib' -require 'rubygems' -require 'sinatra' - -get '/about' do - "今使ってるバージョンは" + Sinatra::VERSION -end -``` - -Sinatraのソースを更新する方法: +追ってSinatraのソースを更新する方法。 ``` shell -cd myproject/sinatra +cd myapp/sinatra git pull ``` -## その他 +### グローバル環境にインストールする場合 -日本語サイト +Sinatraのgemを自身でビルドすることもできます。 -- [Greenbear Laboratory - Rack日本語マニュアル](http://route477.net/w/RackReferenceJa.html) - - Rackの日本語マニュアル +``` shell +git clone git://github.com/sinatra/sinatra.git +cd sinatra +rake sinatra.gemspec +rake install +``` -英語サイト +gemをルートとしてインストールする場合は、最後のステップはこうなります。 -- [プロジェクトサイト](http://sinatra.github.com/) - ドキュメント、 +``` shell +sudo rake install +``` + +## バージョニング(Versioning) + +Sinatraは、[Semantic Versioning](http://semver.org/)におけるSemVerおよびSemVerTagの両方に準拠しています。 + +## 参考文献 + +* [プロジェクトサイト](http://sinatra.github.com/) - ドキュメント、 ニュース、他のリソースへのリンクがあります。 -- [プロジェクトに参加(貢献)する](http://sinatra.github.com/contributing.html) - - バグレポート パッチの送信、サポートなど +* [プロジェクトに参加(貢献)する](http://sinatra.github.com/contributing.html) + - バグレポート パッチの送信、サポートなど -- [Issue tracker](http://github.com/sinatra/sinatra/issues) - - チケット管理とリリース計画 +* [Issue tracker](http://github.com/sinatra/sinatra/issues) -- [Twitter](http://twitter.com/sinatra) +* [Twitter](http://twitter.com/sinatra) -- [メーリングリスト](http://groups.google.com/group/sinatrarb) +* [メーリングリスト](http://groups.google.com/group/sinatrarb/topics) + +* http://freenode.net上のIRC: [#sinatra](irc://chat.freenode.net/#sinatra) + +* [Sinatra Book](http://sinatra-book.gittr.com) クックブック、チュートリアル + +* [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) -- [IRC: \#sinatra](irc://chat.freenode.net/#sinatra) on - [freenode.net](http://freenode.net)