Sinatra é uma DSL para criar rapidamente aplicações web em Ruby com o mínimo esforço:
# myapp.rb
require 'sinatra'
get '/' do
'Hello world!'
end
Instale as gemas necessárias:
gem install sinatra rackup puma
E corra com:
ruby myapp.rb
Veja em: http://localhost:4567
O código que você alterou não terá efeito até que você reinicie o servidor. Reinicie o servidor sempre que alterar ou usar um recarregador de código, como rerun ou rack-unreloader.
Recomenda-se também executar gem install puma
, que Sinatra escolherá se estiver disponível.
yield
e layouts aninhadosNo Sinatra, uma rota é um método HTTP emparelhado com um padrão de correspondência de URL. Cada rota está associada a um bloco:
get '/' do
.. show something ..
end
post '/' do
.. create something ..
end
put '/' do
.. replace something ..
end
patch '/' do
.. modify something ..
end
delete '/' do
.. annihilate something ..
end
options '/' do
.. appease something ..
end
link '/' do
.. affiliate something ..
end
unlink '/' do
.. separate something ..
end
As rotas são correspondidas na ordem em que são definidas. A primeira rota que corresponde à solicitação é invocada.
Rotas com barras finais são diferentes daquelas sem:
get '/foo' do
# Does not match "GET /foo/"
end
Os padrões de rota podem incluir parâmetros nomeados, acessíveis através do hash params
:
get '/hello/:name' do
# matches "GET /hello/foo" and "GET /hello/bar"
# params['name'] is 'foo' or 'bar'
"Hello #{ params [ 'name' ] } !"
end
Você também pode acessar parâmetros nomeados através de parâmetros de bloco:
get '/hello/:name' do | n |
# matches "GET /hello/foo" and "GET /hello/bar"
# params['name'] is 'foo' or 'bar'
# n stores params['name']
"Hello #{ n } !"
end
Os padrões de rota também podem incluir parâmetros splat (ou curinga), acessíveis através do array params['splat']
:
get '/say/*/to/*' do
# matches /say/hello/to/world
params [ 'splat' ] # => ["hello", "world"]
end
get '/download/*.*' do
# matches /download/path/to/file.xml
params [ 'splat' ] # => ["path/to/file", "xml"]
end
Ou com parâmetros de bloco:
get '/download/*.*' do | path , ext |
[ path , ext ] # => ["path/to/file", "xml"]
end
Correspondência de rotas com expressões regulares:
get / / hello / ([ w ]+)/ do
"Hello, #{ params [ 'captures' ] . first } !"
end
Ou com um parâmetro de bloco:
get %r{/hello/([ w ]+)} do | c |
# Matches "GET /meta/hello/world", "GET /hello/world/1234" etc.
"Hello, #{ c } !"
end
Os padrões de rota podem ter parâmetros opcionais:
get '/posts/:format?' do
# matches "GET /posts/" and any extension "GET /posts/json", "GET /posts/xml" etc
end
As rotas também podem utilizar parâmetros de consulta:
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
A propósito, a menos que você desative a proteção contra ataques de passagem de caminho (veja abaixo), o caminho da solicitação poderá ser modificado antes de corresponder às suas rotas.
Você pode personalizar as opções Mustermann usadas para uma determinada rota passando um hash :mustermann_opts
:
get 'A/postsz' , :mustermann_opts => { :type => :regexp , :check_anchors => false } do
# matches /posts exactly, with explicit anchoring
"If you match an anchored pattern clap your hands!"
end
Parece uma condição, mas não é! Essas opções serão mescladas no hash global :mustermann_opts
descrito abaixo.
As rotas podem incluir uma variedade de condições de correspondência, como o agente do usuário:
get '/foo' , :agent => /Songbird ( d . d )[ d / ]*?/ do
"You're using Songbird version #{ params [ 'agent' ] [ 0 ] } "
end
get '/foo' do
# Matches non-songbird browsers
end
Outras condições disponíveis são host_name
e provides
:
get '/' , :host_name => /^admin . / do
"Admin Area, Access denied!"
end
get '/' , :provides => 'html' do
haml :index
end
get '/' , :provides => [ 'rss' , 'atom' , 'xml' ] do
builder :feed
end
provides
pesquisas no cabeçalho Accept da solicitação.
Você pode definir facilmente suas próprias condições:
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
Para uma condição que assume vários valores, use um splat:
set ( :auth ) do |* roles | # <- notice the splat here
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
O valor de retorno de um bloco de rota determina pelo menos o corpo da resposta transmitido ao cliente HTTP ou pelo menos o próximo middleware na pilha Rack. Mais comumente, é uma string, como nos exemplos acima. Mas outros valores também são aceitos.
Você pode retornar um objeto que seria uma resposta válida do Rack, um objeto do corpo do Rack ou um código de status HTTP:
[status (Integer), headers (Hash), response body (responds to #each)]
[status (Integer), response body (responds to #each)]
#each
e não passa nada além de strings para o bloco fornecidoDessa forma podemos, por exemplo, 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 reduzir o clichê e incorporar a lógica de streaming na rota.
Conforme mostrado acima, o Sinatra vem com suporte integrado para o uso de padrões String e expressões regulares como correspondências de rota. No entanto, não para por aí. Você pode definir facilmente seus próprios matchers:
class AllButPattern
def initialize ( except )
@except = except
end
def to_pattern ( options )
return self
end
def params ( route )
return { } unless @except === route
end
end
def all_but ( pattern )
AllButPattern . new ( pattern )
end
get all_but ( "/index" ) do
# ...
end
Observe que o exemplo acima pode ter uma engenharia excessiva, pois também pode ser expresso como:
get /.*/ do
pass if request . path_info == "/index"
# ...
end
Arquivos estáticos são servidos no diretório ./public
. Você pode especificar um local diferente definindo a opção :public_folder
:
set :public_folder , __dir__ + '/static'
Observe que o nome do diretório público não está incluído na URL. Um arquivo ./public/css/style.css
é disponibilizado como http://example.com/css/style.css
.
Use a configuração :static_cache_control
(veja abaixo) para adicionar informações de cabeçalho Cache-Control
.
Cada linguagem de modelo é exposta por meio de seu próprio método de renderização. Esses métodos simplesmente retornam uma string:
get '/' do
erb :index
end
Isso renderiza views/index.erb
.
Em vez de um nome de modelo, você também pode simplesmente passar diretamente o conteúdo do modelo:
get '/' do
code = "<%= Time.now %>"
erb code
end
Os modelos recebem um segundo argumento, o hash de opções:
get '/' do
erb :index , :layout => :post
end
Isso renderizará views/index.erb
incorporado em views/post.erb
(o padrão é views/layout.erb
, se existir).
Quaisquer opções não compreendidas pelo Sinatra serão repassadas ao mecanismo de template:
get '/' do
haml :index , :format => :html5
end
Você também pode definir opções por idioma de modelo em geral:
set :haml , :format => :html5
get '/' do
haml :index
end
As opções passadas para o método de renderização substituem as opções definidas por meio de set
.
Opções disponíveis:
Presume-se que os modelos estejam localizados diretamente no diretório ./views
. Para usar um diretório de visualizações diferente:
set :views , settings . root + '/templates'
Uma coisa importante a lembrar é que você sempre deve referenciar templates com símbolos, mesmo se eles estiverem em um subdiretório (neste caso, use: :'subdir/template'
ou 'subdir/template'.to_sym
). Você deve usar um símbolo porque, caso contrário, os métodos de renderização renderizarão quaisquer strings passadas diretamente para eles.
get '/' do
haml '%div.title Hello World'
end
Renderiza a string do modelo. Opcionalmente, você pode especificar :path
e :line
para um backtrace mais claro se houver um caminho ou linha do sistema de arquivos associado a essa string:
get '/' do
haml '%div.title Hello World' , :path => 'examples/file.haml' , :line => 3
end
Algumas linguagens têm múltiplas implementações. Para especificar qual implementação usar (e para ser thread-safe), você deve simplesmente solicitá-la primeiro:
require 'rdiscount'
get ( '/' ) { markdown :index }
Dependência | haml |
Extensão de arquivo | .haml |
Exemplo | haml :index, :format => :html5 |
Dependência | erubi ou erb (incluído em Ruby) |
Extensões de arquivo | .erb , .rhtml ou .erubi (somente Erubi) |
Exemplo | erb: índice |
Dependência | construtor |
Extensão de arquivo | .construtor |
Exemplo | construtor { |xml| xml.em "oi" } |
Também é necessário um bloco para modelos embutidos (veja exemplo).
Dependência | nokogiri |
Extensão de arquivo | .nokogiri |
Exemplo | nokogiri { |xml| xml.em "oi" } |
Também é necessário um bloco para modelos embutidos (veja exemplo).
Dependência | atrevido incorporado |
Extensão de arquivo | .atrevimento |
Exemplo | atrevimento: folha de estilo,: estilo =>: expandido |
Dependência | atrevido incorporado |
Extensão de arquivo | .scss |
Exemplo | scss: folha de estilo,: estilo =>: expandido |
Dependência | líquido |
Extensão de arquivo | .líquido |
Exemplo | líquido: índice,: locais => {: chave => 'valor'} |
Como você não pode chamar métodos Ruby (exceto yield
) a partir de um template Liquid, você quase sempre deseja passar locais para ele.
Dependência | Qualquer um de: RDiscount, RedCarpet, kramdown, commonmarker pandoc |
Extensões de arquivo | .markdown , .mkd e .md |
Exemplo | markdown :index, :layout_engine => :erb |
Não é possível chamar métodos do Markdown, nem passar locais para ele. Portanto, você normalmente o usará em combinação com outro mecanismo de renderização:
erb :overview , :locals => { :text => markdown ( :introduction ) }
Observe que você também pode chamar o método markdown
de outros modelos:
% h1 Hello From Haml!
% p = markdown ( :greetings )
Como você não pode chamar Ruby do Markdown, você não pode usar layouts escritos em Markdown. Porém, é possível usar outro mecanismo de renderização para o template além do layout, passando a opção :layout_engine
.
Dependência | RDoc |
Extensão de arquivo | .rdoc |
Exemplo | rdoc :README, :layout_engine => :erb |
Não é possível chamar métodos do RDoc, nem passar locais para ele. Portanto, você normalmente o usará em combinação com outro mecanismo de renderização:
erb :overview , :locals => { :text => rdoc ( :introduction ) }
Observe que você também pode chamar o método rdoc
de outros modelos:
% h1 Hello From Haml!
% p = rdoc ( :greetings )
Como você não pode chamar Ruby a partir do RDoc, não pode usar layouts escritos em RDoc. Porém, é possível usar outro mecanismo de renderização para o template além do layout, passando a opção :layout_engine
.
Dependência | Asciidoctor |
Extensão de arquivo | .asciidoc , .adoc e .ad |
Exemplo | asciidoc :README, :layout_engine => :erb |
Como você não pode chamar métodos Ruby diretamente de um modelo AsciiDoc, você quase sempre deseja passar locais para ele.
Dependência | Markaby |
Extensão de arquivo | .mab |
Exemplo | markaby {h1 "Bem-vindo!" } |
Também é necessário um bloco para modelos embutidos (veja exemplo).
Dependência | Rabl |
Extensão de arquivo | .rabl |
Exemplo | rabl :índice |
Dependência | Slim Lang |
Extensão de arquivo | .magro |
Exemplo | fino: índice |
Dependência | yajl-rubi |
Extensão de arquivo | .yajl |
Exemplo | yajl :index, :locals => { :key => 'qux' }, :callback => 'presente', :variable => 'recurso' |
A fonte do modelo é avaliada como uma string Ruby e a variável json resultante é convertida usando #to_json
:
json = { :foo => 'bar' }
json [ :baz ] = key
As opções :callback
e :variable
podem ser usadas para decorar o objeto renderizado:
var resource = { "foo" : "bar" , "baz" : "qux" } ;
present ( resource ) ;
Os modelos são avaliados no mesmo contexto que os manipuladores de rotas. Variáveis de instância definidas em manipuladores de rota são diretamente acessíveis por modelos:
get '/:id' do
@foo = Foo . find ( params [ 'id' ] )
haml '%h1= @foo.name'
end
Ou especifique um Hash explícito de variáveis locais:
get '/:id' do
foo = Foo . find ( params [ 'id' ] )
haml '%h1= bar.name' , :locals => { :bar => foo }
end
Isso normalmente é usado ao renderizar modelos como parciais de outros modelos.
yield
e layouts aninhados Um layout geralmente é apenas um modelo que chama yield
. Esse modelo pode ser usado por meio da opção :template
conforme descrito acima ou pode ser renderizado com um bloco como segue:
erb :post , :layout => false do
erb :index
end
Este código é basicamente equivalente a erb :index, :layout => :post
.
Passar blocos para métodos de renderização é mais útil para criar layouts aninhados:
erb :main_layout , :layout => false do
erb :admin_layout do
erb :user
end
end
Isso também pode ser feito em menos linhas de código com:
erb :admin_layout , :layout => :main_layout do
erb :user
end
Atualmente, os seguintes métodos de renderização aceitam um bloco: erb
, haml
, liquid
, slim
. Além disso, o método render
geral aceita um bloco.
Os modelos podem ser definidos no final do arquivo fonte:
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
!= yield
@@ index
%div.title Hello world.
NOTA: Os modelos embutidos definidos no arquivo de origem que requerem o Sinatra são carregados automaticamente. Chame enable :inline_templates
explicitamente se você tiver modelos embutidos em outros arquivos de origem.
Os modelos também podem ser definidos usando o método template
de nível superior:
template :layout do
"%html n =yield n "
end
template :index do
'%div.title Hello World!'
end
get '/' do
haml :index
end
Se existir um modelo chamado "layout", ele será usado sempre que um modelo for renderizado. Você pode desativar layouts individualmente passando :layout => false
ou desativá-los por padrão via set :haml, :layout => false
:
get '/' do
haml :index , :layout => ! request . xhr?
end
Para associar uma extensão de arquivo a um mecanismo de modelo, use Tilt.register
. Por exemplo, se quiser usar a extensão de arquivo tt
para modelos Haml, você pode fazer o seguinte:
Tilt . register Tilt [ :haml ] , :tt
Primeiro, registre seu mecanismo no Tilt e depois crie um método de renderização:
Tilt . register MyAwesomeTemplateEngine , :myat
helpers do
def myat ( * args ) render ( :myat , * args ) end
end
get '/' do
myat :index
end
Renderiza ./views/index.myat
. Saiba mais sobre a inclinação.
Para implementar seu próprio mecanismo de pesquisa de modelo, 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
Antes os filtros são avaliados antes de cada solicitação dentro do mesmo contexto em que as rotas estarão e podem modificar a solicitação e a resposta. Variáveis de instância definidas em filtros são acessíveis por rotas e modelos:
before do
@note = 'Hi!'
request . path_info = '/foo/bar/baz'
end
get '/foo/*' do
@note #=> 'Hi!'
params [ 'splat' ] #=> 'bar/baz'
end
Os filtros posteriores são avaliados após cada solicitação dentro do mesmo contexto em que estarão as rotas e também podem modificar a solicitação e a resposta. Variáveis de instância definidas antes dos filtros e rotas são acessíveis pelos filtros posteriores:
after do
puts response . status
end
Nota: A menos que você use o método body
em vez de apenas retornar uma String das rotas, o corpo ainda não estará disponível no filtro after, pois será gerado posteriormente.
Os filtros opcionalmente adotam um padrão, fazendo com que sejam avaliados apenas se o caminho da solicitação corresponder a esse padrão:
before '/protected/*' do
authenticate!
end
after '/create/:slug' do | slug |
session [ :last_slug ] = slug
end
Assim como as rotas, os filtros também aceitam condições:
before :agent => /Songbird/ do
# ...
end
after '/blog/*' , :host_name => 'example.com' do
# ...
end
Use o método helpers
de nível superior para definir métodos auxiliares para uso em manipuladores de rotas e modelos: