= Sinatra
<i>注） 本文書は英語から翻訳したものであり、その内容が最新でない場合もあります。最新の情報はオリジナルの英語版を参照して下さい。</i>

SinatraはRubyで下記のような最小労力で手早くウェブアプリケーションを作成するためのDSLです。

  # myapp.rb
  require 'sinatra'
  get '/' do
    'Hello world!'
  end

gemをインストールして動かしてみる。

  gem install sinatra
  ruby -rubygems myapp.rb

http://localhost:4567 を見る。

== ルート

Sinatraでは、ルートはHTTPメソッドとURLマッチングパターンがペアになっています。
ルートはブロックに結び付けられています。

  get '/' do
    .. 何か見せる ..
  end

  post '/' do
    .. 何か生成する ..
  end

  put '/' do
    .. 何か更新する ..
  end

  delete '/' do
    .. 何か削除する ..
  end

ルートは定義された順番にマッチします。 リクエストに最初にマッチしたルートが呼び出されます。

ルートのパターンは名前付きパラメータを含むことができ、
<tt>params</tt>ハッシュで取得できます。

  get '/hello/:name' do
    # matches "GET /hello/foo" and "GET /hello/bar"
    # params[:name] is 'foo' or 'bar'
    "Hello #{params[:name]}!"
  end

また、ブロックパラメータで名前付きパラメータにアクセスすることもできます。

  get '/hello/:name' do |n|
    "Hello #{n}!"
  end

ルートパターンはsplat(またはワイルドカード)を含むこともでき、
<tt>params[:splat]</tt> で取得できます。

  get '/say/*/to/*' do
    # matches /say/hello/to/world
    params[:splat] # => ["hello", "world"]
  end

  get '/download/*.*' do
    # matches /download/path/to/file.xml
    params[:splat] # => ["path/to/file", "xml"]
  end

正規表現を使ったルート:

  get %r{/hello/([\w]+)} do
    "Hello, #{params[:captures].first}!"
  end

ブロックパラーメータを使用した場合:

  get %r{/hello/([\w]+)} do |c|
    "Hello, #{c}!"
  end


=== 条件

ルートにはユーザエージェントのようなさまざまな条件を含めることができます。

  get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
    "You're using Songbird version #{params[:agent][0]}"
  end

  get '/foo' do
    # Matches non-songbird browsers
  end

ほかに+host_name+と+provides+条件が利用可能です:

  get '/', :host_name => /^admin\./ do
    "Admin Area, Access denied!"
  end

  get '/', :provides => 'html' do
    haml :index
  end
  
  get '/', :provides => ['rss', 'atom', 'xml'] do
    builder :feed
  end

独自の条件を定義することも簡単にできます:

  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


=== 戻り値

ルートブロックの戻り値は、HTTPクライアントまたはRackスタックでの次のミドルウェアに渡されるレスポンスボディを決定します。

これは大抵の場合、上の例のように文字列ですが、それ以外の値も使用することができます。

Rackレスポンス、Rackボディオブジェクト、HTTPステータスコードのいずれかとして
妥当なオブジェクトであればどのようなオブジェクトでも返すことができます:

* 3要素の配列: <tt>[ステータス(Fixnum), ヘッダ(Hash), レスポンスボディ(#eachに応答する)]</tt>
* 2要素の配列: <tt>[ステータス(Fixnum), レスポンスボディ(#eachに応答する)]</tt>
* <tt>#each</tt>に応答し、与えられたブロックに文字列を渡すオブジェクト
* ステータスコードを表現するFixnum

そのように、例えばストリーミングの例を簡単に実装することができます:

    class Stream
      def each
        100.times { |i| yield "#{i}\n" }
      end
    end

    get('/') { Stream.new }


== 静的ファイル

静的ファイルは<tt>./public</tt>ディレクトリから配信されます。
<tt>:public</tt>オプションを指定することで別の場所を指定することができます。

  set :public, File.dirname(__FILE__) + '/static'

注意: この静的ファイル用のディレクトリ名はURL中に含まれません。
例えば、<tt>./public/css/style.css</tt>は<tt>http://example.com/css/style.css</tt>でアクセスできます。

== ビュー / テンプレート

テンプレートは<tt>./views</tt>ディレクトリ下に配置されています。
他のディレクトリを使用する場合の例:

  set :views, File.dirname(__FILE__) + '/templates'

テンプレートはシンボルを使用して参照させることを覚えておいて下さい。
サブデレクトリでもこの場合は<tt>:'subdir/template'</tt>のようにします。
レンダリングメソッドは文字列が渡されると、そのまま文字列を出力します。

=== Haml テンプレート

hamlを使うにはhamlライブラリが必要です:

  # hamlを読み込みます
  require 'haml'

  get '/' do
    haml :index
  end

<tt>./views/index.haml</tt>を表示します。

{Haml's options}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options]
はSinatraの設定でグローバルに設定することができます。
{Options and Configurations}[http://www.sinatrarb.com/configuration.html],
を参照してそれぞれ設定を上書きして下さい。

  set :haml, {:format => :html5 } # デフォルトのフォーマットは:xhtml

  get '/' do
    haml :index, :haml_options => {:format => :html4 } # 上書き
  end


=== Erb テンプレート

  # erbを読み込みます
  require 'erb'

  get '/' do
    erb :index
  end

<tt>./views/index.erb</tt>を表示します。

=== Erubis

erubisテンプレートを表示するには、erubisライブラリが必要です:

  # erubisを読み込みます
  require 'erubis'

  get '/' do
    erubis :index
  end

<tt>./views/index.erubis</tt>を表示します。

=== Builder テンプレート

builderを使うにはbuilderライブラリが必要です:

  # builderを読み込みます
  require 'builder'

  get '/' do
    builder :index
  end

<tt>./views/index.builder</tt>を表示します。

=== 鋸 テンプレート

鋸を使うには鋸ライブラリが必要です:

  # 鋸を読み込みます
  require 'nokogiri'

  get '/' do
    nokogiri :index
  end

<tt>./views/index.nokogiri</tt>を表示します。

=== Sass テンプレート

Sassテンプレートを使うにはsassライブラリが必要です:

  # hamlかsassを読み込みます
  require 'sass'

  get '/stylesheet.css' do
    sass :stylesheet
  end

<tt>./views/stylesheet.sass</tt>を表示します。

{Sass' options}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
はSinatraの設定でグローバルに設定することができます。
see {Options and Configurations}[http://www.sinatrarb.com/configuration.html],
を参照してそれぞれ設定を上書きして下さい。

  set :sass, {:style => :compact } # デフォルトのSass styleは :nested

  get '/stylesheet.css' do
    sass :stylesheet, :sass_options => {:style => :expanded } # 上書き
  end

=== Scss テンプレート

Scssテンプレートを使うにはsassライブラリが必要です:

  # hamlかsassを読み込みます
  require 'sass'

  get '/stylesheet.css' do
    scss :stylesheet
  end

<tt>./views/stylesheet.scss</tt>を表示します。

{Sass' options}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
はSinatraの設定でグローバルに設定することができます。
see {Options and Configurations}[http://www.sinatrarb.com/configuration.html],
を参照してそれぞれ設定を上書きして下さい。

  set :scss, :style => :compact # デフォルトのScss styleは:nested

  get '/stylesheet.css' do
    scss :stylesheet, :style => :expanded # 上書き
  end

=== Less テンプレート

Lessテンプレートを使うにはlessライブラリが必要です:

  # lessを読み込みます
  require 'less'

  get '/stylesheet.css' do
    less :stylesheet
  end

<tt>./views/stylesheet.less</tt>を表示します。

=== Liquid テンプレート

Liquidテンプレートを使うにはliquidライブラリが必要です:

  # liquidを読み込みます
  require 'liquid'

  get '/' do
    liquid :index
  end

<tt>./views/index.liquid</tt>を表示します。

LiquidテンプレートからRubyのメソッド(+yield+を除く)を呼び出すことができないため、
ほぼ全ての場合にlocalsを指定する必要があるでしょう:

  liquid :index, :locals => { :key => 'value' }

=== Markdown テンプレート

Markdownテンプレートを使うにはrdiscountライブラリが必要です:

  # rdiscountを読み込みます
  require "rdiscount"
  
  get '/' do
    markdown :index
  end

<tt>./views/index.markdown</tt>を表示します。(+md+と+mkd+も妥当な拡張子です)

markdownからメソッドを呼び出すことも、localsに変数を渡すこともできません。
それゆえ、他のレンダリングエンジンとの組み合わせで使うのが普通です:

  erb :overview, :locals => { :text => markdown(:introduction) }

他のテンプレートからmarkdownメソッドを呼び出してもよいことに注意してください:

  %h1 Hello From Haml!
  %p= markdown(:greetings)

=== Textile テンプレート

Textileテンプレートを使うにはRedClothライブラリが必要です:

  # redclothを読み込みます
  require "redcloth"

  get '/' do
    textile :index
  end

<tt>./views/index.textile</tt>を表示します。

textileからメソッドを呼び出すことも、localsに変数を渡すこともできません。
それゆえ、他のレンダリングエンジンとの組み合わせで使うのが普通です:

  erb :overview, :locals => { :text => textile(:introduction) }

他のテンプレートからtextileメソッドを呼び出してもよいことに注意してください:

  %h1 Hello From Haml!
  %p= textile(:greetings)

=== RDoc テンプレート

RDocテンプレートを使うにはRDocライブラリが必要です:

  # rdoc/markup/to_htmlを読み込みます
  require "rdoc/markup/to_html"

  get '/' do
    rdoc :index
  end

<tt>./views/index.rdoc</tt>を表示します。

rdocからメソッドを呼び出すことも、localsに変数を渡すこともできません。
それゆえ、他のレンダリングエンジンとの組み合わせで使うのが普通です:

  erb :overview, :locals => { :text => rdoc(:introduction) }

他のテンプレートからrdocメソッドを呼び出してもよいことに注意してください:

  %h1 Hello From Haml!
  %p= rdoc(:greetings)

=== Radius テンプレート

Radiusテンプレートを使うにはradiusライブラリが必要です:

  # radiusを読み込みます
  require 'radius'

  get '/' do
    radius :index
  end

<tt>./views/index.radius</tt>を表示します。

RadiusテンプレートからRubyのメソッド(+yield+を除く)を呼び出すことができないため、
ほぼ全ての場合にlocalsを指定する必要があるでしょう:

  radius :index, :locals => { :key => 'value' }

=== Markaby テンプレート

Markabyテンプレートを使うにはmarkabyライブラリが必要です:

  # markabyを読み込みます
  require 'markaby'

  get '/' do
    markaby :index
  end

<tt>./views/index.mab</tt>を表示します。

=== Slim テンプレート

Slimテンプレートを使うにはslimライブラリが必要です:

  # slimを読み込みます
  require 'slim'

  get '/' do
    slim :index
  end

<tt>./views/index.slim</tt>を表示します。

=== CoffeeScript テンプレート

CoffeeScriptテンプレートを表示するにはcoffee-scriptライブラリと`coffee`バイナリが必要です:

  # coffee-scriptを読み込みます
  require 'coffee-script'

  get '/application.js' do
    coffee :application
  end

<tt>./views/application.coffee</tt>を表示します。

=== インラインテンプレート

  get '/' do
    haml '%div.title Hello World'
  end

文字列をテンプレートとして表示します。

=== テンプレート内で変数にアクセスする

テンプレートはルートハンドラと同じコンテキストの中で評価されます。. ルートハンドラでセットされたインスタンス変数は
テンプレート内で直接使うことができます。

  get '/:id' do
    @foo = Foo.find(params[:id])
    haml '%h1= @foo.name'
  end

ローカル変数を明示的に定義することもできます。

  get '/:id' do
    foo = Foo.find(params[:id])
    haml '%h1= foo.name', :locals => { :foo => foo }
  end

このやり方は他のテンプレート内で部分テンプレートとして表示する時に典型的に使用されます。

=== ファイル内テンプレート

テンプレートはソースファイルの最後で定義することもできます。

  require 'rubygems'
  require 'sinatra'

  get '/' do
    haml :index
  end

  __END__

  @@ layout
  %html
    = yield

  @@ index
  %div.title Hello world!!!!!

注意: sinatraをrequireするファイル内で定義されたファイル内テンプレートは自動的に読み込まれます。
他のファイルで定義されているテンプレートを使うには <tt>enable :inline_templates</tt>を明示的に呼んでください。

=== 名前付きテンプレート

テンプレートはトップレベルの<tt>template</tt>メソッドで定義することができます。

  template :layout do
    "%html\n  =yield\n"
  end

  template :index do
    '%div.title Hello World!'
  end

  get '/' do
    haml :index
  end

「layout」というテンプレートが存在する場合、そのテンプレートファイルは他のテンプレートが
表示される度に使用されます。<tt>:layout => false</tt>することでlayoutsを無効にできます。

  get '/' do
    haml :index, :layout => !request.xhr?
  end

== ヘルパー

トップレベルの<tt>helpers</tt>を使用してルートハンドラやテンプレートで使うヘルパメソッドを
定義できます。

  helpers do
    def bar(name)
      "#{name}bar"
    end
  end

  get '/:name' do
    bar(params[:name])
  end

== フィルタ

beforeフィルタはリクエストされたコンテキストを実行する前に評価され、
リクエストとレスポンスを変更することができます。フィルタ内でセットされた
インスタンス変数はルーティングとテンプレートで使用できます。

  before do
    @note = 'Hi!'
    request.path_info = '/foo/bar/baz'
  end

  get '/foo/*' do
    @note #=> 'Hi!'
    params[:splat] #=> 'bar/baz'
  end

afterフィルタは同じコンテキストにあるリクエストの後に評価され、
同じくリクエストとレスポンスを変更することができます。
beforeフィルタとルートで設定されたインスタンス変数は、
afterフィルタからアクセスすることができます:

  after do
    puts response.status
  end

フィルタにはオプションとしてパターンを渡すことができ、
この場合はリクエストのパスがパターンにマッチした場合のみフィルタが評価されます:

  before '/protected/*' do
    authenticate!
  end

  after '/create/:slug' do |slug|
    session[:last_slug] = slug
  end

== 強制終了

ルートかbeforeフィルタ内で直ちに実行を終了する方法:

  halt

ステータスを指定することができます:

  halt 410

body部を指定することもできます ...

  halt 'ここにbodyを書く'

ステータスとbody部を指定する ...

  halt 401, '立ち去れ!'

ヘッダを指定:

  halt 402, {'Content-Type' => 'text/plain'}, 'リベンジ'

== パッシング(Passing)

ルートは<tt>pass</tt>を使って次のルートに飛ばすことができます:

  get '/guess/:who' do
    pass unless params[:who] == 'Frank'
    "見つかっちゃった!"
  end

  get '/guess/*' do
    "はずれです!"
  end

ルートブロックからすぐに抜け出し、次にマッチするルートを実行します。
マッチするルートが見当たらない場合は404が返されます。

== リクエストオブジェクトへのアクセス

受信するリクエストオブジェクトは、`request`メソッドを通じてリクエストレベル(フィルタ、ルート、エラーハンドラ)からアクセスすることができます:

  # アプリケーションが http://example.com/example で動作している場合
  get '/foo' do
    request.body              # クライアントによって送信されたリクエストボディ(下記参照)
    request.scheme            # "http"
    request.script_name       # "/example"
    request.path_info         # "/foo"
    request.port              # 80
    request.request_method    # "GET"
    request.query_string      # ""
    request.content_length    # request.bodyの長さ
    request.media_type        # request.bodyのメディアタイプ
    request.host              # "example.com"
    request.get?              # true (他の動詞についても同様のメソッドあり)
    request.form_data?        # false
    request["SOME_HEADER"]    # SOME_HEADERヘッダの値
    request.referer           # クライアントのリファラまたは'/'
    request.user_agent        # ユーザエージェント (:agent 条件によって使用される)
    request.cookies           # ブラウザクッキーのハッシュ
    request.xhr?              # Ajaxリクエストかどうか
    request.url               # "http://example.com/example/foo"
    request.path              # "/example/foo"
    request.ip                # クライアントのIPアドレス
    request.secure?           # false
    request.env               # Rackによって渡された生のenvハッシュ
  end

<tt>script_name</tt>や<tt>path_info</tt>などのオプションは次のように利用することもできます:

  before { request.path_info = "/" }
  
  get "/" do
    "全てのリクエストはここに来る"
  end

<tt>request.body</tt>はIOまたはStringIOのオブジェクトです:

  post "/api" do
    request.body.rewind  # 既に読まれているときのため
    data = JSON.parse request.body.read
    "Hello #{data['name']}!"
  end


== 設定

どの環境でも起動時に１回だけ実行されます。

  configure do
    ...
  end

環境(RACK_ENV環境変数)が<tt>:production</tt>に設定されている時だけ実行する方法:

  configure :production do
    ...
  end

環境が<tt>:production</tt>か<tt>:test</tt>の場合に設定する方法:

  configure :production, :test do
    ...
  end

== エラーハンドリング

エラーハンドラーはルートコンテキストとbeforeフィルタ内で実行します。
<tt>haml</tt>、<tt>erb</tt>、<tt>halt</tt>などを使うこともできます。

=== Not Found

<tt>Sinatra::NotFound</tt>が起きた時か レスポンスのステータスコードが
404の時に<tt>not_found</tt>ハンドラーが発動します。

  not_found do
    'ファイルが存在しません'
  end

=== エラー

+error+ ハンドラーはルートブロックかbeforeフィルタ内で例外が発生した時はいつでも発動します。
例外オブジェクトはRack変数<tt>sinatra.error</tt>から取得できます。

  error do
    'エラーが発生しました。 - ' + env['sinatra.error'].name
  end

エラーをカスタマイズする場合は、

  error MyCustomError do
    'エラーメッセージ...' + request.env['sinatra.error'].message
  end

と書いておいて,下記のように呼び出します。

  get '/' do
    raise MyCustomError, '何かがまずかったようです'
  end

そうするとこうなります:

  エラーメッセージ... 何かがまずかったようです

あるいは、ステータスコードに対応するエラーハンドラを設定することもできます:

  error 403 do
    'Access forbidden'
  end

  get '/secret' do
    403
  end

範囲指定もできます:

  error 400..510 do
    'Boom'
  end


開発環境として実行している場合、Sinatraは特別な<tt>not_found</tt>と<tt>error</tt>ハンドラーを
インストールしています。

== MIMEタイプ

<tt>send_file</tt>か静的ファイルを使う時、Sinatraが理解でいないMIMEタイプがある場合があります。
その時は +mime_type+ を使ってファイル拡張子毎に登録して下さい。

  mime_type :foo, 'text/foo'

これはcontent_typeヘルパで利用することができます:

  content_type :foo

== Rackミドルウェア

SinatraはRack[http://rack.rubyforge.org/]というRubyのWEBフレームワーク用の
最小限の標準インターフェース 上で動作しています。Rack中でもアプリケーションデベロッパー
向けに一番興味深い機能はミドルウェア(サーバとアプリケーション間に介在し、モニタリング、HTTPリクエストとレスポンス
の手動操作ができるなど、一般的な機能のいろいろなことを提供するもの)をサポートすることです。

Sinatraではトップレベルの+user+ メソッドを使ってRackにパイプラインを構築します。

  require 'sinatra'
  require 'my_custom_middleware'

  use Rack::Lint
  use MyCustomMiddleware

  get '/hello' do
    'Hello World'
  end

<tt>use</tt> の意味は{Rack::Builder}[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSLで定義されていることと全て一致します。
例えば +use+ メソッドはブロック構文のように複数の引数を受け取ることができます。

  use Rack::Auth::Basic do |username, password|
    username == 'admin' && password == 'secret'
  end

Rackはログ、デバッギング、URLルーティング、認証、セッションなどいろいろな機能を備えた標準的ミドルウェアです。
Sinatraはその多くのコンポーネントを自動で使うよう基本設定されているため、+use+で明示的に指定する必要はありません。

== テスト

SinatraでのテストはRack-basedのテストライブラリかフレームワークを使って書くことができます。
{Rack::Test}[http://gitrdoc.com/brynary/rack-test] をおすすめします。やり方:

  require 'my_sinatra_app'
  require 'rack/test'

  class MyAppTest < Test::Unit::TestCase
    include Rack::Test::Methods

    def app
      Sinatra::Application
    end

    def test_my_default
      get '/'
      assert_equal 'Hello World!', last_response.body
    end

    def test_with_params
      get '/meet', :name => 'Frank'
      assert_equal 'Hello Frank!', last_response.body
    end

    def test_with_rack_env
      get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
      assert_equal "あなたはSongbirdを使ってますね!", last_response.body
    end
  end

注意: ビルトインのSinatra::TestモジュールとSinatra::TestHarnessクラスは
0.9.2リリース以降、廃止予定になっています。

== Sinatra::Base - ミドルウェア、ライブラリ、 モジュラーアプリ

トップレベル(グローバル領域)上でいろいろ定義していくのは軽量アプリならうまくいきますが、
RackミドルウェアやRails metal、サーバのコンポーネントを含んだシンプルな
ライブラリやSinatraの拡張プログラムを考慮するような場合はそうとは限りません。
トップレベルのDSLがネームスペースを汚染したり、設定を変えてしまうこと(例:./publicや./view)がありえます。
そこでSinatra::Baseの出番です。

  require 'sinatra/base'

  class MyApp < Sinatra::Base
    set :sessions, true
    set :foo, 'bar'

    get '/' do
      'Hello world!'
    end
  end

このMyAppは独立したRackコンポーネントで、RackミドルウェアやRackアプリケーション
Rails metalとして使用することができます。<tt>config.ru</tt>ファイル内で +use+ か、または
+run+ でこのクラスを指定するか、ライブラリとしてサーバコンポーネントをコントロールします。

   MyApp.run! :host => 'localhost', :port => 9090

Sinatra::Baseのサブクラスで使えるメソッドはトップレベルのDSLを経由して確実に使うことができます。
ほとんどのトップレベルで記述されたアプリは、以下の２点を修正することでSinatra::Baseコンポーネントに変えることができます。

* +sinatra+の代わりに<tt>sinatra/base</tt>を読み込む
(そうしない場合、SinatraのDSLメソッドの全てがメインネームスペースにインポートされます)
* ルート、エラーハンドラー、フィルター、オプションをSinatra::Baseのサブクラスに書く

<tt>Sinatra::Base</tt> はまっさらです。ビルトインサーバを含む、ほとんどのオプションがデフォルト
で無効になっています。オプション詳細については{Options and Configuration}[http://sinatra.github.com/configuration.html]
をご覧下さい。

補足: SinatraのトップレベルDSLはシンプルな委譲(delgation)システムで実装されています。
<tt>Sinatra::Application</tt>クラス(Sinatra::Baseの特別なサブクラス)は、トップレベルに送られる
:get、 :put、 :post、:delete、 :before、:error、:not_found、 :configure、:set messagesのこれら
全てを受け取ります。 詳細を閲覧されたい方はこちら(英語):
{Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1064]
{included into the main namespace}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb#L25].

=== Sinatraをミドルウェアとして利用する

Sinatraは他のRackミドルウェアを利用することができるだけでなく、
全てのSinatraアプリケーションは、それ自体ミドルウェアとして別のRackエンドポイントの前に追加することが可能です。

このエンドポイントには、別のSinatraアプリケーションまたは他のRackベースのアプリケーション(Rails/Ramaze/Camping/...)が用いられるでしょう。

  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
    # middleware will run before filters
    use LoginScreen
    
    before do
      unless session['user_name']
        halt "Access denied, please <a href='/login'>login</a>."
      end
    end
    
    get('/') { "Hello #{session['user_name']}." }
  end

== スコープとバインディング

現在のスコープはどのメソッドや変数が利用可能かを決定します。

=== アプリケーション/クラスのスコープ

全てのSinatraアプリケーションはSinatra::Baseのサブクラスに相当します。
もしトップレベルDSLを利用しているならば(<tt>require 'sinatra'</tt>)このクラスはSinatra::Applicationであり、
そうでなければ、あなたが明示的に作成したサブクラスです。
クラスレベルでは`get`や`before`のようなメソッドを持っています。
しかし`request`オブジェクトや`session`には、全てのリクエストのために1つのアプリケーションクラスが存在するためアクセスできません。

`set`によって作られたオプションはクラスレベルのメソッドです:

    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`に渡されたブロック
* `set`の値として使われるProcまたはブロック

このスコープオブジェクト(クラス)は次のように利用できます:

* configureブロックに渡されたオブジェクト経由(<tt>configure { |c| ... }</tt>)
* リクエストスコープの中での`settings`

=== リクエスト/インスタンスのスコープ

やってくるリクエストごとに、あなたのアプリケーションクラスの新しいインスタンスが作成され、全てのハンドラブロックがそのスコープで実行されます。
このスコープの内側からは`request`や`session`オブジェクトにアクセスすることができ、`erb`や`haml`のような表示メソッドを呼び出すことができます。
リクエストスコープの内側からは、`settings`ヘルパによってアプリケーションスコープにアクセスすることができます。

  class MyApp < Sinatra::Base
    # Hey, I'm in the application scope!
    get '/define_route/:name' do
      # Request scope for '/define_route/:name'
      @value = 42
      
      settings.get("/#{params[:name]}") do
        # Request scope for "/#{params[:name]}"
        @value # => nil (not the same request)
      end
      
      "Route defined!"
    end
  end

次の場所ではリクエストスコープバインディングを持ちます:

* get/head/post/put/delete ブロック
* before/after フィルタ
* helper メソッド
* テンプレート/ビュー

=== デリゲートスコープ

デリゲートスコープは、単にクラススコープにメソッドを転送します。
しかしながら、クラスのバインディングを持っていないため、クラススコープと全く同じふるまいをするわけではありません:
委譲すると明示的に示されたメソッドのみが利用可能であり、またクラススコープと変数/状態を共有することはできません(注: 異なった`self`を持っています)。
<tt>Sinatra::Delegator.delegate :method_name</tt>を呼び出すことによってデリゲートするメソッドを明示的に追加することができます。

次の場所ではデリゲートスコープを持ちます:

* もし<tt>require "sinatra"</tt>しているならば、トップレベルバインディング
* `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].

== コマンドライン

Sinatraアプリケーションは直接実行できます。

  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)

== 最新開発版について

Sinatraの開発版を使いたい場合は、ローカルに開発版を落として、
<tt>LOAD_PATH</tt>の<tt>sinatra/lib</tt>ディレクトリを指定して実行して下さい。

  cd myapp
  git clone git://github.com/sinatra/sinatra.git
  ruby -Isinatra/lib myapp.rb

<tt>sinatra/lib</tt>ディレクトリをto the<tt>LOAD_PATH</tt>に追加する方法もあります。

  $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib'
  require 'rubygems'
  require 'sinatra'

  get '/about' do
    "今使ってるバージョンは" + Sinatra::VERSION
  end

Sinatraのソースを更新する方法:

  cd myproject/sinatra
  git pull

== その他

日本語サイト

* {Greenbear Laboratory Rack日本語マニュアル}[http://mono.kmc.gr.jp/~yhara/w/?RackReferenceJa] - Rackの日本語マニュアル

英語サイト

* {プロジェクトサイト}[http://sinatra.github.com/] - ドキュメント、
  ニュース、他のリソースへのリンクがあります。
* {プロジェクトに参加(貢献)する}[http://sinatra.github.com/contributing.html] - バグレポート
  パッチの送信、サポートなど
* {Issue tracker}[http://github.com/sinatra/sinatra/issues] - チケット管理とリリース計画
* {Twitter}[http://twitter.com/sinatra]
* {メーリングリスト}[http://groups.google.com/group/sinatrarb]
* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] on http://freenode.net
