Sinatra 是一种 DSL,可以用最少的工作在 Ruby 中快速创建 Web 应用程序:
# myapp.rb
require 'sinatra'
get '/' do
'Hello world!'
end
安装所需的 gem:
gem install sinatra rackup puma
并运行:
ruby myapp.rb
查看地址:http://localhost:4567
您更改的代码只有在重新启动服务器后才会生效。每次更改或使用代码重新加载器(例如重新运行或机架卸载器)时,请重新启动服务器。
建议还运行gem install puma
,Sinatra 将选择它(如果可用)。
yield
和嵌套布局的模板在 Sinatra 中,路由是与 URL 匹配模式配对的 HTTP 方法。每条路线都与一个块相关联:
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
路由按照定义的顺序进行匹配。调用与请求匹配的第一个路由。
带有尾部斜杠的路由与没有尾部斜杠的路由不同:
get '/foo' do
# Does not match "GET /foo/"
end
路由模式可能包括命名参数,可通过params
哈希访问:
get '/hello/:name' do
# matches "GET /hello/foo" and "GET /hello/bar"
# params['name'] is 'foo' or 'bar'
"Hello #{ params [ 'name' ] } !"
end
您还可以通过块参数访问命名参数:
get '/hello/:name' do | n |
# matches "GET /hello/foo" and "GET /hello/bar"
# params['name'] is 'foo' or 'bar'
# n stores params['name']
"Hello #{ n } !"
end
路由模式还可以包括 splat (或通配符)参数,可通过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
或者使用块参数:
get '/download/*.*' do | path , ext |
[ path , ext ] # => ["path/to/file", "xml"]
end
正则表达式的路由匹配:
get / / hello / ([ w ]+)/ do
"Hello, #{ params [ 'captures' ] . first } !"
end
或者使用块参数:
get %r{/hello/([ w ]+)} do | c |
# Matches "GET /meta/hello/world", "GET /hello/world/1234" etc.
"Hello, #{ c } !"
end
路由模式可能有可选参数:
get '/posts/:format?' do
# matches "GET /posts/" and any extension "GET /posts/json", "GET /posts/xml" etc
end
路由还可以使用查询参数:
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
顺便说一句,除非您禁用路径遍历攻击保护(见下文),否则请求路径可能会在与您的路由匹配之前被修改。
您可以通过传入:mustermann_opts
哈希来自定义用于给定路由的 Mustermann 选项:
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
看起来像是一个条件,但其实不是一个!这些选项将合并到全局:mustermann_opts
哈希中,如下所述。
路由可能包含多种匹配条件,例如用户代理:
get '/foo' , :agent => /Songbird ( d . d )[ d / ]*?/ do
"You're using Songbird version #{ params [ 'agent' ] [ 0 ] } "
end
get '/foo' do
# Matches non-songbird browsers
end
其他可用条件是host_name
并provides
:
get '/' , :host_name => /^admin . / do
"Admin Area, Access denied!"
end
get '/' , :provides => 'html' do
haml :index
end
get '/' , :provides => [ 'rss' , 'atom' , 'xml' ] do
builder :feed
end
provides
搜索请求的 Accept 标头。
您可以轻松定义自己的条件:
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
对于采用多个值的条件,请使用 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
路由块的返回值至少确定传递到 HTTP 客户端的响应主体或至少确定 Rack 堆栈中的下一个中间件。最常见的是,这是一个字符串,如上面的示例所示。但其他值也被接受。
您可以返回一个对象,该对象可以是有效的 Rack 响应、Rack 主体对象或 HTTP 状态代码:
[status (Integer), headers (Hash), response body (responds to #each)]
[status (Integer), response body (responds to #each)]
#each
的对象,除了字符串之外什么都不传递给给定的块这样我们就可以轻松实现一个流式传输示例:
class Stream
def each
100 . times { | i | yield " #{ i } n " }
end
end
get ( '/' ) { Stream . new }
您还可以使用stream
辅助方法(如下所述)来减少样板并将流逻辑嵌入到路由中。
如上所示,Sinatra 内置支持使用字符串模式和正则表达式作为路由匹配。然而,它并不止于此。您可以轻松定义自己的匹配器:
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
请注意,上面的示例可能是过度设计的,因为它也可以表示为:
get /.*/ do
pass if request . path_info == "/index"
# ...
end
静态文件由./public
目录提供。您可以通过设置:public_folder
选项来指定不同的位置:
set :public_folder , __dir__ + '/static'
请注意,公共目录名称不包含在 URL 中。文件./public/css/style.css
以http://example.com/css/style.css
形式提供。
使用:static_cache_control
设置(见下文)添加Cache-Control
标头信息。
每种模板语言都通过其自己的渲染方法公开。这些方法只是返回一个字符串:
get '/' do
erb :index
end
这将呈现views/index.erb
。
除了模板名称之外,您还可以直接传入模板内容:
get '/' do
code = "<%= Time.now %>"
erb code
end
模板采用第二个参数,即选项哈希:
get '/' do
erb :index , :layout => :post
end
这将渲染嵌入在views/post.erb
中的views/index.erb
(默认为views/layout.erb
,如果存在的话)。
Sinatra 无法理解的任何选项都将传递到模板引擎:
get '/' do
haml :index , :format => :html5
end
一般来说,您还可以设置每种模板语言的选项:
set :haml , :format => :html5
get '/' do
haml :index
end
传递给 render 方法的选项会覆盖通过set
设置的选项。
可用选项:
假定模板直接位于./views
目录下。要使用不同的视图目录:
set :views , settings . root + '/templates'
要记住的一件重要的事情是,您始终必须使用符号引用模板,即使它们位于子目录中(在本例中,使用: :'subdir/template'
或'subdir/template'.to_sym
)。您必须使用符号,因为否则渲染方法将渲染直接传递给它们的任何字符串。
get '/' do
haml '%div.title Hello World'
end
呈现模板字符串。如果存在与该字符串关联的文件系统路径或行,您可以选择指定:path
和:line
以获得更清晰的回溯:
get '/' do
haml '%div.title Hello World' , :path => 'examples/file.haml' , :line => 3
end
有些语言有多种实现。要指定使用什么实现(并且是线程安全的),您应该首先需要它:
require 'rdiscount'
get ( '/' ) { markdown :index }
依赖性 | 哈姆尔 |
文件扩展名 | .haml |
例子 | 哈姆尔:索引,:格式=>:html5 |
依赖性 | erubi 或 erb(包含在 Ruby 中) |
文件扩展名 | .erb 、 .rhtml或.erubi (仅限 Erubi) |
例子 | erb :索引 |
依赖性 | 建设者 |
文件扩展名 | .builder |
例子 | 构建器 { |xml| xml.em“嗨”} |
它还需要一个内联模板块(参见示例)。
依赖性 | 诺科吉里 |
文件扩展名 | .nokogiri |
例子 | 诺科吉里 { |xml| xml.em“嗨”} |
它还需要一个内联模板块(参见示例)。
依赖性 | sass 嵌入式 |
文件扩展名 | .sass |
例子 | sass :stylesheet, :style => :expanded |
依赖性 | sass 嵌入式 |
文件扩展名 | .scss |
例子 | scss :样式表, :样式 => :扩展 |
依赖性 | 液体 |
文件扩展名 | 。液体 |
例子 | 液体 :index, :locals => { :key => 'value' } |
由于您无法从 Liquid 模板调用 Ruby 方法( yield
除外),因此您几乎总是希望将局部变量传递给它。
依赖性 | 以下任意一项:RDiscount、RedCarpet、kramdown、commonmarker pandoc |
文件扩展名 | .markdown 、 .mkd和.md |
例子 | 降价:索引,:layout_engine =>:erb |
无法从 Markdown 调用方法,也无法将本地变量传递给它。因此,您通常会将其与另一个渲染引擎结合使用:
erb :overview , :locals => { :text => markdown ( :introduction ) }
请注意,您还可以从其他模板中调用markdown
方法:
% h1 Hello From Haml!
% p = markdown ( :greetings )
由于您无法从 Markdown 调用 Ruby,因此您无法使用用 Markdown 编写的布局。但是,可以通过传递:layout_engine
选项为模板使用除布局之外的其他渲染引擎。
依赖性 | 远程文档 |
文件扩展名 | .rdoc |
例子 | rdoc:自述文件,:layout_engine =>:erb |
无法从 RDoc 调用方法,也无法将局部变量传递给它。因此,您通常会将其与另一个渲染引擎结合使用:
erb :overview , :locals => { :text => rdoc ( :introduction ) }
请注意,您还可以从其他模板中调用rdoc
方法:
% h1 Hello From Haml!
% p = rdoc ( :greetings )
由于您无法从 RDoc 调用 Ruby,因此您无法使用用 RDoc 编写的布局。但是,可以通过传递:layout_engine
选项为模板使用除布局之外的其他渲染引擎。
依赖性 | 阿西多克托 |
文件扩展名 | .asciidoc 、 .adoc和.ad |
例子 | asciidoc:自述文件,:layout_engine =>:erb |
由于您无法直接从 AsciiDoc 模板调用 Ruby 方法,因此您几乎总是希望将本地变量传递给它。
依赖性 | 马尔卡比 |
文件扩展名 | .mab |
例子 | 马克比 { h1“欢迎!” } |
它还需要一个内联模板块(参见示例)。
依赖性 | 拉布尔 |
文件扩展名 | .rabl |
例子 | 拉布:索引 |
依赖性 | 斯利姆朗 |
文件扩展名 | 。苗条的 |
例子 | 苗条:索引 |
依赖性 | 亚吉鲁比 |
文件扩展名 | .yajl |
例子 | yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => '资源' |
模板源被评估为 Ruby 字符串,并使用#to_json
转换生成的 json 变量:
json = { :foo => 'bar' }
json [ :baz ] = key
:callback
和:variable
选项可用于装饰渲染对象:
var resource = { "foo" : "bar" , "baz" : "qux" } ;
present ( resource ) ;
模板在与路由处理程序相同的上下文中进行评估。路由处理程序中设置的实例变量可以通过模板直接访问:
get '/:id' do
@foo = Foo . find ( params [ 'id' ] )
haml '%h1= @foo.name'
end
或者,指定局部变量的显式哈希:
get '/:id' do
foo = Foo . find ( params [ 'id' ] )
haml '%h1= bar.name' , :locals => { :bar => foo }
end
这通常在将模板渲染为其他模板中的部分时使用。
yield
和嵌套布局的模板布局通常只是一个调用yield
的模板。这样的模板可以通过上面描述的:template
选项来使用,也可以使用块来呈现,如下所示:
erb :post , :layout => false do
erb :index
end
此代码主要相当于erb :index, :layout => :post
。
将块传递给渲染方法对于创建嵌套布局最有用:
erb :main_layout , :layout => false do
erb :admin_layout do
erb :user
end
end
这也可以通过更少的代码行来完成:
erb :admin_layout , :layout => :main_layout do
erb :user
end
目前,以下渲染方法接受块: erb
、 haml
、 liquid
、 slim
。此外,通用render
方法接受块。
模板可以在源文件末尾定义:
require 'sinatra'
get '/' do
haml :index
end
__END__
@@ layout
%html
!= yield
@@ index
%div.title Hello world.
注意:需要 Sinatra 的源文件中定义的内联模板会自动加载。如果其他源文件中有内联模板,请显式enable :inline_templates
。
模板也可以使用顶级template
方法来定义:
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
默认禁用它们:
get '/' do
haml :index , :layout => ! request . xhr?
end
要将文件扩展名与模板引擎关联,请使用Tilt.register
。例如,如果您想对 Haml 模板使用文件扩展名tt
,您可以执行以下操作:
Tilt . register Tilt [ :haml ] , :tt
首先,使用 Tilt 注册您的引擎,然后创建一个渲染方法:
Tilt . register MyAwesomeTemplateEngine , :myat
helpers do
def myat ( * args ) render ( :myat , * args ) end
end
get '/' do
myat :index
end
渲染./views/index.myat
。了解有关倾斜的更多信息。
要实现您自己的模板查找机制,您可以编写自己的#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
Before 过滤器在与路由相同的上下文中的每个请求之前进行评估,并且可以修改请求和响应。过滤器中设置的实例变量可以通过路由和模板访问:
before do
@note = 'Hi!'
request . path_info = '/foo/bar/baz'
end
get '/foo/*' do
@note #=> 'Hi!'
params [ 'splat' ] #=> 'bar/baz'
end
After 过滤器在与路由相同的上下文中的每个请求之后进行评估,并且还可以修改请求和响应。在过滤器之前和路由中设置的实例变量可以通过过滤器之后访问:
after do
puts response . status
end
注意:除非您使用body
方法而不是仅从路由返回 String,否则主体在 after 过滤器中将不可用,因为它是稍后生成的。
过滤器可以选择采用某种模式,从而仅当请求路径与该模式匹配时才对它们进行评估:
before '/protected/*' do
authenticate!
end
after '/create/:slug' do | slug |
session [ :last_slug ] = slug
end
与路由一样,过滤器也采用条件:
before :agent => /Songbird/ do
# ...
end
after '/blog/*' , :host_name => 'example.com' do
# ...
end
使用顶级helpers
方法来定义在路由处理程序和模板中使用的帮助器方法: