sinatra-1.4.7/ 0000755 0000041 0000041 00000000000 12652356512 013223 5 ustar www-data www-data sinatra-1.4.7/Rakefile 0000644 0000041 0000041 00000013774 12652356512 014704 0 ustar www-data www-data require 'rake/clean'
require 'rake/testtask'
require 'fileutils'
require 'date'
# CI Reporter is only needed for the CI
begin
require 'ci/reporter/rake/test_unit'
rescue LoadError
end
task :default => :test
task :spec => :test
CLEAN.include "**/*.rbc"
def source_version
@source_version ||= begin
load './lib/sinatra/version.rb'
Sinatra::VERSION
end
end
def prev_feature
source_version.gsub(/^(\d\.)(\d+)\..*$/) { $1 + ($2.to_i - 1).to_s }
end
def prev_version
return prev_feature + '.0' if source_version.end_with? '.0'
source_version.gsub(/\d+$/) { |s| s.to_i - 1 }
end
# SPECS ===============================================================
task :test do
ENV['LANG'] = 'C'
ENV.delete 'LC_CTYPE'
end
Rake::TestTask.new(:test) do |t|
t.test_files = FileList['test/*_test.rb']
t.ruby_opts = ['-rubygems'] if defined? Gem
t.ruby_opts << '-I.'
t.warning = true
end
Rake::TestTask.new(:"test:core") do |t|
core_tests = %w[base delegator encoding extensions filter
helpers mapped_error middleware radius rdoc
readme request response result route_added_hook
routing server settings sinatra static templates]
t.test_files = core_tests.map {|n| "test/#{n}_test.rb"}
t.ruby_opts = ["-rubygems"] if defined? Gem
t.ruby_opts << "-I."
t.warning = true
end
# Rcov ================================================================
namespace :test do
desc 'Measures test coverage'
task :coverage do
rm_f "coverage"
sh "rcov -Ilib test/*_test.rb"
end
end
# Website =============================================================
desc 'Generate RDoc under doc/api'
task 'doc' => ['doc:api']
task('doc:api') { sh "yardoc -o doc/api" }
CLEAN.include 'doc/api'
# README ===============================================================
task :add_template, [:name] do |t, args|
Dir.glob('README.*') do |file|
code = File.read(file)
if code =~ /^===.*#{args.name.capitalize}/
puts "Already covered in #{file}"
else
template = code[/===[^\n]*Liquid.*index\.liquid<\/tt>[^\n]*/m]
if !template
puts "Liquid not found in #{file}"
else
puts "Adding section to #{file}"
template = template.gsub(/Liquid/, args.name.capitalize).gsub(/liquid/, args.name.downcase)
code.gsub! /^(\s*===.*CoffeeScript)/, "\n" << template << "\n\\1"
File.open(file, "w") { |f| f << code }
end
end
end
end
# Thanks in announcement ===============================================
team = ["Ryan Tomayko", "Blake Mizerany", "Simon Rozet", "Konstantin Haase"]
desc "list of contributors"
task :thanks, [:release,:backports] do |t, a|
a.with_defaults :release => "#{prev_version}..HEAD",
:backports => "#{prev_feature}.0..#{prev_feature}.x"
included = `git log --format=format:"%aN\t%s" #{a.release}`.lines.map { |l| l.force_encoding('binary') }
excluded = `git log --format=format:"%aN\t%s" #{a.backports}`.lines.map { |l| l.force_encoding('binary') }
commits = (included - excluded).group_by { |c| c[/^[^\t]+/] }
authors = commits.keys.sort_by { |n| - commits[n].size } - team
puts authors[0..-2].join(', ') << " and " << authors.last,
"(based on commits included in #{a.release}, but not in #{a.backports})"
end
desc "list of authors"
task :authors, [:commit_range, :format, :sep] do |t, a|
a.with_defaults :format => "%s (%d)", :sep => ", ", :commit_range => '--all'
authors = Hash.new(0)
blake = "Blake Mizerany"
overall = 0
mapping = {
"blake.mizerany@gmail.com" => blake, "bmizerany" => blake,
"a_user@mac.com" => blake, "ichverstehe" => "Harry Vangberg",
"Wu Jiang (nouse)" => "Wu Jiang" }
`git shortlog -s #{a.commit_range}`.lines.map do |line|
line = line.force_encoding 'binary' if line.respond_to? :force_encoding
num, name = line.split("\t", 2).map(&:strip)
authors[mapping[name] || name] += num.to_i
overall += num.to_i
end
puts "#{overall} commits by #{authors.count} authors:"
puts authors.sort_by { |n,c| -c }.map { |e| a.format % e }.join(a.sep)
end
desc "generates TOC"
task :toc, [:readme] do |t, a|
a.with_defaults :readme => 'README.md'
def self.link(title)
title.downcase.gsub(/(?!-)\W /, '-').gsub(' ', '-').gsub(/(?!-)\W/, '')
end
puts "* [Sinatra](#sinatra)"
title = Regexp.new('(?<=\* )(.*)') # so Ruby 1.8 doesn't complain
File.binread(a.readme).scan(/^##.*/) do |line|
puts line.gsub(/#(?=#)/, ' ').gsub('#', '*').gsub(title) { "[#{$1}](##{link($1)})" }
end
end
# PACKAGING ============================================================
if defined?(Gem)
# Load the gemspec using the same limitations as github
def spec
require 'rubygems' unless defined? Gem::Specification
@spec ||= eval(File.read('sinatra.gemspec'))
end
def package(ext='')
"pkg/sinatra-#{spec.version}" + ext
end
desc 'Build packages'
task :package => %w[.gem .tar.gz].map {|e| package(e)}
desc 'Build and install as local gem'
task :install => package('.gem') do
sh "gem install #{package('.gem')}"
end
directory 'pkg/'
CLOBBER.include('pkg')
file package('.gem') => %w[pkg/ sinatra.gemspec] + spec.files do |f|
sh "gem build sinatra.gemspec"
mv File.basename(f.name), f.name
end
file package('.tar.gz') => %w[pkg/] + spec.files do |f|
sh <<-SH
git archive \
--prefix=sinatra-#{source_version}/ \
--format=tar \
HEAD | gzip > #{f.name}
SH
end
task 'release' => ['test', package('.gem')] do
if File.binread("CHANGELOG.md") =~ /= \d\.\d\.\d . not yet released$/i
fail 'please update the changelog first' unless %x{git symbolic-ref HEAD} == "refs/heads/prerelease\n"
end
sh <<-SH
gem install #{package('.gem')} --local &&
gem push #{package('.gem')} &&
git commit --allow-empty -a -m '#{source_version} release' &&
git tag -s v#{source_version} -m '#{source_version} release' &&
git tag -s #{source_version} -m '#{source_version} release' &&
git push && (git push sinatra || true) &&
git push --tags && (git push sinatra --tags || true)
SH
end
end
sinatra-1.4.7/Gemfile 0000644 0000041 0000041 00000003420 12652356512 014515 0 ustar www-data www-data # Why use bundler?
# Well, not all development dependencies install on all rubies. Moreover, `gem
# install sinatra --development` doesn't work, as it will also try to install
# development dependencies of our dependencies, and those are not conflict free.
# So, here we are, `bundle install`.
#
# If you have issues with a gem: `bundle install --without-coffee-script`.
RUBY_ENGINE = 'ruby' unless defined? RUBY_ENGINE
source 'https://rubygems.org' unless ENV['QUICK']
gemspec
gem 'rake'
gem 'rack-test', '>= 0.6.2'
gem "minitest", "~> 5.0"
# Allows stuff like `tilt=1.2.2 bundle install` or `tilt=master ...`.
# Used by the CI.
repos = {'tilt' => "rtomayko/tilt", 'rack' => "rack/rack"}
%w[tilt rack].each do |lib|
dep = case ENV[lib]
when 'stable', nil then nil
when /(\d+\.)+\d+/ then "~> " + ENV[lib].sub("#{lib}-", '')
else {:github => repos[lib], :branch => dep}
end
gem lib, dep
end
if RUBY_ENGINE == 'jruby'
gem 'nokogiri', '!= 1.5.0'
gem 'jruby-openssl'
gem 'trinidad'
end
if RUBY_ENGINE == "ruby" and RUBY_VERSION > '1.9.2'
gem 'less', '~> 2.0'
gem 'therubyracer'
gem 'redcarpet'
gem 'wlang', '>= 2.0.1'
gem 'bluecloth'
gem 'rdiscount'
gem 'RedCloth'
gem 'puma'
gem 'net-http-server'
gem 'yajl-ruby'
gem 'nokogiri'
gem 'thin'
gem 'slim', '~> 2.0'
gem 'coffee-script', '>= 2.0'
gem 'rdoc'
gem 'kramdown'
gem 'maruku'
gem 'creole'
gem 'wikicloth'
gem 'markaby'
gem 'radius'
gem 'asciidoctor'
gem 'liquid'
gem 'stylus'
gem 'rabl'
gem 'builder'
gem 'erubis'
gem 'haml', '>= 3.0'
gem 'sass'
end
if RUBY_ENGINE == "rbx"
gem 'json'
gem 'rubysl'
gem 'rubysl-test-unit'
end
platforms :ruby_18, :jruby do
gem 'json' unless RUBY_VERSION > '1.9' # is there a jruby but 1.8 only selector?
end
sinatra-1.4.7/examples/ 0000755 0000041 0000041 00000000000 12652356512 015041 5 ustar www-data www-data sinatra-1.4.7/examples/stream.ru 0000644 0000041 0000041 00000001064 12652356512 016705 0 ustar www-data www-data # this example does *not* work properly with WEBrick
#
# run *one* of these:
#
# rackup -s mongrel stream.ru # gem install mongrel
# thin -R stream.ru start # gem install thin
# unicorn stream.ru # gem install unicorn
# puma stream.ru # gem install puma
require 'sinatra/base'
class Stream < Sinatra::Base
get '/' do
content_type :txt
stream do |out|
out << "It's gonna be legen -\n"
sleep 0.5
out << " (wait for it) \n"
sleep 1
out << "- dary!\n"
end
end
end
run Stream
sinatra-1.4.7/examples/simple.rb 0000755 0000041 0000041 00000000133 12652356512 016657 0 ustar www-data www-data #!/usr/bin/env ruby -I ../lib -I lib
require 'sinatra'
get('/') { 'this is a simple app' }
sinatra-1.4.7/examples/chat.rb 0000755 0000041 0000041 00000002457 12652356512 016320 0 ustar www-data www-data #!/usr/bin/env ruby -I ../lib -I lib
# coding: utf-8
require 'sinatra'
set :server, 'thin'
connections = []
get '/' do
halt erb(:login) unless params[:user]
erb :chat, :locals => { :user => params[:user].gsub(/\W/, '') }
end
get '/stream', :provides => 'text/event-stream' do
stream :keep_open do |out|
connections << out
out.callback { connections.delete(out) }
end
end
post '/' do
connections.each { |out| out << "data: #{params[:msg]}\n\n" }
204 # response without entity body
end
__END__
@@ layout
Super Simple Chat with Sinatra
<%= yield %>
@@ login
@@ chat
sinatra-1.4.7/README.ko.md 0000644 0000041 0000041 00000247504 12652356512 015126 0 ustar www-data www-data # Sinatra
*주의: 이 문서는 영문판의 번역본이며 최신판 문서와 다를 수 있습니다.*
Sinatra는 최소한의 노력으로 루비 기반 웹 애플리케이션을 신속하게 만들 수 있게
해 주는 [DSL](https://en.wikipedia.org/wiki/Domain-specific_language)입니다.
```ruby
# myapp.rb
require 'sinatra'
get '/' do
'Hello world!'
end
```
아래의 명령어로 젬을 설치합니다.
```shell
gem install sinatra
```
아래의 명령어로 실행합니다.
```shell
ruby myapp.rb
```
[http://localhost:4567](http://localhost:4567) 를 확인해 보세요.
`gem install thin`도 함께 실행하기를 권장합니다.
thin이 설치되어 있을 경우 Sinatra는 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)
* [가능한 템플릿 언어들(Available Template Languages)](#가능한-템플릿-언어들available-template-languages)
* [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-와-중첩-레이아웃)
* [인라인 템플릿](#인라인-템플릿)
* [이름을 가지는 템플릿(Named Templates)](#이름을-가지는-템플릿named-templates)
* [파일 확장자 연결하기](#파일-확장자-연결하기)
* [나만의 고유한 템플릿 엔진 추가하기](#나만의-고유한-템플릿-엔진-추가하기)
* [템플릿 검사를 위한 커스텀 로직 사용하기](#템플릿-검사를-위한-커스텀-로직-사용하기)
* [필터(Filters)](#필터filters)
* [헬퍼(Helpers)](#헬퍼helpers)
* [세션(Sessions) 사용하기](#세션sessions-사용하기)
* [중단하기(Halting)](#중단하기halting)
* [넘기기(Passing)](#넘기기passing)
* [다른 라우터 부르기(Triggering Another Route)](#다른-라우터-부르기triggering-another-route)
* [본문, 상태 코드 및 헤더 설정하기](#본문-상태-코드-및-헤더-설정하기)
* [응답 스트리밍(Streaming Responses)](#응답-스트리밍streaming-responses)
* [로깅(Logging)](#로깅logging)
* [마임 타입(Mime Types)](#마임-타입mime-types)
* [URL 생성하기](#url-생성하기)
* [브라우저 재지정(Browser Redirect)](#브라우저-재지정browser-redirect)
* [캐시 컨트롤(Cache Control)](#캐시-컨트롤cache-control)
* [파일 전송하기(Sending Files)](#파일-전송하기sending-files)
* [요청 객체에 접근하기(Accessing the Request Object)](#요청-객체에-접근하기accessing-the-request-object)
* [첨부(Attachments)](#첨부attachments)
* [날짜와 시간 다루기](#날짜와-시간-다루기)
* [템플릿 파일 참조하기](#템플릿-파일-참조하기)
* [설정(Configuration)](#설정configuration)
* [공격 방어 설정하기(Configuring attack protection)](#공격-방어-설정하기configuring-attack-protection)
* [가능한 설정들(Available Settings)](#가능한-설정들available-settings)
* [환경(Environments)](#환경environments)
* [에러 처리(Error Handling)](#에러-처리error-handling)
* [찾을 수 없음(Not Found)](#찾을-수-없음not-found)
* [에러](#에러)
* [Rack 미들웨어(Rack Middleware)](#rack-미들웨어rack-middleware)
* [테스팅(Testing)](#테스팅testing)
* [Sinatra::Base - 미들웨어(Middleware), 라이브러리(Libraries), 그리고 모듈 앱(Modular Apps)](#sinatrabase---미들웨어middleware-라이브러리libraries-그리고-모듈-앱modular-apps)
* [모듈(Modular) vs. 전통적 방식(Classic Style)](#모듈modular-vs-전통적-방식classic-style)
* [모듈 애플리케이션(Modular Application) 제공하기](#모듈-애플리케이션modular-application-제공하기)
* [config.ru로 전통적 방식의 애플리케이션 사용하기](#configru로-전통적-방식의-애플리케이션-사용하기)
* [언제 config.ru를 사용할까?](#언제-configru를-사용할까)
* [Sinatra를 미들웨어로 사용하기](#sinatra를-미들웨어로-사용하기)
* [동적인 애플리케이션 생성(Dynamic Application Creation)](#동적인-애플리케이션-생성dynamic-application-creation)
* [범위(Scopes)와 바인딩(Binding)](#범위scopes와-바인딩binding)
* [애플리케이션/클래스 범위](#애플리케이션클래스-범위)
* [요청/인스턴스 범위](#요청인스턴스-범위)
* [위임 범위(Delegation Scope)](#위임-범위delegation-scope)
* [명령행(Command Line)](#명령행command-line)
* [다중 스레드(Multi-threading)](#다중-스레드multi-threading)
* [요구사항(Requirement)](#요구사항requirement)
* [최신(The Bleeding Edge)](#최신the-bleeding-edge)
* [Bundler를 사용하여](#bundler를-사용하여)
* [직접 하기(Roll Your Own)](#직접-하기roll-your-own)
* [전역으로 설치(Install Globally)](#전역으로-설치install-globally)
* [버저닝(Versioning)](#버저닝versioning)
* [더 읽을 거리(Further Reading)](#더-읽을-거리further-reading)
## 라우터(Routes)
Sinatra에서, 라우터(route)는 URL-매칭 패턴과 쌍을 이루는 HTTP 메서드입니다.
각각의 라우터는 블록과 연결됩니다.
```ruby
get '/' do
.. 무언가 보여주기(show) ..
end
post '/' do
.. 무언가 만들기(create) ..
end
put '/' do
.. 무언가 대체하기(replace) ..
end
patch '/' do
.. 무언가 수정하기(modify) ..
end
delete '/' do
.. 무언가 없애기(annihilate) ..
end
options '/' do
.. 무언가 주기(appease) ..
end
link '/' do
.. 무언가 관계맺기(affiliate) ..
end
unlink '/' do
.. 무언가 격리하기(separate) ..
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
```
라우터 패턴에는 스플랫(splat, 또는 와일드카드)도 매개변수도 포함될 수 있으며, 이럴 경우 `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|
# "GET /meta/hello/world", "GET /hello/world/1234" 등과 매치
"Hello, #{c}!"
end
```
라우터 패턴에는 선택적인(optional) 매개변수도 올 수 있습니다.
```ruby
get '/posts/:format?' do
# "GET /posts/" 는 물론 "GET /posts/json", "GET /posts/xml" 와 같은 어떤 확장자와도 매칭
end
```
쿼리 파라메터로도 이용가능 합니다.
```ruby
get '/posts' do
# matches "GET /posts?title=foo&author=bar"
title = params['title']
author = params['author']
# uses title and author variables; query is optional to the /posts route
end
```
한편, 경로 탐색 공격 방지(path traversal attack protection, 아래 참조)를 비활성화시키지 않았다면,
요청 경로는 라우터와 매칭되기 이전에 수정될 수 있습니다.
### 조건(Conditions)
라우터는 사용자 에이전트(user agent)같은 다양한 매칭 조건을 포함할 수 있습니다.
```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 Area, Access denied!"
end
get '/', :provides => 'html' do
haml :index
end
get '/', :provides => ['rss', 'atom', 'xml'] do
builder :feed
end
```
`provides`는 request의 허용된 해더를 검색합니다.
사용자 정의 조건도 쉽게 만들 수 있습니다.
```ruby
set(:probability) { |value| condition { rand <= value } }
get '/win_a_car', :probability => 0.1 do
"내가 졌소!"
end
get '/win_a_car' do
"미안해서 어쩌나."
end
```
여러 값을 받는 조건에는 스플랫(splat)을 사용합니다.
```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 상태 코드가 되는 어떠한 객체라도 반환할 수 있습니다.
* 세 요소를 가진 배열: `[상태 (Fixnum), 헤더 (Hash), 응답 본문 (#each에 반응)]`
* 두 요소를 가진 배열: `[상태 (Fixnum), 응답 본문 (#each에 반응)]`
* `#each`에 반응하고 주어진 블록으로 문자열만을 전달하는 객체
* 상태 코드를 의미하는 Fixnum
이것을 이용한 예를 들자면, 스트리밍(streaming) 예제를 쉽게 구현할 수 있습니다.
```ruby
class Stream
def each
100.times { |i| yield "#{i}\n" }
end
end
get('/') { Stream.new }
```
`stream` 헬퍼 메서드(아래 참조)를 사용하면 이런 번거로움을 줄이고 스트리밍 로직을 라우터 속에 포함 시킬 수도 있습니다.
### 커스텀 라우터 매처(Custom Route Matchers)
위에서 보듯, Sinatra에는 문자열 패턴 및 정규표현식을 이용한 라우터 매칭 지원이 내장되어 있습니다.
하지만, 그게 끝은 아닙니다. 여러분 만의 매처(matcher)도 쉽게 정의할 수 있습니다.
```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
```
또는 거꾸로 탐색(negative look ahead)할 수도 있습니다.
```ruby
get %r{^(?!/index$)} do
# ...
end
```
## 정적 파일(Static Files)
정적 파일들은 `./public` 디렉터리에서 제공됩니다. 위치를 다른 곳으로
변경하려면 `:public_folder` 옵션을 지정하면 됩니다.
```ruby
set :public_folder, File.dirname(__FILE__) + '/static'
```
public 디렉터리명은 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
```
템플릿은 두 번째 인자로 옵션값의 해시를 받습니다.
```ruby
get '/' do
erb :index, :layout => :post
end
```
이 구문은 `views/post.erb` 속에 내장된 `views/index.erb`를 렌더합니다.
(`views/layout.erb`파일이 존재할 경우 기본값은 `views/layout.erb`입니다.)
Sinatra가 이해하지 못하는 모든 옵션값들은 템플릿 엔진으로 전달됩니다.
```ruby
get '/' do
haml :index, :format => :html5
end
```
옵션값은 템플릿 언어별로 전역적으로 설정할 수도 있습니다.
```ruby
set :haml, :format => :html5
get '/' do
haml :index
end
```
render 메서드에서 전달된 옵션값들은 `set`을 통해 설정한 옵션값보다 우선합니다.
가능한 옵션값들은 다음과 같습니다.
- locals
-
문서로 전달되는 local 목록. 파셜과 함께 사용하기 좋음.
예제: 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
템플릿은 `./views` 디렉터리에 있는 것으로 가정됩니다. 뷰 디렉터리를
다른 곳으로 하고 싶으시면 이렇게 하세요.
```ruby
set :views, settings.root + '/templates'
```
템플릿은 언제나 심볼로 참조되어야 한다는 것에 주의하세요.
템플릿이 하위 디렉터리에 위치한 경우(그럴 경우에는 `:'subdir/template'`을
사용)에도 예외는 없습니다. 반드시 심볼이어야 하는 이유는, 문자열을 넘기면
렌더링 메서드가 전달된 문자열을 직접 렌더하기 때문입니다.
### 리터럴 템플릿(Literal Templates)
```ruby
get '/' do
haml '%div.title Hello World'
end
```
템플릿 문자열을 렌더합니다.
### 가능한 템플릿 언어들(Available Template Languages)
일부 언어는 여러 개의 구현이 있습니다. (스레드에 안전하게 thread-safe) 어느 구현을
사용할지 저정하려면, 먼저 require 하기만 하면 됩니다.
```ruby
require 'rdiscount' # or require 'bluecloth'
get('/') { markdown :index }
```
#### Haml 템플릿
| 의존성 |
haml |
| 파일 확장자 |
.haml |
| 예제 |
haml :index, :format => :html5 |
#### Erb 템플릿
| 의존성 |
erubis 또는 erb (루비 속에 포함) |
| 파일 확장자 |
.erb, .rhtml, .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 템플릿에서는 루비 메서드(`yield` 제외)를 호출할 수 없기
때문에, 거의 대부분의 경우 locals를 전달해야 합니다.
#### Markdown 템플릿
Markdown에서는 메서드 호출 뿐 아니라 locals 전달도 안됩니다.
따라서 일반적으로는 다른 렌더링 엔진과 함께 사용하게 됩니다.
```ruby
erb :overview, :locals => { :text => markdown(:introduction) }
```
다른 템플릿 속에서 `markdown` 메서드를 호출할 수도 있습니다.
```ruby
%h1 안녕 Haml!
%p= markdown(:greetings)
```
Markdown에서 루비를 호출할 수 없기 때문에, Markdown으로 작성된 레이아웃은
사용할 수 없습니다. 하지만, `:layout_engine` 옵션으로 레이아웃의 템플릿을
다른 렌더링 엔진으로 렌더링 할 수는 있습니다.
#### Textile 템플릿
| 의존성 |
RedCloth |
| 파일 확장자 |
.textile |
| 예제 |
textile :index, :layout_engine => :erb |
Textile에서는 메서드 호출 뿐 아니라 locals 전달도 안됩니다.
따라서 일반적으로는 다른 렌더링 엔진과 함께 사용하게 됩니다.
```ruby
erb :overview, :locals => { :text => textile(:introduction) }
```
다른 템플릿 속에서 `textile` 메서드를 호출할 수도 있습니다.
```ruby
%h1 안녕 Haml!
%p= textile(:greetings)
```
Textile에서 루비를 호출할 수 없기 때문에, 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에서 루비를 호출할 수 없기 때문에, RDoc으로 작성된 레이아웃은
사용할 수 없습니다. 하지만, `:layout_engine` 옵션으로 레이아웃의 템플릿을
다른 렌더링 엔진으로 렌더링 할 수는 있습니다.
#### AsciiDoc 템플릿
| 의존성 |
Asciidoctor |
| 파일 확장자 |
.asciidoc, .adoc and .ad |
| 예제 |
asciidoc :README, :layout_engine => :erb |
AsciiDoc 템플릿에서는 루비 메서드를 호출할 수 없기
때문에, 거의 대부분의 경우 locals를 전달해야 합니다.
#### Radius 템플릿
| 의존성 |
radius |
| 파일 확장자 |
.radius |
| 예제 |
radius :index, :locals => { :key => 'value' } |
Radius 템플릿에서는 루비 메서드를 호출할 수 없기
때문에, 거의 대부분의 경우 locals를 전달해야 합니다.
#### Markaby 템플릿
| 의존성 |
markaby |
| 파일확장 |
.mab |
| 예제 |
markaby { h1 "Welcome!" } |
인라인 템플릿으로 블록을 받을 수도 있습니다(예제 참조).
#### RABL 템플릿
| 의존성 |
rabl |
| 파일 확장자 |
.rabl |
| 예제 |
rabl :index |
#### Slim 템플릿
| 의존성 |
slim |
| 파일 확장자 |
.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에서 루비를 호출할 수 없기 때문에, Creole으로 작성된 레이아웃은
사용할 수 없습니다. 하지만, `:layout_engine` 옵션으로 레이아웃의 템플릿을
다른 렌더링 엔진으로 렌더링 할 수는 있습니다.
#### MediaWiki 템플릿
| 의존성 |
WikiCloth |
| 파일 확장자 |
.mediawiki and .mw |
| 예제 |
mediawiki :wiki, :layout_engine => :erb |
MediaWiki 마크업에서는 메서드 호출 뿐 아니라 locals 전달도 불가능합니다.
따라서 일반적으로는 다른 렌더링 엔진과 함께 사용하게 됩니다.
```ruby
erb :overview, :locals => { :text => mediawiki(:introduction) }
```
다른 템플릿 속에서 `mediawiki` 메서드를 호출할 수도 있습니다.
```ruby
%h1 Hello From Haml!
%p= mediawiki(:greetings)
```
MediaWiki에서 루비를 호출할 수 없기 때문에, MediaWiki으로 작성된 레이아웃은
사용할 수 없습니다. 하지만, `:layout_engine` 옵션으로 레이아웃의 템플릿을
다른 렌더링 엔진으로 렌더링 할 수는 있습니다.
#### CoffeeScript 템플릿
#### Stylus 템플릿
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'
|
템플릿 소스는 루비 문자열로 평가(evaluate)되고, 결과인 json 변수는 `#to_json`으로 변환됩니다.
```ruby
json = { :foo => 'bar' }
json[:baz] = key
```
`:callback`과 `:variable` 옵션은 렌더된 객체를 꾸미는데(decorate) 사용할 수 있습니다.
```javascript
var resource = {"foo":"bar","baz":"qux"};
present(resource);
```
#### WLang 템플릿
| 의존성 |
WLang |
| 파일 확장자 |
.wlang |
| 예제 |
wlang :index, :locals => { :key => 'value' } |
WLang 템플릿에서는 루비 메서드를 사용하는게 일반적이지 않기
때문에, 거의 대부분의 경우 locals를 전달합니다. 그래도
WLang으로 쓰여진 레이아웃과 `yield`는 지원합니다.
### 템플릿에서 변수에 접근하기
템플릿은 라우터 핸들러와 같은 맥락(context)에서 평가됩니다. 라우터
핸들러에서 설정한 인스턴스 변수들은 템플릿에서 직접 접근 가능합니다.
```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
```
이 방법은 주로 템플릿을 다른 템플릿 속에서 파셜(partial)로 렌더링할
때 사용됩니다.
### 템플릿에서의 `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` 메소드도 블럭을 지원합니다.
### 인라인 템플릿
템플릿은 소스 파일의 마지막에서 정의할 수도 있습니다.
```ruby
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
= yield
@@ index
%div.title Hello world.
```
참고: sinatra를 require한 소스 파일에 정의된 인라인 템플릿은 자동으로
로드됩니다. 다른 소스 파일에서 인라인 템플릿을 사용하려면 명시적으로
`enable :inline_templates`을 호출하면 됩니다.
### 이름을 가지는 템플릿(Named Templates)
템플릿은 톱 레벨(top-level)에서 `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`를 사용하면
됩니다. 예를 들어, `tt`라는 파일 확장자를 Textile 템플릿과 연결하고 싶다면,
다음과 같이 하면 됩니다.
```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 참조하세요.
### 템플릿 검사를 위한 커스텀 로직 사용하기
고유한 템플릿 룩업을 구현하기 위해서는 `#find_template` 메서드를 만드셔야 합니다.
```ruby
configure do
set :views [ './views/a', './views/b' ]
end
def find_template(views, name, engine, &block)
Array(views).each do |v|
super(v, name, engine, &block)
end
end
```
## 필터(Filters)
사전 필터(before filter)는 라우터와 동일한 맥락에서 매 요청 전에 평가되며
요청과 응답을 변형할 수 있습니다. 필터에서 설정된 인스턴스 변수들은 라우터와
템플릿에서 접근 가능합니다.
```ruby
before do
@note = 'Hi!'
request.path_info = '/foo/bar/baz'
end
get '/foo/*' do
@note #=> 'Hi!'
params['splat'] #=> 'bar/baz'
end
```
사후 필터(after filter)는 라우터와 동일한 맥락에서 매 요청 이후에 평가되며
마찬가지로 요청과 응답을 변형할 수 있습니다. 사전 필터와 라우터에서 설정된
인스턴스 변수들은 사후 필터에서 접근 가능합니다.
```ruby
after do
puts response.status
end
```
참고: 만약 라우터에서 `body` 메서드를 사용하지 않고 그냥 문자열만 반환한
경우라면, body는 나중에 생성되는 탓에, 아직 사후 필터에서 사용할 수 없을
것입니다.
필터는 패턴을 취할 수도 있으며, 이 경우 요청 경로가 그 패턴과 매치할
경우에만 필터가 평가될 것입니다.
```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
```
이 것은 모듈을 애플리케이션 클래스에 포함(include)시킨 것과 같습니다.
### 세션(Sessions) 사용하기
세션은 요청 동안에 상태를 유지하기 위해 사용합니다.
세션이 활성화되면, 사용자 세션 당 세션 해시 하나씩을 갖게 됩니다.
```ruby
enable :sessions
get '/' do
"value = " << session['value'].inspect
end
get '/:value' do
session['value'] = params['value']
end
```
`enable :sessions`은 실은 모든 데이터를 쿠키 속에 저장하는 것에 주의하세요.
이 방식이 바람직하지 않을 수도 있습니다. (예를 들어, 많은 양의 데이터를
저장하게 되면 트래픽이 늘어납니다).
이런 경우에는 랙 세션 미들웨어(Rack session middleware)를 사용할 수 있습니다.
`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
```
보안 강화을 위해서, 쿠키 속의 세션 데이터는 세션 시크릿(secret)으로
사인(sign)됩니다. 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
```
본문을 넣을 수도 있습니다.
```ruby
halt 'this will be the body'
```
둘 다 할 수도 있습니다.
```ruby
halt 401, 'go away!'
```
헤더를 추가할 경우에는 다음과 같이 하면 됩니다.
```ruby
halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
```
당연히 `halt`와 템플릿은 같이 사용할 수 있습니다.
```ruby
halt erb(:error)
```
### 넘기기(Passing)
라우터는 `pass`를 사용하여 다음 번 매칭되는 라우터로 처리를 넘길 수 있습니다.
```ruby
get '/guess/:who' do
pass unless params['who'] == 'Frank'
'You got me!'
end
get '/guess/*' do
'You missed!'
end
```
이 때 라우터 블록에서 즉각 빠져나오게 되고 제어는 다음 번 매칭되는 라우터로
넘어갑니다. 만약 매칭되는 라우터를 찾지 못하면, 404가 반환됩니다.
### 다른 라우터 부르기(Triggering Another Route)
때로는 `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 명세를 참고하세요.
### 본문, 상태 코드 및 헤더 설정하기
라우터 블록의 반환값과 함께 상태 코드(status code)와 응답 본문(response body)을
설정할수 있고 권장됩니다. 하지만, 경우에 따라서는 본문을 실행 흐름 중의 임의
지점에서 설정해야 할때도 있습니다. 이런 경우 `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`처럼, `header`와 `status`도 매개변수 없이 사용하여 현재 값을
액세스할 수 있습니다.
### 응답 스트리밍(Streaming Responses)
응답 본문의 일정 부분을 계속 생성하는 가운데 데이터를 내보내기 시작하고
싶을 경우가 있습니다. 극단적인 예제로, 클라이언트가 접속을 끊기 전까지
계속 데이터를 내보내고 싶을 경우도 있죠. 여러분만의 래퍼(wrapper)를
만들지 않으려면 `stream` 헬퍼를 사용하면 됩니다.
```ruby
get '/' do
stream do |out|
out << "It's gonna be legen -\n"
sleep 0.5
out << " (wait for it) \n"
sleep 1
out << "- dary!\n"
end
end
```
이렇게 스트리밍 API나 [서버 발송 이벤트Server Sent
Events](https://w3c.github.io/eventsource/)를 구현할 수 있고, 이 방법은
[WebSockets](https://en.wikipedia.org/wiki/WebSocket)을 위한 기반으로 사용됩니다.
이 방법은 일부 콘텐츠가 느린 자원에 의존하는 경우에 스로풋(throughtput)을
높이기 위해 사용되기도 합니다.
스트리밍 동작, 특히 동시 요청의 수는 애플리케이션을 서빙하는 웹서버에 크게
의존합니다. 일부의 경우 아예 스트리밍을 지원하지 조차 않습니다. 만약 서버가
스트리밍을 지원하지 않는다면, 본문은 `stream` 으로 전달된 블록이 수행을 마친
후에 한꺼번에 반환됩니다. 이런 한번에 쏘는 샷건같은 방식으로는 스트리밍은
움직이지 않습니다.
선택적 매개변수 `keep_open`이 설정되어 있다면, 스트림 객체에서 `close`를
호출하지 않을 것이고, 나중에 실행 흐름 상의 어느 시점에서 스트림을 닫을 수
있습니다. 이 옵션은 Thin과 Rainbow 같은 이벤트 기반 서버에서만 작동하고
다른 서버들은 여전히 스트림을 닫습니다.
```ruby
# long polling
set :server, :thin
connections = []
get '/subscribe' do
# register a client's interest in server events
stream(:keep_open) do |out|
connections << out
# purge dead connections
connections.reject!(&:closed?)
end
end
post '/:message' do
connections.each do |out|
# notify client that a new message has arrived
out << params['message'] << "\n"
# indicate client to connect again
out.close
end
# acknowledge
"message received"
end
```
### 로깅(Logging)
요청 스코프(request scope) 내에서, `Logger`의 인스턴스인 `logger`
헬퍼를 사용할 수 있습니다.
```ruby
get '/' do
logger.info "loading data"
# ...
end
```
이 로거는 자동으로 Rack 핸들러에서 설정한 로그설정을 참고합니다.
만약 로깅이 비활성상태라면, 이 메서드는 더미(dummy) 객체를 반환하기 때문에,
라우터나 필터에서 이 부분에 대해 걱정할 필요는 없습니다.
로깅은 `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 Types)
`send_file`이나 정적인 파일을 사용할 때에 Sinatra가 인식하지 못하는
마임 타입이 있을 수 있습니다. 이 경우 `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
```
이것은 리버스 프록시(reverse proxies)와 Rack 라우터가 있다면 참고합니다.
이 메서드는 `to`라는 별칭으로도 사용할 수 있습니다. (아래 예제 참조)
### 브라우저 재지정(Browser Redirect)
`redirect` 헬퍼 메서드를 사용하여 브라우저를 리다이렉트 시킬 수 있습니다.
```ruby
get '/foo' do
redirect to('/bar')
end
```
다른 부가적인 매개변수들은 `halt`에 전달하는 인자들과 비슷합니다.
```ruby
redirect to('/bar'), 303
redirect 'http://www.google.com/', 'wrong place, buddy'
```
`redirect back`을 사용하면 쉽게 사용자가 왔던 페이지로 다시 돌아가게
할 수 있습니다.
```ruby
get '/foo' do
"do something"
end
get '/bar' do
do_something
redirect back
end
```
리다이렉트와 함께 인자를 전달하려면, 쿼리로 붙이거나,
```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 헤더를 다음과 같이 간단하게 설정할 수 있습니다.
```ruby
get '/' do
cache_control :public
"cache it!"
end
```
프로 팁: 캐싱은 사전 필터에서 설정하세요.
```ruby
before do
cache_control :public, :must_revalidate, :max_age => 60
end
```
`expires` 헬퍼를 사용하여 그에 상응하는 헤더를 설정한다면,
`Cache-Control`이 자동으로 설정됩니다.
```ruby
before do
expires 500, :public, :must_revalidate
end
```
캐시를 잘 사용하려면, `etag` 또는 `last_modified`을 사용해 보세요.
무거운 작업을 하기 *전*에 이들 헬퍼를 호출하길 권장합니다. 이렇게 하면,
클라이언트 캐시에 현재 버전이 이미 들어 있을 경우엔 즉각 응답을
뿌릴(flush) 것입니다.
```ruby
get "/article/:id" do
@article = Article.find params['id']
last_modified @article.updated_at
etag @article.sha1
erb :article
end
```
[약한 ETag](https://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation)를
사용할 수 도 있습니다.
```ruby
etag @article.sha1, :weak
```
이들 헬퍼는 어떠한 캐싱도 하지 않으며, 대신 캐시에 필요한 정보를 제공합니다.
손쉬운 리버스 프록시(reverse-proxy) 캐싱 솔루션을 찾고 있다면,
[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
```
정적 파일에 `Cache-Control` 헤더 정보를 추가하려면 `:static_cache_control`
설정(아래 참조)을 쓰세요.
RFC 2616에 따르면 If-Match 또는 If-None-Match 헤더가 `*`로 설정된 경우 요청한
리소스(resource)가 이미 존재하느냐 여부에 따라 다르게 취급해야 한다고 되어
있습니다. Sinatra는 (get 처럼) 안전하거나 (put 처럼) 멱등인 요청에 대한 리소스는
이미 존재한다고 가정하지만, 다른 리소스(예를 들면 post 요청 같은)의 경우는
새 리소스로 취급합니다. 이 행동은 `:new_resource` 옵션을 전달하여 변경할 수 있습니다.
```ruby
get '/create' do
etag '', :new_resource => true
Article.create
erb :new_article
end
```
약한 ETag를 사용하고자 한다면, `:kind`으로 전달합시다.
```ruby
etag '', :new_resource => true, :kind => :weak
```
### 파일 전송하기(Sending Files)
응답(response)으로 파일의 컨탠츠를 리턴하려면, `send_file` 헬퍼 메서드를 사용하면 됩니다.
```ruby
get '/' do
send_file 'foo.png'
end
```
이 메서드는 몇 가지 옵션을 받습니다.
```ruby
send_file 'foo.png', :type => :jpg
```
옵션들:
- filename
- 응답에서 사용되는 파일명. 기본값은 실제 파일명.
- last_modified
- Last-Modified 헤더값. 기본값은 파일의 mtime.
- type
- Content-Type 헤더값. 없으면 파일 확장자로부터 유추.
- disposition
-
Content-Disposition 헤더값. 가능한 값들: nil (기본값),
:attachment 및 :inline
- length
- Content-Length 헤더값, 기본값은 파일 크기.
- status
-
전송할 상태 코드. 오류 페이지로 정적 파일을 전송할 경우에 유용.
Rack 핸들러가 지원할 경우, Ruby 프로세스로부터의 스트리밍이 아닌
다른 수단이 사용가능함. 이 헬퍼 메서드를 사용하게 되면, Sinatra는
자동으로 범위 요청(range request)을 처리함.
### 요청 객체에 접근하기(Accessing the Request Object)
들어오는 요청 객에는 요청 레벨(필터, 라우터, 오류 핸들러)에서 `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_HEADER"] # SOME_HEADER 헤더의 값
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에 의해 처리되는 로우(raw) env 해시
end
```
`script_name`, `path_info`같은 일부 옵션들은 이렇게 쓸 수도 있습니다.
```ruby
before { request.path_info = "/" }
get "/" do
"all requests end up here"
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
"store it!"
end
```
파일명을 전달할 수도 있습니다.
```ruby
get '/' do
attachment "info.txt"
"store it!"
end
```
### 날짜와 시간 다루기
Sinatra는 `time_for_` 헬퍼 메서드를 제공합니다. 이 메서드는
주어진 값으로부터 Time 객체를 생성한다. `DateTime`, `Date` 같은
비슷한 클래스들도 변환됩니다.
```ruby
get '/' do
pass if Time.now > time_for('Dec 23, 2012')
"still time"
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
```
여러분은 이것을 간단하게 확장(extension)으로 만들어 다른 사람들과 공유할 수 있다!
`find_template`은 그 파일이 실제 존재하는지 검사하지 않음에 유의합니다.
모든 가능한 경로에 대해 주어진 블록을 호출할 뿐입니다. 이것은 성능 문제는
되지 않습니다. 왜냐하면 `render`는 파일이 발견되는 즉시 `break`하기 때문입니다.
또한, 템플릿 위치(그리고 콘텐츠)는 개발 모드에서 실행 중이 아니라면 캐시될 것입니다.
정말로 멋진 메세드를 작성하고 싶다면 이 점을 명심하세요.
## 설정(Configuration)
모든 환경에서, 시작될 때, 한번만 실행되게 하려면 이렇게 하면 됩니다.
```ruby
configure do
# 옵션 하나 설정
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
```
### 공격 방어 설정하기(Configuring attack protection)
Sinatra는 [Rack::Protection](https://github.com/sinatra/rack-protection#readme)을 사용하여
일반적이고 일어날 수 있는 공격에 대비합니다. 이 모듈은 간단하게 비활성시킬 수 있습니다.
(하지만 애플리케이션에 엄청나게 많은 취약성을 야기합니다.)
```ruby
disable :protection
```
하나의 방어층만 스킵하려면, 옵션 해시에 `protection`을 설정하면 됩니다.
```ruby
set :protection, :except => :path_traversal
```
배열로 넘김으로써 방어층 여러 개를 비활성화할 수 있습니다.
```ruby
set :protection, :except => [:path_traversal, :session_hijacking]
```
기본적으로 `:sessions`가 활성 중일 때만 Sinatra는 방어층을 설정합니다.
때로는 자신만의 세션을 설정할 때도 있습니다. 이런 경우 `:session` 옵션을
넘겨줌으로써 세션을 기반으로한 방어층을 설정 할 수 있습니다.
```ruby
use Rack::Session::Pool
set :protection, :session => true
```
### 가능한 설정들(Available Settings)
- absolute_redirects
-
만약 비활성이면, Sinatra는 상대경로 리다이렉트를 허용할 것이지만,
이렇게 되면 Sinatra는 더 이상 오직 절대경로 리다이렉트만 허용하고 있는
RFC 2616(HTTP 1.1)에 위배됨.
-
적정하게 설정되지 않은 리버스 프록시 하에서 앱을 실행 중이라면 활성화시킬 것.
rul 헬퍼는, 만약 두 번째 매개변수로 false를 전달하지만 않는다면,
여전히 절대경로 URL을 생성할 것임에 유의.
- 기본값은 비활성.
- add_charset
-
content_type가 문자셋 정보에 자동으로 추가하게 될 마임(mime) 타입.
이 옵션은 오버라이딩하지 말고 추가해야 함.
settings.add_charset << "application/foobar"
- app_file
-
메인 애플리케이션 파일의 경로. 프로젝트 루트, 뷰, public 폴더,
인라인 템플릿을 파악할 때 사용됨.
- bind
- 바인드할 IP 주소(기본값: 0.0.0.0 이나
`environment`가 개발로 설정 되어있으면 localhost). 오직
빌트인(built-in) 서버에서만 사용됨.
- default_encoding
- 인코딩을 알 수 없을 때 인코딩(기본값은 "utf-8").
- dump_errors
- 로그안의 에러 출력.
- environment
-
현재 환경, 기본값은 ENV['RACK_ENV'] ENV에 없을 경우엔 "development".
- logging
- 로거(logger) 사용.
- lock
-
Ruby 프로세스 당 요청을 동시에 할 경우에만 매 요청에 걸쳐 잠금(lock)을 설정.
- 앱이 스레드에 안전(thread-safe)하지 않으면 활성화시킬 것. 기본값은 비활성.
- method_override
-
put/delete를 지원하지 않는 브라우저에서 put/delete 폼을 허용하는
_method 꼼수 사용.
- port
- 접속 포트. 빌트인 서버에서만 사용됨.
- prefixed_redirects
-
절대경로가 주어지지 않은 리다이렉트에 request.script_name를
삽입할지 여부를 결정. 활성화 하면 redirect '/foo'는
redirect to('/foo')처럼 동작. 기본값은 비활성.
- protection
- 웹 공격 방어를 활성화시킬 건지 여부. 위의 보안 섹션 참조.
- public_dir
- public_folder의 별칭. 밑을 참조.
- public_folder
-
public 파일이 제공될 폴더의 경로.
static 파일 제공이 활성화된 경우만 사용됨(아래 static참조).
만약 설정이 없으면 app_file로부터 유추됨.
- reload_templates
-
요청 간에 템플릿을 리로드(reload)할 건지 여부. 개발 모드에서는 활성됨.
- root
-
프로젝트 루트 디렉터리 경로. 설정이 없으면 app_file 설정으로부터 유추됨.
- raise_errors
-
예외 발생(애플리케이션은 중단됨).
기본값은 environment가 "test"인 경우는 활성, 그렇지 않으면 비활성.
- run
-
활성화되면, Sinatra가 웹서버의 시작을 핸들링.
rackup 또는 다른 도구를 사용하는 경우라면 활성화시키지 말 것.
- running
- 빌트인 서버가 실행 중인가? 이 설정은 변경하지 말 것!
- server
-
빌트인 서버로 사용할 서버 또는 서버 목록.
기본값은 루비구현에 따라 다르며 순서는 우선순위를 의미.
- sessions
-
Rack::Session::Cookie를 사용한 쿠키 기반 세션 활성화.
보다 자세한 정보는 '세션 사용하기' 참조.
- show_exceptions
-
예외 발생 시에 브라우저에 스택 추적을 보임.
기본값은 environment가 "development"인
경우는 활성, 나머지는 비활성.
-
:after_handler를 설정함으로써 브라우저에서
스택 트레이스를 보여주기 전에 앱에 특화된 에러 핸들링을
할 수도 있음.
- static
- Sinatra가 정적(static) 파일을 핸들링할 지 여부를 설정.
- 이 기능이 가능한 서버를 사용하는 경우라면 비활성시킬 것.
- 비활성시키면 성능이 올라감.
-
기본값은 전통적 방식에서는 활성, 모듈 앱에서는 비활성.
- 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가지의 미리 정의된 `environments` `"development"`, `"production"`, `"test"`
가 있습니다. 환경은 `RACK_ENV` 환경 변수를 통해서도 설정됩니다. 기본값은
`"development"`입니다. `"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)
예외 핸들러는 라우터 및 사전 필터와 동일한 맥락에서 실행됩니다.
이 말인즉, `haml`, `erb`, `halt`같은 이들이 제공하는 모든 것들을 사용할 수
있다는 뜻입니다.
### 찾을 수 없음(Not Found)
`Sinatra::NotFound` 예외가 발생하거나 또는 응답의 상태 코드가 404라면,
`not_found` 핸들러가 호출됩니다.
```ruby
not_found do
'아무 곳에도 찾을 수 없습니다.'
end
```
### 에러
`error` 핸들러는 라우터 또는 필터에서 뭐든 오류가 발생할 경우에 호출됩니다.
하지만 개발 환경에서는 예외 확인 옵션을 `:after_handler`로 설정되어 있을 경우에만
실행됨을 주의하세요.
```ruby
set :show_exceptions, :after_handler
```
예외 객체는 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
'액세스가 금지됨'
end
get '/secret' do
403
end
```
범위로 지정할 수도 있습니다.
```ruby
error 400..510 do
'어이쿠'
end
```
Sinatra는 개발 환경에서 동작할 때 브라우저에 괜찮은 스택 트레이스와 추가적인
디버그 정보를 보여주기 위해 특별한 `not_found` 와 `error` 핸들러를 설치합니다.
## Rack 미들웨어(Middleware)
Sinatra는 [Rack](http://rack.github.io/) 위에서 동작하며, Rack은 루비 웹
프레임워크를 위한 최소한의 표준 인터페이스입니다. 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://www.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#readme),
[Rack wiki](https://github.com/rack/rack/wiki/List-of-Middleware)
에서 유용한 미들웨어들을 찾을 수 있습니다.
## 테스팅(Testing)
Sinatra 테스트는 많은 Rack 기반 테스팅 라이브러리, 프레임워크를 사용하여 작성가능합니다.
그 중 [Rack::Test](http://www.rubydoc.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 "You're using Songbird!", last_response.body
end
end
```
주의: Sinatra를 모듈러 방식으로 사용한다면, `Sinatra::Application`
를 앱에서 사용하는 클래스 이름으로 바꾸세요.
## Sinatra::Base - 미들웨어(Middleware), 라이브러리(Libraries), 그리고 모듈 앱(Modular Apps)
톱레벨에서 앱을 정의하는 것은 마이크로 앱(micro-app) 수준에서는 잘 동작하지만,
Rack 미들웨어나, Rails 메탈(metal) 또는 서버 컴포넌트를 갖는 간단한 라이브러리,
또는 더 나아가 Sinatra 익스텐션(extension) 같은 재사용 가능한 컴포넌트들을 구축할
경우에는 심각한 약점이 있습니다. 톱레벨은 마이크로 앱 스타일의 설정을 가정하는 것
입니다. (즉, 하나의 단일 애플리케이션 파일과 `./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로 접근 가능한 것들과
동일합니다. 대부분의 톱레벨 앱들은 다음 두 가지만 수정하면 `Sinatra::Base` 컴포넌트로
변환 가능합니다.
* 파일은 `sinatra`가 아닌 `sinatra/base`를 require해야 합니다.
그렇지 않으면 모든 Sinatra의 DSL 메서드들이 메인 네임스페이스에 불러지게
됩니다.
* 앱의 라우터, 예외 핸들러, 필터, 옵션은 `Sinatra::Base`의 서브클래스에 두어야
합니다.
`Sinatra::Base`는 백지상태(blank slate)입니다. 빌트인 서버를 비롯한 대부분의 옵션들이
기본값으로 꺼져 있습니다. 가능한 옵션들과 그 작동에 대한 상세는 [옵션과
설정](http://www.sinatrarb.com/configuration.html)을 참조하세요.
### 모듈(Modular) vs. 전통적 방식(Classic Style)
일반적인 믿음과는 반대로, 전통적 방식에 잘못된 부분은 없습니다. 여러분 애플리케이션에
맞다면, 모듈 애플리케이션으로 전환할 필요는 없습니다.
모듈 방식이 아닌 전통적 방식을 사용할 경우 생기는 주된 단점은 루비 프로세스 당
하나의 Sinatra 애플리케이션만 사용할 수 있다는 점입니다. 만약 하나 이상을 사용할
계획이라면 모듈 방식으로 전환하세요. 모듈 방식과 전통적 방식을 섞어쓰지 못할
이유는 없습니다.
방식을 전환할 경우에는, 기본값 설정의 미묘한 차이에 유의해야 합니다.
| 설정 |
전통적 방식 |
모듈 |
| app_file |
sinatra를 로딩하는 파일 |
Sinatra::Base를 서브클래싱한 파일 |
| run |
$0 == app_file |
false |
| logging |
true |
false |
| method_override |
true |
false |
| inline_templates |
true |
false |
| static |
true |
File.exist?(public_folder) |
### 모듈 애플리케이션(Modular Application) 제공하기
모듈 앱을 시작하는 두 가지 일반적인 옵션이 있습니다.
`run!`으로 능동적으로 시작하는 방법은 이렇습니다.
```ruby
# my_app.rb
require 'sinatra/base'
class MyApp < Sinatra::Base
# ... 여기에 앱 코드가 온다 ...
# 루비 파일이 직접 실행될 경우에 서버를 시작
run! if app_file == $0
end
```
이렇게 시작할 수도 있습니다.
```shell
ruby my_app.rb
```
`config.ru`와 함께 사용할수도 있습니다. 이 경우는 어떠한 Rack 핸들러도 사용할 수 있도록
허용 합다.
```ruby
# config.ru
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를 최종점(endpoint)이 아니라, 오로지 미들웨어로만 사용하고자 할 때.
**모듈 방식으로 전환했다는 이유만으로 `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' && params['password'] == 'admin'
session['user_name'] = params['name']
else
redirect '/login'
end
end
end
class MyApp < Sinatra::Base
# 미들웨어는 사전 필터보다 앞서 실행됨
use LoginScreen
before do
unless session['user_name']
halt "접근 거부됨, 로그인 하세요."
end
end
get('/') { "Hello #{session['user_name']}." }
end
```
### 동적인 애플리케이션 생성(Dynamic Application Creation)
어떤 상수에 할당하지 않고 런타임에서 새 애플리케이션들을 생성하려면,
`Sinatra.new`를 쓰면 됩니다.
```ruby
require 'sinatra/base'
my_app = Sinatra.new { get('/') { "hi" } }
my_app.run!
```
선택적 인자로 상속할 애플리케이션을 받을 수 있습니다.
```ruby
# config.ru
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
```
이 방법은 Sintra 익스텐션을 테스팅하거나 또는 여러분의 라이브러리에서 Sinatra를
사용할 경우에 특히 유용합니다.
이 방법은 Sinatra를 미들웨어로 사용하는 것을 아주 쉽게 만들어 주기도 합니다.
```ruby
require 'sinatra/base'
use Sinatra do
get('/') { ... }
end
run RailsProject::Application
```
## 범위(Scopes)와 바인딩(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`의 값으로 사용된 Procs/blocks
* `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 (동일한 요청이 아님)
end
"라우터가 정의됨!"
end
end
```
요청 범위에는 이런 것들이 있습니다.
* get/head/post/put/delete/options 블록
* before/after 필터
* 헬퍼(helper) 메서드
* 템플릿/뷰
### 위임 범위(Delegation Scope)
위임 범위(delegation scope)는 메서드를 단순히 클래스 범위로 보냅니다(forward).
하지만 클래스 바인딩을 갖지 않기에 완전히 클래스 범위처럼 동작하지는 않습니다.
오직 명시적으로 위임(delegation) 표시된 메서드들만 사용 가능하고,
또한 클래스 범위와 변수/상태를 공유하지 않습니다 (유의: `self`가 다름).
`Sinatra::Delegator.delegate :method_name`을 호출하여 메서드 위임을 명시적으로
추가할 수 있습니다.
위임 범위에는 이런 것들이 있습니다.
* 톱레벨 바인딩, `require "sinatra"`를 한 경우
* `Sinatra::Delegator` 믹스인으로 확장된 객체
직접 코드를 살펴보길 바랍니다.
[Sinatra::Delegator 믹스인](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633)
은 [메인 객체를 확장한 것](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30)입니다.
## 명령행(Command Line)
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 # rack 서버/핸들러 지정 (기본값은 thin)
-x # mutex 잠금 켜기 (기본값은 off)
```
### 다중 스레드(Multi-threading)
_Konstantin의 [StackOverflow의 답변][so-answer]에서 가져왔습니다_
시나트라는 동시성 모델을 전혀 사용하지 않지만, Thin, Puma, WEBrick 같은
기저의 Rack 핸들러(서버)는 사용합니다. 시나트라 자신은 스레드에 안전하므로
랙 핸들러가 동시성 스레드 모델을 사용한다고해도 문제가 되지는 않습니다.
이는 서버를 시작할 때, 서버에 따른 정확한 호출 방법을 사용했을 때의
이야기입니다. 밑의 예제는 다중 스레드 Thin 서버를 시작하는 방법입니다.
```ruby
# app.rb
require 'sinatra/base'
class App < Sinatra::Base
get '/' do
"Hello, World"
end
end
App.run!
```
서버를 시작하는 명령어는 다음과 같습니다.
```shell
thin --threaded start
```
[so-answer]: http://stackoverflow.com/questions/6278817/is-sinatra-multi-threaded/6282999#6282999)
## 요구사항(Requirement)
다음의 루비 버전은 공식적으로 지원됩니다.
- Ruby 1.8.7
-
1.8.7은 완전하게 지원되지만, 꼭 그래야할 특별한 이유가 없다면,
1.9.2로 업그레이드하거나 또는 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으로 전환할 경우 모든
세션이 무효화되므로 주의하세요. 1.9.3에 대한 지원은 Sinatra 2.0 이전에는
중단되지 않을 것입니다.
- Ruby 2.x
-
2.x은 완전하게 지원되고 권장합니다. 현재 공식 지원 중지 계획은 없습니다.
- Rubinius
-
Rubinius는 공식적으로 지원됩니다. (Rubinius >= 2.x)
gem install puma를 권장합니다.
- JRuby
-
JRuby의 마지막 안정판은 공식적으로 지원됩니다. C 확장을 JRuby와 사용하는
것은 권장되지 않습니다.
gem install trinidad를 권장합니다.
새로 나오는 루비 버전도 주시하고 있습니다.
다음 루비 구현체들은 공식적으로 지원하지 않지만
여전히 Sinatra를 실행할 수 있는 것으로 알려져 있습니다.
* JRuby와 Rubinius 예전 버전
* Ruby Enterprise Edition
* MacRuby, Maglev, IronRuby
* Ruby 1.9.0 및 1.9.1 (이 버전들은 사용하지 말 것을 권합니다)
공식적으로 지원하지 않는다는 것의 의미는 무언가가 그 플랫폼에서만 잘못 동작하고,
지원되는 플랫폼에서는 정상적으로 동작할 경우, 우리의 문제가 아니라 그 플랫폼의 문제로
간주한다는 뜻입니다.
또한 우리는 CI를 ruby-head (MRI의 이후 릴리즈) 브랜치에 맞춰 실행하지만,
계속해서 변하고 있기 때문에 아무 것도 보장할 수는 없습니다.
앞으로 나올 2.x가 완전히 지원되길 기대합시다.
Sinatra는 선택한 루비 구현체가 지원하는 어떠한 운영체제에서도 작동해야
합니다.
MacRuby를 사용한다면, gem install control_tower 를 실행해 주세요.
현재 Cardinal, SmallRuby, BlueRuby 또는 1.8.7 이전의 루비 버전에서는
Sinatra를 실행할 수 없을 것입니다.
## 최신(The Bleeding Edge)
Sinatra의 가장 최근 코드를 사용하고자 한다면, 애플리케이션을 마스터 브랜치에 맞춰
실행하면 되므로 부담가지지 마세요. 하지만 덜 안정적일 것입니다.
주기적으로 사전배포(prerelease) 젬을 푸시하기 때문에, 최신 기능들을 얻기 위해
다음과 같이 할 수도 있습니다.
```shell
gem install sinatra --pre
```
### Bundler를 사용하여
여러분 애플리케이션을 최신 Sinatra로 실행하고자 한다면,
[Bundler](http://bundler.io)를 사용할 것을 권장합니다.
우선, 아직 설치하지 않았다면 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과 Tilt)은
Bundler가 자동으로 찾아서 추가할 것입니다.
이제 앱을 실행할 수 있습니다.
```shell
bundle exec ruby myapp.rb
```
### 직접 하기(Roll Your Own)
로컬 클론(clone)을 생성한 다음 `$LOAD_PATH`에 `sinatra/lib` 디렉터리를 주고
여러분 앱을 실행합니다.
```shell
cd myapp
git clone git://github.com/sinatra/sinatra.git
ruby -I sinatra/lib myapp.rb
```
이후에 Sinatra 소스를 업데이트하려면 이렇게 하세요.
```shell
cd myapp/sinatra
git pull
```
### 전역으로 설치(Install Globally)
젬을 직접 빌드할 수 있습니다.
```shell
git clone git://github.com/sinatra/sinatra.git
cd sinatra
rake sinatra.gemspec
rake install
```
만약 젬을 루트로 설치한다면, 마지막 단계는 다음과 같이 해야 합니다.
```shell
sudo rake install
```
## 버저닝(Versioning)
Sinatra는 [시맨틱 버저닝Semantic Versioning](http://semver.org/)
[(번역)](http://surpreem.com/archives/380)의 SemVer,
SemVerTag를 준수합니다.
## 더 읽을 거리(Further Reading)
* [프로젝트 웹사이트](http://www.sinatrarb.com/) - 추가 문서들, 뉴스,
그리고 다른 리소스들에 대한 링크.
* [기여하기](http://www.sinatrarb.com/contributing) - 버그를 찾았나요?
도움이 필요한가요? 패치를 하셨나요?
* [이슈 트래커](https://github.com/sinatra/sinatra/issues)
* [트위터](https://twitter.com/sinatra)
* [메일링 리스트](http://groups.google.com/group/sinatrarb/topics)
* IRC: [#sinatra](irc://chat.freenode.net/#sinatra) http://freenode.net
* 슬랙의 [Sinatra & Friends](https://sinatrarb.slack.com)입니다.
[여기](https://sinatra-slack.herokuapp.com/)에서 가입가능합니다.
* [Sinatra Book](https://github.com/sinatra/sinatra-book/) Cookbook 튜토리얼
* [Sinatra Recipes](http://recipes.sinatrarb.com/) 커뮤니티가 만드는 레시피
* http://www.rubydoc.info/에 있는 [최종 릴리스](http://www.rubydoc.info/gems/sinatra)
또는 [current HEAD](http://www.rubydoc.info/github/sinatra/sinatra)에 대한 API 문서
* [CI server](https://travis-ci.org/sinatra/sinatra)
sinatra-1.4.7/AUTHORS.md 0000644 0000041 0000041 00000006762 12652356512 014705 0 ustar www-data www-data Sinatra was designed and developed by Blake Mizerany in California.
### Current Team
* **Konstantin Haase** (maintainer)
* **Zachary Scott**
* **Kashyap Kondamudi**
* **Ashley Williams**
* **Trevor Bramble**
### Alumni
* **Blake Mizerany** (creator)
* **Ryan Tomayko**
* **Simon Rozet**
* **Katrina Owen**
### Thanks
Sinatra would not have been possible without strong company backing.
In the past, financial and emotional support have been provided mainly by
[Heroku](http://heroku.com), [GitHub](https://github.com) and
[Engine Yard](http://www.engineyard.com/), and is now taken care of by
[Travis CI](http://travis-ci.com/).
Special thanks to the following extraordinary individuals, without whom
Sinatra would not be possible:
* [Ryan Tomayko](http://tomayko.com/) (rtomayko) for constantly fixing
whitespace errors __60d5006__
* [Ezra Zygmuntowicz](http://brainspl.at/) (ezmobius) for initial help and
letting Blake steal some of merbs internal code.
* [Chris Schneider](http://gittr.com) (cschneid) for The Book, the blog,
[irclogger.com](http://irclogger.com/sinatra/), and a bunch of useful
patches.
* [Markus Prinz](http://nuclearsquid.com/) (cypher) for patches over the
years, caring about the README, and hanging in there when times were rough.
* [Erik Kastner](http://metaatem.net/) (kastner) for fixing `MIME_TYPES` under
Rack 0.5.
* [Ben Bleything](http://blog.bleything.net/) (bleything) for caring about HTTP
status codes and doc fixes.
* [Igal Koshevoy](http://twitter.com/igalko) (igal) for root path detection under
Thin/Passenger.
* [Jon Crosby](http://joncrosby.me/) (jcrosby) for coffee breaks, doc fixes, and
just because, man.
* [Karel Minarik](https://github.com/karmi) (karmi) for screaming until the
website came back up.
* [Jeremy Evans](http://code.jeremyevans.net/) (jeremyevans) for unbreaking
optional path params (twice!)
* [The GitHub guys](https://github.com/) for stealing Blake's table.
* [Nickolas Means](http://nmeans.org/) (nmeans) for Sass template support.
* [Victor Hugo Borja](https://github.com/vic) (vic) for splat'n routes specs and
doco.
* [Avdi Grimm](http://avdi.org/) (avdi) for basic RSpec support.
* [Jack Danger Canty](http://jåck.com/) for a more accurate root directory
and for making me watch [this](http://www.youtube.com/watch?v=ueaHLHgskkw) just
now.
* Mathew Walker for making escaped paths work with static files.
* Millions of Us for having the problem that led to Sinatra's conception.
* [Songbird](http://getsongbird.com/) for the problems that helped Sinatra's
future become realized.
* [Rick Olson](http://techno-weenie.net/) (technoweenie) for the killer plug
at RailsConf '08.
* Steven Garcia for the amazing custom artwork you see on 404's and 500's
* [Pat Nakajima](http://patnakajima.com/) (nakajima) for fixing non-nested
params in nested params Hash's.
* Gabriel Andretta for having people wonder whether our documentation is
actually in English or in Spanish.
* Vasily Polovnyov, Nickolay Schwarz, Luciano Sousa, Wu Jiang,
Mickael Riga, Bernhard Essl, Janos Hardi, Kouhei Yanagita and
"burningTyger" for willingly translating whatever ends up in the README.
* [Wordy](https://wordy.com/) for proofreading our README. **73e137d**
* cactus for digging through code and specs, multiple times.
* Nicolás Sanguinetti (foca) for strong demand of karma and shaping
helpers/register.
And last but not least:
* [Frank Sinatra](http://www.sinatra.com/) (chairman of the board) for having so much class he
deserves a web-framework named after him.
sinatra-1.4.7/README.ja.md 0000644 0000041 0000041 00000267267 12652356512 015117 0 ustar www-data www-data # Sinatra
*注)
本文書は英語から翻訳したものであり、その内容が最新でない場合もあります。最新の情報はオリジナルの英語版を参照して下さい。*
Sinatraは最小の労力でRubyによるWebアプリケーションを手早く作るための[DSL](https://ja.wikipedia.org/wiki/メインページドメイン固有言語)です。
```ruby
# myapp.rb
require 'sinatra'
get '/' do
'Hello world!'
end
```
gemをインストールし、
```shell
gem install sinatra
```
次のように実行します。
```shell
ruby myapp.rb
```
[http://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 テンプレート
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 テンプレート
#### 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 テンプレート
#### Stylus テンプレート
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](https://w3c.github.io/eventsource/)の実装を可能にし、[WebSockets](https://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://www.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](https://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/sinatra/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://www.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://www.rubydoc.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://www.sinatrarb.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 |
File.exist?(public_folder) |
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://bundler.io)を使うのがお薦めのやり方です。
まず、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://www.sinatrarb.com/) - ドキュメント、ニュース、他のリソースへのリンクがあります。
* [プロジェクトに参加(貢献)する](http://www.sinatrarb.com/contributing.html) - バグレポート パッチの送信、サポートなど
* [Issue tracker](https://github.com/sinatra/sinatra/issues)
* [Twitter](https://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://www.rubydoc.info/上のAPIドキュメント: [最新版(latest release)用](http://www.rubydoc.info/gems/sinatra)または[現在のHEAD用](http://www.rubydoc.info/github/sinatra/sinatra)
* [CIサーバ](https://travis-ci.org/sinatra/sinatra)
* [Greenbear Laboratory Rack日本語マニュアル](http://route477.net/w/RackReferenceJa.html)
sinatra-1.4.7/README.es.md 0000644 0000041 0000041 00000213173 12652356512 015117 0 ustar www-data www-data # Sinatra
*Atención: Este documento es una traducción de la versión en inglés y puede estar desactualizado.*
Sinatra es un
[DSL](https://es.wikipedia.org/wiki/Lenguaje_específico_del_dominio) para
crear aplicaciones web rápidamente en Ruby con un mínimo esfuerzo:
```ruby
# miapp.rb
require 'sinatra'
get '/' do
'Hola mundo!'
end
```
Instalar la gema y correr la aplicación con:
```shell
gem install sinatra
ruby miapp.rb
```
Ver en [http://localhost:4567](http://localhost:4567).
Se recomienda ejecutar `gem install thin`, porque Sinatra lo utilizará si está disponible.
## Rutas
En Sinatra, una ruta es un método HTTP junto a un patrón de un URL.
Cada ruta está asociada a un bloque:
```ruby
get '/' do
.. mostrar algo ..
end
post '/' do
.. crear algo ..
end
put '/' do
.. reemplazar algo ..
end
patch '/' do
.. modificar algo ..
end
delete '/' do
.. aniquilar algo ..
end
options '/' do
.. informar algo ..
end
link '/' do
.. afiliar a algo ..
end
unlink '/' do
.. separar algo ..
end
```
Las rutas son comparadas en el orden en el que son definidas. La primera ruta
que coincide con la petición es escogida.
Los patrones de las rutas pueden incluir parámetros nombrados, accesibles a
través del hash `params`:
```ruby
get '/hola/:nombre' do
# coincide con "GET /hola/foo" y "GET /hola/bar"
# params['nombre'] es 'foo' o 'bar'
"Hola #{params['nombre']}!"
end
```
También puede acceder a los parámetros nombrados usando parámetros de bloque:
```ruby
get '/hola/:nombre' do |n|
# coincide con "GET /hola/foo" y "GET /hola/bar"
# params['nombre'] es 'foo' o 'bar'
# n almacena params['nombre']
"Hola #{n}!"
end
```
Los patrones de ruta también pueden incluir parámetros splat (o wildcard),
accesibles a través del arreglo `params['splat']`:
```ruby
get '/decir/*/al/*' do
# coincide con /decir/hola/al/mundo
params['splat'] # => ["hola", "mundo"]
end
get '/descargar/*.*' do
# coincide con /descargar/path/al/archivo.xml
params['splat'] # => ["path/al/archivo", "xml"]
end
```
O, con parámetros de bloque:
```ruby
get '/descargar/*.*' do |path, ext|
[path, ext] # => ["path/al/archivo", "xml"]
end
```
Rutas con Expresiones Regulares:
```ruby
get /\A\/hola\/([\w]+)\z/ do
"Hola, #{params['captures'].first}!"
end
```
O con un parámetro de bloque:
```ruby
get %r{/hola/([\w]+)} do |c|
"Hola, #{c}!"
end
```
Los patrones de ruta pueden contener parámetros opcionales:
```ruby
get '/posts/:formato?' do
# coincide con "GET /posts/" y además admite cualquier extensión, por
# ejemplo, "GET /posts/json", "GET /posts/xml", etc.
end
```
A propósito, a menos que desactives la protección para el ataque *path
traversal* (ver más abajo), el path de la petición puede ser modificado
antes de que se compare con los de tus rutas.
## Condiciones
Las rutas pueden incluir una variedad de condiciones de selección, como por
ejemplo el user agent:
```ruby
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
"Estás usando la versión de Songbird #{params['agent'][0]}"
end
get '/foo' do
# Coincide con navegadores que no sean songbird
end
```
Otras condiciones disponibles son `host_name` y `provides`:
```ruby
get '/', :host_name => /^admin\./ do
"Área de Administración, Acceso denegado!"
end
get '/', :provides => 'html' do
haml :index
end
get '/', :provides => ['rss', 'atom', 'xml'] do
builder :feed
end
```
Puede definir sus propias condiciones fácilmente:
```ruby
set(:probabilidad) { |valor| condition { rand <= valor } }
get '/gana_un_auto', :probabilidad => 0.1 do
"Ganaste!"
end
get '/gana_un_auto' do
"Lo siento, perdiste."
end
```
Si su condición acepta más de un argumento, puede pasarle un arreglo. Al
definir la condición, se puede utilizar el operador splat en
la lista de parámetros:
```ruby
set(:autorizar) do |*roles| # <- mirá el splat
condition do
unless sesion_iniciada? && roles.any? {|rol| usuario_actual.tiene_rol? rol }
redirect "/iniciar_sesion/", 303
end
end
end
get "/mi/cuenta/", :autorizar => [:usuario, :administrador] do
"Detalles de mi cuenta"
end
get "/solo/administradores/", :autorizar => :administrador do
"Únicamente para administradores!"
end
```
### Valores de Retorno
El valor de retorno de un bloque de ruta que determina al menos el cuerpo de la
respuesta que se le pasa al cliente HTTP o al siguiente middleware en la pila
de Rack. Lo más común es que sea un string, como en los ejemplos anteriores.
Sin embargo, otros valores también son aceptados.
Puede devolver cualquier objeto que sea una respuesta Rack válida, un objeto
que represente el cuerpo de una respuesta Rack o un código de estado HTTP:
* Un arreglo con tres elementos: `[estado (Fixnum), cabeceras (Hash), cuerpo de
la respuesta (responde a #each)]`
* Un arreglo con dos elementos: `[estado (Fixnum), cuerpo de la respuesta
(responde a #each)]`
* Un objeto que responde a `#each` y que le pasa únicamente strings al bloque
dado
* Un Fixnum representando el código de estado
De esa manera, podemos fácilmente implementar un ejemplo de streaming:
```ruby
class Stream
def each
100.times { |i| yield "#{i}\n" }
end
end
get('/') { Stream.new }
```
### Comparadores de Rutas Personalizados
Como se mostró anteriormente, Sinatra permite utilizar strings y expresiones
regulares para definir las rutas. Sin embargo, la cosa no termina ahí. Podés
definir tus propios comparadores muy fácilmente:
```ruby
class PatronCualquieraMenos
Match = Struct.new(:captures)
def initialize(excepto)
@excepto = excepto
@capturas = Match.new([])
end
def match(str)
@capturas unless @excepto === str
end
end
def cualquiera_menos(patron)
PatronCualquieraMenos.new(patron)
end
get cualquiera_menos("/index") do
# ...
end
```
Tenga en cuenta que el ejemplo anterior es un poco rebuscado. Un resultado
similar puede conseguirse más sencillamente:
```ruby
get // do
pass if request.path_info == "/index"
# ...
end
```
O, usando un lookahead negativo:
```ruby
get %r{^(?!/index$)} do
# ...
end
```
### Archivos Estáticos
Los archivos estáticos son servidos desde el directorio público
`./public`. Puede especificar una ubicación diferente ajustando la
opción `:public_folder`:
```ruby
set :public_folder, File.dirname(__FILE__) + '/estaticos'
```
Note que el nombre del directorio público no está incluido en la URL. Por
ejemplo, el archivo `./public/css/style.css` se accede a través de
`http://ejemplo.com/css/style.css`.
Use la configuración `:static_cache_control` para agregar el encabezado
`Cache-Control` (ver la sección de configuración para más detalles).
### Vistas / Plantillas
Cada lenguaje de plantilla se expone a través de un método de renderizado que
lleva su nombre. Estos métodos simplemente devuelven un string:
```ruby
get '/' do
erb :index
end
```
Renderiza `views/index.erb`.
En lugar del nombre de la plantilla podés proporcionar directamente el
contenido de la misma:
```ruby
get '/' do
codigo = "<%= Time.now %>"
erb codigo
end
```
Los métodos de renderizado, aceptan además un segundo argumento, el hash de
opciones:
```ruby
get '/' do
erb :index, :layout => :post
end
```
Renderiza `views/index.erb` incrustado en `views/post.erb` (por
defecto, la plantilla `:index` es incrustada en `views/layout.erb` siempre y
cuando este último archivo exista).
Cualquier opción que Sinatra no entienda le será pasada al motor de renderizado
de la plantilla:
```ruby
get '/' do
haml :index, :format => :html5
end
```
Además, puede definir las opciones para un lenguaje de plantillas de forma
general:
```ruby
set :haml, :format => :html5
get '/' do
haml :index
end
```
Las opciones pasadas al método de renderizado tienen precedencia sobre las
definidas mediante `set`.
Opciones disponibles:
- locals
-
Lista de variables locales pasadas al documento. Resultan muy útiles cuando
se combinan con parciales.
Ejemplo: erb "<%= foo %>", :locals => {:foo => "bar"}
- default_encoding
-
Encoding utilizado cuando el de un string es dudoso. Por defecto toma el
valor de settings.default_encoding.
- views
-
Directorio desde donde se cargan las vistas. Por defecto toma el valor de
settings.views.
- layout
-
Si es true o false indica que se debe usar, o no, un layout,
respectivamente. También puede ser un símbolo que especifique qué plantilla
usar. Ejemplo: erb :index, :layout => !request.xhr?
- content_type
-
Content-Type que produce la plantilla. El valor por defecto depende de cada
lenguaje de plantillas.
- scope
-
Ámbito en el que se renderiza la plantilla. Por defecto utiliza la instancia
de la aplicación. Tené en cuenta que si cambiás esta opción las variables de
instancia y los helpers van a dejar de estar disponibles.
- layout_engine
-
Motor de renderizado de plantillas que usa para el layout. Resulta
conveniente para lenguajes que no soportan layouts. Por defecto toma el valor
del motor usado para renderizar la plantilla.
Ejemplo: set :rdoc, :layout_engine => :erb
-
Se asume que las plantillas están ubicadas directamente bajo el directorio
./views. Para usar un directorio de vistas diferente:
set :views, settings.root + '/plantillas'
-
Es importante acordarse que siempre tenés que referenciar a las plantillas con
símbolos, incluso cuando se encuentran en un subdirectorio (en este caso
tenés que usar: `:'subdir/plantilla'` o `'subdir/plantilla'.to_sym`). Tenés que
usar un símbolo porque los métodos de renderización van a renderizar
directamente cualquier string que se les pase como argumento.
### Lenguajes de Plantillas Disponibles
Algunos lenguajes tienen varias implementaciones. Para especificar que
implementación usar (y para ser thread-safe), deberías requerirla antes de
usarla:
```ruby
require 'rdiscount' # o require 'bluecloth'
get('/') { markdown :index }
```
### Plantillas Haml
| Dependencias |
haml |
| Expresiones de Archivo |
.haml |
| Ejemplo |
haml :index, :format => :html5 |
### Plantillas Erb
| Dependencias |
erubis
o erb (incluida en Ruby)
|
| Extensiones de Archivo |
.erb, .rhtml o .erubis (solamente con Erubis) |
| Ejemplo |
erb :index |
### Plantillas Builder
| Dependencias |
builder
|
| Extensiones de Archivo |
.builder |
| Ejemplo |
builder { |xml| xml.em "hola" } |
Además, acepta un bloque con la definición de la plantilla (ver ejemplo).
### Plantillas Nokogiri
| Dependencias |
nokogiri |
| Extensiones de Archivo |
.nokogiri |
| Ejemplo |
nokogiri { |xml| xml.em "hola" } |
Además, acepta un bloque con la definición de la plantilla (ver ejemplo).
### Plantillas Sass
| Dependencias |
sass |
| Extensiones de Archivo |
.sass |
| Ejemplo |
sass :stylesheet, :style => :expanded |
### Plantillas SCSS
| Dependencias |
sass |
| Extensiones de Archivo |
.scss |
| Ejemplo |
scss :stylesheet, :style => :expanded |
### Plantillas Less
| Dependencias |
less |
| Extensiones de Archivo |
.less |
| Ejemplo |
less :stylesheet |
### Plantillas Liquid
| Dependencias |
liquid |
| Extensiones de Archivo |
.liquid |
| Ejemplo |
liquid :index, :locals => { :clave => 'valor' } |
Como no va a poder llamar a métodos de Ruby (excepto por `yield`) desde una
plantilla Liquid, casi siempre va a querer pasarle locales.
### Plantillas Markdown
No es posible llamar métodos desde markdown, ni pasarle locales. Por lo tanto,
generalmente va a usarlo en combinación con otro motor de renderizado:
```ruby
erb :resumen, :locals => { :texto => markdown(:introduccion) }
```
Tenga en cuenta que también podés llamar al método `markdown` desde otras
plantillas:
```ruby
%h1 Hola Desde Haml!
%p= markdown(:saludos)
```
Como no puede utilizar Ruby desde Markdown, no puede usar layouts escritos en
Markdown. De todos modos, es posible usar un motor de renderizado para el
layout distinto al de la plantilla pasando la opción `:layout_engine`.
### Plantillas Textile
| Dependencias |
RedCloth |
| Extensiones de Archivo |
.textile |
| Ejemplo |
textile :index, :layout_engine => :erb |
No es posible llamar métodos desde textile, ni pasarle locales. Por lo tanto,
generalmente vas a usarlo en combinación con otro motor de renderizado:
```ruby
erb :resumen, :locals => { :texto => textile(:introduccion) }
```
Tené en cuenta que también podés llamar al método `textile` desde otras
plantillas:
```ruby
%h1 Hola Desde Haml!
%p= textile(:saludos)
```
Como no podés utilizar Ruby desde Textile, no podés usar layouts escritos en
Textile. De todos modos, es posible usar un motor de renderizado para el
layout distinto al de la plantilla pasando la opción `:layout_engine`.
### Plantillas RDoc
| Dependencias |
RDoc |
| Extensiones de Archivo |
.rdoc |
| Ejemplo |
rdoc :README, :layout_engine => :erb |
No es posible llamar métodos desde rdoc, ni pasarle locales. Por lo tanto,
generalmente vas a usarlo en combinación con otro motor de renderizado:
```ruby
erb :resumen, :locals => { :texto => rdoc(:introduccion) }
```
Tené en cuenta que también podés llamar al método `rdoc` desde otras
plantillas:
```ruby
%h1 Hola Desde Haml!
%p= rdoc(:saludos)
```
Como no podés utilizar Ruby desde RDoc, no podés usar layouts escritos en RDoc.
De todos modos, es posible usar un motor de renderizado para el layout distinto
al de la plantilla pasando la opción `:layout_engine`.
### Plantillas Radius
| Dependencias |
Radius |
| Extensiones de Archivo |
.radius |
| Ejemplo |
radius :index, :locals => { :clave => 'valor' } |
Desde que no se puede utilizar métodos de Ruby (excepto por `yield`) de una
plantilla Radius, casi siempre se necesita pasar locales.
### Plantillas Markaby
| Dependencias |
Markaby |
| Extensiones de Archivo |
.mab |
| Ejemplo |
markaby { h1 "Bienvenido!" } |
Además, acepta un bloque con la definición de la plantilla (ver ejemplo).
### Plantillas RABL
| Dependencias |
Rabl |
| Extensiones de Archivo |
.rabl |
| Ejemplo |
rabl :index |
### Plantillas Slim
| Dependencias |
Slim Lang |
| Extensiones de Archivo |
.slim |
| Ejemplo |
slim :index |
### Plantillas Creole
| Dependencias |
Creole |
| Extensiones de Archivo |
.creole |
| Ejemplo |
creole :wiki, :layout_engine => :erb |
No es posible llamar métodos desde creole, ni pasarle locales. Por lo tanto,
generalmente va a usarlo en combinación con otro motor de renderizado:
```ruby
erb :resumen, :locals => { :texto => cerole(:introduccion) }
```
Debe tomar en cuenta que también puede llamar al método `creole` desde otras
plantillas:
```ruby
%h1 Hola Desde Haml!
%p= creole(:saludos)
```
Como no podés utilizar Ruby desde Creole, no podés usar layouts escritos en
Creole. De todos modos, es posible usar un motor de renderizado para el layout
distinto al de la plantilla pasando la opción `:layout_engine`.
### Plantillas CoffeeScript
### Plantillas Stylus
### Plantillas Yajl
| Dependencias |
yajl-ruby |
| Extensiones de Archivo |
.yajl |
| Ejemplo |
yajl :index,
:locals => { :key => 'qux' },
:callback => 'present',
:variable => 'resource'
|
El contenido de la plantilla se evalúa como código Ruby, y la variable `json` es convertida a JSON mediante `#to_json`.
```ruby
json = { :foo => 'bar' }
json[:baz] = key
```
Las opciones `:callback` y `:variable` se pueden utilizar para decorar el objeto renderizado:
```ruby
var resource = {"foo":"bar","baz":"qux"}; present(resource);
```
### Plantillas WLang
| Dependencias |
wlang |
| Extensiones de Archivo |
.wlang |
| Ejemplo |
wlang :index, :locals => { :clave => 'valor' } |
Como no vas a poder llamar a métodos de Ruby (excepto por `yield`) desde una
plantilla WLang, casi siempre vas a querer pasarle locales.
### Plantillas Embebidas
```ruby
get '/' do
haml '%div.titulo Hola Mundo'
end
```
Renderiza el template embebido en el string.
### Accediendo a Variables en Plantillas
Las plantillas son evaluadas dentro del mismo contexto que los manejadores de
ruta. Las variables de instancia asignadas en los manejadores de ruta son
accesibles directamente por las plantillas:
```ruby
get '/:id' do
@foo = Foo.find(params['id'])
haml '%h1= @foo.nombre'
end
```
O es posible especificar un Hash de variables locales explícitamente:
```ruby
get '/:id' do
foo = Foo.find(params['id'])
haml '%h1= bar.nombre', :locals => { :bar => foo }
end
```
Esto es usado típicamente cuando se renderizan plantillas como parciales desde
adentro de otras plantillas.
### Plantillas Inline
Las plantillas pueden ser definidas al final del archivo fuente:
```ruby
require 'rubygems'
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
= yield
@@ index
%div.titulo Hola mundo!!!!!
```
NOTA: únicamente las plantillas inline definidas en el archivo fuente que
requiere Sinatra son cargadas automáticamente. Llamá `enable
:inline_templates` explícitamente si tenés plantillas inline en otros
archivos fuente.
### Plantillas Nombradas
Las plantillas también pueden ser definidas usando el método top-level
`template`:
```ruby
template :layout do
"%html\n =yield\n"
end
template :index do
'%div.titulo Hola Mundo!'
end
get '/' do
haml :index
end
```
Si existe una plantilla con el nombre "layout", va a ser usada cada vez que
una plantilla es renderizada. Podés desactivar los layouts individualmente
pasando `:layout => false` o globalmente con
`set :haml, :layout => false`:
```ruby
get '/' do
haml :index, :layout => !request.xhr?
end
```
### Asociando Extensiones de Archivo
Para asociar una extensión de archivo con un motor de renderizado, usá
`Tilt.register`. Por ejemplo, si querés usar la extensión `tt` para
las plantillas Textile, podés hacer lo siguiente:
```ruby
Tilt.register :tt, Tilt[:textile]
```
### Agregando Tu Propio Motor de Renderizado
Primero, registrá tu motor con Tilt, y después, creá tu método de renderizado:
```ruby
Tilt.register :mipg, MiMotorParaPlantillaGenial
helpers do
def mypg(*args) render(:mypg, *args) end
end
get '/' do
mypg :index
end
```
Renderiza `./views/index.mypg`. Mirá https://github.com/rtomayko/tilt
para aprender más de Tilt.
## Filtros
Los filtros `before` son evaluados antes de cada petición dentro del mismo
contexto que las rutas. Pueden modificar la petición y la respuesta. Las
variables de instancia asignadas en los filtros son accesibles por las rutas y
las plantillas:
```ruby
before do
@nota = 'Hey!'
request.path_info = '/foo/bar/baz'
end
get '/foo/*' do
@nota #=> 'Hey!'
params['splat'] #=> 'bar/baz'
end
```
Los filtros `after` son evaluados después de cada petición dentro del mismo
contexto y también pueden modificar la petición y la respuesta. Las variables
de instancia asignadas en los filtros `before` y en las rutas son accesibles por
los filtros `after`:
```ruby
after do
puts response.status
end
```
Nota: A menos que uses el método `body` en lugar de simplemente devolver un
string desde una ruta, el cuerpo de la respuesta no va a estar disponible en
un filtro after, debido a que todavía no se ha generado.
Los filtros aceptan un patrón opcional, que cuando está presente causa que los
mismos sean evaluados únicamente si el path de la petición coincide con ese
patrón:
```ruby
before '/protegido/*' do
autenticar!
end
after '/crear/:slug' do |slug|
session[:ultimo_slug] = slug
end
```
Al igual que las rutas, los filtros también pueden aceptar condiciones:
```ruby
before :agent => /Songbird/ do
# ...
end
after '/blog/*', :host_name => 'ejemplo.com' do
# ...
end
```
## Ayudantes
Usá el método top-level *helpers* para definir métodos ayudantes que
pueden ser utilizados dentro de los manejadores de rutas y las plantillas:
```ruby
helpers do
def bar(nombre)
"#{nombre}bar"
end
end
get '/:nombre' do
bar(params['nombre'])
end
```
Por cuestiones organizativas, puede resultar conveniente organizar los métodos
ayudantes en distintos módulos:
```ruby
module FooUtils
def foo(nombre) "#{nombre}foo" end
end
module BarUtils
def bar(nombre) "#{nombre}bar" end
end
helpers FooUtils, BarUtils
```
El efecto de utilizar *helpers* de esta manera es el mismo que resulta de
incluir los módulos en la clase de la aplicación.
### Usando Sesiones
Una sesión es usada para mantener el estado a través de distintas peticiones.
Cuando están activadas, proporciona un hash de sesión para cada sesión de usuario:
```ruby
enable :sessions
get '/' do
"valor = " << session[:valor].inspect
end
get '/:valor' do
session[:valor] = params['valor']
end
```
Tené en cuenta que `enable :sessions` guarda todos los datos en una
cookie, lo cual no es siempre deseable (guardar muchos datos va a incrementar
el tráfico, por citar un ejemplo). Podés usar cualquier middleware Rack para
manejar sesiones, de la misma manera que usarías cualquier otro middleware,
pero con la salvedad de que *no* tenés que llamar a `enable :sessions`:
```ruby
use Rack::Session::Pool, :expire_after => 2592000
get '/' do
"valor = " << session[:valor].inspect
end
get '/:valor' do
session[:valor] = params['valor']
end
```
Para incrementar la seguridad, los datos de la sesión almacenados en
la cookie son firmados con un secreto de sesión. Este secreto, es
generado aleatoriamente por Sinatra. De cualquier manera, hay que
tener en cuenta que cada vez que inicies la aplicación se va a generar
uno nuevo. Así, si querés que todas las instancias de tu aplicación
compartan un único secreto, tenés que definirlo vos:
```ruby
set :session_secret, 'super secreto'
```
Si necesitás una configuración más específica, `sessions` acepta un
Hash con opciones:
```ruby
set :sessions, :domain => 'foo.com'
```
### Interrupción
Para detener inmediatamente una petición dentro de un filtro o una ruta usá:
```ruby
halt
```
También podés especificar el estado:
```ruby
halt 410
```
O el cuerpo:
```ruby
halt 'esto va a ser el cuerpo'
```
O los dos:
```ruby
halt 401, 'salí de acá!'
```
Con cabeceras:
```ruby
halt 402, { 'Content-Type' => 'text/plain' }, 'venganza'
```
Obviamente, es posible utilizar `halt` con una plantilla:
```ruby
halt erb(:error)
```
### Paso
Una ruta puede pasarle el procesamiento a la siguiente ruta que coincida con
la petición usando `pass`:
```ruby
get '/adivina/:quien' do
pass unless params['quien'] == 'Franco'
'Adivinaste!'
end
get '/adivina/*' do
'Erraste!'
end
```
Se sale inmediatamente del bloque de la ruta y se le pasa el control a la
siguiente ruta que coincida. Si no coincide ninguna ruta, se devuelve 404.
### Ejecutando Otra Ruta
Cuando querés obtener el resultado de la llamada a una ruta, `pass` no te va a
servir. Para lograr esto, podés usar `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
```
Notá que en el ejemplo anterior, es conveniente mover `"bar"` a un
helper, y llamarlo desde `/foo` y `/bar`. Así, vas a simplificar
las pruebas y a mejorar el rendimiento.
Si querés que la petición se envíe a la misma instancia de la aplicación en
lugar de otra, usá `call!` en lugar de `call`.
En la especificación de Rack podés encontrar más información sobre
`call`.
### Asignando el Código de Estado, los Encabezados y el Cuerpo de una Respuesta
Es posible, y se recomienda, asignar el código de estado y el cuerpo de una
respuesta con el valor de retorno de una ruta. De cualquier manera, en varios
escenarios, puede que sea conveniente asignar el cuerpo en un punto arbitrario
del flujo de ejecución con el método `body`. A partir de ahí, podés usar ese
mismo método para acceder al cuerpo de la respuesta:
```ruby
get '/foo' do
body "bar"
end
after do
puts body
end
```
También es posible pasarle un bloque a `body`, que será ejecutado por el Rack
handler (podés usar esto para implementar streaming, mirá "Valores de retorno").
De manera similar, también podés asignar el código de estado y encabezados:
```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
```
También, al igual que `body`, tanto `status` como `headers` pueden utilizarse
para obtener sus valores cuando no se les pasa argumentos.
### Streaming De Respuestas
A veces vas a querer empezar a enviar la respuesta a pesar de que todavía no
terminaste de generar su cuerpo. También es posible que, en algunos casos,
quieras seguir enviando información hasta que el cliente cierre la conexión.
Cuando esto ocurra, el helper `stream` te va a ser de gran ayuda:
```ruby
get '/' do
stream do |out|
out << "Esto va a ser legen -\n"
sleep 0.5
out << " (esperalo) \n"
sleep 1
out << "- dario!\n"
end
end
```
Podés implementar APIs de streaming,
[Server-Sent Events](https://w3c.github.io/eventsource/) y puede ser usado
como base para [WebSockets](https://es.wikipedia.org/wiki/WebSockets). También
puede ser usado para incrementar el throughput si solo una parte del contenido
depende de un recurso lento.
Hay que tener en cuenta que el comportamiento del streaming, especialmente el
número de peticiones concurrentes, depende del servidor web utilizado para
alojar la aplicación. Puede que algunos servidores no soporten streaming
directamente, así el cuerpo de la respuesta será enviado completamente de una
vez cuando el bloque pasado a `stream` finalice su ejecución. Si estás usando
Shotgun, el streaming no va a funcionar.
Cuando se pasa `keep_open` como parámetro, no se va a enviar el mensaje
`close` al objeto de stream. Queda en vos cerrarlo en el punto de ejecución
que quieras. Nuevamente, hay que tener en cuenta que este comportamiento es
posible solo en servidores que soporten eventos, como Thin o Rainbows. El
resto de los servidores van a cerrar el stream de todos modos:
```ruby
set :server, :thin
conexiones = []
get '/' do
# mantenemos abierto el stream
stream(:keep_open) { |salida| conexiones << salida }
end
post '/' do
# escribimos a todos los streams abiertos
conexiones.each { |salida| salida << params['mensaje'] << "\n" }
"mensaje enviado"
end
```
### Log (Registro)
En el ámbito de la petición, el helper `logger` (registrador) expone
una instancia de `Logger`:
```ruby
get '/' do
logger.info "cargando datos"
# ...
end
```
Este logger tiene en cuenta la configuración de logueo de tu Rack
handler. Si el logueo está desactivado, este método va a devolver un
objeto que se comporta como un logger pero que en realidad no hace
nada. Así, no vas a tener que preocuparte por esta situación.
Tené en cuenta que el logueo está habilitado por defecto únicamente
para `Sinatra::Application`. Si heredaste de
`Sinatra::Base`, probablemente quieras habilitarlo manualmente:
```ruby
class MiApp < Sinatra::Base
configure :production, :development do
enable :logging
end
end
```
Para evitar que se inicialice cualquier middleware de logging, configurá
`logging` a `nil`. Tené en cuenta que, cuando hagas esto, `logger` va a
devolver `nil`. Un caso común es cuando querés usar tu propio logger. Sinatra
va a usar lo que encuentre en `env['rack.logger']`.
### Tipos Mime
Cuando usás `send_file` o archivos estáticos tal vez tengas tipos mime
que Sinatra no entiende. Usá `mime_type` para registrarlos a través de la
extensión de archivo:
```ruby
configure do
mime_type :foo, 'text/foo'
end
```
También lo podés usar con el ayudante `content_type`:
```ruby
get '/' do
content_type :foo
"foo foo foo"
end
```
### Generando URLs
Para generar URLs deberías usar el método `url`. Por ejemplo, en Haml:
```ruby
%a{:href => url('/foo')} foo
```
Tiene en cuenta proxies inversos y encaminadores de Rack, si están presentes.
Este método también puede invocarse mediante su alias `to` (mirá un ejemplo
a continuación).
### Redirección del Navegador
Podés redireccionar al navegador con el método `redirect`:
```ruby
get '/foo' do
redirect to('/bar')
end
```
Cualquier parámetro adicional se utiliza de la misma manera que los argumentos
pasados a `halt`:
```ruby
redirect to('/bar'), 303
redirect 'http://www.google.com/', 'te confundiste de lugar, compañero'
```
También podés redireccionar fácilmente de vuelta hacia la página desde donde
vino el usuario con `redirect back`:
```ruby
get '/foo' do
"hacer algo"
end
get '/bar' do
hacer_algo
redirect back
end
```
Para pasar argumentos con una redirección, podés agregarlos a la cadena de
búsqueda:
```ruby
redirect to('/bar?suma=42')
```
O usar una sesión:
```ruby
enable :sessions
get '/foo' do
session[:secreto] = 'foo'
redirect to('/bar')
end
get '/bar' do
session[:secreto]
end
```
### Cache Control
Asignar tus encabezados correctamente es el cimiento para realizar un cacheo
HTTP correcto.
Podés asignar el encabezado Cache-Control fácilmente:
```ruby
get '/' do
cache_control :public
"cachealo!"
end
```
Pro tip: configurar el cacheo en un filtro `before`:
```ruby
before do
cache_control :public, :must_revalidate, :max_age => 60
end
```
Si estás usando el helper `expires` para definir el encabezado correspondiente,
`Cache-Control` se va a definir automáticamente:
```ruby
before do
expires 500, :public, :must_revalidate
end
```
Para usar cachés adecuadamente, deberías considerar usar `etag` o
`last_modified`. Es recomendable que llames a estos asistentes *antes* de hacer
cualquier trabajo pesado, ya que van a enviar la respuesta inmediatamente si
el cliente ya tiene la versión actual en su caché:
```ruby
get '/articulo/:id' do
@articulo = Articulo.find params['id']
last_modified @articulo.updated_at
etag @articulo.sha1
erb :articulo
end
```
También es posible usar una
[weak ETag](https://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation):
```ruby
etag @articulo.sha1, :weak
```
Estos helpers no van a cachear nada por vos, sino que van a facilitar la
información necesaria para poder hacerlo. Si estás buscando soluciones rápidas
de cacheo con proxys reversos, mirá
[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
"hola"
end
```
Usá la configuración `:static_cache_control` para agregar el encabezado
`Cache-Control` a archivos estáticos (ver la sección de configuración
para más detalles).
De acuerdo con la RFC 2616 tu aplicación debería comportarse diferente si a las
cabeceras If-Match o If-None-Match se le asigna el valor `*` cuando el
recurso solicitado ya existe. Sinatra asume para peticiones seguras (como get)
y potentes (como put) que el recurso existe, mientras que para el resto
(como post) asume que no. Podés cambiar este comportamiento con la opción
`:new_resource`:
```ruby
get '/crear' do
etag '', :new_resource => true
Articulo.create
erb :nuevo_articulo
end
```
Si querés seguir usando una weak ETag, indicalo con la opción `:kind`:
```ruby
etag '', :new_resource => true, :kind => :weak
```
### Enviando Archivos
Para enviar archivos, podés usar el método `send_file`:
```ruby
get '/' do
send_file 'foo.png'
end
```
Además acepta un par de opciones:
```ruby
send_file 'foo.png', :type => :jpg
```
Estas opciones son:
[filename]
nombre del archivo devuelto, por defecto es el nombre real del archivo.
[last_modified]
valor para el encabezado Last-Modified, por defecto toma el mtime del archivo.
[type]
el content type que se va a utilizar, si no está presente se intenta adivinar
a partir de la extensión del archivo.
[disposition]
se utiliza para el encabezado Content-Disposition, y puede tomar alguno de los
siguientes valores: `nil` (por defecto), `:attachment` e
`:inline`
[length]
encabezado Content-Length, por defecto toma el tamaño del archivo.
[status]
código de estado devuelto. Resulta útil al enviar un archivo estático como una
página de error.
Si el Rack handler lo soporta, se intentará no transmitir directamente desde el
proceso de Ruby. Si usás este método, Sinatra se va a encargar automáticamente de las
peticiones de rango.
### Accediendo al objeto de la petición
El objeto de la petición entrante puede ser accedido desde el nivel de la
petición (filtros, rutas y manejadores de errores) a través del método
`request`:
```ruby
# app corriendo en http://ejemplo.com/ejemplo
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 # cuerpo de la petición enviado por el cliente (ver más abajo)
request.scheme # "http"
request.script_name # "/ejemplo"
request.path_info # "/foo"
request.port # 80
request.request_method # "GET"
request.query_string # ""
request.content_length # longitud de request.body
request.media_type # tipo de medio de request.body
request.host # "ejemplo.com"
request.get? # true (hay métodos análogos para los otros verbos)
request.form_data? # false
request["UNA_CABECERA"] # valor de la cabecera UNA_CABECERA
request.referrer # la referencia del cliente o '/'
request.user_agent # user agent (usado por la condición :agent)
request.cookies # hash de las cookies del navegador
request.xhr? # es una petición ajax?
request.url # "http://ejemplo.com/ejemplo/foo"
request.path # "/ejemplo/foo"
request.ip # dirección IP del cliente
request.secure? # false (sería true sobre ssl)
request.forwarded? # true (si se está corriendo atrás de un proxy reverso)
requuest.env # hash de entorno directamente entregado por Rack
end
```
Algunas opciones, como `script_name` o `path_info` pueden
también ser escritas:
```ruby
before { request.path_info = "/" }
get "/" do
"todas las peticiones llegan acá"
end
```
El objeto `request.body` es una instancia de IO o StringIO:
```ruby
post "/api" do
request.body.rewind # en caso de que alguien ya lo haya leído
datos = JSON.parse request.body.read
"Hola #{datos['nombre']}!"
end
```
### Archivos Adjuntos
Podés usar el helper `attachment` para indicarle al navegador que
almacene la respuesta en el disco en lugar de mostrarla en pantalla:
```ruby
get '/' do
attachment
"guardalo!"
end
```
También podés pasarle un nombre de archivo:
```ruby
get '/' do
attachment "info.txt"
"guardalo!"
end
```
### Fecha y Hora
Sinatra pone a tu disposición el helper `time_for`, que genera un objeto `Time`
a partir del valor que recibe como argumento. Este valor puede ser un
`String`, pero también es capaz de convertir objetos `DateTime`, `Date` y de
otras clases similares:
```ruby
get '/' do
pass if Time.now > time_for('Dec 23, 2012')
"todavía hay tiempo"
end
```
Este método es usado internamente por métodos como `expires` y `last_modified`,
entre otros. Por lo tanto, es posible extender el comportamiento de estos
métodos sobreescribiendo `time_for` en tu aplicación:
```ruby
helpers do
def time_for(value)
case value
when :ayer then Time.now - 24*60*60
when :mañana then Time.now + 24*60*60
else super
end
end
end
get '/' do
last_modified :ayer
expires :mañana
"hola"
end
```
### Buscando los Archivos de las Plantillas
El helper `find_template` se utiliza para encontrar los archivos de las
plantillas que se van a renderizar:
```ruby
find_template settings.views, 'foo', Tilt[:haml] do |archivo|
puts "podría ser #{archivo}"
end
```
Si bien esto no es muy útil, lo interesante es que podés sobreescribir este
método, y así enganchar tu propio mecanismo de búsqueda. Por ejemplo, para
poder utilizar más de un directorio de vistas:
```ruby
set :views, ['vistas', 'plantillas']
helpers do
def find_template(views, name, engine, &block)
Array(views).each { |v| super(v, name, engine, &block) }
end
end
```
Otro ejemplo consiste en usar directorios diferentes para los distintos motores
de renderizado:
```ruby
set :views, :sass => 'vistas/sass', :haml => 'plantillas', :defecto => 'vistas'
helpers do
def find_template(views, name, engine, &block)
_, folder = views.detect { |k,v| engine == Tilt[k] }
folder ||= views[:defecto]
super(folder, name, engine, &block)
end
end
```
¡Es muy fácil convertir estos ejemplos en una extensión y compartirla!
Notá que `find_template` no verifica si un archivo existe realmente, sino
que llama al bloque que recibe para cada path posible. Esto no representa un
problema de rendimiento debido a que `render` va a usar `break` ni bien
encuentre un archivo que exista. Además, las ubicaciones de las plantillas (y
su contenido) se cachean cuando no estás en el modo de desarrollo. Es bueno
tener en cuenta lo anterior si escribís un método extraño.
## Configuración
Ejecutar una vez, en el inicio, en cualquier entorno:
```ruby
configure do
# asignando una opción
set :opcion, 'valor'
# asignando varias opciones
set :a => 1, :b => 2
# atajo para `set :opcion, true`
enable :opcion
# atajo para `set :opcion, false`
disable :opcion
# también podés tener configuraciones dinámicas usando bloques
set(:css_dir) { File.join(views, 'css') }
end
```
Ejecutar únicamente cuando el entorno (la variable de entorno RACK_ENV) es
`:production`:
```ruby
configure :production do
...
end
```
Ejecutar cuando el entorno es `:production` o `:test`:
```ruby
configure :production, :test do
...
end
```
Podés acceder a estas opciones utilizando el método `settings`:
```ruby
configure do
set :foo, 'bar'
end
get '/' do
settings.foo? # => true
settings.foo # => 'bar'
...
end
```
### Configurando la Protección de Ataques
Sinatra usa [Rack::Protection](https://github.com/sinatra/rack-protection#readme)
para defender a tu aplicación de los ataques más comunes. Si por algún motivo,
querés desactivar esta funcionalidad, podés hacerlo como se indica a
continuación (ten en cuenta que tu aplicación va a quedar expuesta a un
montón de vulnerabilidades bien conocidas):
```ruby
disable :protection
```
También es posible desactivar una única capa de defensa:
```ruby
set :protection, :except => :path_traversal
```
O varias:
```ruby
set :protection, :except => [:path_traversal, :session_hijacking]
```
### Configuraciones Disponibles
- absolute_redirects
-
Si está deshabilitada, Sinatra va a permitir
redirecciones relativas, sin embargo, como consecuencia
de esto, va a dejar de cumplir con el RFC 2616 (HTTP
1.1), que solamente permite redirecciones absolutas.
Activalo si tu apliación está corriendo atrás de un proxy
reverso que no se ha configurado adecuadamente. Notá que
el helper url va a seguir produciendo URLs absolutas, a
menos que le pasés false como segundo parámetro.
Deshabilitada por defecto.
- add_charset
-
Tipos mime a los que el helper content_type les
añade automáticamente el charset.
En general, no deberías asignar directamente esta opción,
sino añadirle los charsets que quieras:
settings.add_charset << "application/foobar"
- app_file
-
Path del archivo principal de la aplicación, se utiliza
para detectar la raíz del proyecto, el directorio de las
vistas y el público, así como las plantillas inline.
- bind
-
Dirección IP que utilizará el servidor integrado (por
defecto: 0.0.0.0).
- default_encoding
-
Encoding utilizado cuando el mismo se desconoce (por
defecto "utf-8").
- dump_errors
-
Mostrar errores en el log.
- environment
-
Entorno actual, por defecto toma el valor de
ENV['RACK_ENV'], o "development" si no
está disponible.
- logging
-
Define si se utiliza el logger.
- lock
-
Coloca un lock alrededor de cada petición, procesando
solamente una por proceso.
Habilitá esta opción si tu aplicación no es thread-safe.
Se encuentra deshabilitada por defecto.
- method_override
-
Utiliza el parámetro _method para permtir
formularios put/delete en navegadores que no los
soportan.
- port
-
Puerto en el que escuchará el servidor integrado.
- prefixed_redirects
-
Define si inserta request.script_name en las
redirecciones cuando no se proporciona un path absoluto.
De esta manera, cuando está habilitada,
redirect '/foo' se comporta de la misma manera
que redirect to('/foo'). Se encuentra
deshabilitada por defecto.
- protection
-
Define si deben activarse las protecciones para los
ataques web más comunes. Para más detalles mirá la
sección sobre la configuración de protección de ataques
más arriba.
- public_dir
-
Alias para public_folder, que se encuentra a
continuación.
- public_folder
-
Lugar del directorio desde donde se sirven los archivos
públicos. Solo se utiliza cuando se sirven archivos
estáticos (ver la opción static). Si no
está presente, se infiere del valor de la opción
app_file.
- reload_templates
-
Define si se recargan las plantillas entre peticiones.
Se encuentra activado en el entorno de desarrollo.
- root
-
Lugar del directorio raíz del proyecto. Si no está
presente, se infiere del valor de la opción
app_file.
- raise_errors
-
Elevar excepciones (detiene la aplicación). Se
encuentra activada por defecto cuando el valor de
environment es "test". En caso
contrario estará desactivada.
- run
-
Cuando está habilitada, Sinatra se va a encargar de
iniciar el servidor web, no la habilites cuando estés
usando rackup o algún otro medio.
- running
-
Indica si el servidor integrado está ejecutándose, ¡no
cambiés esta configuración!.
- server
-
Servidor, o lista de servidores, para usar como servidor
integrado. Por defecto: ['thin', 'mongrel', 'webrick'],
el orden establece la prioridad.
- sessions
-
Habilita el soporte de sesiones basadas en cookies a
través de Rack::Session::Cookie. Ver la
sección 'Usando Sesiones' para más información.
- show_exceptions
-
Muestra un stack trace en el navegador cuando ocurre una
excepción. Se encuentra activada por defecto cuando el
valor de environment es "development".
En caso contrario estará desactivada.
- static
-
Define si Sinatra debe encargarse de servir archivos
estáticos.
Deshabilitala cuando uses un servidor capaz de
hacerlo por sí solo, porque mejorará el
rendimiento. Se encuentra habilitada por
defecto en el estilo clásico y desactivado en el
el modular.
- static_cache_control
-
Cuando Sinatra está sirviendo archivos estáticos, y
esta opción está habilitada, les va a agregar encabezados
Cache-Control a las respuestas. Para esto
utiliza el helper cache_control. Se encuentra
deshabilitada por defecto. Notar que es necesario
utilizar un array cuando se asignan múltiples valores:
set :static_cache_control, [:public, :max_age => 300].
- views
-
Path del directorio de las vistas. Si no está presente,
se infiere del valor de la opción app_file.
## Entornos
Existen tres entornos (`environments`) predefinidos: `development`,
`production` y `test`. El entorno por defecto es
`development` y tiene algunas particularidades:
* Se recargan las plantillas entre una petición y la siguiente, a diferencia
de `production` y `test`, donde se cachean.
* Se instalan manejadores de errores `not_found` y `error`
especiales que muestran un stack trace en el navegador cuando son disparados.
Para utilizar alguno de los otros entornos puede asignarse el valor
correspondiente a la variable de entorno `RACK_ENV`, o bien utilizar la opción
`-e` al ejecutar la aplicación:
```shell
ruby mi_app.rb -e
```
Los métodos `development?`, `test?` y `production?` te permiten conocer el
entorno actual.
## Manejo de Errores
Los manejadores de errores se ejecutan dentro del mismo contexto que las rutas
y los filtros `before`, lo que significa que podés usar, por ejemplo,
`haml`, `erb`, `halt`, etc.
### No encontrado (Not Found)
Cuando se eleva una excepción `Sinatra::NotFound`, o el código de
estado de la respuesta es 404, el manejador `not_found` es invocado:
```ruby
not_found do
'No existo'
end
```
### Error
El manejador `error` es invocado cada vez que una excepción es elevada
desde un bloque de ruta o un filtro. El objeto de la excepción se puede
obtener de la variable Rack `sinatra.error`:
```ruby
error do
'Disculpá, ocurrió un error horrible - ' + env['sinatra.error'].message
end
```
Errores personalizados:
```ruby
error MiErrorPersonalizado do
'Lo que pasó fue...' + env['sinatra.error'].message
end
```
Entonces, si pasa esto:
```ruby
get '/' do
raise MiErrorPersonalizado, 'algo malo'
end
```
Obtenés esto:
Lo que pasó fue... algo malo
También, podés instalar un manejador de errores para un código de estado:
```ruby
error 403 do
'Acceso prohibido'
end
get '/secreto' do
403
end
```
O un rango:
```ruby
error 400..510 do
'Boom'
end
```
Sinatra instala manejadores `not_found` y `error` especiales
cuando se ejecuta dentro del entorno de desarrollo "development".
## Rack Middleware
Sinatra corre sobre [Rack](http://rack.github.io/), una interfaz minimalista
que es un estándar para frameworks webs escritos en Ruby. Una de las
características más interesantes de Rack para los desarrolladores de aplicaciones
es el soporte de "middleware" -- componentes que se ubican entre el servidor y
tu aplicación, supervisando y/o manipulando la petición/respuesta HTTP para
proporcionar varios tipos de funcionalidades comunes.
Sinatra hace muy sencillo construir tuberías de Rack middleware a través del
método top-level `use`:
```ruby
require 'sinatra'
require 'mi_middleware_personalizado'
use Rack::Lint
use MiMiddlewarePersonalizado
get '/hola' do
'Hola Mundo'
end
```
La semántica de `use` es idéntica a la definida para el DSL
Rack::Builder[http://www.rubydoc.info/github/rack/rack/master/Rack/Builder] (más
frecuentemente usado en archivos rackup). Por ejemplo, el método `use`
acepta argumentos múltiples/variables así como bloques:
```ruby
use Rack::Auth::Basic do |nombre_de_usuario, password|
nombre_de_usuario == 'admin' && password == 'secreto'
end
```
Rack es distribuido con una variedad de middleware estándar para logging,
debugging, enrutamiento URL, autenticación y manejo de sesiones. Sinatra
usa muchos de estos componentes automáticamente de acuerdo a su configuración
para que usualmente no tengas que usarlas (con `use`) explícitamente.
Podés encontrar middleware útil en
[rack](https://github.com/rack/rack/tree/master/lib/rack),
[rack-contrib](https://github.com/rack/rack-contrib#readme),
o en la [Rack wiki](https://github.com/rack/rack/wiki/List-of-Middleware).
## Pruebas
Las pruebas para las aplicaciones Sinatra pueden ser escritas utilizando
cualquier framework o librería de pruebas basada en Rack. Se recomienda usar
[Rack::Test](http://www.rubydoc.info/github/brynary/rack-test/master/frames):
```ruby
require 'mi_app_sinatra'
require 'minitest/autorun'
require 'rack/test'
class MiAppTest < Minitest::Test
include Rack::Test::Methods
def app
Sinatra::Application
end
def test_mi_defecto
get '/'
assert_equal 'Hola Mundo!', last_response.body
end
def test_con_parametros
get '/saludar', :name => 'Franco'
assert_equal 'Hola Frank!', last_response.body
end
def test_con_entorno_rack
get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
assert_equal "Estás usando Songbird!", last_response.body
end
end
```
## Sinatra::Base - Middleware, Librerías, y Aplicaciones Modulares
Definir tu aplicación en el nivel superior funciona bien para micro-aplicaciones
pero trae inconvenientes considerables a la hora de construir componentes
reutilizables como Rack middleware, Rails metal, librerías simples con un
componente de servidor o incluso extensiones de Sinatra. El DSL de alto nivel
asume una configuración apropiada para micro-aplicaciones (por ejemplo, un
único archivo de aplicación, los directorios `./public` y
`./views`, logging, página con detalles de excepción, etc.). Ahí es
donde `Sinatra::Base` entra en el juego:
```ruby
require 'sinatra/base'
class MiApp < Sinatra::Base
set :sessions, true
set :foo, 'bar'
get '/' do
'Hola Mundo!'
end
end
```
Las subclases de `Sinatra::Base` tienen disponibles exactamente los
mismos métodos que los provistos por el DSL de top-level. La mayoría de las
aplicaciones top-level se pueden convertir en componentes
`Sinatra::Base` con dos modificaciones:
* Tu archivo debe requerir `sinatra/base` en lugar de `sinatra`; de otra
manera, todos los métodos del DSL de sinatra son importados dentro del
espacio de nombres principal.
* Poné las rutas, manejadores de errores, filtros y opciones de tu aplicación
en una subclase de `Sinatra::Base`.
`Sinatra::Base` es una pizarra en blanco. La mayoría de las opciones están
desactivadas por defecto, incluyendo el servidor incorporado. Mirá
[Opciones y Configuraciones](http://www.sinatrarb.com/configuration.html)
para detalles sobre las opciones disponibles y su comportamiento.
### Estilo Modular vs. Clásico
Contrariamente a la creencia popular, no hay nada de malo con el estilo clásico.
Si se ajusta a tu aplicación, no es necesario que la cambies a una modular.
La desventaja de usar el estilo clásico en lugar del modular consiste en que
solamente podés tener una aplicación Sinatra por proceso Ruby. Si tenés
planificado usar más, cambiá al estilo modular. Al mismo tiempo, ten en
cuenta que no hay ninguna razón por la cuál no puedas mezclar los estilos
clásico y modular.
A continuación se detallan las diferencias (sútiles) entre las configuraciones
de ambos estilos:
| Configuración |
Clásica |
Modular |
| app_file |
archivo que carga sinatra |
archivo con la subclase de Sinatra::Base |
| run |
$0 == app_file |
false |
| logging |
true |
false |
| method_override |
true |
false |
| inline_templates |
true |
false |
| static |
true |
File.exist?(public_folder) |
### Sirviendo una Aplicación Modular
Las dos opciones más comunes para iniciar una aplicación modular son, iniciarla
activamente con `run!`:
```ruby
# mi_app.rb
require 'sinatra/base'
class MiApp < Sinatra::Base
# ... código de la app ...
# iniciar el servidor si el archivo fue ejecutado directamente
run! if app_file == $0
end
```
Iniciar con:
```shell
ruby mi_app.rb
```
O, con un archivo `config.ru`, que permite usar cualquier handler Rack:
```ruby
# config.ru
require './mi_app'
run MiApp
```
Después ejecutar:
```shell
rackup -p 4567
```
### Usando una Aplicación Clásica con un Archivo config.ru
Escribí el archivo de tu aplicación:
```ruby
# app.rb
require 'sinatra'
get '/' do
'Hola mundo!'
end
```
Y el `config.ru` correspondiente:
```ruby
require './app'
run Sinatra::Application
```
### ¿Cuándo usar config.ru?
Indicadores de que probablemente querés usar `config.ru`:
* Querés realizar el deploy con un handler Rack distinto (Passenger, Unicorn,
Heroku, ...).
* Querés usar más de una subclase de `Sinatra::Base`.
* Querés usar Sinatra únicamente para middleware, pero no como un endpoint.
No hay necesidad de utilizar un archivo `config.ru` exclusivamente
porque tenés una aplicación modular, y no necesitás una aplicación modular para
iniciarla con `config.ru`.
### Utilizando Sinatra como Middleware
Sinatra no solo es capaz de usar otro Rack middleware, sino que a su vez,
cualquier aplicación Sinatra puede ser agregada delante de un endpoint Rack
como middleware. Este endpoint puede ser otra aplicación Sinatra, o cualquier
aplicación basada en Rack (Rails/Ramaze/Camping/...):
```ruby
require 'sinatra/base'
class PantallaDeLogin < Sinatra::Base
enable :sessions
get('/login') { haml :login }
post('/login') do
if params['nombre'] == 'admin' && params['password'] == 'admin'
session['nombre_de_usuario'] = params['nombre']
else
redirect '/login'
end
end
end
class MiApp < Sinatra::Base
# el middleware se ejecutará antes que los filtros
use PantallaDeLogin
before do
unless session['nombre_de_usuario']
halt "Acceso denegado, por favor iniciá sesión."
end
end
get('/') { "Hola #{session['nombre_de_usuario']}." }
end
```
### Creación Dinámica de Aplicaciones
Puede que en algunas ocasiones quieras crear nuevas aplicaciones en
tiempo de ejecución sin tener que asignarlas a una constante. Para
esto tenés `Sinatra.new`:
```ruby
require 'sinatra/base'
mi_app = Sinatra.new { get('/') { "hola" } }
mi_app.run!
```
Acepta como argumento opcional una aplicación desde la que se
heredará:
```ruby
# config.ru
require 'sinatra/base'
controller = Sinatra.new do
enable :logging
helpers MisHelpers
end
map('/a') do
run Sinatra.new(controller) { get('/') { 'a' } }
end
map('/b') do
run Sinatra.new(controller) { get('/') { 'b' } }
end
```
Construir aplicaciones de esta forma resulta especialmente útil para
testear extensiones Sinatra o para usar Sinatra en tus librerías.
Por otro lado, hace extremadamente sencillo usar Sinatra como
middleware:
```ruby
require 'sinatra/base'
use Sinatra do
get('/') { ... }
end
run ProyectoRails::Application
```
## Ámbitos y Ligaduras
El ámbito en el que te encontrás determina que métodos y variables están
disponibles.
### Ámbito de Aplicación/Clase
Cada aplicación Sinatra es una subclase de `Sinatra::Base`. Si estás
usando el DSL de top-level (`require 'sinatra'`), entonces esta clase es
`Sinatra::Application`, de otra manera es la subclase que creaste
explícitamente. Al nivel de la clase tenés métodos como `get` o `before`, pero
no podés acceder a los objetos `request` o `session`, ya que hay una única
clase de la aplicación para todas las peticiones.
Las opciones creadas utilizando `set` son métodos al nivel de la clase:
```ruby
class MiApp < Sinatra::Base
# Ey, estoy en el ámbito de la aplicación!
set :foo, 42
foo # => 42
get '/foo' do
# Hey, ya no estoy en el ámbito de la aplicación!
end
end
```
Tenés la ligadura al ámbito de la aplicación dentro de:
* El cuerpo de la clase de tu aplicación
* Métodos definidos por extensiones
* El bloque pasado a `helpers`
* Procs/bloques usados como el valor para `set`
Este ámbito puede alcanzarse de las siguientes maneras:
* A través del objeto pasado a los bloques de configuración (`configure { |c| ...}`)
* Llamando a `settings` desde dentro del ámbito de la petición
### Ámbito de Petición/Instancia
Para cada petición entrante, una nueva instancia de la clase de tu aplicación
es creada y todos los bloques de rutas son ejecutados en ese ámbito. Desde este
ámbito podés acceder a los objetos `request` y `session` o llamar a los métodos
de renderización como `erb` o `haml`. Podés acceder al ámbito de la aplicación
desde el ámbito de la petición utilizando `settings`:
```ruby
class MiApp < Sinatra::Base
# Ey, estoy en el ámbito de la aplicación!
get '/definir_ruta/:nombre' do
# Ámbito de petición para '/definir_ruta/:nombre'
@valor = 42
settings.get("/#{params['nombre']}") do
# Ámbito de petición para "/#{params['nombre']}"
@valor # => nil (no es la misma petición)
end
"Ruta definida!"
end
end
```
Tenés la ligadura al ámbito de la petición dentro de:
* bloques pasados a get/head/post/put/delete/options
* filtros before/after
* métodos ayudantes
* plantillas/vistas
### Ámbito de Delegación
El ámbito de delegación solo reenvía métodos al ámbito de clase. De cualquier
manera, no se comporta 100% como el ámbito de clase porque no tenés la ligadura
de la clase: únicamente métodos marcados explícitamente para delegación están
disponibles y no compartís variables/estado con el ámbito de clase (léase:
tenés un `self` diferente). Podés agregar delegaciones de método llamando a
`Sinatra::Delegator.delegate :nombre_del_metodo`.
Tenés la ligadura al ámbito de delegación dentro de:
* La ligadura del top-level, si hiciste `require "sinatra"`
* Un objeto extendido con el mixin `Sinatra::Delegator`
Hechale un vistazo al código: acá está el
[Sinatra::Delegator mixin](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633)
que [extiende el objeto main](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30).
## Línea de Comandos
Las aplicaciones Sinatra pueden ser ejecutadas directamente:
```shell
ruby miapp.rb [-h] [-x] [-e ENTORNO] [-p PUERTO] [-o HOST] [-s MANEJADOR]
```
Las opciones son:
```
-h # ayuda
-p # asigna el puerto (4567 es usado por defecto)
-o # asigna el host (0.0.0.0 es usado por defecto)
-e # asigna el entorno (development es usado por defecto)
-s # especifica el servidor/manejador rack (thin es usado por defecto)
-x # activa el mutex lock (está desactivado por defecto)
```
### Multi-threading
_Basado en [esta respuesta en StackOverflow][so-answer] escrita por Konstantin_
Sinatra no impone ningún modelo de concurrencia, sino que lo deja en manos del
handler Rack que se esté usando (Thin, Puma, WEBrick). Sinatra en sí mismo es
thread-safe, así que no hay problema en que el Rack handler use un modelo de
concurrencia basado en hilos.
Esto significa que, cuando estemos arrancando el servidor, tendríamos que
especificar la opción adecuada para el handler Rack específico. En este ejemplo
vemos cómo arrancar un servidor Thin multihilo:
```ruby
# app.rb
require 'sinatra/base'
class App < Sinatra::Base
get '/' do
"¡Hola, Mundo!"
end
end
App.run!
```
Para arrancar el servidor, el comando sería:
```shell
thin --threaded start
```
[so-answer]: http://stackoverflow.com/questions/6278817/is-sinatra-multi-threaded/6282999#6282999)
## Versiones de Ruby Soportadas
Las siguientes versiones de Ruby son soportadas oficialmente:
- Ruby 1.8.7
-
1.8.7 es soportado completamente. Sin embargo, si no hay nada que te lo
prohiba, te recomendamos que uses 1.9.2 o cambies a JRuby o Rubinius. No se
dejará de dar soporte a 1.8.7 hasta Sinatra 2.0 y Ruby 2.0, aunque si se
libera la versión 1.8.8 de Ruby las cosas podrían llegar a cambiar. Sin
embargo, que eso ocurra es muy poco probable, e incluso el caso de que lo
haga, puede que se siga dando soporte a 1.8.7. Hemos dejado de soportar
Ruby 1.8.6. Si querés ejecutar Sinatra sobre 1.8.6, podés utilizar la
versión 1.2, pero ten en cuenta que una vez que Sinatra 1.4.0 sea liberado,
ya no se corregirán errores por más que se reciban reportes de los mismos.
- Ruby 1.9.2
-
1.9.2 es soportado y recomendado. No uses 1.9.2p0, porque se producen fallos
de segmentación cuando se ejecuta Sinatra. El soporte se mantendrá al menos
hasta que se libere la versión 1.9.4/2.0 de Ruby. El soporte para la última
versión de la serie 1.9 se mantendrá mientras lo haga el equipo principal de Ruby.
- Ruby 1.9.3
-
1.9.3 es soportado y recomendado. Ten en cuenta que el cambio a 1.9.3 desde
una versión anterior va a invalidar todas las sesiones.
- Rubinius
-
Rubinius es soportado oficialmente (Rubinius >= 1.2.4). Todo funciona
correctamente, incluyendo los lenguajes de plantillas. La próxima versión,
2.0, también es soportada, incluyendo el modo 1.9.
- JRuby
-
JRuby es soportado oficialmente (JRuby >= 1.6.7). No se conocen problemas
con librerías de plantillas de terceras partes. Sin embargo, si elegís usar
JRuby, deberías examinar sus Rack handlers porque el servidor web Thin no es
soportado completamente. El soporte de JRuby para extensiones C se encuentra
en una etapa experimental, sin embargo, de momento, solamente RDiscount,
Redcarpet, RedCloth y Yajl, así como Thin y Mongrel se ven afectadas.
Siempre le prestamos atención a las nuevas versiones de Ruby.
Las siguientes implementaciones de Ruby no se encuentran soportadas
oficialmente. De cualquier manera, pueden ejecutar Sinatra:
* Versiones anteriores de JRuby y Rubinius
* Ruby Enterprise Edition
* MacRuby, Maglev e IronRuby
* Ruby 1.9.0 y 1.9.1 (pero no te recomendamos que los uses)
No ser soportada oficialmente, significa que si las cosas se rompen
ahí y no en una plataforma soportada, asumimos que no es nuestro problema sino
el suyo.
Nuestro servidor CI también se ejecuta sobre ruby-head (que será la próxima
versión 2.1.0) y la rama 1.9.4. Como están en movimiento constante, no podemos
garantizar nada. De todas formas, podés contar con que tanto 1.9.4-p0 como
2.1.0-p0 sea soportadas.
Sinatra debería funcionar en cualquier sistema operativo soportado por la
implementación de Ruby elegida.
En este momento, no vas a poder ejecutar Sinatra en Cardinal, SmallRuby,
BlueRuby o cualquier versión de Ruby anterior a 1.8.7.
## A la Vanguardia
Si querés usar el código de Sinatra más reciente, sentite libre de ejecutar
tu aplicación sobre la rama master, en general es bastante estable.
También liberamos prereleases de vez en cuando, así, podés hacer:
```shell
gem install sinatra --pre
```
Para obtener algunas de las últimas características.
### Con Bundler
Esta es la manera recomendada para ejecutar tu aplicación sobre la última
versión de Sinatra usando [Bundler](http://bundler.io).
Primero, instalá Bundler si no lo hiciste todavía:
```shell
gem install bundler
```
Después, en el directorio de tu proyecto, creá un archivo `Gemfile`:
```ruby
source :rubygems
gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
# otras dependencias
gem 'haml' # por ejemplo, si usás haml
gem 'activerecord', '~> 3.0' # quizás también necesités ActiveRecord 3.x
```
Tené en cuenta que tenés que listar todas las dependencias directas de tu
aplicación. No es necesario listar las dependencias de Sinatra (Rack y Tilt)
porque Bundler las agrega directamente.
Ahora podés arrancar tu aplicación así:
```shell
bundle exec ruby miapp.rb
```
### Con Git
Cloná el repositorio localmente y ejecutá tu aplicación, asegurándote que el
directorio `sinatra/lib` esté en el `$LOAD_PATH`:
```shell
cd miapp
git clone git://github.com/sinatra/sinatra.git
ruby -Isinatra/lib miapp.rb
```
Para actualizar el código fuente de Sinatra en el futuro:
```shell
cd miapp/sinatra
git pull
```
### Instalación Global
Podés construir la gem vos mismo:
```shell
git clone git://github.com/sinatra/sinatra.git
cd sinatra
rake sinatra.gemspec
rake install
```
Si instalás tus gems como root, el último paso debería ser
```shell
sudo rake install
```
## Versionado
Sinatra utiliza el [Versionado Semántico](http://semver.org/),
siguiendo las especificaciones SemVer y SemVerTag.
## Lecturas Recomendadas
* [Sito web del proyecto](http://www.sinatrarb.com/) - Documentación
adicional, noticias, y enlaces a otros recursos.
* [Contribuyendo](http://www.sinatrarb.com/contributing) - ¿Encontraste un
error?. ¿Necesitás ayuda?. ¿Tenés un parche?.
* [Seguimiento de problemas](https://github.com/sinatra/sinatra/issues)
* [Twitter](https://twitter.com/sinatra)
* [Lista de Correo](http://groups.google.com/group/sinatrarb/topics)
* [IRC: #sinatra](irc://chat.freenode.net/#sinatra) en http://freenode.net
* [Sinatra Book](https://github.com/sinatra/sinatra-book/) Tutorial (en inglés).
* [Sinatra Recipes](http://recipes.sinatrarb.com/) Recetas contribuidas
por la comunidad (en inglés).
* Documentación de la API para la
[última versión liberada](http://www.rubydoc.info/gems/sinatra) o para la
[rama de desarrollo actual](http://www.rubydoc.info/github/sinatra/sinatra)
en http://www.rubydoc.info/
* [Servidor de CI](https://travis-ci.org/sinatra/sinatra)
sinatra-1.4.7/README.pt-pt.md 0000644 0000041 0000041 00000042456 12652356512 015560 0 ustar www-data www-data # Sinatra
*Atenção: Este documento é apenas uma tradução da versão em inglês e
pode estar desatualizado.*
Sinatra é uma
[DSL](https://pt.wikipedia.org/wiki/Linguagem_de_domínio_específico) para
criar rapidamente aplicações web em Ruby com o mínimo de esforço:
```ruby
# minhaapp.rb
require 'rubygems'
require 'sinatra'
get '/' do
'Olá Mundo!'
end
```
Instale a gem e execute com:
```shell
sudo gem install sinatra
ruby minhaapp.rb
```
Aceda em: [http://localhost:4567](http://localhost:4567)
## Rotas
No Sinatra, uma rota é um metodo HTTP associado a uma URL correspondente
padrão. Cada rota é associada a um bloco:
```ruby
get '/' do
.. mostrar algo ..
end
post '/' do
.. criar algo ..
end
put '/' do
.. atualizar algo ..
end
delete '/' do
.. apagar algo ..
end
```
Rotas são encontradas na ordem em que são definidas. A primeira rota que
é encontrada invoca o pedido.
Padrões de rota podem incluir parâmetros nomeados, acessíveis através da
hash `params`:
```ruby
get '/ola/:nome' do
# corresponde a "GET /ola/foo" e "GET /ola/bar"
# params['nome'] é 'foo' ou 'bar'
"Olá #{params['nome']}!"
end
```
Pode também aceder a parâmetros nomeados através do bloco de parâmetros:
```ruby
get '/ola/:nome' do |n|
"Olá #{n}!"
end
```
Padrões de rota podem também incluir parâmetros splat (asteriscos),
acessíveis através do array `params['splat']`.
```ruby
get '/diga/*/ao/*' do
# corresponde a /diga/ola/ao/mundo
params['splat'] # => ["ola", "mundo"]
end
get '/download/*.*' do
# corresponde a /download/pasta/do/arquivo.xml
params['splat'] # => ["pasta/do/arquivo", "xml"]
end
```
Rotas correspondem-se com expressões regulares:
```ruby
get /\A\/ola\/([\w]+)\z/ do
"Olá, #{params['captures'].first}!"
end
```
Ou com um bloco de parâmetro:
```ruby
get %r{/ola/([\w]+)} do |c|
"Olá, #{c}!"
end
```
Rotas podem incluir uma variedade de condições correspondentes, por
exemplo, o agente usuário:
```ruby
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
"Você está a utilizar a versão #{params['agent'][0]} do Songbird."
end
get '/foo' do
# Corresponde a um navegador não Songbird
end
```
## Arquivos estáticos
Arquivos estáticos são disponibilizados a partir do directório
`./public`. Você pode especificar um local diferente através da opção
`:public_folder`
```ruby
set :public_folder, File.dirname(__FILE__) + '/estatico'
```
Note que o nome do directório público não é incluido no URL. Um arquivo
`./public/css/style.css` é disponibilizado como
`http://example.com/css/style.css`.
## Views / Templates
Templates presumem-se estar localizados sob o directório `./views`. Para
utilizar um directório de views diferente:
```ruby
set :views, File.dirname(__FILE__) + '/modelo'
```
Uma coisa importante a ser lembrada é que você sempre tem as referências
dos templates como símbolos, mesmo se eles estiverem num sub-directório
(nesse caso utilize `:'subdir/template'`). Métodos de renderização irão
processar qualquer string passada directamente para elas.
### Haml Templates
A gem/biblioteca haml é necessária para renderizar templates HAML:
```ruby
# É necessário requerir 'haml' na aplicação.
require 'haml'
get '/' do
haml :index
end
```
Renderiza `./views/index.haml`.
[Opções
Haml](http://haml.info/docs/yardoc/file.REFERENCE.html#options)
podem ser definidas globalmente através das configurações do sinatra,
veja [Opções e
Configurações](http://www.sinatrarb.com/configuration.html), e substitua
em uma requisição individual.
```ruby
set :haml, {:format => :html5 } # o formato padrão do Haml é :xhtml
get '/' do
haml :index, :haml_options => {:format => :html4 } # substituido
end
```
### Erb Templates
```ruby
# É necessário requerir 'erb' na aplicação.
require 'erb'
get '/' do
erb :index
end
```
Renderiza `./views/index.erb`
### Erubis
A gem/biblioteca erubis é necessária para renderizar templates erubis:
```ruby
# É necessário requerir 'erubis' na aplicação.
require 'erubis'
get '/' do
erubis :index
end
```
Renderiza `./views/index.erubis`
### Builder Templates
A gem/biblioteca builder é necessária para renderizar templates builder:
```ruby
# É necessário requerir 'builder' na aplicação.
require 'builder'
get '/' do
content_type 'application/xml', :charset => 'utf-8'
builder :index
end
```
Renderiza `./views/index.builder`.
### Sass Templates
A gem/biblioteca sass é necessária para renderizar templates sass:
```ruby
# É necessário requerir 'haml' ou 'sass' na aplicação.
require 'sass'
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
sass :stylesheet
end
```
Renderiza `./views/stylesheet.sass`.
[Opções
Sass](http://sass-lang.com/documentation/file.SASS_REFERENCE.html#options)
podem ser definidas globalmente através das configurações do sinatra,
veja [Opções e
Configurações](http://www.sinatrarb.com/configuration.html), e substitua
em uma requisição individual.
```ruby
set :sass, {:style => :compact } # o estilo padrão do Sass é :nested
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
sass :stylesheet, :style => :expanded # substituido
end
```
### Less Templates
A gem/biblioteca less é necessária para renderizar templates Less:
```ruby
# É necessário requerir 'less' na aplicação.
require 'less'
get '/stylesheet.css' do
content_type 'text/css', :charset => 'utf-8'
less :stylesheet
end
```
Renderiza `./views/stylesheet.less`.
### Templates Inline
```ruby
get '/' do
haml '%div.title Olá Mundo'
end
```
Renderiza a string, em uma linha, no template.
### Acedendo a Variáveis nos Templates
Templates são avaliados dentro do mesmo contexto que os manipuladores de
rota. Variáveis de instância definidas em rotas manipuladas são
directamente acedidas por templates:
```ruby
get '/:id' do
@foo = Foo.find(params['id'])
haml '%h1= @foo.nome'
end
```
Ou, especifique um hash explícito para variáveis locais:
```ruby
get '/:id' do
foo = Foo.find(params['id'])
haml '%h1= foo.nome', :locals => { :foo => foo }
end
```
Isso é tipicamente utilizado quando renderizamos templates parciais
(partials) dentro de outros templates.
### Templates Inline
Templates podem ser definidos no final do arquivo fonte(.rb):
```ruby
require 'rubygems'
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
= yield
@@ index
%div.title Olá Mundo!!!!!
```
NOTA: Templates inline definidos no arquivo fonte são automaticamente
carregados pelo sinatra. Digite \`enable :inline\_templates\` se tem
templates inline no outro arquivo fonte.
### Templates nomeados
Templates também podem ser definidos utilizando o método top-level
`template`:
```ruby
template :layout do
"%html\n =yield\n"
end
template :index do
'%div.title Olá Mundo!'
end
get '/' do
haml :index
end
```
Se existir um template com nome “layout”, ele será utilizado sempre que
um template for renderizado. Pode desactivar layouts usando
`:layout => false`.
```ruby
get '/' do
haml :index, :layout => !request.xhr?
end
```
## Helpers
Use o método de alto nível `helpers` para definir métodos auxiliares
para utilizar em manipuladores de rotas e modelos:
```ruby
helpers do
def bar(nome)
"#{nome}bar"
end
end
get '/:nome' do
bar(params['nome'])
end
```
## Filtros
Filtros Before são avaliados antes de cada requisição dentro do contexto
da requisição e podem modificar a requisição e a reposta. Variáveis de
instância definidas nos filtros são acedidas através de rotas e
templates:
```ruby
before do
@nota = 'Olá!'
request.path_info = '/foo/bar/baz'
end
get '/foo/*' do
@nota #=> 'Olá!'
params['splat'] #=> 'bar/baz'
end
```
Filtros After são avaliados após cada requisição dentro do contexto da
requisição e também podem modificar o pedido e a resposta. Variáveis de
instância definidas nos filtros before e rotas são acedidas através dos
filtros after:
```ruby
after do
puts response.status
end
```
Filtros opcionalmente têm um padrão, fazendo com que sejam avaliados
somente se o caminho do pedido coincidir com esse padrão:
```ruby
before '/protected/*' do
autenticar!
end
after '/create/:slug' do |slug|
session[:last_slug] = slug
end
```
## Halting
Para parar imediatamente uma requisição dentro de um filtro ou rota
utilize:
```ruby
halt
```
Pode também especificar o status ao parar…
```ruby
halt 410
```
Ou com um corpo de texto…
```ruby
halt 'isto será o corpo de texto'
```
Ou também…
```ruby
halt 401, 'vamos embora!'
```
Com cabeçalhos…
```ruby
halt 402, {'Content-Type' => 'text/plain'}, 'revanche'
```
## Passing
Dentro de uma rota, pode passar para a próxima rota correspondente
usando `pass`:
```ruby
get '/adivinhar/:quem' do
pass unless params['quem'] == 'Frank'
'Apanhaste-me!'
end
get '/adivinhar/*' do
'Falhaste!'
end
```
O bloqueio da rota é imediatamente encerrado e o controle continua com a
próxima rota de parâmetro. Se o parâmetro da rota não for encontrado, um
404 é retornado.
## Configuração
Correndo uma vez, na inicialização, em qualquer ambiente:
```ruby
configure do
...
end
```
Correndo somente quando o ambiente (`RACK_ENV` environment variável) é
definido para `:production`:
```ruby
configure :production do
...
end
```
Correndo quando o ambiente é definido para `:production` ou `:test`:
```ruby
configure :production, :test do
...
end
```
## Lidar com Erros
Lida-se com erros no mesmo contexto das rotas e filtros before, o que
signifca que `haml`, `erb`, etc, estão disponíveis.
### Não Encontrado
Quando um `Sinatra::NotFound` exception é levantado, ou o código de
status da reposta é 404, o manipulador `not_found` é invocado:
```ruby
not_found do
'Isto está longe de ser encontrado'
end
```
### Erro
O manipulador `error` é invocado sempre que uma exceção é lançada a
partir de um bloco de rota ou um filtro. O objecto da exceção pode ser
obtido a partir da variável Rack `sinatra.error`:
```ruby
error do
'Peço desculpa, houve um erro desagradável - ' + env['sinatra.error'].message
end
```
Erros personalizados:
```ruby
error MeuErroPersonalizado do
'O que aconteceu foi...' + env['sinatra.error'].message
end
```
Então, se isso acontecer:
```ruby
get '/' do
raise MeuErroPersonalizado, 'alguma coisa desagradável'
end
```
O resultado será:
```
O que aconteceu foi...alguma coisa desagradável
```
Alternativamente, pode definir um manipulador de erro para um código de
status:
```ruby
error 403 do
'Accesso negado'
end
get '/secreto' do
403
end
```
Ou um range (alcance):
```ruby
error 400..510 do
'Boom'
end
```
O Sinatra define os manipuladores especiais `not_found` e `error` quando
corre no ambiente de desenvolvimento.
## Mime Types
Quando utilizamos `send_file` ou arquivos estáticos pode ter mime types
Sinatra não entendidos. Use `mime_type` para os registar por extensão de
arquivos:
```ruby
mime_type :foo, 'text/foo'
```
Pode também utilizar isto com o helper `content_type`:
```ruby
content_type :foo
```
## Middleware Rack
O Sinatra corre no [Rack](http://rack.github.io/), uma interface
padrão mínima para frameworks web em Ruby. Uma das capacidades mais
interessantes do Rack, para desenvolver aplicações, é o suporte de
“middleware” – componentes que residem entre o servidor e a aplicação,
monitorizando e/ou manipulando o pedido/resposta (request/response) HTTP
para providenciar varios tipos de funcionalidades comuns.
O Sinatra torna a construção de pipelines do middleware Rack fácil a um
nível superior utilizando o método `use`:
```ruby
require 'sinatra'
require 'meu_middleware_personalizado'
use Rack::Lint
use MeuMiddlewarePersonalizado
get '/ola' do
'Olá mundo'
end
```
A semântica de `use` é idêntica aquela definida para a DSL
[Rack::Builder](http://www.rubydoc.info/github/rack/rack/master/Rack/Builder)
(mais frequentemente utilizada para arquivos rackup). Por exemplo, o
método `use` aceita múltiplos argumentos/variáveis, bem como blocos:
```ruby
use Rack::Auth::Basic do |utilizador, senha|
utilizador == 'admin' && senha == 'secreto'
end
```
O Rack é distribuido com uma variedade de middleware padrões para logs,
debugs, rotas de URL, autenticação, e manipuladores de sessão.Sinatra
utiliza muitos desses componentes automaticamente dependendo da
configuração, por isso, tipicamente nao é necessário utilizar `use`
explicitamente.
## Testando
Testes no Sinatra podem ser escritos utilizando qualquer biblioteca ou
framework de teste baseados no Rack.
[Rack::Test](http://gitrdoc.com/brynary/rack-test) é recomendado:
```ruby
require 'minha_aplicacao_sinatra'
require 'rack/test'
class MinhaAplicacaoTeste < Minitest::Test
include Rack::Test::Methods
def app
Sinatra::Application
end
def meu_test_default
get '/'
assert_equal 'Ola Mundo!', last_response.body
end
def teste_com_parametros
get '/atender', :name => 'Frank'
assert_equal 'Olá Frank!', last_response.bodymeet
end
def test_com_ambiente_rack
get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
assert_equal "Você está utilizando o Songbird!", last_response.body
end
end
```
NOTA: Os módulos de classe embutidos `Sinatra::Test` e
`Sinatra::TestHarness` são depreciados na versão 0.9.2.
## Sinatra::Base - Middleware, Bibliotecas e aplicativos modulares
Definir sua aplicação a um nível superior de trabalho funciona bem para
micro aplicativos, mas tem consideráveis incovenientes na construção de
componentes reutilizáveis como um middleware Rack, metal Rails,
bibliotecas simples como um componente de servidor, ou mesmo extensões
Sinatra. A DSL de nível superior polui o espaço do objeto e assume um
estilo de configuração de micro aplicativos (exemplo: um simples arquivo
de aplicação, directórios `./public` e `./views`, logs, página de detalhes
de excepção, etc.). É onde o Sinatra::Base entra em jogo:
```ruby
require 'sinatra/base'
class MinhaApp < Sinatra::Base
set :sessions, true
set :foo, 'bar'
get '/' do
'Olá mundo!'
end
end
```
A classe MinhaApp é um componente Rack independente que pode utilizar
como um middleware Rack, uma aplicação Rack, ou metal Rails. Pode
utilizar ou executar esta classe com um arquivo rackup `config.ru`;
ou, controlar um componente de servidor fornecendo como biblioteca:
```ruby
MinhaApp.run! :host => 'localhost', :port => 9090
```
Os métodos disponíveis para subclasses `Sinatra::Base` são exatamente como
aqueles disponíveis via a DSL de nível superior. Aplicações de nível
mais alto podem ser convertidas para componentes `Sinatra::Base` com duas
modificações:
- Seu arquivo deve requerer `sinatra/base` ao invés de `sinatra`;
outra coisa, todos os métodos DSL do Sinatra são importados para o
espaço principal.
- Coloque as rotas da sua aplicação, manipuladores de erro, filtros e
opções na subclasse de um `Sinatra::Base`.
`Sinatra::Base` é um quadro branco. Muitas opções são desactivadas por
padrão, incluindo o servidor embutido. Veja [Opções e
Configurações](http://www.sinatrarb.com/configuration.html) para
detalhes de opções disponíveis e seus comportamentos.
SIDEBAR: A DSL de alto nível do Sinatra é implementada utilizando um simples
sistema de delegação. A classe `Sinatra::Application` – uma subclasse especial
da `Sinatra::Base` – recebe todos os `:get`, `:put`, `:post`, `:delete`,
`:before`, `:error`, `:not_found`, `:configure`, e `:set` messages enviados
para o alto nível. Dê você mesmo uma vista de olhos ao código: aqui está o
[Sinatra::Delegator
mixin](http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128)
sendo [incluido dentro de um espaço
principal](http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28)
## Linha de Comandos
As aplicações Sinatra podem ser executadas directamente:
```shell
ruby minhaapp.rb [-h] [-x] [-e AMBIENTE] [-p PORTA] [-o HOST] [-s SERVIDOR]
```
As opções são:
```
-h # ajuda
-p # define a porta (padrão é 4567)
-o # define o host (padrão é 0.0.0.0)
-e # define o ambiente (padrão é development)
-s # especifica o servidor/manipulador rack (padrão é thin)
-x # activa o bloqueio (padrão é desligado)
```
## A última versão
Se gostaria de utilizar o código da última versão do Sinatra, crie um
clone local e execute sua aplicação com o directório `sinatra/lib` no
`LOAD_PATH`:
```shell
cd minhaapp
git clone git://github.com/sinatra/sinatra.git
ruby -I sinatra/lib minhaapp.rb
```
Alternativamente, pode adicionar o directório do `sinatra/lib` no
`LOAD_PATH` do seu aplicativo:
```ruby
$LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib'
require 'rubygems'
require 'sinatra'
get '/sobre' do
"Estou correndo a versão" + Sinatra::VERSION
end
```
Para actualizar o código do Sinatra no futuro:
```shell
cd meuprojeto/sinatra
git pull
```
## Mais
- [Website do Projeto](http://www.sinatrarb.com/) - Documentação
adicional, novidades e links para outros recursos.
- [Contribuir](http://www.sinatrarb.com/contributing) - Encontrou um
bug? Precisa de ajuda? Tem um patch?
- [Acompanhar Questões](https://github.com/sinatra/sinatra/issues)
- [Twitter](https://twitter.com/sinatra)
- [Lista de Email](http://groups.google.com/group/sinatrarb/topics)
- [IRC: \#sinatra](irc://chat.freenode.net/#sinatra) em
[freenode.net](http://freenode.net)
sinatra-1.4.7/README.ru.md 0000644 0000041 0000041 00000325647 12652356512 015150 0 ustar www-data www-data # Sinatra
## Содержание
* [Sinatra](#sinatra)
* [Маршруты](#Маршруты)
* [Условия](#Условия)
* [Возвращаемые значения](#Возвращаемые-значения)
* [Собственные детекторы совпадений для маршрутов](#Собственные-детекторы-совпадений-для-маршрутов)
* [Статические файлы](#Статические-файлы)
* [Представления / Шаблоны](#Представления--Шаблоны)
* [Буквальные шаблоны](#Буквальные-шаблоны)
* [Доступные шаблонизаторы](#Доступные-шаблонизаторы)
* [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` и вложенные раскладки (layout)](#Шаблоны-с-yield-и-вложенные-раскладки-layout)
* [Включённые шаблоны](#Включённые-шаблоны)
* [Именованные шаблоны](#Именованные-шаблоны)
* [Привязка файловых расширений](#Привязка-файловых-расширений)
* [Добавление собственного движка рендеринга](#Добавление-собственного-движка-рендеринга)
* [Фильтры](#Фильтры)
* [Методы-помощники](#Методы-помощники)
* [Использование сессий](#Использование-сессий)
* [Прерывание](#Прерывание)
* [Передача](#Передача)
* [Вызов другого маршрута](#Вызов-другого-маршрута)
* [Задание тела, кода и заголовков ответа](#Задание-тела-кода-и-заголовков-ответа)
* [Стриминг ответов](#Стриминг-ответов)
* [Логирование](#Логирование)
* [Mime-типы](#mime-типы)
* [Генерирование URL](#Генерирование-url)
* [Перенаправление (редирект)](#Перенаправление-редирект)
* [Управление кэшированием](#Управление-кэшированием)
* [Отправка файлов](#Отправка-файлов)
* [Доступ к объекту запроса](#Доступ-к-объекту-запроса)
* [Вложения](#Вложения)
* [Работа со временем и датами](#Работа-со-временем-и-датами)
* [Поиск шаблонов](#Поиск-шаблонов)
* [Конфигурация](#Конфигурация)
* [Настройка защиты от атак](#Настройка-защиты-от-атак)
* [Доступные настройки](#Доступные-настройки)
* [Режим, окружение](#Режим-окружение)
* [Обработка ошибок](#Обработка-ошибок)
* [Not Found](#not-found)
* [Error](#error)
* [Rack "прослойки"](#rack-прослойки)
* [Тестирование](#Тестирование)
* [Sinatra::Base — "прослойки", библиотеки и модульные приложения](#sinatrabase--прослойки-библиотеки-и-модульные-приложения)
* [Модульные приложения против классических](#Модульные-приложения-против-классических)
* [Запуск модульных приложений](#Запуск-модульных-приложений)
* [Запуск классических приложений с config.ru](#Запуск-классических-приложений-с-configru)
* [Когда использовать config.ru?](#Когда-использовать-configru)
* [Использование Sinatra в качестве "прослойки"](#Использование-sinatra-в-качестве-прослойки)
* [Создание приложений "на лету"](#Создание-приложений-на-лету)
* [Области видимости и привязка](#Области-видимости-и-привязка)
* [Область видимости приложения / класса](#Область-видимости-приложения--класса)
* [Область видимости запроса / экземпляра](#Область-видимости-запроса--экземпляра)
* [Область видимости делегирования](#Область-видимости-делегирования)
* [Командная строка](#Командная-строка)
* [Multi-threading](#multi-threading)
* [Системные требования](#Системные-требования)
* [На острие](#На-острие)
* [С помощью Bundler](#С-помощью-bundler)
* [Вручную](#Вручную)
* [Установка глобально](#Установка-глобально)
* [Версии](#Версии)
* [Дальнейшее чтение](#Дальнейшее-чтение)
*Внимание: Этот документ является переводом английской версии и может быть
устаревшим*
Sinatra — это предметно-ориентированный каркас
([DSL](https://ru.wikipedia.org/wiki/Предметно-ориентированный_язык))
для быстрого создания функциональных веб-приложений на Ruby с минимумом усилий:
```ruby
# myapp.rb
require 'sinatra'
get '/' do
'Hello world!'
end
```
Установите gem:
```shell
gem install sinatra
```
и запустите приложение с помощью:
```shell
ruby myapp.rb
```
Оцените результат: [http://localhost:4567](http://localhost:4567)
Рекомендуется также установить Thin, сделать это можно командой: `gem install
thin`. Thin — это более производительный и функциональный сервер для
разработки приложений на Sinatra.
## Маршруты
В 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
```
Маршруты сверяются с запросом в порядке очередности их записи в файле
приложения. Первый же совпавший с запросом маршрут и будет вызван.
Шаблоны маршрутов могут включать в себя именованные параметры, доступные в xэше
`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|
"Hello #{n}!"
end
```
Шаблоны маршрутов также могут включать в себя splat (или '*' маску,
обозначающую любой символ) параметры, доступные в массиве `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 /meta/hello/world", "GET /hello/world/1234" и так далее
get %r{/hello/([\w]+)} do |c|
"Hello, #{c}!"
end
```
Шаблоны маршрутов могут иметь необязательные параметры:
```ruby
get '/posts/:format?' do
# соответствует "GET /posts/", "GET /posts/json", "GET /posts/xml" и т.д.
end
```
Кстати, если вы не отключите защиту от обратного пути в директориях (path
traversal, см. ниже), путь запроса может быть изменен до начала поиска
подходящего маршрута.
### Условия
Маршруты могут включать различные условия совпадений, например, клиентское
приложение (user agent):
```ruby
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
"You're using Songbird version #{params['agent'][0]}"
end
get '/foo' do
# соответствует не-songbird браузерам
end
```
Другими доступными условиями являются `host_name` и `provides`:
```ruby
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
```
Вы можете задать собственные условия:
```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
```
Для условия, которое принимает несколько параметров, используйте звездочку:
```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
"Your Account Details"
end
get "/only/admin/", :auth => :admin do
"Only admins are allowed here!"
end
```
### Возвращаемые значения
Возвращаемое значение блока маршрута ограничивается телом ответа, которое
будет передано HTTP клиенту, или следующей "прослойкой" (middleware) в Rack
стеке. Чаще всего это строка, как в примерах выше. Но также приемлемы и
другие значения.
Вы можете вернуть любой объект, который будет либо корректным Rack ответом,
объектом Rack body, либо кодом состояния HTTP:
* массив с тремя переменными: `[код (Fixnum), заголовки (Hash), тело ответа
(должно отвечать на #each)]`;
* массив с двумя переменными: `[код (Fixnum), тело ответа (должно отвечать
на #each)]`;
* объект, отвечающий на `#each`, который передает только строковые типы
данных в этот блок;
* Fixnum, представляющий код состояния HTTP.
Таким образом, легко можно реализовать, например, поточный пример:
```ruby
class Stream
def each
100.times { |i| yield "#{i}\n" }
end
end
get('/') { Stream.new }
```
Вы также можете использовать метод `stream` (описываемый ниже), чтобы
уменьшить количество дублируемого кода и держать логику стриминга прямо в
маршруте.
### Собственные детекторы совпадений для маршрутов
Как показано выше, Sinatra поставляется со встроенной поддержкой строк и
регулярных выражений в качестве шаблонов URL. Но и это еще не все. Вы можете
легко определить свои собственные детекторы совпадений (matchers) для
маршрутов:
```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
```
## Статические файлы
Статические файлы отдаются из `./public` директории. Вы можете указать другое
место, используя опцию `:public_folder`:
```ruby
set :public_folder, File.dirname(__FILE__) + '/static'
```
Учтите, что имя директории со статическими файлами не включено в URL.
Например, файл `./public/css/style.css` будет доступен как
`http://example.com/css/style.css`.
Используйте опцию `:static_cache_control` (см. ниже), чтобы добавить заголовок
`Cache-Control`.
## Представления / Шаблоны
Каждый шаблонизатор представлен своим собственным методом. Эти методы попросту
возвращают строку:
```ruby
get '/' do
erb :index
end
```
Отобразит `views/index.erb`.
Вместо имени шаблона вы так же можете передавать непосредственно само
содержимое шаблона:
```ruby
get '/' do
code = "<%= Time.now %>"
erb code
end
```
Эти методы принимают второй аргумент, хеш с опциями:
```ruby
get '/' do
erb :index, :layout => :post
end
```
Отобразит `views/index.erb`, вложенным в `views/post.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). Если же значение Symbol,
то указывает, какой шаблон использовать в качестве лэйаута. Например:
erb :index, :layout => !request.xhr?
- content_type
-
Content-Type отображенного шаблона. По умолчанию: задается шаблонизатором.
- scope
-
Область видимости, в которой рендерятся шаблоны. По умолчанию: экземпляр
приложения. Если вы измените эту опцию, то переменные экземпляра и
методы-помощники станут недоступными в ваших шаблонах.
- layout_engine
-
Шаблонизатор, который следует использовать для отображения лэйаута.
Полезная опция для шаблонизаторов, в которых нет никакой поддержки
лэйаутов. По умолчанию: тот же шаблонизатор, что используется и для самого
шаблона. Пример: set :rdoc, :layout_engine => :erb
По умолчанию считается, что шаблоны находятся в директории `./views`. Чтобы
использовать другую директорию с шаблонами:
```ruby
set :views, settings.root + '/templates'
```
Важное замечание: вы всегда должны ссылаться на шаблоны с помощью символов
(Symbol), даже когда они в поддиректории (в этом случае используйте
`:'subdir/template'`). Вы должны использовать символы, потому что иначе
шаблонизаторы попросту отображают любые строки, переданные им.
### Буквальные шаблоны
```ruby
get '/' do
haml '%div.title Hello World'
end
```
Отобразит шаблон, переданный строкой.
### Доступные шаблонизаторы
Некоторые языки шаблонов имеют несколько реализаций. Чтобы указать, какую
реализацию использовать, вам следует просто подключить нужную библиотеку:
```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`), то
вы почти всегда будете передавать в шаблон локальные переменные.
#### Markdown шаблоны
В Markdown невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:
```ruby
erb :overview, :locals => { :text => markdown(:introduction) }
```
Заметьте, что вы можете вызывать метод `markdown` из других шаблонов:
```ruby
%h1 Hello From Haml!
%p= markdown(:greetings)
```
Вы не можете вызывать Ruby из Markdown, соответственно, вы не можете
использовать лэйауты на Markdown. Тем не менее, есть возможность использовать
один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
опции `:layout_engine`.
#### Textile шаблоны
| Зависимости |
RedCloth |
| Расширения файлов |
.textile |
| Пример |
textile :index, :layout_engine => :erb |
В Textile невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:
```ruby
erb :overview, :locals => { :text => textile(:introduction) }
```
Заметьте, что вы можете вызывать метод `textile` из других шаблонов:
```ruby
%h1 Hello From Haml!
%p= textile(:greetings)
```
Вы не можете вызывать Ruby из Textile, соответственно, вы не можете
использовать лэйауты на Textile. Тем не менее, есть возможность использовать
один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
опции `:layout_engine`.
#### RDoc шаблоны
| Зависимости |
RDoc |
| Расширения файлов |
.rdoc |
| Пример |
rdoc :README, :layout_engine => :erb |
В RDoc невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:
```ruby
erb :overview, :locals => { :text => rdoc(:introduction) }
```
Заметьте, что вы можете вызывать метод `rdoc` из других шаблонов:
```ruby
%h1 Hello From Haml!
%p= rdoc(:greetings)
```
Вы не можете вызывать Ruby из RDoc, соответственно, вы не можете использовать
лэйауты на RDoc. Тем не менее, есть возможность использовать один шаблонизатор
для отображения шаблона, а другой для лэйаута с помощью опции
`:layout_engine`.
#### AsciiDoc шаблоны
| Зависимости |
Asciidoctor |
| Расширения файлов |
.asciidoc, .adoc и .ad |
| Пример |
asciidoc :README, :layout_engine => :erb |
Так как в AsciiDoc шаблонах невозможно вызывать методы из Ruby напрямую, то вы
почти всегда будете передавать в шаблон локальные переменные.
#### Radius шаблоны
| Зависимости |
Radius |
| Расширения файлов |
.radius |
| Пример |
radius :index, :locals => { :key => 'value' } |
Так как в Radius шаблонах невозможно вызывать методы из Ruby напрямую, то вы
почти всегда будете передавать в шаблон локальные переменные.
#### 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 невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:
```ruby
erb :overview, :locals => { :text => creole(:introduction) }
```
Заметьте, что вы можете вызывать метод `creole` из других шаблонов:
```ruby
%h1 Hello From Haml!
%p= creole(:greetings)
```
Вы не можете вызывать Ruby из Creole, соответственно, вы не можете
использовать лэйауты на Creole. Тем не менее, есть возможность использовать
один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
опции `:layout_engine`.
#### MediaWiki шаблоны
| Зависимости |
WikiCloth |
| Расширения файлов |
.mediawiki и .mw |
| Пример |
mediawiki :wiki, :layout_engine => :erb |
В разметке MediaWiki невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:
```ruby
erb :overview, :locals => { :text => mediawiki(:introduction) }
```
Заметьте, что вы можете вызывать метод `mediawiki` из других шаблонов:
```ruby
%h1 Hello From Haml!
%p= mediawiki(:greetings)
```
Вы не можете вызывать Ruby из MediaWiki, соответственно, вы не можете
использовать лэйауты на MediaWiki. Тем не менее, есть возможность использовать
один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
опции `:layout_engine`.
#### CoffeeScript шаблоны
#### 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 напрямую (за
исключением `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` и вложенные раскладки (layout)
Раскладка (layout) обычно представляет собой шаблон, который исполняет
`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` также принимает блок.
### Включённые шаблоны
Шаблоны также могут быть определены в конце исходного файла:
```ruby
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
= yield
@@ index
%div.title Hello world.
```
Заметьте: включённые шаблоны, определенные в исходном файле, который подключил
Sinatra, будут загружены автоматически. Вызовите `enable :inline_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`. Например, если вы хотите использовать расширение `tt` для
шаблонов Textile:
```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
## Фильтры
`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`-фильтрах, так как оно
будет сгенерировано позднее.
Фильтры могут использовать шаблоны URL и будут интерпретированы, только если
путь запроса совпадет с этим шаблоном:
```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`, чтобы определить методы-помощники, которые в
дальнейшем можно будет использовать в обработчиках маршрутов и шаблонах:
```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` все данные сохраняются в
куках (cookies). Это может быть не совсем то, что вы хотите (например,
сохранение больших объемов данных увеличит ваш трафик). В таком случае вы
можете использовать альтернативную Rack "прослойку" (middleware), реализующую
механизм сессий. Для этого *не надо* вызывать `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
```
Для повышения безопасности данные сессии в куках подписываются секретным
ключом. Секретный ключ генерируется Sinatra. Тем не менее, так как этот ключ
будет меняться с каждым запуском приложения, вы, возможно, захотите установить
ключ вручную, чтобы у всех экземпляров вашего приложения был один и тот же
ключ:
```ruby
set :session_secret, 'super secret'
```
Если вы хотите больше настроек для сессий, вы можете задать их, передав хеш
опций в параметр `sessions`:
```ruby
set :sessions, :domain => 'foo.com'
```
Чтобы сделать сессию доступной другим приложениям, размещенным на поддоменах
foo.com, добавьте *.* перед доменом:
```ruby
set :sessions, :domain => '.foo.com'
```
### Прерывание
Чтобы незамедлительно прервать обработку запроса внутри фильтра или маршрута,
используйте:
```ruby
halt
```
Можно также указать статус при прерывании:
```ruby
halt 410
```
Тело:
```ruby
halt 'this will be the body'
```
И то, и другое:
```ruby
halt 401, 'go away!'
```
Можно указать заголовки:
```ruby
halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
```
И, конечно, можно использовать шаблоны с `halt`:
```ruby
halt erb(:error)
```
### Передача
Маршрут может передать обработку запроса следующему совпадающему маршруту,
используя `pass`:
```ruby
get '/guess/:who' do
pass unless params['who'] == 'Frank'
'You got me!'
end
get '/guess/*' do
'You missed!'
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.
### Задание тела, кода и заголовков ответа
Хорошим тоном является установка кода состояния HTTP и тела ответа в
возвращаемом значении обработчика маршрута. Тем не менее, в некоторых
ситуациях вам, возможно, понадобится задать тело ответа в произвольной точке
потока исполнения. Вы можете сделать это с помощью метода-помощника `body`.
Если вы задействуете метод `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`, вызванные без аргументов,
возвращают свои текущие значения.
### Стриминг ответов
Иногда требуется начать отправлять данные клиенту прямо в процессе
генерирования частей этих данных. В особых случаях требуется постоянно
отправлять данные до тех пор, пока клиент не закроет соединение. Вы можете
использовать метод `stream` вместо написания собственных "оберток".
```ruby
get '/' do
stream do |out|
out << "It's gonna be legen -\n"
sleep 0.5
out << " (wait for it) \n"
sleep 1
out << "- dary!\n"
end
end
```
Что позволяет вам реализовать стриминговые API,
[Server Sent Events](https://w3c.github.io/eventsource/),
и может служить основой для [WebSockets](https://en.wikipedia.org/wiki/WebSocket).
Также такой подход можно использовать для увеличения производительности в случае,
когда какая-то часть контента зависит от медленного ресурса.
Заметьте, что возможности стриминга, особенно количество одновременно
обслуживаемых запросов, очень сильно зависят от используемого веб-сервера.
Некоторые серверы могут и вовсе не поддерживать стриминг. Если сервер не
поддерживает стриминг, то все данные будут отправлены за один раз сразу после
того, как блок, переданный в `stream`, завершится. Стриминг вообще не работает
при использовании Shotgun.
Если метод используется с параметром `keep_open`, то он не будет вызывать
`close` у объекта потока, что позволит вам закрыть его позже в любом другом
месте. Это работает только с событийными серверами, например, с Thin и
Rainbows. Другие же серверы все равно будут закрывать поток:
```ruby
# long polling
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
```
### Логирование
В области видимости запроса метод `logger` предоставляет доступ к экземпляру
`Logger`:
```ruby
get '/' do
logger.info "loading data"
# ...
end
```
Этот логер автоматически учитывает ваши настройки логирования в Rack. Если
логирование выключено, то этот метод вернет пустой (dummy) объект, поэтому вы
можете смело использовать его в маршрутах и фильтрах.
Заметьте, что логирование включено по умолчанию только для
`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-типы
Когда вы используете `send_file` или статические файлы, у вас могут быть
mime-типы, которые Sinatra не понимает по умолчанию. Используйте `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, если они
присутствуют.
Наряду с `url` вы можете использовать `to` (смотрите пример ниже).
### Перенаправление (редирект)
Вы можете перенаправить браузер пользователя с помощью метода `redirect`:
```ruby
get '/foo' do
redirect to('/bar')
end
```
Любые дополнительные параметры используются по аналогии с аргументами метода
`halt`:
```ruby
redirect to('/bar'), 303
redirect 'http://www.google.com/', 'wrong place, buddy'
```
Вы также можете перенаправить пользователя обратно, на страницу, с которой он
пришел, с помощью `redirect back`:
```ruby
get '/foo' do
"do something"
end
get '/bar' do
do_something
redirect back
end
```
Чтобы передать какие-либо параметры вместе с перенаправлением, либо добавьте
их в строку запроса:
```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
```
### Управление кэшированием
Установка корректных заголовков — основа правильного HTTP кэширования.
Вы можете легко выставить заголовок Cache-Control таким образом:
```ruby
get '/' do
cache_control :public
"cache it!"
end
```
Совет: задавайте кэширование в `before`-фильтре:
```ruby
before do
cache_control :public, :must_revalidate, :max_age => 60
end
```
Если вы используете метод `expires` для задания соответствующего заголовка, то
`Cache-Control` будет выставлен автоматически:
```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](https://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` (см. ниже), чтобы добавить заголовок
`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
процесса. Метод `send_file` также обеспечивает автоматическую обработку
частичных (range) запросов с помощью Sinatra.
### Доступ к объекту запроса
Объект входящего запроса доступен на уровне обработки запроса (в фильтрах,
маршрутах, обработчиках ошибок) с помощью `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.media_type # медиатип тела запроса
request.host # "example.com"
request.get? # true (есть аналоги для других методов HTTP)
request.form_data? # false
request["some_param"] # значение параметра some_param. Шорткат для хеша params
request.referrer # источник запроса клиента либо '/'
request.user_agent # user agent (используется для :agent условия)
request.cookies # хеш, содержащий cookies браузера
request.xhr? # является ли запрос ajax запросом?
request.url # "http://example.com/example/foo"
request.path # "/example/foo"
request.ip # IP-адрес клиента
request.secure? # false (true, если запрос сделан через SSL)
request.forwarded? # true (если сервер работает за обратным прокси)
request.env # "сырой" env хеш, полученный Rack
end
```
Некоторые опции, такие как `script_name` или `path_info`, доступны для
изменения:
```ruby
before { request.path_info = "/" }
get "/" do
"all requests end up here"
end
```
`request.body` является IO или StringIO объектом:
```ruby
post "/api" do
request.body.rewind # в случае, если кто-то уже прочитал тело запроса
data = JSON.parse request.body.read
"Hello #{data['name']}!"
end
```
### Вложения
Вы можете использовать метод `attachment`, чтобы сказать браузеру, что ответ
сервера должен быть сохранен на диск, а не отображен:
```ruby
get '/' do
attachment
"store it!"
end
```
Вы также можете указать имя файла:
```ruby
get '/' do
attachment "info.txt"
"store it!"
end
```
### Работа со временем и датами
Sinatra предлагает метод-помощник `time_for`, который из заданного значения
создает объект Time. Он также может конвертировать `DateTime`, `Date` и
подобные классы:
```ruby
get '/' do
pass if Time.now > time_for('Dec 23, 2012')
"still time"
end
```
Этот метод используется внутри Sinatra методами `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`, как только файл
не будет найден. Содержимое и местонахождение шаблонов будет закэшировано,
если приложение запущено не в режиме разработки (`set :environment,
:development`). Вы должны помнить об этих нюансах, если пишите по-настоящему
"сумасшедший" метод.
## Конфигурация
Этот блок исполняется один раз при старте в любом окружении, режиме
(environment):
```ruby
configure do
# задание одной опции
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/sinatra/rack-protection#readme) для защиты
приложения от простых атак. Вы можете легко выключить эту защиту (что сделает
ваше приложение чрезвычайно уязвимым):
```ruby
disable :protection
```
Чтобы пропустить какой-либо уровень защиты, передайте хеш опций в параметр
`protection`:
```ruby
set :protection, :except => :path_traversal
```
Вы также можете отключить сразу несколько уровней защиты:
```ruby
set :protection, :except => [:path_traversal, :session_hijacking]
```
### Доступные настройки
- absolute_redirects
-
если отключено, то Sinatra будет позволять использование относительных
перенаправлений, но при этом перестанет соответствовать RFC 2616 (HTTP
1.1), который разрешает только абсолютные перенаправления.
-
Включайте эту опцию, если ваше приложение работает за обратным прокси,
который настроен не совсем корректно. Обратите внимание, метод url все
равно будет генерировать абсолютные URL, если вы не передадите false
вторым аргументом.
- Отключено по умолчанию.
- add_charset
-
mime-типы, к которым метод content_type будет автоматически добавлять
информацию о кодировке. Вам следует добавлять значения к этой опции
вместо ее переопределения: settings.add_charset << "application/foobar"
- app_file
-
путь к главному файлу приложения, используется для нахождения корневой
директории проекта, директорий с шаблонами и статическими файлами,
вложенных шаблонов.
- bind
-
используемый IP-адрес (по умолчанию: 0.0.0.0). Используется только
встроенным сервером.
- default_encoding
- кодировка, если неизвестна (по умолчанию: "utf-8").
- dump_errors
- отображать ошибки в логе.
- environment
-
текущее окружение, по умолчанию, значение ENV['RACK_ENV'] или
"development", если ENV['RACK_ENV'] недоступна.
- logging
- использовать логер.
- lock
-
создает блокировку для каждого запроса, которая гарантирует обработку
только одного запроса в текущий момент времени в Ruby процессе.
-
Включайте, если ваше приложение не потоко-безопасно (thread-safe).
Отключено по умолчанию.
- method_override
-
использовать "магический" параметр _method, для поддержки
PUT/DELETE форм в браузерах, которые не поддерживают эти методы.
- port
-
порт, на котором будет работать сервер.
Используется только встроенным сервером.
- prefixed_redirects
-
добавлять или нет параметр request.script_name к редиректам, если не
задан абсолютный путь. Таким образом, redirect '/foo' будет вести себя
как redirect to('/foo'). Отключено по умолчанию.
- protection
- включена или нет защита от атак. Смотрите секцию выше.
- public_dir
- Алиас для public_folder.
- public_folder
-
путь к директории, откуда будут раздаваться статические файлы.
Используется, только если включена раздача статических файлов
(см. опцию static ниже).
- reload_templates
-
перезагружать или нет шаблоны на каждый запрос. Включено в режиме
разработки.
- root
- путь к корневой директории проекта.
- raise_errors
-
выбрасывать исключения (будет останавливать приложение).
По умолчанию включено только в окружении test.
- run
-
если включено, Sinatra будет самостоятельно запускать веб-сервер. Не
включайте, если используете rackup или аналогичные средства.
- running
- работает ли сейчас встроенный сервер? Не меняйте эту опцию!
- server
-
сервер или список серверов, которые следует использовать в качестве
встроенного сервера. По умолчанию: ['thin', 'mongrel', 'webrick'], порядок
задает приоритет.
- sessions
-
включить сессии на основе кук (cookie) на базе Rack::Session::Cookie.
Смотрите секцию "Использование сессий" выше.
- show_exceptions
-
показывать исключения/стек вызовов (stack trace) в браузере. По умолчанию
включено только в окружении development.
-
Может быть установлено в
:after_handler для запуска специфичной для приложения обработки ошибок,
перед показом трассировки стека в браузере.
- static
- должна ли Sinatra осуществлять раздачу статических файлов.
- Отключите, когда используете какой-либо веб-сервер для этой цели.
- Отключение значительно улучшит производительность приложения.
- По умолчанию включено в классических и отключено в модульных приложениях.
- static_cache_control
-
когда Sinatra отдает статические файлы, используйте эту опцию, чтобы
добавить им заголовок Cache-Control. Для этого используется
метод-помощник cache_control. По умолчанию отключено.
-
Используйте массив, когда надо задать несколько значений:
set :static_cache_control, [:public, :max_age => 300]
- threaded
-
если включено, то Thin будет использовать EventMachine.defer для
обработки запросов.
- traps
- должна ли Синатра обрабатывать системные сигналы или нет.
- views
- путь к директории с шаблонами.
## Режим, окружение
Есть 3 предопределенных режима, окружения: `"development"`, `"production"` и
`"test"`. Режим может быть задан через переменную окружения `RACK_ENV`.
Значение по умолчанию — `"development"`. В этом режиме работы все шаблоны
перезагружаются между запросами. А также задаются специальные обработчики
`not_found` и `error`, чтобы вы могли увидеть стек вызовов. В окружениях
`"production"` и `"test"` шаблоны по умолчанию кэшируются.
Для запуска приложения в определенном окружении используйте ключ `-e`
```
ruby my_app.rb -e [ENVIRONMENT]
```
Вы можете использовать предопределенные методы `development?`, `test?` и
+production?, чтобы определить текущее окружение.
## Обработка ошибок
Обработчики ошибок исполняются в том же контексте, что и маршруты, и
`before`-фильтры, а это означает, что всякие прелести вроде `haml`, `erb`,
`halt` и т.д. доступны и им.
### Not Found
Когда выброшено исключение `Sinatra::NotFound`, или кодом ответа является 404,
то будет вызван `not_found` обработчик:
```ruby
not_found do
'This is nowhere to be found.'
end
```
### Error
Обработчик ошибок `error` будет вызван, когда исключение выброшено из блока
маршрута, либо из фильтра. Объект-исключение доступен как переменная
`sinatra.error` в Rack:
```ruby
error do
'Sorry there was a nasty error - ' + env['sinatra.error'].message
end
```
Конкретные ошибки:
```ruby
error MyCustomError do
'So what happened was...' + env['sinatra.error'].message
end
```
Тогда, если это произошло:
```ruby
get '/' do
raise MyCustomError, 'something bad'
end
```
То вы получите:
```
So what happened was... something bad
```
Также вы можете установить обработчик ошибок для кода состояния HTTP:
```ruby
error 403 do
'Access forbidden'
end
get '/secret' do
403
end
```
Либо набора кодов:
```ruby
error 400..510 do
'Boom'
end
```
Sinatra устанавливает специальные `not_found` и `error` обработчики, когда
приложение запущено в режиме разработки (окружение `:development`).
## Rack "прослойки"
Sinatra использует [Rack](http://rack.github.io/), минимальный стандартный
интерфейс для веб-фреймворков на Ruby. Одной из самых интересных для
разработчиков возможностей Rack является поддержка "прослоек" ("middleware") —
компонентов, находящихся "между" сервером и вашим приложением, которые
отслеживают и/или манипулируют HTTP запросами/ответами для предоставления
различной функциональности.
В Sinatra очень просто использовать такие "прослойки" с помощью метода `use`:
```ruby
require 'sinatra'
require 'my_custom_middleware'
use Rack::Lint
use MyCustomMiddleware
get '/hello' do
'Hello World'
end
```
Семантика `use` идентична той, что определена для
[Rack::Builder](http://www.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#readme),
или в
[Rack wiki](https://github.com/rack/rack/wiki/List-of-Middleware).
## Тестирование
Тесты для Sinatra приложений могут быть написаны с помощью библиотек,
фреймворков, поддерживающих тестирование Rack.
[Rack::Test](http://www.rubydoc.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 "You're using Songbird!", last_response.body
end
end
```
## Sinatra::Base — "прослойки", библиотеки и модульные приложения
Описание своего приложения самым простейшим способом (с помощью DSL верхнего
уровня, классический стиль) отлично работает для крохотных приложений. В таких
случаях используется конфигурация, рассчитанная на микро-приложения
(единственный файл приложения, `./public` и `./views` директории, логирование,
страница информации об исключении и т.д.). Тем не менее, такой метод имеет
множество недостатков при создании компонентов, таких как Rack middleware
("прослоек"), Rails metal, простых библиотек с серверными компонентами,
расширений Sinatra. И тут на помощь приходит `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 верхнего уровня. Большинство таких приложений могут быть
конвертированы в `Sinatra::Base` компоненты с помощью двух модификаций:
* Вы должны подключать `sinatra/base` вместо `sinatra`, иначе все методы,
предоставляемые Sinatra, будут импортированы в глобальное пространство
имен.
* Поместите все маршруты, обработчики ошибок, фильтры и опции в подкласс
`Sinatra::Base`.
`Sinatra::Base` — это чистый лист. Большинство опций, включая встроенный
сервер, по умолчанию отключены. Смотрите
[Опции и конфигурация](http://www.sinatrarb.com/configuration.html)
для детальной информации об опциях и их поведении.
### Модульные приложения против классических
Вопреки всеобщему убеждению, в классическом стиле (самом простом) нет ничего
плохого. Если этот стиль подходит вашему приложению, вы не обязаны
переписывать его в модульное приложение.
Основным недостатком классического стиля является тот факт, что у вас может
быть только одно приложение Sinatra на один процесс Ruby. Если вы планируете
использовать больше, переключайтесь на модульный стиль. Вы можете смело
смешивать модульный и классический стили.
Переходя с одного стиля на другой, примите во внимание следующие изменения в
настройках:
Опция Классический Модульный
app_file файл с приложением файл с подклассом Sinatra::Base
run $0 == app_file false
logging true false
method_override true false
inline_templates true false
static true File.exist?(public_folder)
### Запуск модульных приложений
Есть два общепринятых способа запускать модульные приложения: запуск напрямую
с помощью `run!`:
```ruby
# my_app.rb
require 'sinatra/base'
class MyApp < Sinatra::Base
# ... здесь код приложения ...
# запускаем сервер, если исполняется текущий файл
run! if app_file == $0
end
```
Затем:
```
ruby my_app.rb
```
Или с помощью конфигурационного файла `config.ru`, который позволяет
использовать любой Rack-совместимый сервер приложений.
```ruby
# config.ru
require './my_app'
run MyApp
```
Запускаем:
```
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 только в качестве "прослойки" Rack.
**Совсем необязательно переходить на использование `config.ru` лишь потому,
что вы стали использовать модульный стиль приложения. И необязательно
использовать модульный стиль, чтобы запускать приложение с помощью
`config.ru`.**
### Использование Sinatra в качестве "прослойки"
Не только сама Sinatra может использовать "прослойки" Rack, но и любое Sinatra
приложение само может быть добавлено к любому Rack endpoint в качестве
"прослойки". Этим endpoint (конечной точкой) может быть другое 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' && params['password'] == 'admin'
session['user_name'] = params['name']
else
redirect '/login'
end
end
end
class MyApp < Sinatra::Base
# "прослойка" будет запущена перед фильтрами
use LoginScreen
before do
unless session['user_name']
halt "Access denied, please login."
end
end
get('/') { "Hello #{session['user_name']}." }
end
```
### Создание приложений "на лету"
Иногда требуется создавать Sinatra приложения "на лету" (например, из другого
приложения). Это возможно с помощью `Sinatra.new`:
```ruby
require 'sinatra/base'
my_app = Sinatra.new { get('/') { "hi" } }
my_app.run!
```
Этот метод может принимать аргументом приложение, от которого следует
наследоваться:
```ruby
# config.ru
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 и при использовании
Sinatra внутри вашей библиотеки.
Благодаря этому, использовать Sinatra как "прослойку" очень просто:
```ruby
require 'sinatra/base'
use Sinatra do
get('/') { ... }
end
run RailsProject::Application
```
## Области видимости и привязка
Текущая область видимости определяет методы и переменные, доступные в данный
момент.
### Область видимости приложения / класса
Любое 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`;
* блока, переданного в `Sinatra.new`.
Вы можете получить доступ к объекту области видимости (классу приложения)
следующими способами:
* через объект, переданный блокам конфигурации (`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 (другой запрос)
end
"Route defined!"
end
end
```
У вас будет область видимости запроса в:
* get/head/post/put/delete/options блоках;
* before/after фильтрах;
* методах-помощниках;
* шаблонах/отображениях.
### Область видимости делегирования
Область видимости делегирования просто перенаправляет методы в область
видимости класса. Однако, она не полностью ведет себя как область видимости
класса, так как у вас нет привязки к классу. Только методы, явно помеченные
для делегирования, будут доступны, а переменных/состояний области видимости
класса не будет (иначе говоря, у вас будет другой `self` объект). Вы можете
непосредственно добавить методы делегирования, используя
`Sinatra::Delegator.delegate :method_name`.
У вас будет контекст делегирования внутри:
* привязки верхнего уровня, если вы сделали `require 'sinatra'`;
* объекта, расширенного с помощью `Sinatra::Delegator`.
Посмотрите сами в код: вот
[примесь Sinatra::Delegator](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633)
[расширяет главный объект](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30).
## Командная строка
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 # указание rack сервера/обработчика (по умолчанию thin)
-x # включить мьютекс-блокировку (по умолчанию выключена)
```
### Multi-threading
_Данный раздел является перефразированным [ответом пользователя Konstantin][so-answer] на StackOverflow_
Sinatra не навязывает каких-либо моделей параллелизма, но для этих целей можно
использовать любой Rack обработчик, например Thin, Puma или WEBrick. Сама
по себе Sinatra потокобезопасна, поэтому нет никаких проблем в использовании
поточной модели параллелизма в Rack обработчике. Это означает, что когда
запускается сервер, вы должны указать правильный метод вызова для конкретного
Rack обработчика. Пример ниже показывает, как можно запустить мультитредовый
Thin сервер:
```ruby
# app.rb
require 'sinatra/base'
class App < Sinatra::Base
get '/' do
"Hello, World"
end
end
App.run!
```
Чтобы запустить сервер, вы должны выполнить следующую команду:
```shell
thin --threaded start
```
[so-answer]: http://stackoverflow.com/questions/6278817/is-sinatra-multi-threaded/6282999#6282999)
## Системные требования
Следующие версии Ruby официально поддерживаются:
- Ruby 1.8.7
- 1.8.7 полностью поддерживается, тем не менее, если вас ничто не держит на
этой версии, рекомендуем обновиться до 1.9.2 или перейти на JRuby или
Rubinius. Поддержка 1.8.7 не будет прекращена до выхода Sinatra 2.0 и Ruby
2.0, разве что в случае релиза 1.8.8 (что маловероятно). Но даже тогда,
возможно, поддержка не будет прекращена. Ruby 1.8.6 больше не
поддерживается. Если вы хотите использовать 1.8.6, откатитесь до Sinatra
1.2, которая будет получать все исправления ошибок до тех пор, пока не
будет выпущена Sinatra 1.4.0.
- Ruby 1.9.2
- 1.9.2 полностью поддерживается и рекомендована к использованию.
Не используйте 1.9.2p0,
известно, что эта версия очень нестабильна при использовании Sinatra. Эта
версия будет поддерживаться по крайней мере до выхода Ruby 1.9.4/2.0, а
поддержка последней версии 1.9 будет осуществляться до тех пор, пока она
поддерживается командой разработчиков Ruby.
- Ruby 1.9.3
- 1.9.3 полностью поддерживается. Заметьте, что переход на 1.9.3 с
ранних версий сделает недействительными все сессии.
- Rubinius
- Rubinius официально поддерживается (Rubinius >= 1.2.4), всё, включая все
языки шаблонов, работает. Предстоящий релиз 2.0 также поддерживается.
- JRuby
- JRuby официально поддерживается (JRuby >= 1.6.5). Нет никаких проблем с
использованием альтернативных шаблонов. Тем не менее, если вы выбираете
JRuby, то, пожалуйста, посмотрите на JRuby Rack-серверы, так как Thin не
поддерживается полностью на JRuby. Поддержка расширений на C в JRuby все
еще экспериментальная, что на данный момент затрагивает только RDiscount,
Redcarpet и RedCloth.
Мы также следим за предстоящими к выходу версиями Ruby.
Следующие реализации Ruby не поддерживаются официально, но известно, что на
них запускается Sinatra:
* старые версии JRuby и Rubinius;
* Ruby Enterprise Edition;
* MacRuby, Maglev, IronRuby;
* Ruby 1.9.0 и 1.9.1 (настоятельно не рекомендуются к использованию).
То, что версия официально не поддерживается, означает, что, если что-то не
работает на этой версии, а на поддерживаемой работает — это не наша проблема,
а их.
Мы также запускаем наши CI-тесты на версии Ruby, находящейся в разработке
(предстоящей 2.0.0), и на 1.9.4, но мы не можем ничего гарантировать, так как
они находятся в разработке. Предполагается, что 1.9.4p0 и 2.0.0p0 будут
поддерживаться.
Sinatra должна работать на любой операционной системе, в которой есть одна из
указанных выше версий Ruby.
Пока невозможно запустить Sinatra на Cardinal, SmallRuby, BlueRuby и на любой
версии Ruby до 1.8.7.
## На острие
Если вы хотите использовать самый последний код Sinatra, не бойтесь запускать
свое приложение вместе с кодом из master ветки Sinatra, она весьма стабильна.
Мы также время от времени выпускаем предварительные версии, так что вы можете
делать так:
```
gem install sinatra --pre
```
Чтобы воспользоваться некоторыми самыми последними возможностями.
### С помощью Bundler
Если вы хотите запускать свое приложение с последней версией Sinatra, то
рекомендуем использовать [Bundler](http://bundler.io).
Сначала установите Bundler, если у вас его еще нет:
```
gem install bundler
```
Затем создайте файл `Gemfile` в директории вашего проекта:
```ruby
source :rubygems
gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
# другие зависимости
gem 'haml' # например, если используете haml
gem 'activerecord', '~> 3.0' # может быть, вам нужен и ActiveRecord 3.x
```
Обратите внимание, вам нужно будет указывать все зависимости вашего приложения
в этом файле. Однако, непосредственные зависимости Sinatra (Rack и Tilt)
Bundler автоматически скачает и добавит.
Теперь вы можете запускать свое приложение так:
```
bundle exec ruby myapp.rb
```
### Вручную
Создайте локальный клон репозитория и запускайте свое приложение с
`sinatra/lib` директорией в `$LOAD_PATH`:
```
cd myapp
git clone git://github.com/sinatra/sinatra.git
ruby -Isinatra/lib myapp.rb
```
Чтобы обновить исходники Sinatra:
```
cd myapp/sinatra
git pull
```
### Установка глобально
Вы можете самостоятельно собрать gem:
```
git clone git://github.com/sinatra/sinatra.git
cd sinatra
rake sinatra.gemspec
rake install
```
Если вы устанавливаете пакеты (gem) от пользователя root, то вашим последним
шагом должна быть команда
```
sudo rake install
```
## Версии
Sinatra использует [Semantic Versioning](http://semver.org/), SemVer и
SemVerTag.
## Дальнейшее чтение
* [Веб-сайт проекта](http://www.sinatrarb.com/) — Дополнительная
документация, новости и ссылки на другие ресурсы.
* [Участие в проекте](http://www.sinatrarb.com/contributing) — Обнаружили
баг? Нужна помощь? Написали патч?
* [Отслеживание проблем/ошибок](https://github.com/sinatra/sinatra/issues)
* [Twitter](https://twitter.com/sinatra)
* [Группы рассылки](http://groups.google.com/group/sinatrarb/topics)
* IRC: [#sinatra](irc://chat.freenode.net/#sinatra) на http://freenode.net
* [Sinatra и Друзья](https://sinatrarb.slack.com) на Slack, а так же
[ссылка](https://sinatra-slack.herokuapp.com/) для инвайта.
* [Sinatra Book](https://github.com/sinatra/sinatra-book/) учебник и сборник рецептов
* [Sinatra Recipes](http://recipes.sinatrarb.com/) сборник рецептов
* API документация к [последнему релизу](http://www.rubydoc.info/gems/sinatra)
или [текущему HEAD](http://www.rubydoc.info/github/sinatra/sinatra) на
http://www.rubydoc.info/
* [Сервер непрерывной интеграции](https://travis-ci.org/sinatra/sinatra)
sinatra-1.4.7/lib/ 0000755 0000041 0000041 00000000000 12652356512 013771 5 ustar www-data www-data sinatra-1.4.7/lib/sinatra.rb 0000644 0000041 0000041 00000000061 12652356512 015754 0 ustar www-data www-data require 'sinatra/main'
enable :inline_templates
sinatra-1.4.7/lib/sinatra/ 0000755 0000041 0000041 00000000000 12652356512 015432 5 ustar www-data www-data sinatra-1.4.7/lib/sinatra/main.rb 0000644 0000041 0000041 00000002330 12652356512 016701 0 ustar www-data www-data require 'sinatra/base'
module Sinatra
class Application < Base
# we assume that the first file that requires 'sinatra' is the
# app_file. all other path related options are calculated based
# on this path by default.
set :app_file, caller_files.first || $0
set :run, Proc.new { File.expand_path($0) == File.expand_path(app_file) }
if run? && ARGV.any?
require 'optparse'
OptionParser.new { |op|
op.on('-p port', 'set the port (default is 4567)') { |val| set :port, Integer(val) }
op.on('-o addr', "set the host (default is #{bind})") { |val| set :bind, val }
op.on('-e env', 'set the environment (default is development)') { |val| set :environment, val.to_sym }
op.on('-s server', 'specify rack server/handler (default is thin)') { |val| set :server, val }
op.on('-x', 'turn on the mutex lock (default is off)') { set :lock, true }
}.parse!(ARGV.dup)
end
end
at_exit { Application.run! if $!.nil? && Application.run? }
end
# include would include the module in Object
# extend only extends the `main` object
extend Sinatra::Delegator
class Rack::Builder
include Sinatra::Delegator
end
sinatra-1.4.7/lib/sinatra/images/ 0000755 0000041 0000041 00000000000 12652356512 016677 5 ustar www-data www-data sinatra-1.4.7/lib/sinatra/images/404.png 0000644 0000041 0000041 00000044715 12652356512 017727 0 ustar www-data www-data PNG
IHDR , ) ҈ IIDATxA 0p@
L妀 !HH? AB@B>vΰGqLѭBd0WS>T]ή%vu W?+%T(=Cn]!3FO`Irfm `o/QܱA'tDvK1x;dOB%Lh {eƛ;'>Y]'sïFNsIݐ/
@-Y](B}dpL2Ah_V ~nF]'YKMDf\~S0!ڔeQy+%_1
kLft9g9W`!>aDH`$<#aXY^|jnqV7Tz
Lf{a⒟r%L8ILB.¸p3q8YQڒMtw@hz|ԫ$1;@32M# >Y,vçg0P\ۧQm,㰾aJ!T#x>u ~:}wcPZyR{S4!)G2w̱26˪ Ci\,a,ϛ!!&=9qF Hsea(\4W@[u60@ii+'_/Pt ܛ|L*OmJNKB&u~l6]C5
V2{(,8
Ui@n/W
36~tH,]kNV'=xӃh0SŧF\zhB ӊ~)9[HB\;9Rlc?v$ 顥qwͲ@ȪAS_}H` D1S;9ן^!п{B9%Xs= !1L}H,zh{38!?y*dM@HOFuQ`/{
t(qtA҈U\AIi(I
U*]J,p$(6I3<`ό_#YWΌ@p?!z+]fᄟJG VrA.l/^흘(
e4bbNB!@zNok7#_"(bq6B" ET$OL'(09sO~v/~]Ǻ{>çO~`V ] !.Nw-K wp@W+׃Ep(:`{gff~wɩB4xuݿ~}O?|ܽBC u\vk@Xцk3[_-nIϿxwK(Zֶ퇛7oذ۷odz{Ǐ|BJ*R%M6Yf@b)$ΆJwn>kmƍoBtzX*;5%ĉwޅrDb$TtJd~suzLbsCdlgW! ]n*i!&"x\2xs2ћ7]?8x'OcwBNkG dh`:G7t^xLO&=^2JQ`wm̡+ϏLO#gg6%` O`l67::z/[PNIX A&iAzL}E<[Stp5Ro.[e|a[@ DKKK48?11>6O& 5Cxv, ݽeuuH$b|~4"?13jhEan97ڹND\KAҌ3RP劋0R5)`N>d2U=C4754$\$AfX)In>SM\
̐
3G<@&bC ZSꉳJٹ\>!eOě|_.Ɋ9X9-U*Ŀ3x}; C F}-6éTk "|.1-. x.偺Bf$0>b.LVZ%~'pUuTB~EZ<?"6' "Bl:NDիWq]* 'H#?3¡%%(AR $t5i6a@d%PR$!G O-d:=p+pHUq(_5D