Sinatra
¶ ↑
Atenção: Este documento é apenas uma tradução da versão em inglês e pode estar desatualizado.
Alguns dos trechos de código a seguir utilizam caracteres UTF-8. Então, caso esteja utilizando uma versão de ruby inferior à 2.0.0
, adicione o encoding no início de seus arquivos:
# encoding: utf-8
Sinatra
é uma DSL para criar aplicações web em Ruby com o mínimo de esforço e rapidez:
# minha_app.rb require 'sinatra' get '/' do 'Olá Mundo!' end
Instale a gem:
gem install sinatra
Em seguida execute:
ruby minha_app.rb
Acesse: localhost:4567
É recomendado também executar gem install thin
. Caso esta gem esteja disponível, o Sinatra
irá utilizá-la.
Conteúdo¶ ↑
Rotas¶ ↑
No Sinatra
, uma rota é um método HTTP emparelhado com um padrão de URL. Cada rota possui um bloco de execução:
get '/' do .. mostrando alguma coisa .. end post '/' do .. criando alguma coisa .. end put '/' do .. atualizando alguma coisa .. end patch '/' do .. modificando alguma coisa .. end delete '/' do .. removendo alguma coisa .. end options '/' do .. estabelecendo alguma coisa ..pe end
As rotas são interpretadas na ordem em que são definidas. A primeira rota encontrada responde a requisição.
Padrões de rota podem conter parâmetros nomeados, acessíveis por meio do hash params
:
get '/ola/:nome' do # corresponde a "GET /ola/foo" e "GET /ola/bar" # params['nome'] é 'foo' ou 'bar' "Olá #{params['nome']}!" end
Você também pode acessar parâmetros nomeados por meio dos parâmetros de um bloco:
get '/ola/:nome' do |n| # corresponde a "GET /ola/foo" e "GET /ola/bar" # params['nome'] é 'foo' ou 'bar' # n guarda o valor de params['nome'] "Olá #{n}!" end
Padrões de rota também podem conter parâmetros splat (curinga), acessível por meio do array params['splat']
:
get '/diga/*/para/*' do # corresponde a /diga/ola/para/mundo params['splat'] # => ["ola", "mundo"] end get '/download/*.*' do # corresponde a /download/caminho/do/arquivo.xml params['splat'] # => ["caminho/do/arquivo", "xml"] end
Ou com parâmetros de um bloco:
get '/download/*.*' do |caminho, ext| [caminho, ext] # => ["caminho/do/arquivo", "xml"] end
Rotas podem casar com expressões regulares:
get /\/ola\/([\w]+)/ do "Olá, #{params['captures'].first}!" end
Ou com parâmetros de um bloco:
get %r{/ola/([\w]+)} do |c| # corresponde a "GET /meta/ola/mundo", "GET /ola/mundo/1234" etc. "Olá, #{c}!" end
Padrões de rota podem contar com parâmetros opcionais:
get '/posts/:formato?' do # corresponde a "GET /posts/" e qualquer extensão "GET /posts/json", "GET /posts/xml", etc. end
Rotas também podem utilizar query strings:
get '/posts' do # corresponde a "GET /posts?titulo=foo&autor=bar" titulo = params['titulo'] autor = params['autor'] # utiliza as variaveis titulo e autor; a query é opicional para a rota /posts end
A propósito, a menos que você desative a proteção contra ataques (veja abaixo), o caminho solicitado pode ser alterado antes de concluir a comparação com as suas rotas.
Condições¶ ↑
Rotas podem incluir uma variedade de condições, tal como o user agent
:
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do "Você está usando o Songbird versão #{params['agent'][0]}" end get '/foo' do # Correspondente a navegadores que não sejam Songbird end
Outras condições disponíveis são host_name
e provides
:
get '/', :host_name => /^admin\./ do "Área administrativa. Acesso negado!" end get '/', :provides => 'html' do haml :index end get '/', :provides => ['rss', 'atom', 'xml'] do builder :feed end
provides
procura pelos Accept header das requisições
Você pode facilmente definir suas próprias condições:
set(:probabilidade) { |valor| condition { rand <= valor } } get '/ganha_um_carro', :probabilidade => 0.1 do "Você ganhou!" end get '/ganha_um_carro' do "Sinto muito, você perdeu." end
Use splat, para uma condição que leva vários valores:
set(:auth) do |*roles| # <- observe o splat aqui condition do unless logged_in? && roles.any? {|role| current_user.in_role? role } redirect "/login/", 303 end end end get "/minha/conta/", :auth => [:usuario, :administrador] do "Detalhes da sua conta" end get "/apenas/administrador/", :auth => :administrador do "Apenas administradores são permitidos aqui!" end
Retorno de valores¶ ↑
O valor de retorno do bloco de uma rota determina pelo menos o corpo da resposta passado para o cliente HTTP, ou pelo menos o próximo middleware na pilha Rack
. Frequentemente, isto é uma string
, tal como nos exemplos acima. Entretanto, outros valores também são aceitos.
Você pode retornar uma resposta válida ou um objeto para o Rack
, sendo eles de qualquer tipo de objeto que queira. Além disso, é possível retornar um código de status HTTP.
-
Um array com três elementros:
[status (Fixnum), cabecalho (Hash), corpo da resposta (responde à #each)]
-
Um array com dois elementros:
[status (Fixnum), corpo da resposta (responde à #each)]
-
Um objeto que responda à
#each
sem passar nada, mas, sim,strings
para um dado bloco -
Um objeto
Fixnum
representando o código de status
Dessa forma, podemos implementar facilmente um exemplo de streaming:
class Stream def each 100.times { |i| yield "#{i}\n" } end end get('/') { Stream.new }
Você também pode usar o método auxiliar stream
(descrito abaixo) para incorporar a lógica de streaming na rota.
Validadores de Rota Personalizados¶ ↑
Como apresentado acima, a estrutura do Sinatra
conta com suporte embutido para uso de padrões de String e expressões regulares como validadores de rota. No entanto, ele não pára por aí. Você pode facilmente definir os seus próprios validadores:
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
Note que o exemplo acima pode ser robusto e complicado em excesso. Pode também ser implementado como:
get // do pass if request.path_info == "/index" # ... end
Ou, usando algo mais denso à frente:
get %r{(?!/index)} do # ... end
Arquivos estáticos¶ ↑
Arquivos estáticos são disponibilizados a partir do diretório ./public
. Você pode especificar um local diferente pela opção :public_folder
set :public_folder, File.dirname(__FILE__) + '/estatico'
Note que o nome do diretório público não é incluido na URL. Um arquivo ./public/css/style.css
é disponibilizado como http://exemplo.com/css/style.css
.
Views / Templates¶ ↑
Cada linguagem de template é exposta através de seu próprio método de renderização. Estes metodos simplesmente retornam uma string:
get '/' do erb :index end
Isto renderiza views/index.rb
Ao invés do nome do template, você também pode passar direto o conteúdo do template:
get '/' do code = "<%= Time.now %>" erb code end
Templates também aceitam um segundo argumento, um hash de opções:
get '/' do erb :index, :layout => :post end
Isto irá renderizar a views/index.erb
inclusa dentro da views/post.erb
(o padrão é a views/layout.erb
, se existir).
Qualquer opção não reconhecida pelo Sinatra
será passada adiante para o engine de template:
get '/' do haml :index, :format => :html5 end
Você também pode definir opções padrões para um tipo de template:
set :haml, :format => :html5 get '/' do haml :index end
Opções passadas para o método de renderização sobrescreve as opções definitas através do método set
.
Opções disponíveis:
- locals
- Lista de locais passado para o documento. Conveniente para *partials* Exemplo: erb "<%= foo %>", :locals => {:foo => "bar"}
- default_encoding
- String encoding para ser utilizada em caso de incerteza. o padrão é settings.default_encoding.
- views
- Diretório de onde os templates são carregados. O padrão é settings.views.
- layout
- Para definir quando utilizar ou não um layout (true ou false). E se for um Symbol, especifica qual template usar. Exemplo: erb :index, :layout => !request.xhr?
- content_type
- O *Content-Type* que o template produz. O padrão depente da linguagem de template utilizada.
- scope
- Escopo em que o template será renderizado. Padrão é a instancia da aplicação. Se você mudar isto as variáveis de instânciae metodos auxiliares não serão disponibilizados.
- layout_engine
- A engine de template utilizada para renderizar seu layout. Útil para linguagens que não suportam templates de outra forma. O padrão é a engine do template utilizado. Exemplo: set :rdoc, :layout_engine => :erb
- layout_options
- Opções especiais utilizadas apenas para renderizar o layout. Exemplo: set :rdoc, :layout_options => { :views => 'views/layouts' }
É pressuposto que os templates estarão localizados direto sob o diretório ./views
. Para usar um diretório diferente:
set :views, settings.root + '/templates'
Uma coisa importante para se lembrar é que você sempre deve referenciar os templates utilizando symbols, mesmo que eles estejam em um subdiretório (neste caso use: :'subdir/template'
or 'subdir/template'.to_sym
). Você deve utilizar um symbol porque senão o método de renderização irá renderizar qualquer outra string que você passe diretamente para ele
Literal Templates¶ ↑
get '/' do haml '%div.title Olá Mundo' end
Renderiza um template string.
Linguagens de template disponíveis¶ ↑
Algumas linguagens possuem multiplas implementações. Para especificar qual implementação deverá ser utilizada (e para ser thread-safe), você deve simplesmente requere-la primeiro:
require 'rdiscount' # ou require 'bluecloth' get('/') { markdown :index }
Haml Templates¶ ↑
Dependencia | haml |
Extencao do Arquivo | .haml |
Exemplo | haml :index, :format => :html5 |
Erb Templates¶ ↑
Dependencia | erubis or erb (included in Ruby) |
Extencao do Arquivos | .erb, .rhtml or .erubis (Erubis only) |
Exemplo | erb :index |
Builder Templates¶ ↑
Dependencia | builder |
Extencao do Arquivo | .builder |
Exemplo | builder { |xml| xml.em "hi" } |
It also takes a block for inline templates (see exemplo).
Nokogiri Templates¶ ↑
Dependencia | nokogiri |
Extencao do Arquivo | .nokogiri |
Exemplo | nokogiri { |xml| xml.em "hi" } |
It also takes a block for inline templates (see exemplo).
Sass Templates¶ ↑
Dependencia | sass |
Extencao do Arquivo | .sass |
Exemplo | sass :stylesheet, :style => :expanded |
SCSS Templates¶ ↑
Dependencia | sass |
Extencao do Arquivo | .scss |
Exemplo | scss :stylesheet, :style => :expanded |
Less Templates¶ ↑
Dependencia | less |
Extencao do Arquivo | .less |
Exemplo | less :stylesheet |
Liquid Templates¶ ↑
Dependencia | liquid |
Extencao do Arquivo | .liquid |
Exemplo | liquid :index, :locals => { :key => 'value' } |
Já que você não pode chamar o Ruby (exceto pelo método yield
) pelo template Liquid, você quase sempre precisará passar o locals
para ele.
Markdown Templates¶ ↑
Dependencia | Anyone of: RDiscount, RedCarpet, BlueCloth, kramdown, maruku |
Extencao do Arquivos | .markdown, .mkd and .md |
Exemplo | markdown :index, :layout_engine => :erb |
Não é possível chamar métodos por este template, nem passar locals para o mesmo. Portanto normalmente é utilizado junto a outra engine de renderização:
erb :overview, :locals => { :text => markdown(:introducao) }
Note que vcoê também pode chamar o método markdown
dentro de outros templates:
Já que você não pode chamar o Ruby pelo Markdown, você não pode utilizar um layout escrito em Markdown. Contudo é possível utilizar outra engine de renderização como template, deve-se passar a :layout_engine
como opção.
Dependencia | RedCloth |
Extencao do Arquivo | .textile |
Exemplo | textile :index, :layout_engine => :erb |
Não é possível chamar métodos por este template, nem passar locals para o mesmo. Portanto normalmente é utilizado junto a outra engine de renderização:
erb :overview, :locals => { :text => textile(:introducao) }
Note que vcoê também pode chamar o método textile
dentro de outros templates:
Já que você não pode chamar o Ruby pelo Textile, você não pode utilizar um layout escrito em Textile. Contudo é possível utilizar outra engine de renderização como template, deve-se passar a :layout_engine
como opção.
RDoc Templates¶ ↑
Dependencia | RDoc |
Extencao do Arquivo | .rdoc |
Exemplo | rdoc :README, :layout_engine => :erb |
Não é possível chamar métodos por este template, nem passar locals para o mesmo. Portanto normalmente é utilizado junto a outra engine de renderização:
erb :overview, :locals => { :text => rdoc(:introducao) }
Note que vcoê também pode chamar o método rdoc
dentro de outros templates:
Já que você não pode chamar o Ruby pelo RDoc, você não pode utilizar um layout escrito em RDoc. Contudo é possível utilizar outra engine de renderização como template, deve-se passar a :layout_engine
como opção.
AsciiDoc Templates¶ ↑
Dependencia | Asciidoctor |
Extencao do Arquivo | .asciidoc, .adoc and .ad |
Exemplo | asciidoc :README, :layout_engine => :erb |
Já que você não pode chamar o Ruby pelo template AsciiDoc, você quase sempre precisará passar o locals
para ele.
Radius Templates¶ ↑
Dependencia | Radius |
Extencao do Arquivo | .radius |
Exemplo | radius :index, :locals => { :key => 'value' } |
Já que você não pode chamar o Ruby pelo template Radius, você quase sempre precisará passar o locals
para ele.
Markaby Templates¶ ↑
Dependencia | Markaby |
Extencao do Arquivo | .mab |
Exemplo | markaby { h1 "Welcome!" } |
Este também recebe um bloco para templates (veja o exemplo).
RABL Templates¶ ↑
Dependencia | Rabl |
Extencao do Arquivo | .rabl |
Exemplo | rabl :index |
Slim Templates¶ ↑
Dependencia | Slim Lang |
Extencao do Arquivo | .slim |
Exemplo | slim :index |
Creole Templates¶ ↑
Dependencia | Creole |
Extencao do Arquivo | .creole |
Exemplo | creole :wiki, :layout_engine => :erb |
Não é possível chamar métodos por este template, nem passar locals para o mesmo. Portanto normalmente é utilizado junto a outra engine de renderização:
erb :overview, :locals => { :text => creole(:introduction) }
Note que vcoê também pode chamar o método creole
dentro de outros templates:
Já que você não pode chamar o Ruby pelo Creole, você não pode utilizar um layout escrito em Creole. Contudo é possível utilizar outra engine de renderização como template, deve-se passar a :layout_engine
como opção.
MediaWiki Templates¶ ↑
Dependencia | WikiCloth |
Extencao do Arquivo | .mediawiki and .mw |
Exemplo | mediawiki :wiki, :layout_engine => :erb |
It is not possible to call methods from MediaWiki markup, nor to pass locals to it. You therefore will usually use it in combination with another rendering engine:
erb :overview, :locals => { :text => mediawiki(:introduction) }
Note that you may also call the mediawiki
method from within other templates:
Já que você não pode chamar o Ruby pelo MediaWiki, você não pode utilizar um layout escrito em MediaWiki. Contudo é possível utilizar outra engine de renderização como template, deve-se passar a :layout_engine
como opção.
CoffeeScript Templates¶ ↑
Dependencia | CoffeeScript and a way to execute javascript |
Extencao do Arquivo | .coffee |
Exemplo | coffee :index |
Stylus Templates¶ ↑
Dependencia | Stylus and a way to execute javascript |
Extencao do Arquivo | .styl |
Exemplo | stylus :index |
Antes que vcoê possa utilizar o template Stylus primeiro você deve carregar stylus
e stylus/tilt
:
require 'sinatra' require 'stylus' require 'stylus/tilt' get '/' do stylus :exemplo end
Yajl Templates¶ ↑
Dependencia | yajl-ruby |
Extencao do Arquivo | .yajl |
Exemplo | yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource' |
O código-fonte do template é executado como uma string Ruby e a variável resultante em json é convertida utilizando #to_json
:
json = { :foo => 'bar' } json[:baz] = key
O :callback
e :variable
são opções que podem ser utilizadas para o objeto de renderização:
var resource = {"foo":"bar","baz":"qux"}; present(resource);
WLang Templates¶ ↑
Dependencia | WLang |
Extencao do Arquivo | .wlang |
Exemplo | wlang :index, :locals => { :key => 'value' } |
Já que você não pode chamar o Ruby (exceto pelo método yield
) pelo template WLang, você quase sempre precisará passar o locals
para ele.
Acessando Variáveis nos Templates¶ ↑
Templates são avaliados dentro do mesmo contexto como manipuladores de rota. Variáveis de instância setadas em rotas manipuladas são diretamente acessadas por templates:
get '/:id' do @foo = Foo.find(params['id']) haml '%h1= @foo.nome' end
Ou, especifique um hash explícito para variáveis locais:
get '/:id' do foo = Foo.find(params['id']) haml '%h1= foo.nome', :locals => { :foo => foo } end
Isso é tipicamente utilizando quando renderizamos templates como partials dentro de outros templates.
Templates com yield
e layouts aninhados¶ ↑
O layout geralmente é apenas um template que executa yield
. Tal template pode ser utilizado pela opção :template
descrita acima ou pode ser renderizado através de um bloco, como a seguir:
erb :post, :layout => false do erb :index end
Este código é quase equivalente a erb :index, :layout => :post
Passando blocos para os métodos de renderização é útil para criar layouts aninhados:
erb :main_layout, :layout => false do erb :admin_layout do erb :user end end
Também pode ser feito com menos linhas de código:
erb :admin_layout, :layout => :main_layout do erb :user end
Atualmente os métodos listados aceitam blocos: erb
, haml
, liquid
, slim
, wlang
. E também o método render
.
Templates Inline¶ ↑
Templates podem ser definidos no final do arquivo fonte(.rb):
require 'sinatra' get '/' do haml :index end __END__
NOTA: Templates inline definidos no arquivo fonte são automaticamente carregados pelo sinatra. Digite `enable :inline_templates` se você tem templates inline no outro arquivo fonte.
Templates nomeados¶ ↑
Templates também podem ser definidos utilizando o método top-level template
:
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 toda vez que um template for renderizado. Você pode desabilitar layouts passando :layout => false
.
get '/' do haml :index, :layout => !request.xhr? end
Associando extensões de arquivos¶ ↑
Para associar uma extensão de arquivo com um engine de template use o método Tilt.register
. Por exemplo, se você quiser usar a extensão tt
para os templates Textile você pode fazer o seguinte:
Tilt.register :tt, Tilt[:textile]
Adicionando seu Próprio Engine de Template¶ ↑
Primeiro registre seu engine utilizando o Tilt, e então crie um método de renderização:
Tilt.register :myat, MyAwesomeTemplateEngine helpers do def myat(*args) render(:myat, *args) end end get '/' do myat :index end
Renderize ./views/index.myat
. Veja github.com/rtomayko/tilt para saber mais sobre Tilt.
Customizando lógica para encontrar templates¶ ↑
Para implementar sua própria lógica para busca de templates você pode escrever seu próprio método #find_template
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
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 setadas nos filtros são acessadas através de rotas e templates:
before do @nota = 'Oi!' request.path_info = '/foo/bar/baz' end get '/foo/*' do @nota #=> 'Oi!' 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 a requisição e a resposta. Variáveis de instância e rotas definidas nos filtros before são acessadas através dos filtros after:
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:
before '/protected/*' do authenticate! end after '/create/:slug' do |slug| session[:last_slug] = slug end
Helpers¶ ↑
Use o método de alto nível helpers
para definir métodos auxiliares para utilizar em manipuladores de rotas e modelos:
helpers do def bar(nome) "#{nome}bar" end end get '/:nome' do bar(params['nome']) end
Utilizando Sessões¶ ↑
Sessões são usadas para manter um estado durante uma requisição. Se ativa, você terá disponível um hash de sessão para cada sessão de usuário:
enable :sessions get '/' do "value = " << session[:value].inspect end get '/:value' do session['value'] = params['value'] end
Note que enable :sessions
utilizará um cookie para guardar todos os dados da sessão. Isso nem sempre pode ser o que você quer (guardar muitos dados irá aumentar o seu tráfego, por exemplo). Você pode utilizar qualquer Rack
middleware de sessão: para fazer isso não utilize o método enable :sessions
, ao invés disso utilize seu middleware de sessão como utilizaria qualquer outro:
use Rack::Session::Pool, :expire_after => 2592000 get '/' do "value = " << session[:value].inspect end get '/:value' do session['value'] = params['value'] end
Para melhorar a segurança, os dados da sessão guardados no cookie é assinado com uma chave secreta da sessão. Uma chave aleatória é gerada para você pelo Sinatra
. Contudo, já que a chave mudará cada vez que você inicia sua aplicação, você talvez queira defini-la você mesmo para que todas as instâncias da aplicação compartilhe-a:
set :session_secret, 'super secret'
Se você quiser fazer outras configurações, você também pode guardar um hash com as opções nas configurações da session
:
set :sessions, :domain => 'foo.com'
Para compartilhar sua sessão entre outros aplicativos em um subdomínio de foo.com, utilize o prefixo .:
set :sessions, :domain => '.foo.com'
Halting¶ ↑
Para parar imediatamente uma requisição com um filtro ou rota utilize:
halt
Você também pode especificar o status quando parar…
halt 410
Ou com corpo de texto…
halt 'isso será o corpo do texto'
Ou também…
halt 401, 'vamos embora!'
Com cabeçalhos…
halt 402, {'Content-Type' => 'text/plain'}, 'revanche'
É obviamente possivel combinar um template com o halt
:
halt erb(:error)
Passing¶ ↑
Uma rota pode processar aposta para a próxima rota correspondente usando pass
:
get '/adivinhar/:quem' do pass unless params['quem'] == 'Frank' 'Você me pegou!' end get '/adivinhar/*' do 'Você falhou!' 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.
Desencadeando Outra Rota¶ ↑
As vezes o pass
não é o que você quer, ao invés dele talvez você queira obter o resultado chamando outra rota. Utilize o método call
neste caso:
get '/foo' do status, headers, body = call env.merge("PATH_INFO" => '/bar') [status, headers, body.map(&:upcase)] end get '/bar' do "bar" end
Note que no exemplo acima você ganharia performance ao simplemente mover o "bar"
em um helper usado por ambos /foo
e /bar
.
Se você quer que a requisição seja enviada para a mesma instancia da aplicação ao invês de uma duplicada, use call!
no lugar de call
.
Veja a especificação do Rack
se você quer aprender mais sobre o call
.
Configuração¶ ↑
Rodando uma vez, na inicialização, em qualquer ambiente:
configure do ... end
Rodando somente quando o ambiente (APP_ENV
environment variável) é setado para :production
:
configure :production do ... end
Rodando quando o ambiente é setado para :production
ou :test
:
configure :production, :test do ... end
Tratamento de Erros¶ ↑
Tratamento de erros rodam dentro do mesmo contexto como rotas e filtros before, o que significa que você pega todos os presentes que tem para oferecer, como haml
, erb
, halt
, etc.
Não Encontrado¶ ↑
Quando um Sinatra::NotFound
exception é levantado, ou o código de status da reposta é 404, o manipulador not_found
é invocado:
not_found do 'Isto está longe de ser encontrado' end
Erro¶ ↑
O manipulador error
é invocado toda a vez que uma exceção é lançada a partir de um bloco de rota ou um filtro. O objeto da exceção pode ser obtido a partir da variável Rack
sinatra.error
:
error do 'Desculpe, houve um erro desagradável - ' + env['sinatra.error'].message end
Erros customizados:
error MeuErroCustomizado do 'Então que aconteceu foi...' + env['sinatra.error'].message end
Então, se isso acontecer:
get '/' do raise MeuErroCustomizado, 'alguma coisa ruim' end
Você receberá isso:
Então que aconteceu foi... alguma coisa ruim
Alternativamente, você pode instalar manipulador de erro para um código de status:
error 403 do 'Accesso negado' end get '/secreto' do 403 end
Ou um range:
error 400..510 do 'Boom' end
O Sinatra
instala os manipuladores especiais not_found
e error
quando roda sobre o ambiente de desenvolvimento.
Mime Types¶ ↑
Quando utilizamos send_file
ou arquivos estáticos você pode ter mime types Sinatra
não entendidos. Use mime_type
para registrar eles por extensão de arquivos:
mime_type :foo, 'text/foo'
Você também pode utilizar isto com o helper content_type
:
content_type :foo
Rack
Middleware¶ ↑
O Sinatra
roda no Rack, uma interface padrão mínima para frameworks web em Ruby. Um das capacidades mais interessantes do Rack
para desenvolver aplicativos é suporte a “middleware” – componentes que ficam entre o servidor e sua aplicação monitorando e/ou manipulando o request/response do HTTP para prover vários tipos de funcionalidades comuns.
O Sinatra
faz construtores pipelines do middleware Rack
facilmente em um nível superior utilizando o método use
:
require 'sinatra' require 'meu_middleware_customizado' use Rack::Lint use MeuMiddlewareCustomizado get '/ola' do 'Olá mundo' end
A semântica de use
é idêntica aquela definida para a DSL Rack::Builder (mais frequentemente utilizada para arquivos rackup). Por exemplo, o método use
aceita múltiplos argumentos/variáveis bem como blocos:
use Rack::Auth::Basic do |usuario, senha| usuario == '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
utilizada muitos desses componentes automaticamente baseando sobre configuração, então, tipicamente você não tem use
explicitamente.
Testando¶ ↑
Testes no Sinatra
podem ser escritos utilizando qualquer biblioteca ou framework de teste baseados no Rack
. Rack::Test é recomendado:
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 em 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: uma simples arquivo de aplicação, diretórios ./public
e ./views
, logs, página de detalhes de exceção, etc.). É onde o Sinatra::Base
entra em jogo:
require 'sinatra/base' class MinhaApp < Sinatra::Base set :sessions, true set :foo, 'bar' get '/' do 'Ola mundo!' end end
A classe MinhaApp
é um componente Rack
independente que pode agir como um middleware Rack
, uma aplicação Rack
, ou metal Rails. Você pode utilizar ou executar esta classe com um arquivo rackup config.ru
; ou, controlar um componente de servidor fornecendo como biblioteca:
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 desinatra
; outra coisa, todos os métodos DSL doSinatra
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 desabilitadas por padrão, incluindo o servidor embutido. Veja Opções e Configurações 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ê uma olhada no código você mesmo: aqui está o Sinatra::Delegator mixin sendo incluido dentro de um espaço principal
Linha de Comando¶ ↑
Aplicações Sinatra
podem ser executadas diretamente:
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 # ativa o bloqueio (padrão é desligado)
Multi-threading¶ ↑
Parafraseando esta resposta no StackOverflow por Konstantin
Sinatra
não impõe nenhum modelo de concorrencia, mas deixa isso como responsabilidade do Rack
(servidor) subjacente como o Thin, Puma ou WEBrick. Sinatra
por si só é thread-safe, então não há nenhum problema se um Rack
handler usar um modelo de thread de concorrência. Isso significaria que ao iniciar o servidor, você teria que espeficiar o método de invocação correto para o Rack
handler específico. Os seguintes exemplos é uma demonstração de como iniciar um servidor Thin multi-thread:
# app.rb require 'sinatra/base' class App < Sinatra::Base get '/' do 'Olá mundo' end end App.run!
Para iniciar o servidor seria:
thin --threaded start
A última versão¶ ↑
Se você gostaria de utilizar o código da última versão do Sinatra
, crie um clone local e execute sua aplicação com o diretório sinatra/lib
no LOAD_PATH
:
cd minhaapp git clone git://github.com/sinatra/sinatra.git ruby -I sinatra/lib minhaapp.rb
Alternativamente, você pode adicionar o diretório do sinatra/lib
no LOAD_PATH
do seu aplicativo:
$LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib' require 'rubygems' require 'sinatra' get '/sobre' do "Estou rodando a versão" + Sinatra::VERSION end
Para atualizar o código do Sinatra
no futuro:
cd meuprojeto/sinatra git pull
Mais¶ ↑
-
Website do Projeto - Documentação adicional, novidades e links para outros recursos.
-
Contribuir - Encontrar um bug? Precisa de ajuda? Tem um patch?
-
Sinatra Book Livro de Receitas
-
Documentação da API para a última release