sinatra 2.2.2 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sinatra might be problematic. Click here for more details.

data/README.zh.md DELETED
@@ -1,2934 +0,0 @@
1
- # Sinatra
2
-
3
- *注:本文档是英文版的翻译,内容更新有可能不及时。如有不一致的地方,请以英文版为准。*
4
-
5
- Sinatra 是一门基于
6
- Ruby 的[领域专属语言](https://en.wikipedia.org/wiki/Domain-specific_language),致力于轻松、快速地创建网络应用:
7
-
8
- ```ruby
9
- # myapp.rb
10
- require 'sinatra'
11
-
12
- get '/' do
13
- 'Hello world!'
14
- end
15
- ```
16
-
17
- 安装 Sinatra 这个 gem:
18
-
19
- ```shell
20
- gem install sinatra
21
- ```
22
-
23
- 然后运行 myapp.rb 中的代码:
24
-
25
- ```shell
26
- ruby myapp.rb
27
- ```
28
-
29
- 在该地址查看: [http://localhost:4567](http://localhost:4567)
30
-
31
- 推荐运行 `gem install thin` 安装 Thin。这样,Sinatra 会优先选择 Thin 作为服务器。
32
-
33
- ## 目录
34
-
35
- * [Sinatra](#sinatra)
36
- * [目录](#目录)
37
- * [路由](#路由)
38
- * [条件](#条件)
39
- * [返回值](#返回值)
40
- * [自定义路由匹配器](#自定义路由匹配器)
41
- * [静态文件](#静态文件)
42
- * [视图 / 模板](#视图--模板)
43
- * [字面量模板](#字面量模板)
44
- * [可选的模板语言](#可选的模板语言)
45
- * [Haml 模板](#haml-模板)
46
- * [Erb 模板](#erb-模板)
47
- * [Builder 模板](#builder-模板)
48
- * [Nokogiri 模板](#nokogiri-模板)
49
- * [Sass 模板](#sass-模板)
50
- * [SCSS 模板](#scss-模板)
51
- * [Less 模板](#less-模板)
52
- * [Liquid 模板](#liquid-模板)
53
- * [Markdown 模板](#markdown-模板)
54
- * [Textile 模板](#textile-模板)
55
- * [RDoc 模板](#rdoc-模板)
56
- * [AsciiDoc 模板](#asciidoc-模板)
57
- * [Radius 模板](#radius-模板)
58
- * [Markaby 模板](#markaby-模板)
59
- * [RABL 模板](#rabl-模板)
60
- * [Slim 模板](#slim-模板)
61
- * [Creole 模板](#creole-模板)
62
- * [MediaWiki 模板](#mediawiki-模板)
63
- * [CoffeeScript 模板](#coffeescript-模板)
64
- * [Stylus 模板](#stylus-模板)
65
- * [Yajl 模板](#yajl-模板)
66
- * [WLang 模板](#wlang-模板)
67
- * [在模板中访问变量](#在模板中访问变量)
68
- * [带 `yield` 的模板和嵌套布局](#带-yield-的模板和嵌套布局)
69
- * [内联模板](#内联模板)
70
- * [具名模板](#具名模板)
71
- * [关联文件扩展名](#关联文件扩展名)
72
- * [添加自定义模板引擎](#添加自定义模板引擎)
73
- * [自定义模板查找逻辑](#自定义模板查找逻辑)
74
- * [过滤器](#过滤器)
75
- * [辅助方法](#辅助方法)
76
- * [使用会话](#使用会话)
77
- * [中断请求](#中断请求)
78
- * [传递请求](#传递请求)
79
- * [触发另一个路由](#触发另一个路由)
80
- * [设置响应主体、状态码和响应首部](#设置响应主体状态码和响应首部)
81
- * [响应的流式传输](#响应的流式传输)
82
- * [日志](#日志)
83
- * [媒体类型](#媒体类型)
84
- * [生成 URL](#生成-url)
85
- * [浏览器重定向](#浏览器重定向)
86
- * [缓存控制](#缓存控制)
87
- * [发送文件](#发送文件)
88
- * [访问请求对象](#访问请求对象)
89
- * [附件](#附件)
90
- * [处理日期和时间](#处理日期和时间)
91
- * [查找模板文件](#查找模板文件)
92
- * [配置](#配置)
93
- * [配置攻击防护](#配置攻击防护)
94
- * [可选的设置](#可选的设置)
95
- * [环境](#环境)
96
- * [错误处理](#错误处理)
97
- * [未找到](#未找到)
98
- * [错误](#错误)
99
- * [Rack 中间件](#rack-中间件)
100
- * [测试](#测试)
101
- * [Sinatra::Base - 中间件、库和模块化应用](#sinatrabase---中间件库和模块化应用)
102
- * [模块化风格 vs. 经典风格](#模块化风格-vs-经典风格)
103
- * [运行一个模块化应用](#运行一个模块化应用)
104
- * [使用 config.ru 运行经典风格的应用](#使用-configru-运行经典风格的应用)
105
- * [何时使用 config.ru?](#何时使用-configru)
106
- * [把 Sinatra 当作中间件使用](#把-sinatra-当作中间件使用)
107
- * [创建动态应用](#创建动态应用)
108
- * [作用域和绑定](#作用域和绑定)
109
- * [应用/类作用域](#应用类作用域)
110
- * [请求/实例作用域](#请求实例作用域)
111
- * [代理作用域](#代理作用域)
112
- * [命令行](#命令行)
113
- * [多线程](#多线程)
114
- * [必要条件](#必要条件)
115
- * [紧跟前沿](#紧跟前沿)
116
- * [通过 Bundler 使用 Sinatra](#通过-bundler-使用-sinatra)
117
- * [使用自己本地的 Sinatra](#使用自己本地的-sinatra)
118
- * [全局安装](#全局安装)
119
- * [版本](#版本)
120
- * [更多资料](#更多资料)
121
-
122
- ## 路由
123
-
124
- 在 Sinatra 中,一个路由分为两部分:HTTP 方法和 URL 匹配范式。每个路由都有一个要执行的代码块:
125
-
126
- ```ruby
127
- get '/' do
128
- .. 显示内容 ..
129
- end
130
-
131
- post '/' do
132
- .. 创建内容 ..
133
- end
134
-
135
- put '/' do
136
- .. 替换内容 ..
137
- end
138
-
139
- patch '/' do
140
- .. 修改内容 ..
141
- end
142
-
143
- delete '/' do
144
- .. 删除内容 ..
145
- end
146
-
147
- options '/' do
148
- .. 显示命令列表 ..
149
- end
150
-
151
- link '/' do
152
- .. 建立某种联系 ..
153
- end
154
-
155
- unlink '/' do
156
- .. 解除某种联系 ..
157
- end
158
- ```
159
-
160
- 路由按照它们定义时的顺序进行匹配。第一个与请求匹配的路由会被调用。
161
-
162
- 路由范式可以包括具名参数,具名参数可以通过 `params` hash 访问:
163
-
164
- ```ruby
165
- get '/hello/:name' do
166
- # 匹配 "GET /hello/foo" 和 "GET /hello/bar"
167
- # params['name'] 的值是 'foo' 或者 'bar'
168
- "Hello #{params['name']}!"
169
- end
170
- ```
171
-
172
- 也可以通过代码块参数访问具名参数:
173
-
174
- ```ruby
175
- get '/hello/:name' do |n|
176
- # 匹配 "GET /hello/foo" 和 "GET /hello/bar"
177
- # params['name'] 的值是 'foo' 或者 'bar'
178
- # n 存储 params['name'] 的值
179
- "Hello #{n}!"
180
- end
181
- ```
182
-
183
- 路由范式也可以包含通配符参数, 参数值可以通过 `params['splat']` 数组访问。
184
-
185
- ```ruby
186
- get '/say/*/to/*' do
187
- # 匹配 "GET /say/hello/to/world"
188
- params['splat'] # => ["hello", "world"]
189
- end
190
-
191
- get '/download/*.*' do
192
- # 匹配 "GET /download/path/to/file.xml"
193
- params['splat'] # => ["path/to/file", "xml"]
194
- end
195
- ```
196
-
197
- 或者通过代码块参数访问:
198
-
199
- ```ruby
200
- get '/download/*.*' do |path, ext|
201
- [path, ext] # => ["path/to/file", "xml"]
202
- end
203
- ```
204
-
205
- 通过正则表达式匹配路由:
206
-
207
- ```ruby
208
- get /\/hello\/([\w]+)/ do
209
- "Hello, #{params['captures'].first}!"
210
- end
211
- ```
212
-
213
- 或者使用代码块参数:
214
-
215
- ```ruby
216
- get %r{/hello/([\w]+)} do |c|
217
- # 匹配 "GET /meta/hello/world"、"GET /hello/world/1234" 等
218
- "Hello, #{c}!"
219
- end
220
- ```
221
-
222
- 路由范式可以包含可选参数:
223
-
224
- ```ruby
225
- get '/posts/:format?' do
226
- # 匹配 "GET /posts/" 和任意扩展 "GET /posts/json"、"GET /posts/xml" 等
227
- end
228
- ```
229
-
230
- 路由也可以使用查询参数:
231
-
232
- ```ruby
233
- get '/posts' do
234
- # 匹配 "GET /posts?title=foo&author=bar"
235
- title = params['title']
236
- author = params['author']
237
- # 使用 title 和 author 变量;对于 /posts 路由来说,查询字符串是可选的
238
- end
239
- ```
240
- 顺便一提,除非你禁用了路径遍历攻击防护(见下文),请求路径可能在匹配路由前发生改变。
241
-
242
- 你也可以通过`:mustermann_opt`选项定义[Mustermann](https://github.com/sinatra/mustermann)来匹配路由。
243
-
244
- ```ruby
245
- get '\A/posts\z', :mustermann_opts => { :type => :regexp, :check_anchors => false } do
246
- # matches /posts exactly, with explicit anchoring
247
- "If you match an anchored pattern clap your hands!"
248
- end
249
- ```
250
-
251
- 它看起来像一个[条件](https://github.com/sinatra/sinatra/blob/master/README.zh.md#%E6%9D%A1%E4%BB%B6),但实际不是!这些选项将被合并到全局的`mustermann_opts`。
252
-
253
- ### 条件
254
-
255
- 路由可以包含各种匹配条件,比如 user agent:
256
-
257
- ```ruby
258
- get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
259
- "你正在使用 Songbird,版本是 #{params['agent'][0]}"
260
- end
261
-
262
- get '/foo' do
263
- # 匹配非 Songbird 浏览器
264
- end
265
- ```
266
-
267
- 其它可以使用的条件有 `host_name` 和 `provides`:
268
-
269
- ```ruby
270
- get '/', :host_name => /^admin\./ do
271
- "管理员区域,无权进入!"
272
- end
273
-
274
- get '/', :provides => 'html' do
275
- haml :index
276
- end
277
-
278
- get '/', :provides => ['rss', 'atom', 'xml'] do
279
- builder :feed
280
- end
281
- ```
282
-
283
- `provides` 会搜索请求的 Accept 首部字段。
284
-
285
- 也可以轻易地使用自定义条件:
286
-
287
- ```ruby
288
- set(:probability) { |value| condition { rand <= value } }
289
-
290
- get '/win_a_car', :probability => 0.1 do
291
- "You won!"
292
- end
293
-
294
- get '/win_a_car' do
295
- "Sorry, you lost."
296
- end
297
- ```
298
-
299
- 对于一个需要提供多个值的条件,可以使用 splat:
300
-
301
- ```ruby
302
- set(:auth) do |*roles| # <- 注意此处使用了 splat
303
- condition do
304
- unless logged_in? && roles.any? {|role| current_user.in_role? role }
305
- redirect "/login/", 303
306
- end
307
- end
308
- end
309
-
310
- get "/my/account/", :auth => [:user, :admin] do
311
- "Your Account Details"
312
- end
313
-
314
- get "/only/admin/", :auth => :admin do
315
- "Only admins are allowed here!"
316
- end
317
- ```
318
-
319
- ### 返回值
320
-
321
- 路由代码块的返回值至少决定了返回给
322
- HTTP 客户端的响应主体,或者至少决定了在
323
- Rack 堆栈中的下一个中间件。大多数情况下,返回值是一个字符串,就像上面的例子中的一样。但是,其它类型的值也是可以接受的。
324
-
325
- 你可以返回任何对象,该对象要么是一个合理的 Rack 响应,要么是一个 Rack body 对象,要么是 HTTP 状态码:
326
-
327
- * 一个包含三个元素的数组: `[状态 (Integer), 响应首部 (Hash), 响应主体 (可以响应 #each 方法)]`
328
- * 一个包含两个元素的数组: `[状态 (Integer), 响应主体 (可以响应 #each 方法)]`
329
- * 一个响应 `#each` 方法,只传回字符串的对象
330
- * 一个代表状态码的数字
331
-
332
- 例如,我们可以轻松地实现流式传输:
333
-
334
- ```ruby
335
- class Stream
336
- def each
337
- 100.times { |i| yield "#{i}\n" }
338
- end
339
- end
340
-
341
- get('/') { Stream.new }
342
- ```
343
-
344
- 也可以使用 `stream` 辅助方法(见下文描述)以减少样板代码并在路由中直接使用流式传输。
345
-
346
- ### 自定义路由匹配器
347
-
348
- 如上文所示,Sinatra
349
- 本身支持使用字符串和正则表达式作为路由匹配。但不限于此,你可以轻松地定义自己的匹配器:
350
-
351
- ```ruby
352
- class AllButPattern
353
- Match = Struct.new(:captures)
354
-
355
- def initialize(except)
356
- @except = except
357
- @captures = Match.new([])
358
- end
359
-
360
- def match(str)
361
- @captures unless @except === str
362
- end
363
- end
364
-
365
- def all_but(pattern)
366
- AllButPattern.new(pattern)
367
- end
368
-
369
- get all_but("/index") do
370
- # ...
371
- end
372
- ```
373
-
374
- 上面的例子可能太繁琐了, 因为它也可以用更简单的方式表述:
375
-
376
- ```ruby
377
- get // do
378
- pass if request.path_info == "/index"
379
- # ...
380
- end
381
- ```
382
-
383
- 或者,使用消极向前查找:
384
-
385
- ```ruby
386
- get %r{(?!/index)} do
387
- # ...
388
- end
389
- ```
390
-
391
- ## 静态文件
392
-
393
- 静态文件从 `./public` 目录提供服务。可以通过设置`:public_folder` 选项设定一个不同的位置:
394
-
395
- ```ruby
396
- set :public_folder, __dir__ + '/static'
397
- ```
398
-
399
- 请注意 public 目录名并没有包含在 URL 中。文件 `./public/css/style.css` 可以通过
400
- `http://example.com/css/style.css` 访问。
401
-
402
- 可以使用 `:static_cache_control` 设置(见下文)添加 `Cache-Control` 首部信息。
403
-
404
- ## 视图 / 模板
405
-
406
- 每一门模板语言都将自身的渲染方法暴露给
407
- Sinatra 调用。这些渲染方法只是简单地返回字符串。
408
-
409
- ```ruby
410
- get '/' do
411
- erb :index
412
- end
413
- ```
414
-
415
- 这段代码会渲染 `views/index.erb` 文件。
416
-
417
- 除了模板文件名,也可以直接传入模板内容:
418
-
419
- ```ruby
420
- get '/' do
421
- code = "<%= Time.now %>"
422
- erb code
423
- end
424
- ```
425
-
426
- 渲染方法接受第二个参数,即选项 hash:
427
-
428
- ```ruby
429
- get '/' do
430
- erb :index, :layout => :post
431
- end
432
- ```
433
-
434
- 这段代码会将 `views/index.erb` 嵌入在 `views/post.erb`
435
- 布局中并一起渲染(`views/layout.erb` 是默认的布局,如果它存在的话)。
436
-
437
- 任何 Sinatra 不能理解的选项都会传递给模板引擎。
438
-
439
- ```ruby
440
- get '/' do
441
- haml :index, :format => :html5
442
- end
443
- ```
444
-
445
- 也可以为每种模板语言设置通用的选项:
446
-
447
- ```ruby
448
- set :haml, :format => :html5
449
-
450
- get '/' do
451
- haml :index
452
- end
453
- ```
454
-
455
- 在渲染方法中传入的选项会覆盖通过 `set` 设置的通用选项。
456
-
457
- 可用的选项:
458
-
459
- <dl>
460
- <dt>locals</dt>
461
- <dd>
462
- 传递给模板文档的 locals 对象列表。对于 partials
463
- 很方便。例如:<tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>
464
- </dd>
465
-
466
- <dt>default_encoding</dt>
467
- <dd>默认的字符编码。默认值为 <tt>settings.default_encoding</tt>。</dd>
468
-
469
- <dt>views</dt>
470
- <dd>存放模板文件的目录。默认为 <tt>settings.views</tt>。</dd>
471
-
472
- <dt>layout</dt>
473
- <dd>
474
- 是否使用布局 (<tt>true</tt> 或 <tt>false</tt>)。
475
- 如果使用一个符号类型的值,则是用于明确使用的模板。例如:
476
- <tt>erb :index, :layout => !request.xhr?</tt>
477
- </dd>
478
-
479
- <dt>content_type</dt>
480
- <dd>由模板生成的 Content-Type。默认值由模板语言决定。</dd>
481
-
482
- <dt>scope</dt>
483
- <dd>
484
- 渲染模板时的作用域。默认值为应用类的实例对象。如果更改此项,实例变量和辅助方法将不可用。
485
- </dd>
486
-
487
- <dt>layout_engine</dt>
488
- <dd>
489
- 渲染布局所使用的模板引擎。用于不支持布局的模板语言。默认值为模板所使用的引擎。例如:
490
- <tt>set :rdoc, :layout_engine => :erb</tt>
491
- </dd>
492
-
493
- <dt>layout_options</dt>
494
- <dd>
495
- 渲染布局的特殊选项。例如:
496
- <tt>set :rdoc, :layout_options => { :views => 'views/layouts' }</tt>
497
- </dd>
498
- </dl>
499
-
500
- Sinatra 假定模板文件直接位于 `./views` 目录。要使用不同的视图目录:
501
-
502
- ```ruby
503
- set :views, settings.root + '/templates'
504
- ```
505
-
506
-
507
- 需要牢记的一点是,你必须通过符号引用模板, 即使它们存放在子目录下
508
- (在这种情况下,使用 `:'subdir/template'` 或 `'subdir/template'.to_sym`)。
509
- 如果你不使用符号,渲染方法会直接渲染你传入的任何字符串。
510
-
511
- ### 字面量模板
512
-
513
- ```ruby
514
- get '/' do
515
- haml '%div.title Hello World'
516
- end
517
- ```
518
-
519
- 这段代码直接渲染模板字符串。
520
-
521
- ### 可选的模板语言
522
-
523
- 一些语言有多种实现。为了确定使用哪种实现(以及保证线程安全),你应该首先引入该实现:
524
-
525
- ```ruby
526
- require 'rdiscount' # 或 require 'bluecloth'
527
- get('/') { markdown :index }
528
- ```
529
-
530
- #### Haml 模板
531
-
532
- <table>
533
- <tr>
534
- <td>依赖项</td>
535
- <td><a href="http://haml.info/" title="haml">haml</a></td>
536
- </tr>
537
- <tr>
538
- <td>文件扩展名</td>
539
- <td><tt>.haml</tt></td>
540
- </tr>
541
- <tr>
542
- <td>例子</td>
543
- <td><tt>haml :index, :format => :html5</tt></td>
544
- </tr>
545
- </table>
546
-
547
- #### Erb 模板
548
-
549
- <table>
550
- <tr>
551
- <td>依赖项</td>
552
- <td>
553
- <a href="http://www.kuwata-lab.com/erubis/" title="erubis">erubis</a>
554
- 或 erb (Ruby 标准库中已经包含)
555
- </td>
556
- </tr>
557
- <tr>
558
- <td>文件扩展名</td>
559
- <td><tt>.erb</tt>, <tt>.rhtml</tt> or <tt>.erubis</tt> (仅用于 Erubis)</td>
560
- </tr>
561
- <tr>
562
- <td>例子</td>
563
- <td><tt>erb :index</tt></td>
564
- </tr>
565
- </table>
566
-
567
- #### Builder 模板
568
-
569
- <table>
570
- <tr>
571
- <td>依赖项</td>
572
- <td>
573
- <a href="https://github.com/jimweirich/builder" title="builder">builder</a>
574
- </td>
575
- </tr>
576
- <tr>
577
- <td>文件扩展名</td>
578
- <td><tt>.builder</tt></td>
579
- </tr>
580
- <tr>
581
- <td>例子</td>
582
- <td><tt>builder { |xml| xml.em "hi" }</tt></td>
583
- </tr>
584
- </table>
585
-
586
- `builder` 渲染方法也接受一个代码块,用于内联模板(见例子)。
587
-
588
- #### Nokogiri 模板
589
-
590
- <table>
591
- <tr>
592
- <td>依赖项</td>
593
- <td><a href="http://www.nokogiri.org/" title="nokogiri">nokogiri</a></td>
594
- </tr>
595
- <tr>
596
- <td>文件扩展名</td>
597
- <td><tt>.nokogiri</tt></td>
598
- </tr>
599
- <tr>
600
- <td>例子</td>
601
- <td><tt>nokogiri { |xml| xml.em "hi" }</tt></td>
602
- </tr>
603
- </table>
604
-
605
- `nokogiri` 渲染方法也接受一个代码块,用于内联模板(见例子)。
606
-
607
- #### Sass 模板
608
-
609
- <table>
610
- <tr>
611
- <td>依赖项</td>
612
- <td><a href="http://sass-lang.com/" title="sass">sass</a></td>
613
- </tr>
614
- <tr>
615
- <td>文件扩展名</td>
616
- <td><tt>.sass</tt></td>
617
- </tr>
618
- <tr>
619
- <td>例子</td>
620
- <td><tt>sass :stylesheet, :style => :expanded</tt></td>
621
- </tr>
622
- </table>
623
-
624
- #### SCSS 模板
625
-
626
- <table>
627
- <tr>
628
- <td>依赖项</td>
629
- <td><a href="http://sass-lang.com/" title="sass">sass</a></td>
630
- </tr>
631
- <tr>
632
- <td>文件扩展名</td>
633
- <td><tt>.scss</tt></td>
634
- </tr>
635
- <tr>
636
- <td>例子</td>
637
- <td><tt>scss :stylesheet, :style => :expanded</tt></td>
638
- </tr>
639
- </table>
640
-
641
- #### Less 模板
642
-
643
- <table>
644
- <tr>
645
- <td>依赖项</td>
646
- <td><a href="http://lesscss.org/" title="less">less</a></td>
647
- </tr>
648
- <tr>
649
- <td>文件扩展名</td>
650
- <td><tt>.less</tt></td>
651
- </tr>
652
- <tr>
653
- <td>例子</td>
654
- <td><tt>less :stylesheet</tt></td>
655
- </tr>
656
- </table>
657
-
658
- #### Liquid 模板
659
-
660
- <table>
661
- <tr>
662
- <td>依赖项</td>
663
- <td><a href="https://shopify.github.io/liquid/" title="liquid">liquid</a></td>
664
- </tr>
665
- <tr>
666
- <td>文件扩展名</td>
667
- <td><tt>.liquid</tt></td>
668
- </tr>
669
- <tr>
670
- <td>例子</td>
671
- <td><tt>liquid :index, :locals => { :key => 'value' }</tt></td>
672
- </tr>
673
- </table>
674
-
675
- 因为不能在 Liquid 模板中调用 Ruby 方法(除了 `yield`),你几乎总是需要传递 locals 对象给它。
676
-
677
- #### Markdown 模板
678
-
679
- <table>
680
- <tr>
681
- <td>依赖项</td>
682
- <td>
683
- 下列任一:
684
- <a href="https://github.com/davidfstr/rdiscount" title="RDiscount">RDiscount</a>,
685
- <a href="https://github.com/vmg/redcarpet" title="RedCarpet">RedCarpet</a>,
686
- <a href="https://github.com/ged/bluecloth" title="bluecloth">BlueCloth</a>,
687
- <a href="http://kramdown.gettalong.org/" title="kramdown">kramdown</a>,
688
- <a href="https://github.com/bhollis/maruku" title="maruku">maruku</a>
689
- </td>
690
- </tr>
691
- <tr>
692
- <td>文件扩展名</td>
693
- <td><tt>.markdown</tt>, <tt>.mkd</tt> and <tt>.md</tt></td>
694
- </tr>
695
- <tr>
696
- <td>例子</td>
697
- <td><tt>markdown :index, :layout_engine => :erb</tt></td>
698
- </tr>
699
- </table>
700
-
701
- 不能在 markdown 中调用 Ruby 方法,也不能传递 locals 给它。
702
- 因此,你一般会结合其它的渲染引擎来使用它:
703
-
704
- ```ruby
705
- erb :overview, :locals => { :text => markdown(:introduction) }
706
- ```
707
-
708
- 请注意你也可以在其它模板中调用 markdown 方法:
709
-
710
- ```ruby
711
- %h1 Hello From Haml!
712
- %p= markdown(:greetings)
713
- ```
714
-
715
- 因为不能在 Markdown 中使用 Ruby 语言,你不能使用 Markdown 书写的布局。
716
- 不过,使用其它渲染引擎作为模板的布局是可能的,这需要通过传入 `:layout_engine` 选项。
717
-
718
- #### Textile 模板
719
-
720
- <table>
721
- <tr>
722
- <td>依赖项</td>
723
- <td><a href="http://redcloth.org/" title="RedCloth">RedCloth</a></td>
724
- </tr>
725
- <tr>
726
- <td>文件扩展名</td>
727
- <td><tt>.textile</tt></td>
728
- </tr>
729
- <tr>
730
- <td>例子</td>
731
- <td><tt>textile :index, :layout_engine => :erb</tt></td>
732
- </tr>
733
- </table>
734
-
735
- 不能在 textile 中调用 Ruby 方法,也不能传递 locals 给它。
736
- 因此,你一般会结合其它的渲染引擎来使用它:
737
-
738
- ```ruby
739
- erb :overview, :locals => { :text => textile(:introduction) }
740
- ```
741
-
742
- 请注意你也可以在其他模板中调用 `textile` 方法:
743
-
744
- ```ruby
745
- %h1 Hello From Haml!
746
- %p= textile(:greetings)
747
- ```
748
-
749
- 因为不能在 Textile 中调用 Ruby 方法,你不能用 Textile 书写布局。
750
- 不过,使用其它渲染引擎作为模版的布局是可能的,这需要通过传递 `:layout_engine` 选项。
751
-
752
- #### RDoc 模板
753
-
754
- <table>
755
- <tr>
756
- <td>依赖项</td>
757
- <td><a href="http://rdoc.sourceforge.net/" title="RDoc">RDoc</a></td>
758
- </tr>
759
- <tr>
760
- <td>文件扩展名</td>
761
- <td><tt>.rdoc</tt></td>
762
- </tr>
763
- <tr>
764
- <td>例子</td>
765
- <td><tt>rdoc :README, :layout_engine => :erb</tt></td>
766
- </tr>
767
- </table>
768
-
769
- 不能在 rdoc 中调用 Ruby 方法,也不能传递 locals 给它。
770
- 因此,你一般会结合其它的渲染引擎来使用它:
771
-
772
- ```ruby
773
- erb :overview, :locals => { :text => rdoc(:introduction) }
774
- ```
775
-
776
- 请注意你也可以在其他模板中调用 `rdoc` 方法:
777
-
778
- ```ruby
779
- %h1 Hello From Haml!
780
- %p= rdoc(:greetings)
781
- ```
782
-
783
- 因为不能在 RDoc 中调用 Ruby 方法,你不能用 RDoc 书写布局。
784
- 不过,使用其它渲染引擎作为模版的布局是可能的,这需要通过传递 `:layout_engine` 选项。
785
-
786
- #### AsciiDoc 模板
787
-
788
- <table>
789
- <tr>
790
- <td>依赖项</td>
791
- <td><a href="http://asciidoctor.org/" title="Asciidoctor">Asciidoctor</a></td>
792
- </tr>
793
- <tr>
794
- <td>文件扩展名</td>
795
- <td><tt>.asciidoc</tt>, <tt>.adoc</tt> and <tt>.ad</tt></td>
796
- </tr>
797
- <tr>
798
- <td>例子</td>
799
- <td><tt>asciidoc :README, :layout_engine => :erb</tt></td>
800
- </tr>
801
- </table>
802
-
803
- 因为不能在 AsciiDoc 模板中直接调用 Ruby 方法,你几乎总是需要传递 locals 对象给它。
804
-
805
- #### Radius 模板
806
-
807
- <table>
808
- <tr>
809
- <td>依赖项</td>
810
- <td><a href="https://github.com/jlong/radius" title="Radius">Radius</a></td>
811
- </tr>
812
- <tr>
813
- <td>文件扩展名</td>
814
- <td><tt>.radius</tt></td>
815
- </tr>
816
- <tr>
817
- <td>例子</td>
818
- <td><tt>radius :index, :locals => { :key => 'value' }</tt></td>
819
- </tr>
820
- </table>
821
-
822
- 因为不能在 Radius 模板中直接调用 Ruby 方法,你几乎总是可以传递 locals 对象给它。
823
-
824
- #### Markaby 模板
825
-
826
- <table>
827
- <tr>
828
- <td>依赖项</td>
829
- <td><a href="http://markaby.github.io/" title="Markaby">Markaby</a></td>
830
- </tr>
831
- <tr>
832
- <td>文件扩展名</td>
833
- <td><tt>.mab</tt></td>
834
- </tr>
835
- <tr>
836
- <td>例子</td>
837
- <td><tt>markaby { h1 "Welcome!" }</tt></td>
838
- </tr>
839
- </table>
840
-
841
- `markaby` 渲染方法也接受一个代码块,用于内联模板(见例子)。
842
-
843
- #### RABL 模板
844
-
845
- <table>
846
- <tr>
847
- <td>依赖项</td>
848
- <td><a href="https://github.com/nesquena/rabl" title="Rabl">Rabl</a></td>
849
- </tr>
850
- <tr>
851
- <td>文件扩展名</td>
852
- <td><tt>.rabl</tt></td>
853
- </tr>
854
- <tr>
855
- <td>例子</td>
856
- <td><tt>rabl :index</tt></td>
857
- </tr>
858
- </table>
859
-
860
- #### Slim 模板
861
-
862
- <table>
863
- <tr>
864
- <td>依赖项</td>
865
- <td><a href="http://slim-lang.com/" title="Slim Lang">Slim Lang</a></td>
866
- </tr>
867
- <tr>
868
- <td>文件扩展名</td>
869
- <td><tt>.slim</tt></td>
870
- </tr>
871
- <tr>
872
- <td>例子</td>
873
- <td><tt>slim :index</tt></td>
874
- </tr>
875
- </table>
876
-
877
- #### Creole 模板
878
-
879
- <table>
880
- <tr>
881
- <td>依赖项</td>
882
- <td><a href="https://github.com/minad/creole" title="Creole">Creole</a></td>
883
- </tr>
884
- <tr>
885
- <td>文件扩展名</td>
886
- <td><tt>.creole</tt></td>
887
- </tr>
888
- <tr>
889
- <td>例子</td>
890
- <td><tt>creole :wiki, :layout_engine => :erb</tt></td>
891
- </tr>
892
- </table>
893
-
894
- 不能在 creole 中调用 Ruby 方法,也不能传递 locals 对象给它。
895
- 因此你一般会结合其它的渲染引擎来使用它:
896
-
897
- ```ruby
898
- erb :overview, :locals => { :text => creole(:introduction) }
899
- ```
900
-
901
- 注意你也可以在其它模板内调用 `creole` 方法:
902
-
903
- ```ruby
904
- %h1 Hello From Haml!
905
- %p= creole(:greetings)
906
- ```
907
-
908
- 因为不能在 Creole 模板文件内调用 Ruby 方法,你不能用 Creole 书写布局文件。
909
- 然而,使用其它渲染引擎作为模版的布局是可能的,这需要通过传递 `:layout_engine` 选项。
910
-
911
- #### MediaWiki 模板
912
-
913
- <table>
914
- <tr>
915
- <td>依赖项</td>
916
- <td><a href="https://github.com/nricciar/wikicloth" title="WikiCloth">WikiCloth</a></td>
917
- </tr>
918
- <tr>
919
- <td>文件扩展名</td>
920
- <td><tt>.mediawiki</tt> and <tt>.mw</tt></td>
921
- </tr>
922
- <tr>
923
- <td>例子</td>
924
- <td><tt>mediawiki :wiki, :layout_engine => :erb</tt></td>
925
- </tr>
926
- </table>
927
-
928
- 在 MediaWiki 标记文件内不能调用 Ruby 方法,也不能传递 locals 对象给它。
929
- 因此你一般会结合其它的渲染引擎来使用它:
930
-
931
- ```ruby
932
- erb :overview, :locals => { :text => mediawiki(:introduction) }
933
- ```
934
-
935
- 注意你也可以在其它模板内调用 `mediawiki` 方法:
936
-
937
- ```ruby
938
- %h1 Hello From Haml!
939
- %p= mediawiki(:greetings)
940
- ```
941
-
942
- 因为不能在 MediaWiki 文件内调用 Ruby 方法,你不能用 MediaWiki 书写布局文件。
943
- 然而,使用其它渲染引擎作为模版的布局是可能的,这需要通过传递 `:layout_engine` 选项。
944
-
945
- #### CoffeeScript 模板
946
-
947
- <table>
948
- <tr>
949
- <td>依赖项</td>
950
- <td>
951
- <a href="https://github.com/josh/ruby-coffee-script" title="Ruby CoffeeScript">
952
- CoffeeScript
953
- </a> 以及一种
954
- <a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme" title="ExecJS">
955
- 执行 JavaScript 的方式
956
- </a>
957
- </td>
958
- </tr>
959
- <tr>
960
- <td>文件扩展名</td>
961
- <td><tt>.coffee</tt></td>
962
- </tr>
963
- <tr>
964
- <td>例子</td>
965
- <td><tt>coffee :index</tt></td>
966
- </tr>
967
- </table>
968
-
969
- #### Stylus 模板
970
-
971
- <table>
972
- <tr>
973
- <td>依赖项</td>
974
- <td>
975
- <a href="https://github.com/forgecrafted/ruby-stylus" title="Ruby Stylus">
976
- Stylus
977
- </a> 以及一种
978
- <a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme" title="ExecJS">
979
- 执行 JavaScript 的方式
980
- </a>
981
- </td>
982
- </tr>
983
- <tr>
984
- <td>文件扩展名</td>
985
- <td><tt>.styl</tt></td>
986
- </tr>
987
- <tr>
988
- <td>例子</td>
989
- <td><tt>stylus :index</tt></td>
990
- </tr>
991
- </table>
992
-
993
- 在使用 Stylus 模板之前,你需要先加载 `stylus` 和 `stylus/tilt`:
994
-
995
- ```ruby
996
- require 'sinatra'
997
- require 'stylus'
998
- require 'stylus/tilt'
999
-
1000
- get '/' do
1001
- stylus :example
1002
- end
1003
- ```
1004
-
1005
- #### Yajl 模板
1006
-
1007
- <table>
1008
- <tr>
1009
- <td>依赖项</td>
1010
- <td><a href="https://github.com/brianmario/yajl-ruby" title="yajl-ruby">yajl-ruby</a></td>
1011
- </tr>
1012
- <tr>
1013
- <td>文件扩展名</td>
1014
- <td><tt>.yajl</tt></td>
1015
- </tr>
1016
- <tr>
1017
- <td>例子</td>
1018
- <td>
1019
- <tt>
1020
- yajl :index,
1021
- :locals => { :key => 'qux' },
1022
- :callback => 'present',
1023
- :variable => 'resource'
1024
- </tt>
1025
- </td>
1026
- </tr>
1027
- </table>
1028
-
1029
- 模板文件的源码作为一个 Ruby 字符串被求值,得到的 json 变量是通过 `#to_json` 方法转换的:
1030
-
1031
- ```ruby
1032
- json = { :foo => 'bar' }
1033
- json[:baz] = key
1034
- ```
1035
-
1036
- 可以使用 `:callback` 和 `:variable` 选项装饰被渲染的对象:
1037
-
1038
- ```javascript
1039
- var resource = {"foo":"bar","baz":"qux"};
1040
- present(resource);
1041
- ```
1042
-
1043
- #### WLang 模板
1044
-
1045
- <table>
1046
- <tr>
1047
- <td>依赖项</td>
1048
- <td><a href="https://github.com/blambeau/wlang/" title="WLang">WLang</a></td>
1049
- </tr>
1050
- <tr>
1051
- <td>文件扩展名</td>
1052
- <td><tt>.wlang</tt></td>
1053
- </tr>
1054
- <tr>
1055
- <td>例子</td>
1056
- <td><tt>wlang :index, :locals => { :key => 'value' }</tt></td>
1057
- </tr>
1058
- </table>
1059
-
1060
- 因为在 WLang 中调用 Ruby 方法不符合语言习惯,你几乎总是需要传递 locals 给 WLang 木板。
1061
- 然而,可以用 WLang 编写布局文件,也可以在 WLang 中使用 `yield` 方法。
1062
-
1063
- ### 在模板中访问变量
1064
-
1065
- 模板的求值发生在路由处理器内部的上下文中。模板可以直接访问路由处理器中设置的实例变量。
1066
-
1067
- ```ruby
1068
- get '/:id' do
1069
- @foo = Foo.find(params['id'])
1070
- haml '%h1= @foo.name'
1071
- end
1072
- ```
1073
-
1074
- 或者,也可以显式地指定一个由局部变量组成的 locals 哈希:
1075
-
1076
- ```ruby
1077
- get '/:id' do
1078
- foo = Foo.find(params['id'])
1079
- haml '%h1= foo.name', :locals => { :foo => foo }
1080
- end
1081
- ```
1082
-
1083
- locals 哈希典型的使用情景是在别的模板中渲染 partials。
1084
-
1085
- ### 带 `yield` 的模板和嵌套布局
1086
-
1087
- 布局通常就是使用了 `yield` 方法的模板。
1088
- 这样的布局文件可以通过上面描述的 `:template` 选项指定,也可以通过下面的代码块渲染:
1089
-
1090
- ```ruby
1091
- erb :post, :layout => false do
1092
- erb :index
1093
- end
1094
- ```
1095
-
1096
- 这段代码几乎完全等同于 `erb :index, :layout => :post`。
1097
-
1098
- 向渲染方法传递代码块对于创建嵌套布局是最有用的:
1099
-
1100
- ```ruby
1101
- erb :main_layout, :layout => false do
1102
- erb :admin_layout do
1103
- erb :user
1104
- end
1105
- end
1106
- ```
1107
-
1108
- 代码行数可以更少:
1109
-
1110
- ```ruby
1111
- erb :admin_layout, :layout => :main_layout do
1112
- erb :user
1113
- end
1114
- ```
1115
-
1116
- 当前,以下的渲染方法接受一个代码块:`erb`、`haml`、`liquid`、`slim ` 和 `wlang`。
1117
- 通用的 `render` 方法也接受。
1118
-
1119
- ### 内联模板
1120
-
1121
- 模板可以在源文件的末尾定义:
1122
-
1123
- ```ruby
1124
- require 'sinatra'
1125
-
1126
- get '/' do
1127
- haml :index
1128
- end
1129
-
1130
- __END__
1131
-
1132
- @@ layout
1133
- %html
1134
- = yield
1135
-
1136
- @@ index
1137
- %div.title Hello world.
1138
- ```
1139
-
1140
- 注意:在引入了 sinatra 的源文件中定义的内联模板会自动载入。
1141
- 如果你在其他源文件中也有内联模板,需要显式调用 `enable :inline_templates`。
1142
-
1143
- ### 具名模板
1144
-
1145
- 可以使用顶层 `template` 方法定义模板:
1146
-
1147
- ```ruby
1148
- template :layout do
1149
- "%html\n =yield\n"
1150
- end
1151
-
1152
- template :index do
1153
- '%div.title Hello World!'
1154
- end
1155
-
1156
- get '/' do
1157
- haml :index
1158
- end
1159
- ```
1160
-
1161
- 如果存在名为 “layout” 的模板,该模板会在每个模板渲染的时候作为布局使用。
1162
- 你可以为渲染方法传送 `:layout => false` 来禁用该次渲染的布局,
1163
- 也可以设置 `set :haml, :layout => false` 来默认禁用布局。
1164
-
1165
- ```ruby
1166
- get '/' do
1167
- haml :index, :layout => !request.xhr?
1168
- end
1169
- ```
1170
-
1171
- ### 关联文件扩展名
1172
-
1173
- 为了将一个文件扩展名到对应的模版引擎,要使用 `Tilt.register`。
1174
- 比如,如果你喜欢使用 `tt` 作为 Textile 模版的扩展名,你可以这样做:
1175
-
1176
- ```ruby
1177
- Tilt.register :tt, Tilt[:textile]
1178
- ```
1179
-
1180
- ### 添加自定义模板引擎
1181
-
1182
- 首先,通过 Tilt 注册你自定义的引擎,然后创建一个渲染方法:
1183
-
1184
- ```ruby
1185
- Tilt.register :myat, MyAwesomeTemplateEngine
1186
-
1187
- helpers do
1188
- def myat(*args) render(:myat, *args) end
1189
- end
1190
-
1191
- get '/' do
1192
- myat :index
1193
- end
1194
- ```
1195
-
1196
- 这段代码将会渲染 `./views/index.myat` 文件。
1197
- 查看 https://github.com/rtomayko/tilt 以了解更多关于 Tilt 的信息。
1198
-
1199
- ### 自定义模板查找逻辑
1200
-
1201
- 要实现自定义的模板查找机制,你可以构建自己的 `#find_template` 方法:
1202
-
1203
- ```ruby
1204
- configure do
1205
- set :views, [ './views/a', './views/b' ]
1206
- end
1207
-
1208
- def find_template(views, name, engine, &block)
1209
- Array(views).each do |v|
1210
- super(v, name, engine, &block)
1211
- end
1212
- end
1213
- ```
1214
-
1215
- ## 过滤器
1216
-
1217
- `before` 过滤器在每个请求之前调用,调用的上下文与请求的上下文相同,并且可以修改请求和响应。
1218
- 在过滤器中设置的变量可以被路由和模板访问:
1219
-
1220
- ```ruby
1221
- before do
1222
- @note = 'Hi!'
1223
- request.path_info = '/foo/bar/baz'
1224
- end
1225
-
1226
- get '/foo/*' do
1227
- @note #=> 'Hi!'
1228
- params['splat'] #=> 'bar/baz'
1229
- end
1230
- ```
1231
-
1232
- `after` 过滤器在每个请求之后调用,调用上下文与请求的上下文相同,并且也会修改请求和响应。
1233
- 在 `before` 过滤器和路由中设置的实例变量可以被 `after` 过滤器访问:
1234
-
1235
- ```ruby
1236
- after do
1237
- puts response.status
1238
- end
1239
- ```
1240
-
1241
- 请注意:除非你显式使用 `body` 方法,而不是在路由中直接返回字符串,
1242
- 响应主体在 `after` 过滤器是不可访问的, 因为它在之后才会生成。
1243
-
1244
- 过滤器可以可选地带有范式, 只有请求路径满足该范式时才会执行:
1245
-
1246
- ```ruby
1247
- before '/protected/*' do
1248
- authenticate!
1249
- end
1250
-
1251
- after '/create/:slug' do |slug|
1252
- session['last_slug'] = slug
1253
- end
1254
- ```
1255
-
1256
- 和路由一样,过滤器也可以带有条件:
1257
-
1258
- ```ruby
1259
- before :agent => /Songbird/ do
1260
- # ...
1261
- end
1262
-
1263
- after '/blog/*', :host_name => 'example.com' do
1264
- # ...
1265
- end
1266
- ```
1267
-
1268
- ## 辅助方法
1269
-
1270
- 使用顶层的 `helpers` 方法来定义辅助方法, 以便在路由处理器和模板中使用:
1271
-
1272
- ```ruby
1273
- helpers do
1274
- def bar(name)
1275
- "#{name}bar"
1276
- end
1277
- end
1278
-
1279
- get '/:name' do
1280
- bar(params['name'])
1281
- end
1282
- ```
1283
-
1284
- 也可以在多个分散的模块中定义辅助方法:
1285
-
1286
- ```ruby
1287
- module FooUtils
1288
- def foo(name) "#{name}foo" end
1289
- end
1290
-
1291
- module BarUtils
1292
- def bar(name) "#{name}bar" end
1293
- end
1294
-
1295
- helpers FooUtils, BarUtils
1296
- ```
1297
-
1298
- 以上代码块与在应用类中包含模块等效。
1299
-
1300
- ### 使用会话
1301
-
1302
- 会话用于在请求之间保持状态。如果激活了会话,每一个用户会话都对应一个会话 hash:
1303
-
1304
- ```ruby
1305
- enable :sessions
1306
-
1307
- get '/' do
1308
- "value = " << session['value'].inspect
1309
- end
1310
-
1311
- get '/:value' do
1312
- session['value'] = params['value']
1313
- end
1314
- ```
1315
-
1316
- #### 会话加密
1317
-
1318
- 为提高安全性,cookie 中的会话数据使用`HMAC-SHA1`进行加密。会话加密的最佳实践应当是像`HMAC-SHA1`这样生成大于或等于64字节 (512 bits, 128 hex characters)的随机值。应当避免使用少于32字节(256 bits, 64 hex characters)的随机值。应当使用生成器来创建安全的密钥,而不是拍脑袋决定。
1319
-
1320
- 默认情况下,Sinatra会生成一个32字节的密钥,但随着应用程序的每次重新启动,它都会发生改变。如果有多个应用程序的实例,使用Sinatra生成密钥,每个实例将有不同的密钥,这可能不是您想要的。
1321
-
1322
- 为了更好的安全性和可用性,[建议](https://12factor.net/config)生成安全的随机密钥,并将其存储在运行应用程序的每个主机上的环境变量中,以便所有应用程序实例都将共享相同的密钥。并且应该定期更新会话密钥。下面是一些创建64比特密钥的例子:
1323
-
1324
- #### 生成密钥
1325
-
1326
- ```ruby
1327
- $ ruby -e "require 'securerandom'; puts SecureRandom.hex(64)"
1328
- 99ae8af...snip...ec0f262ac
1329
- ```
1330
-
1331
- #### 生成密钥(小贴士)
1332
-
1333
- MRI Ruby目前认为[sysrandom gem](https://github.com/cryptosphere/sysrandom)使用系统的随机数生成器要比用户态的`OpenSSL`好。
1334
-
1335
- ```ruby
1336
- $ gem install sysrandom
1337
- Building native extensions. This could take a while...
1338
- Successfully installed sysrandom-1.x
1339
- 1 gem installed
1340
-
1341
- $ ruby -e "require 'sysrandom/securerandom'; puts SecureRandom.hex(64)"
1342
- 99ae8af...snip...ec0f262ac
1343
- ```
1344
-
1345
- #### 从环境变量使用密钥
1346
-
1347
- 将Sinatra的SESSION_SECRET环境变量设置为生成的值。在主机的重新启动之间保存这个值。由于这样做的方法会因系统而异,仅供说明之用:
1348
- ```
1349
- # echo "export SESSION_SECRET=99ae8af...snip...ec0f262ac" >> ~/.bashrc
1350
- ```
1351
-
1352
- #### 应用的密钥配置
1353
-
1354
- 如果SESSION SECRET环境变量不可用,将把应用的随机密钥设置为不安全的。
1355
-
1356
- 关于[sysrandom gem](https://github.com/cryptosphere/sysrandom)的更多用法:
1357
-
1358
- ```ruby
1359
- require 'securerandom'
1360
- # -or- require 'sysrandom/securerandom'
1361
- set :session_secret, ENV.fetch('SESSION_SECRET') { SecureRandom.hex(64) }
1362
- ```
1363
-
1364
- #### 会话配置
1365
-
1366
- 如果你想进一步配置会话,可以在设置 `sessions` 时提供一个选项 hash 作为第二个参数:
1367
-
1368
- ```ruby
1369
- set :sessions, :domain => 'foo.com'
1370
- ```
1371
-
1372
- 为了在 foo.com 的子域名间共享会话数据,可以在域名前添加一个 *.*:
1373
-
1374
- ```ruby
1375
- set :sessions, :domain => '.foo.com'
1376
- ```
1377
-
1378
- #### 选择你自己的会话中间件
1379
-
1380
- 请注意 `enable :sessions` 实际将所有的数据保存在一个 cookie 中。
1381
- 这可能并不总是你想要的(cookie 中存储大量的数据会增加你的流量)。
1382
- 你可以使用任何 Rack session 中间件:要达到此目的,**不要**使用 `enable :sessions`,
1383
- 而是按照自己的需要引入想使用的中间件:
1384
-
1385
- ```ruby
1386
- enable :sessions
1387
- set :session_store, Rack::Session::Pool
1388
- ```
1389
-
1390
- 另一种选择是不要调用enable:sessions,而是像你想要的其他中间件一样加入你的中间件。
1391
-
1392
- 重要的是要注意,使用此方法时,默认情况下不会启用基于会话的保护。
1393
-
1394
- 还需要添加Rack中间件:
1395
-
1396
- ```ruby
1397
- use Rack::Session::Pool, :expire_after => 2592000
1398
- use Rack::Protection::RemoteToken
1399
- use Rack::Protection::SessionHijacking
1400
- ```
1401
- 更多[安全防护配置](https://github.com/sinatra/sinatra#configuring-attack-protection)的信息。
1402
-
1403
- ### 中断请求
1404
-
1405
- 要想在过滤器或路由中立即中断一个请求:
1406
-
1407
- ```ruby
1408
- halt
1409
- ```
1410
-
1411
- 你也可以指定中断时的状态码:
1412
-
1413
- ```ruby
1414
- halt 410
1415
- ```
1416
-
1417
- 或者响应主体:
1418
-
1419
- ```ruby
1420
- halt 'this will be the body'
1421
- ```
1422
-
1423
- 或者同时指定两者:
1424
-
1425
- ```ruby
1426
- halt 401, 'go away!'
1427
- ```
1428
-
1429
- 也可以指定响应首部:
1430
-
1431
- ```ruby
1432
- halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
1433
- ```
1434
-
1435
- 当然也可以使用模板:
1436
-
1437
- ```
1438
- halt erb(:error)
1439
- ```
1440
-
1441
- ### 传递请求
1442
-
1443
- 一个路由可以放弃对请求的处理并将处理让给下一个匹配的路由,这要通过 `pass` 实现:
1444
-
1445
- ```ruby
1446
- get '/guess/:who' do
1447
- pass unless params['who'] == 'Frank'
1448
- 'You got me!'
1449
- end
1450
-
1451
- get '/guess/*' do
1452
- 'You missed!'
1453
- end
1454
- ```
1455
-
1456
- 执行 `pass` 后,控制流从该路由代码块直接退出,并继续前进到下一个匹配的路由。
1457
- 如果没有匹配的路由,将返回 404。
1458
-
1459
- ### 触发另一个路由
1460
-
1461
- 有些时候,`pass` 并不是你想要的,你希望得到的是调用另一个路由的结果。
1462
- 使用 `call` 就可以做到这一点:
1463
-
1464
- ```ruby
1465
- get '/foo' do
1466
- status, headers, body = call env.merge("PATH_INFO" => '/bar')
1467
- [status, headers, body.map(&:upcase)]
1468
- end
1469
-
1470
- get '/bar' do
1471
- "bar"
1472
- end
1473
- ```
1474
-
1475
- 请注意在以上例子中,你只需简单地移动 `"bar"` 到一个被 `/foo` 和 `/bar` 同时使用的辅助方法中,
1476
- 就可以简化测试和增加性能。
1477
-
1478
- 如果你希望请求发送到同一个应用,而不是应用副本,应使用 `call!` 而不是 `call`。
1479
-
1480
- 如果想更多了解关于 `call` 的信息,请查看 Rack 规范。
1481
-
1482
- ### 设置响应主体、状态码和响应首部
1483
-
1484
- 推荐在路由代码块的返回值中设定状态码和响应主体。
1485
- 但是,在某些场景下你可能想在别处设置响应主体,这时你可以使用 `body` 辅助方法。
1486
- 设置之后,你可以在那以后使用该方法访问响应主体:
1487
-
1488
- ```ruby
1489
- get '/foo' do
1490
- body "bar"
1491
- end
1492
-
1493
- after do
1494
- puts body
1495
- end
1496
- ```
1497
-
1498
- 也可以传递一个代码块给 `body` 方法,
1499
- 它会被 Rack 处理器执行(这可以用来实现流式传输,参见“返回值”)。
1500
-
1501
- 与响应主体类似,你也可以设定状态码和响应首部:
1502
-
1503
- ```ruby
1504
- get '/foo' do
1505
- status 418
1506
- headers \
1507
- "Allow" => "BREW, POST, GET, PROPFIND, WHEN",
1508
- "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
1509
- body "I'm a tea pot!"
1510
- end
1511
- ```
1512
-
1513
- 正如 `body` 方法,不带参数调用 `headers` 和 `status` 方法可以访问它们的当前值。
1514
-
1515
- ### 响应的流式传输
1516
-
1517
- 有时你可能想在完全生成响应主体前返回数据。
1518
- 更极端的情况是,你希望在客户端关闭连接前一直发送数据。
1519
- 为满足这些需求,可以使用 `stream` 辅助方法而不必重新造轮子:
1520
-
1521
- ```ruby
1522
- get '/' do
1523
- stream do |out|
1524
- out << "It's gonna be legen -\n"
1525
- sleep 0.5
1526
- out << " (wait for it) \n"
1527
- sleep 1
1528
- out << "- dary!\n"
1529
- end
1530
- end
1531
- ```
1532
-
1533
- `stream` 辅助方法允许你实现流式 API 和
1534
- [服务器端发送事件](https://w3c.github.io/eventsource/),
1535
- 同时它也是实现 [WebSockets](https://en.wikipedia.org/wiki/WebSocket) 的基础。
1536
- 如果你应用的部分(不是全部)内容依赖于访问缓慢的资源,它也可以用来提高并发能力。
1537
-
1538
- 请注意流式传输,尤其是并发请求数,高度依赖于应用所使用的服务器。
1539
- 一些服务器可能根本不支持流式传输。
1540
- 如果服务器不支持,传递给 `stream` 方法的代码块执行完毕之后,响应主体会一次性地发送给客户端。
1541
- Shotgun 完全不支持流式传输。
1542
-
1543
- 如果 `:keep_open` 作为可选参数传递给 `stream` 方法,将不会在流对象上调用 `close` 方法,
1544
- 这允许你在控制流的下游某处手动关闭。该参数只对事件驱动的服务器(如 Thin 和 Rainbows)生效。
1545
- 其它服务器仍会关闭流式传输:
1546
-
1547
- ```ruby
1548
- # 长轮询
1549
-
1550
- set :server, :thin
1551
- connections = []
1552
-
1553
- get '/subscribe' do
1554
- # 在服务器端的事件中注册客户端
1555
- stream(:keep_open) do |out|
1556
- connections << out
1557
- # 清除关闭的连接
1558
- connections.reject!(&:closed?)
1559
- end
1560
- end
1561
-
1562
- post '/:message' do
1563
- connections.each do |out|
1564
- # 通知客户端有条新消息
1565
- out << params['message'] << "\n"
1566
-
1567
- # 使客户端重新连接
1568
- out.close
1569
- end
1570
-
1571
- # 确认
1572
- "message received"
1573
- end
1574
- ```
1575
-
1576
- ### 日志
1577
-
1578
- 在请求作用域下,`logger` 辅助方法会返回一个 `Logger` 类的实例:
1579
-
1580
- ```ruby
1581
- get '/' do
1582
- logger.info "loading data"
1583
- # ...
1584
- end
1585
- ```
1586
-
1587
- 该 `logger` 方法会自动参考 Rack 处理器的日志设置。
1588
- 若日志被禁用,该方法会返回一个无关痛痒的对象,所以你完全不必担心这会影响路由和过滤器。
1589
-
1590
- 注意只有 `Sinatra::Application` 默认开启了日志,若你的应用继承自 `Sinatra::Base`,
1591
- 很可能需要手动开启:
1592
-
1593
- ```ruby
1594
- class MyApp < Sinatra::Base
1595
- configure :production, :development do
1596
- enable :logging
1597
- end
1598
- end
1599
- ```
1600
-
1601
- 为避免使用任何与日志有关的中间件,需要将 `logging` 设置项设为 `nil`。
1602
- 然而,在这种情况下,`logger` 辅助方法会返回 `nil`。
1603
- 一种常见的使用场景是你想要使用自己的日志工具。
1604
- Sinatra 会使用 `env['rack.logger']` 的值作为日志工具,无论该值是什么。
1605
-
1606
- ### 媒体类型
1607
-
1608
- 使用 `send_file` 或者静态文件的时候,Sinatra 可能不会识别你的媒体类型。
1609
- 使用 `mime_type` 通过文件扩展名来注册媒体类型:
1610
-
1611
- ```ruby
1612
- mime_type :foo, 'text/foo'
1613
- ```
1614
-
1615
- 你也可以使用 `content_type` 辅助方法:
1616
-
1617
- ```ruby
1618
- get '/' do
1619
- content_type :foo
1620
- "foo foo foo"
1621
- end
1622
- ```
1623
-
1624
- ### 生成 URL
1625
-
1626
- 为了生成 URL,你应当使用 `url` 辅助方法,例如,在 Haml 中:
1627
-
1628
- ```ruby
1629
- %a{:href => url('/foo')} foo
1630
- ```
1631
-
1632
- 如果使用了反向代理和 Rack 路由,生成 URL 的时候会考虑这些因素。
1633
-
1634
- 这个方法还有一个别名 `to` (见下面的例子)。
1635
-
1636
- ### 浏览器重定向
1637
-
1638
- 你可以通过 `redirect` 辅助方法触发浏览器重定向:
1639
-
1640
- ```ruby
1641
- get '/foo' do
1642
- redirect to('/bar')
1643
- end
1644
- ```
1645
-
1646
- 其他参数的用法,与 `halt` 相同:
1647
-
1648
- ```ruby
1649
- redirect to('/bar'), 303
1650
- redirect 'http://www.google.com/', 'wrong place, buddy'
1651
- ```
1652
-
1653
- 用 `redirect back` 可以把用户重定向到原始页面:
1654
-
1655
- ```ruby
1656
- get '/foo' do
1657
- "<a href='/bar'>do something</a>"
1658
- end
1659
-
1660
- get '/bar' do
1661
- do_something
1662
- redirect back
1663
- end
1664
- ```
1665
-
1666
- 如果想传递参数给 redirect,可以用查询字符串:
1667
-
1668
- ```ruby
1669
- redirect to('/bar?sum=42')
1670
- ```
1671
-
1672
- 或者使用会话:
1673
-
1674
- ```ruby
1675
- enable :sessions
1676
-
1677
- get '/foo' do
1678
- session['secret'] = 'foo'
1679
- redirect to('/bar')
1680
- end
1681
-
1682
- get '/bar' do
1683
- session['secret']
1684
- end
1685
- ```
1686
-
1687
- ### 缓存控制
1688
-
1689
- 正确设置响应首部是合理利用 HTTP 缓存的基础。
1690
-
1691
- 可以这样设定 Cache-Control 首部字段:
1692
-
1693
- ```ruby
1694
- get '/' do
1695
- cache_control :public
1696
- "cache it!"
1697
- end
1698
- ```
1699
-
1700
- 核心提示: 应当在 `before` 过滤器中设定缓存。
1701
-
1702
- ```ruby
1703
- before do
1704
- cache_control :public, :must_revalidate, :max_age => 60
1705
- end
1706
- ```
1707
-
1708
- 如果你使用 `expires` 辅助方法设定响应的响应首部, 会自动设定 `Cache-Control` 字段:
1709
-
1710
- ```ruby
1711
- before do
1712
- expires 500, :public, :must_revalidate
1713
- end
1714
- ```
1715
-
1716
- 为了合理使用缓存,你应该考虑使用 `etag` 或 `last_modified` 方法。
1717
- 推荐在执行繁重任务*之前*使用这些辅助方法,这样一来,
1718
- 如果客户端在缓存中已经有相关内容,就会立即得到响应:
1719
-
1720
- ```ruby
1721
- get '/article/:id' do
1722
- @article = Article.find params['id']
1723
- last_modified @article.updated_at
1724
- etag @article.sha1
1725
- erb :article
1726
- end
1727
- ```
1728
-
1729
- 也可以使用 [weak ETag](https://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation):
1730
-
1731
- ```ruby
1732
- etag @article.sha1, :weak
1733
- ```
1734
-
1735
- 这些辅助方法并不会为你做任何缓存,而是将必要的信息发送给你的缓存。
1736
- 如果你正在寻找快捷的反向代理缓存方案,可以尝试
1737
- [rack-cache](https://github.com/rtomayko/rack-cache):
1738
-
1739
- ```ruby
1740
- require "rack/cache"
1741
- require "sinatra"
1742
-
1743
- use Rack::Cache
1744
-
1745
- get '/' do
1746
- cache_control :public, :max_age => 36000
1747
- sleep 5
1748
- "hello"
1749
- end
1750
- ```
1751
-
1752
- 使用 `:statis_cache_control` 设置(见下文)为静态文件添加 `Cache-Control` 首部字段。
1753
-
1754
- 根据 RFC 2616,如果 If-Match 或 If-None-Match 首部设置为 `*`,根据所请求的资源存在与否,
1755
- 你的应用应当有不同的行为。
1756
- Sinatra 假设安全请求(如 GET)和幂等性请求(如 PUT)所访问的资源是已经存在的,
1757
- 而其它请求(如 POST 请求)所访问的资源是新资源。
1758
- 你可以通过传入 `:new_resource` 选项改变这一行为。
1759
-
1760
- ```ruby
1761
- get '/create' do
1762
- etag '', :new_resource => true
1763
- Article.create
1764
- erb :new_article
1765
- end
1766
- ```
1767
-
1768
- 如果你仍想使用 weak ETag,可以传入一个 `:kind` 选项:
1769
-
1770
- ```ruby
1771
- etag '', :new_resource => true, :kind => :weak
1772
- ```
1773
-
1774
- ### 发送文件
1775
-
1776
- 为了将文件的内容作为响应返回,可以使用 `send_file` 辅助方法:
1777
-
1778
- ```ruby
1779
- get '/' do
1780
- send_file 'foo.png'
1781
- end
1782
- ```
1783
-
1784
- 该辅助方法接受一些选项:
1785
-
1786
- ```ruby
1787
- send_file 'foo.png', :type => :jpg
1788
- ```
1789
-
1790
- 可用的选项有:
1791
-
1792
- <dl>
1793
- <dt>filename</dt>
1794
- <dd>响应中使用的文件名,默认是真实的文件名。</dd>
1795
-
1796
- <dt>last_modified</dt>
1797
- <dd>Last-Modified 响应首部的值,默认是文件的 mtime (修改时间)。</dd>
1798
-
1799
- <dt>type</dt>
1800
- <dd>Content-Type 响应首部的值,如果未指定,会根据文件扩展名猜测。</dd>
1801
-
1802
- <dt>disposition</dt>
1803
- <dd>
1804
- Content-Disposition 响应首部的值,
1805
- 可选的值有: <tt>nil</tt> (默认)、<tt>:attachment</tt> 和
1806
- <tt>:inline</tt>
1807
- </dd>
1808
-
1809
- <dt>length</dt>
1810
- <dd>Content-Length 响应首部的值,默认是文件的大小。</dd>
1811
-
1812
- <dt>status</dt>
1813
- <dd>
1814
- 将要返回的状态码。当以一个静态文件作为错误页面时,这很有用。
1815
-
1816
- 如果 Rack 处理器支持的话,Ruby 进程也能使用除 streaming 以外的方法。
1817
- 如果你使用这个辅助方法, Sinatra会自动处理 range 请求。
1818
- </dd>
1819
- </dl>
1820
-
1821
- ### 访问请求对象
1822
-
1823
- 传入的请求对象可以在请求层(过滤器、路由、错误处理器内部)通过 `request` 方法访问:
1824
-
1825
- ```ruby
1826
- # 在 http://example.com/example 上运行的应用
1827
- get '/foo' do
1828
- t = %w[text/css text/html application/javascript]
1829
- request.accept # ['text/html', '*/*']
1830
- request.accept? 'text/xml' # true
1831
- request.preferred_type(t) # 'text/html'
1832
- request.body # 客户端设定的请求主体(见下文)
1833
- request.scheme # "http"
1834
- request.script_name # "/example"
1835
- request.path_info # "/foo"
1836
- request.port # 80
1837
- request.request_method # "GET"
1838
- request.query_string # ""
1839
- request.content_length # request.body 的长度
1840
- request.media_type # request.body 的媒体类型
1841
- request.host # "example.com"
1842
- request.get? # true (其它动词也具有类似方法)
1843
- request.form_data? # false
1844
- request["some_param"] # some_param 参数的值。[] 是访问 params hash 的捷径
1845
- request.referrer # 客户端的 referrer 或者 '/'
1846
- request.user_agent # 用户代理 (:agent 条件使用该值)
1847
- request.cookies # 浏览器 cookies 哈希
1848
- request.xhr? # 这是否是 ajax 请求?
1849
- request.url # "http://example.com/example/foo"
1850
- request.path # "/example/foo"
1851
- request.ip # 客户端 IP 地址
1852
- request.secure? # false (如果是 ssl 则为 true)
1853
- request.forwarded? # true (如果是运行在反向代理之后)
1854
- request.env # Rack 中使用的未处理的 env hash
1855
- end
1856
- ```
1857
-
1858
- 一些选项,例如 `script_name` 或者 `path_info` 也是可写的:
1859
-
1860
- ```ruby
1861
- before { request.path_info = "/" }
1862
-
1863
- get "/" do
1864
- "all requests end up here"
1865
- end
1866
- ```
1867
-
1868
- `request.body` 是一个 IO 或者 StringIO 对象:
1869
-
1870
- ```ruby
1871
- post "/api" do
1872
- request.body.rewind # 如果已经有人读了它
1873
- data = JSON.parse request.body.read
1874
- "Hello #{data['name']}!"
1875
- end
1876
- ```
1877
-
1878
- ### 附件
1879
-
1880
- 你可以使用 `attachment` 辅助方法来告诉浏览器响应应当被写入磁盘而不是在浏览器中显示。
1881
-
1882
- ```ruby
1883
- get '/' do
1884
- attachment
1885
- "store it!"
1886
- end
1887
- ```
1888
-
1889
- 你也可以传递给该方法一个文件名:
1890
-
1891
- ```ruby
1892
- get '/' do
1893
- attachment "info.txt"
1894
- "store it!"
1895
- end
1896
- ```
1897
-
1898
- ### 处理日期和时间
1899
-
1900
- Sinatra 提供了一个 `time_for` 辅助方法,其目的是根据给定的值生成 Time 对象。
1901
- 该方法也能够转换 `DateTime`、`Date` 和类似的类:
1902
-
1903
- ```ruby
1904
- get '/' do
1905
- pass if Time.now > time_for('Dec 23, 2012')
1906
- "still time"
1907
- end
1908
- ```
1909
-
1910
- `expires`、`last_modified` 和类似方法都在内部使用了该方法。
1911
- 因此,通过在应用中重写 `time_for` 方法,你可以轻松地扩展这些方法的行为:
1912
-
1913
- ```ruby
1914
- helpers do
1915
- def time_for(value)
1916
- case value
1917
- when :yesterday then Time.now - 24*60*60
1918
- when :tomorrow then Time.now + 24*60*60
1919
- else super
1920
- end
1921
- end
1922
- end
1923
-
1924
- get '/' do
1925
- last_modified :yesterday
1926
- expires :tomorrow
1927
- "hello"
1928
- end
1929
- ```
1930
-
1931
- ### 查找模板文件
1932
-
1933
- `find_template` 辅助方法用于在渲染时查找模板文件:
1934
-
1935
- ```ruby
1936
- find_template settings.views, 'foo', Tilt[:haml] do |file|
1937
- puts "could be #{file}"
1938
- end
1939
- ```
1940
-
1941
- 这其实并不是很有用,除非你需要重载这个方法来实现你自己的查找机制。
1942
- 比如,如果你想使用不只一个视图目录:
1943
-
1944
- ```ruby
1945
- set :views, ['views', 'templates']
1946
-
1947
- helpers do
1948
- def find_template(views, name, engine, &block)
1949
- Array(views).each { |v| super(v, name, engine, &block) }
1950
- end
1951
- end
1952
- ```
1953
-
1954
- 另一个例子是对不同的引擎使用不同的目录:
1955
-
1956
- ```ruby
1957
- set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views'
1958
-
1959
- helpers do
1960
- def find_template(views, name, engine, &block)
1961
- _, folder = views.detect { |k,v| engine == Tilt[k] }
1962
- folder ||= views[:default]
1963
- super(folder, name, engine, &block)
1964
- end
1965
- end
1966
- ```
1967
-
1968
- 你可以很容易地封装成一个扩展,然后与他人分享!
1969
-
1970
- 请注意 `find_template` 并不会检查文件是否存在,而是为任何可能的路径调用传入的代码块。
1971
- 这并不会导致性能问题,因为 `render` 会在找到文件的时候马上使用 `break`。
1972
- 同样的,模板的路径(和内容)会在 development 以外的模式下被缓存。
1973
- 你应该时刻提醒自己这一点, 如果你真的想写一个非常疯狂的方法的话。
1974
-
1975
- ## 配置
1976
-
1977
- 在启动时运行一次,在任何环境下都是如此:
1978
-
1979
- ```ruby
1980
- configure do
1981
- # 设置一个选项
1982
- set :option, 'value'
1983
-
1984
- # 设置多个选项
1985
- set :a => 1, :b => 2
1986
-
1987
- # 等同于 `set :option, true`
1988
- enable :option
1989
-
1990
- # 等同于 `set :option, false`
1991
- disable :option
1992
-
1993
- # 也可以用代码块做动态设置
1994
- set(:css_dir) { File.join(views, 'css') }
1995
- end
1996
- ```
1997
-
1998
- 只有当环境 (`APP_ENV` 环境变量) 被设定为 `:production` 时才运行:
1999
-
2000
- ```ruby
2001
- configure :production do
2002
- ...
2003
- end
2004
- ```
2005
-
2006
- 当环境被设定为 `:production` 或者 `:test` 时运行:
2007
-
2008
- ```ruby
2009
- configure :production, :test do
2010
- ...
2011
- end
2012
- ```
2013
-
2014
- 你可以用 `settings` 访问这些配置项:
2015
-
2016
- ```ruby
2017
- configure do
2018
- set :foo, 'bar'
2019
- end
2020
-
2021
- get '/' do
2022
- settings.foo? # => true
2023
- settings.foo # => 'bar'
2024
- ...
2025
- end
2026
- ```
2027
-
2028
- ### 配置攻击防护
2029
-
2030
- Sinatra 使用 [Rack::Protection](https://github.com/sinatra/sinatra/tree/master/rack-protection#readme)
2031
- 来抵御常见的攻击。你可以轻易地禁用该行为(但这会大大增加应用被攻击的概率)。
2032
-
2033
- ```ruby
2034
- disable :protection
2035
- ```
2036
-
2037
- 为了绕过某单层防护,可以设置 `protection` 为一个选项 hash:
2038
-
2039
- ```ruby
2040
- set :protection, :except => :path_traversal
2041
- ```
2042
-
2043
- 你可以传入一个数组,以禁用一系列防护措施:
2044
-
2045
- ```ruby
2046
- set :protection, :except => [:path_traversal, :session_hijacking]
2047
- ```
2048
-
2049
- 默认地,如果 `:sessions` 是启用的,Sinatra 只会使用基于会话的防护措施。
2050
- 当然,有时你可能想根据自己的需要设置会话。
2051
- 在这种情况下,你可以通过传入 `:session` 选项来开启基于会话的防护。
2052
-
2053
- ```ruby
2054
- use Rack::Session::Pool
2055
- set :protection, :session => true
2056
- ```
2057
-
2058
- ### 可选的设置
2059
-
2060
- <dl>
2061
- <dt>absolute_redirects</dt>
2062
- <dd>
2063
- 如果被禁用,Sinatra 会允许使用相对路径重定向。
2064
- 然而这样的话,Sinatra 就不再遵守 RFC 2616 (HTTP 1.1), 该协议只允许绝对路径重定向。
2065
- </dd>
2066
- <dd>
2067
- 如果你的应用运行在一个未恰当设置的反向代理之后,你需要启用这个选项。
2068
- 注意 <tt>url</tt> 辅助方法仍然会生成绝对 URL,除非你传入<tt>false</tt> 作为第二参数。
2069
- </dd>
2070
- <dd>默认禁用。</dd>
2071
-
2072
- <dt>add_charset</dt>
2073
- <dd>
2074
- 设置 <tt>content_type</tt> 辅助方法会自动为媒体类型加上字符集信息。
2075
- 你应该添加而不是覆盖这个选项:
2076
- <tt>settings.add_charset << "application/foobar"</tt>
2077
- </dd>
2078
-
2079
- <dt>app_file</dt>
2080
- <dd>
2081
- 主应用文件的路径,用来检测项目的根路径, views 和 public 文件夹和内联模板。
2082
- </dd>
2083
-
2084
- <dt>bind</dt>
2085
- <dd>
2086
- 绑定的 IP 地址 (默认: <tt>0.0.0.0</tt>,开发环境下为 <tt>localhost</tt>)。
2087
- 仅对于内置的服务器有用。
2088
- </dd>
2089
-
2090
- <dt>default_encoding</dt>
2091
- <dd>默认编码 (默认为 <tt>"utf-8"</tt>)。</dd>
2092
-
2093
- <dt>dump_errors</dt>
2094
- <dd>在日志中显示错误。</dd>
2095
-
2096
- <dt>environment</dt>
2097
- <dd>
2098
- 当前环境,默认是 <tt>ENV['APP_ENV']</tt>,
2099
- 或者 <tt>"development"</tt> (如果 ENV['APP_ENV'] 不可用)。
2100
- </dd>
2101
-
2102
- <dt>logging</dt>
2103
- <dd>使用 logger。</dd>
2104
-
2105
- <dt>lock</dt>
2106
- <dd>对每一个请求放置一个锁,只使用进程并发处理请求。</dd>
2107
- <dd>如果你的应用不是线程安全则需启动。默认禁用。</dd>
2108
-
2109
- <dt>method_override</dt>
2110
- <dd>
2111
- 使用 <tt>_method</tt> 魔法,以允许在不支持的浏览器中在使用 put/delete 方法提交表单。
2112
- </dd>
2113
-
2114
- <dt>port</dt>
2115
- <dd>监听的端口号。只对内置服务器有用。</dd>
2116
-
2117
- <dt>prefixed_redirects</dt>
2118
- <dd>
2119
- 如果没有使用绝对路径,是否添加 <tt>request.script_name</tt> 到重定向请求。
2120
- 如果添加,<tt>redirect '/foo'</tt> 会和 <tt>redirect to('/foo')</tt> 相同。
2121
- 默认禁用。
2122
- </dd>
2123
-
2124
- <dt>protection</dt>
2125
- <dd>是否启用网络攻击防护。参见上面的保护部分</dd>
2126
-
2127
- <dt>public_dir</dt>
2128
- <dd>public_folder 的别名。见下文。</dd>
2129
-
2130
- <dt>public_folder</dt>
2131
- <dd>
2132
- public 文件存放的路径。只有启用了静态文件服务(见下文的 <tt>static</tt>)才会使用。
2133
- 如果未设置,默认从 <tt>app_file</tt> 推断。
2134
- </dd>
2135
-
2136
- <dt>reload_templates</dt>
2137
- <dd>
2138
- 是否每个请求都重新载入模板。在开发模式下开启。
2139
- </dd>
2140
-
2141
- <dt>root</dt>
2142
- <dd>到项目根目录的路径。默认从 <tt>app_file</tt> 设置推断。</dd>
2143
-
2144
- <dt>raise_errors</dt>
2145
- <dd>
2146
- 抛出异常(会停止应用)。
2147
- 当 <tt>environment</tt> 设置为 <tt>"test"</tt> 时会默认开启,其它环境下默认禁用。
2148
- </dd>
2149
-
2150
- <dt>run</dt>
2151
- <dd>如果启用,Sinatra 会负责 web 服务器的启动。若使用 rackup 或其他方式则不要启用。</dd>
2152
-
2153
- <dt>running</dt>
2154
- <dd>内置的服务器在运行吗? 不要修改这个设置!</dd>
2155
-
2156
- <dt>server</dt>
2157
- <dd>服务器,或用于内置服务器的服务器列表。顺序表明了优先级,默认顺序依赖 Ruby 实现。</dd>
2158
-
2159
- <dt>sessions</dt>
2160
- <dd>
2161
- 使用 <tt>Rack::Session::Cookie</tt>,启用基于 cookie 的会话。
2162
- 查看“使用会话”部分以获得更多信息。
2163
- </dd>
2164
-
2165
- <dt>show_exceptions</dt>
2166
- <dd>
2167
- 当有异常发生时,在浏览器中显示一个 stack trace。
2168
- 当 <tt>environment</tt> 设置为 <tt>"development"</tt> 时,默认启用,
2169
- 否则默认禁用。
2170
- </dd>
2171
- <dd>
2172
- 也可以设置为 <tt>:after_handler</tt>,
2173
- 这会在浏览器中显示 stack trace 之前触发应用级别的错误处理。
2174
- </dd>
2175
-
2176
- <dt>static</dt>
2177
- <dd>决定 Sinatra 是否服务静态文件。</dd>
2178
- <dd>当服务器能够自行服务静态文件时,会禁用。</dd>
2179
- <dd>禁用会增强性能。</dd>
2180
- <dd>在经典风格中默认启用,在模块化应用中默认禁用。</dd>
2181
-
2182
- <dt>static_cache_control</dt>
2183
- <dd>
2184
- 当 Sinatra 提供静态文件服务时,设置此选项为响应添加 <tt>Cache-Control</tt> 首部。
2185
- 使用 <tt>cache_control</tt> 辅助方法。默认禁用。
2186
- </dd>
2187
- <dd>
2188
- 当设置多个值时使用数组:
2189
- <tt>set :static_cache_control, [:public, :max_age => 300]</tt>
2190
- </dd>
2191
-
2192
- <dt>threaded</dt>
2193
- <dd>
2194
- 若设置为 <tt>true</tt>,会告诉 Thin 使用 <tt>EventMachine.defer</tt> 处理请求。
2195
- </dd>
2196
-
2197
- <dt>traps</dt>
2198
- <dd>Sinatra 是否应该处理系统信号。</dd>
2199
-
2200
- <dt>views</dt>
2201
- <dd>views 文件夹的路径。若未设置则会根据 <tt>app_file</tt> 推断。</dd>
2202
-
2203
- <dt>x_cascade</dt>
2204
- <dd>若没有路由匹配,是否设置 X-Cascade 首部。默认为 <tt>true</tt>。</dd>
2205
- </dl>
2206
-
2207
- ## 环境
2208
-
2209
- Sinatra 中有三种预先定义的环境:"development"、"production" 和 "test"。
2210
- 环境可以通过 `APP_ENV` 环境变量设置。默认值为 "development"。
2211
- 在开发环境下,每次请求都会重新加载所有模板,
2212
- 特殊的 `not_found` 和 `error` 错误处理器会在浏览器中显示 stack trace。
2213
- 在测试和生产环境下,模板默认会缓存。
2214
-
2215
- 在不同的环境下运行,设置 `APP_ENV` 环境变量:
2216
-
2217
- ```shell
2218
- APP_ENV=production ruby my_app.rb
2219
- ```
2220
-
2221
- 可以使用预定义的三种方法: `development?`、`test?` 和 `production?` 来检查当前环境:
2222
-
2223
- ```ruby
2224
- get '/' do
2225
- if settings.development?
2226
- "development!"
2227
- else
2228
- "not development"
2229
- end
2230
- end
2231
- ```
2232
-
2233
- ## 错误处理
2234
-
2235
- 错误处理器在与路由和 before 过滤器相同的上下文中运行,
2236
- 这意味着你可以使用许多好东西,比如 `haml`, `erb`, `halt`,等等。
2237
-
2238
- ### 未找到
2239
-
2240
- 当一个 `Sinatra::NotFound` 错误被抛出时,或者当响应的状态码是 404 时,
2241
- 会调用 `not_found` 处理器:
2242
-
2243
- ```ruby
2244
- not_found do
2245
- 'This is nowhere to be found.'
2246
- end
2247
- ```
2248
-
2249
- ### 错误
2250
-
2251
- 在任何路由代码块或过滤器抛出异常时,会调用 `error` 处理器。
2252
- 但注意在开发环境下只有将 show exceptions 项设置为 `:after_handler` 时,才会生效。
2253
-
2254
- ```ruby
2255
- set :show_exceptions, :after_handler
2256
- ```
2257
-
2258
- 可以用 Rack 变量 `sinatra.error` 访问异常对象:
2259
-
2260
- ```ruby
2261
- error do
2262
- 'Sorry there was a nasty error - ' + env['sinatra.error'].message
2263
- end
2264
- ```
2265
-
2266
- 自定义错误:
2267
-
2268
- ```ruby
2269
- error MyCustomError do
2270
- 'So what happened was...' + env['sinatra.error'].message
2271
- end
2272
- ```
2273
-
2274
- 当下面的代码执行时:
2275
-
2276
- ```ruby
2277
- get '/' do
2278
- raise MyCustomError, 'something bad'
2279
- end
2280
- ```
2281
-
2282
- 你会得到错误信息:
2283
-
2284
- ```
2285
- So what happened was... something bad
2286
- ```
2287
-
2288
- 或者,你也可以为状态码设置错误处理器:
2289
-
2290
- ```ruby
2291
- error 403 do
2292
- 'Access forbidden'
2293
- end
2294
-
2295
- get '/secret' do
2296
- 403
2297
- end
2298
- ```
2299
-
2300
- 或者为某个范围内的状态码统一设置错误处理器:
2301
-
2302
- ```ruby
2303
- error 400..510 do
2304
- 'Boom'
2305
- end
2306
- ```
2307
-
2308
- 在开发环境下,Sinatra会使用特殊的 `not_found` 和 `error` 处理器,
2309
- 以便在浏览器中显示美观的 stack traces 和额外的调试信息。
2310
-
2311
- ## Rack 中间件
2312
-
2313
- Sinatra 依赖 [Rack](http://rack.github.io/), 一个面向 Ruby 网络框架的最小化标准接口。
2314
- Rack 最有趣的功能之一是支持“中间件”——位于服务器和你的应用之间的组件,
2315
- 它们监控或操作 HTTP 请求/响应以提供多种常用功能。
2316
-
2317
- Sinatra 通过顶层的 `use` 方法,让建立 Rack 中间件管道异常简单:
2318
-
2319
- ```ruby
2320
- require 'sinatra'
2321
- require 'my_custom_middleware'
2322
-
2323
- use Rack::Lint
2324
- use MyCustomMiddleware
2325
-
2326
- get '/hello' do
2327
- 'Hello World'
2328
- end
2329
- ```
2330
-
2331
- `use` 的语义和在 [Rack::Builder](http://www.rubydoc.info/github/rack/rack/master/Rack/Builder)
2332
- DSL (在 rackup 文件中最频繁使用)中定义的完全一样。例如,`use` 方法接受
2333
- 多个/可变参数,以及代码块:
2334
-
2335
- ```ruby
2336
- use Rack::Auth::Basic do |username, password|
2337
- username == 'admin' && password == 'secret'
2338
- end
2339
- ```
2340
-
2341
- Rack 拥有有多种标准中间件,用于日志、调试、URL 路由、认证和会话处理。
2342
- 根据配置,Sinatra 可以自动使用这里面的许多组件,
2343
- 所以你一般不需要显式地 `use` 它们。
2344
-
2345
- 你可以在 [rack](https://github.com/rack/rack/tree/master/lib/rack)、
2346
- [rack-contrib](https://github.com/rack/rack-contrib#readm) 或
2347
- [Rack wiki](https://github.com/rack/rack/wiki/List-of-Middleware)
2348
- 中找到有用的中间件。
2349
-
2350
- ## 测试
2351
-
2352
- 可以使用任何基于 Rack 的测试程序库或者框架来编写Sinatra的测试。
2353
- 推荐使用 [Rack::Test](http://www.rubydoc.info/github/brynary/rack-test/master/frames):
2354
-
2355
- ```ruby
2356
- require 'my_sinatra_app'
2357
- require 'minitest/autorun'
2358
- require 'rack/test'
2359
-
2360
- class MyAppTest < Minitest::Test
2361
- include Rack::Test::Methods
2362
-
2363
- def app
2364
- Sinatra::Application
2365
- end
2366
-
2367
- def test_my_default
2368
- get '/'
2369
- assert_equal 'Hello World!', last_response.body
2370
- end
2371
-
2372
- def test_with_params
2373
- get '/meet', :name => 'Frank'
2374
- assert_equal 'Hello Frank!', last_response.body
2375
- end
2376
-
2377
- def test_with_rack_env
2378
- get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
2379
- assert_equal "You're using Songbird!", last_response.body
2380
- end
2381
- end
2382
- ```
2383
-
2384
- 注意:如果你使用 Sinatra 的模块化风格,应该用你应用的类名替代 `Sinatra::Application`。
2385
-
2386
- ## Sinatra::Base - 中间件、库和模块化应用
2387
-
2388
- 在顶层定义你的应用很适合微型项目,
2389
- 但是在构建可复用的组件(如 Rack 中间件、Rails metal、带服务器组件的库或 Sinatra 扩展)时,
2390
- 却有相当大的缺陷。
2391
- 顶层 DSL 认为你采用的是微型应用风格的配置 (例如:唯一应用文件、
2392
- `./public` 和 `./views` 目录、日志、异常细节页面等)。
2393
- 如果你的项目不采用微型应用风格,应该使用 `Sinatra::Base`:
2394
-
2395
- ```ruby
2396
- require 'sinatra/base'
2397
-
2398
- class MyApp < Sinatra::Base
2399
- set :sessions, true
2400
- set :foo, 'bar'
2401
-
2402
- get '/' do
2403
- 'Hello world!'
2404
- end
2405
- end
2406
- ```
2407
-
2408
- Sinatra::Base 的子类可以使用的方法实际上就是顶层 DSL 中可以使用的方法。
2409
- 大部分顶层应用可以通过两方面的改变转换为 Sinatra::Base 组件:
2410
-
2411
- * 你的文件应当引入 `sinatra/base` 而不是 `sinatra`;
2412
- 否则,Sinatra 的所有 DSL 方法将会被导入主命名空间。
2413
-
2414
- * 把应用的路由、错误处理器、过滤器和选项放在一个 Sinatra::Base 的子类中。
2415
-
2416
- `Sinatra::Base` 是一个白板。大部分选项(包括内置的服务器)默认是禁用的。
2417
- 可以参考[配置](http://www.sinatrarb.com/configuration.html)
2418
- 以查看可用选项的具体细节和它们的行为。如果你想让你的应用更像顶层定义的应用(即经典风格),
2419
- 你可以继承 `Sinatra::Applicaiton`。
2420
-
2421
- ```ruby
2422
- require 'sinatra/base'
2423
-
2424
- class MyApp < Sinatra::Application
2425
- get '/' do
2426
- 'Hello world!'
2427
- end
2428
- end
2429
- ```
2430
-
2431
- ### 模块化风格 vs. 经典风格
2432
-
2433
- 与通常的认识相反,经典风格并没有任何错误。
2434
- 如果它适合你的应用,你不需要切换到模块化风格。
2435
-
2436
- 与模块化风格相比,经典风格的主要缺点在于,每个 Ruby 进程只能有一个 Sinatra 应用。
2437
- 如果你计划使用多个 Sinatra 应用,应该切换到模块化风格。
2438
- 你也完全可以混用模块化风格和经典风格。
2439
-
2440
- 如果从一种风格转换到另一种,你需要注意默认设置中的一些细微差别:
2441
-
2442
- <table>
2443
- <tr>
2444
- <th>设置</th>
2445
- <th>经典风格</th>
2446
- <th>模块化风格</th>
2447
- <th>模块化风格</th>
2448
- </tr>
2449
-
2450
- <tr>
2451
- <td>app_file</td>
2452
- <td>加载 sinatra 的文件</td>
2453
- <td>继承 Sinatra::Base 的文件</td>
2454
- <td>继承 Sinatra::Application 的文件</td>
2455
- </tr>
2456
-
2457
- <tr>
2458
- <td>run</td>
2459
- <td>$0 == app_file</td>
2460
- <td>false</td>
2461
- <td>false</td>
2462
- </tr>
2463
-
2464
- <tr>
2465
- <td>logging</td>
2466
- <td>true</td>
2467
- <td>false</td>
2468
- <td>true</td>
2469
- </tr>
2470
-
2471
- <tr>
2472
- <td>method_override</td>
2473
- <td>true</td>
2474
- <td>false</td>
2475
- <td>true</td>
2476
- </tr>
2477
-
2478
- <tr>
2479
- <td>inline_templates</td>
2480
- <td>true</td>
2481
- <td>false</td>
2482
- <td>true</td>
2483
- </tr>
2484
-
2485
- <tr>
2486
- <td>static</td>
2487
- <td>true</td>
2488
- <td>File.exist?(public_folder)</td>
2489
- <td>true</td>
2490
- </tr>
2491
- </table>
2492
-
2493
- ### 运行一个模块化应用
2494
-
2495
- 模块化应用的启动有两种常见方式,其中之一是使用 `run!` 方法主动启动:
2496
-
2497
- ```ruby
2498
- # my_app.rb
2499
- require 'sinatra/base'
2500
-
2501
- class MyApp < Sinatra::Base
2502
- # ... 这里是应用代码 ...
2503
-
2504
- # 如果直接执行该文件,那么启动服务器
2505
- run! if app_file == $0
2506
- end
2507
- ```
2508
-
2509
- 执行该文件就会启动服务器:
2510
-
2511
- ```shell
2512
- ruby my_app.rb
2513
- ```
2514
-
2515
- 另一种方式是使用 `config.ru` 文件,这种方式允许你使用任何 Rack 处理器:
2516
-
2517
- ```ruby
2518
- # config.ru (用 rackup 启动)
2519
- require './my_app'
2520
- run MyApp
2521
- ```
2522
-
2523
- 运行:
2524
-
2525
- ```shell
2526
- rackup -p 4567
2527
- ```
2528
-
2529
- ### 使用 config.ru 运行经典风格的应用
2530
-
2531
- 编写你的应用:
2532
-
2533
- ```ruby
2534
- # app.rb
2535
- require 'sinatra'
2536
-
2537
- get '/' do
2538
- 'Hello world!'
2539
- end
2540
- ```
2541
-
2542
- 添加相应的 `config.ru`:
2543
-
2544
- ```ruby
2545
- require './app'
2546
- run Sinatra::Application
2547
- ```
2548
-
2549
- ### 何时使用 config.ru?
2550
-
2551
- 下列情况,推荐使用 `config.ru`:
2552
-
2553
- * 部署时使用不同的 Rack 处理器 (Passenger、Unicorn、Heroku 等)。
2554
- * 使用多个 `Sinatra::Base` 的子类。
2555
- * 把 Sinatra 当作中间件使用,而非端点。
2556
-
2557
- **你不必仅仅因为想使用模块化风格而切换到 `config.ru`,同样的,
2558
- 你也不必仅仅因为要运行 `config.ru` 而切换到模块化风格。**
2559
-
2560
- ### 把 Sinatra 当作中间件使用
2561
-
2562
- Sinatra 可以使用其它 Rack 中间件,
2563
- 反过来,任何 Sinatra 应用程序自身都可以被当作中间件,添加到任何 Rack 端点前面。
2564
- 此端点可以是任何 Sinatra 应用,或任何基于 Rack 的应用程序 (Rails/Ramaze/Camping/...):
2565
-
2566
- ```ruby
2567
- require 'sinatra/base'
2568
-
2569
- class LoginScreen < Sinatra::Base
2570
- enable :sessions
2571
-
2572
- get('/login') { haml :login }
2573
-
2574
- post('/login') do
2575
- if params['name'] == 'admin' && params['password'] == 'admin'
2576
- session['user_name'] = params['name']
2577
- else
2578
- redirect '/login'
2579
- end
2580
- end
2581
- end
2582
-
2583
- class MyApp < Sinatra::Base
2584
- # 中间件的执行发生在 before 过滤器之前
2585
- use LoginScreen
2586
-
2587
- before do
2588
- unless session['user_name']
2589
- halt "Access denied, please <a href='/login'>login</a>."
2590
- end
2591
- end
2592
-
2593
- get('/') { "Hello #{session['user_name']}." }
2594
- end
2595
- ```
2596
-
2597
- ### 创建动态应用
2598
-
2599
- 有时你希望在运行时创建新应用,而不必把应用预先赋值给常量。这时可以使用 `Sinatra.new`:
2600
-
2601
- ```ruby
2602
- require 'sinatra/base'
2603
- my_app = Sinatra.new { get('/') { "hi" } }
2604
- my_app.run!
2605
- ```
2606
-
2607
- `Sinatra.new` 接受一个可选的参数,表示要继承的应用:
2608
-
2609
- ```ruby
2610
- # config.ru (用 rackup 启动)
2611
- require 'sinatra/base'
2612
-
2613
- controller = Sinatra.new do
2614
- enable :logging
2615
- helpers MyHelpers
2616
- end
2617
-
2618
- map('/a') do
2619
- run Sinatra.new(controller) { get('/') { 'a' } }
2620
- end
2621
-
2622
- map('/b') do
2623
- run Sinatra.new(controller) { get('/') { 'b' } }
2624
- end
2625
- ```
2626
-
2627
- 当你测试 Sinatra 扩展或在自己的类库中使用 Sinatra 时,这非常有用。
2628
-
2629
- 这也让把 Sinatra 当作中间件使用变得极其容易:
2630
-
2631
- ```ruby
2632
- require 'sinatra/base'
2633
-
2634
- use Sinatra do
2635
- get('/') { ... }
2636
- end
2637
-
2638
- run RailsProject::Application
2639
- ```
2640
-
2641
- ## 作用域和绑定
2642
-
2643
- 当前作用域决定了可以使用的方法和变量。
2644
-
2645
- ### 应用/类作用域
2646
-
2647
- 每个 Sinatra 应用都对应 `Sinatra::Base` 类的一个子类。
2648
- 如果你在使用顶层 DSL (`require 'sinatra'`),那么这个类就是 `Sinatra::Application`,
2649
- 否则该类是你显式创建的子类。
2650
- 在类层面,你可以使用 `get` 或 `before` 这样的方法,
2651
- 但不能访问 `request` 或 `session` 对象, 因为对于所有的请求,只有单一的应用类。
2652
-
2653
- 通过 `set` 创建的选项是类方法:
2654
-
2655
- ```ruby
2656
- class MyApp < Sinatra::Base
2657
- # 嘿,我在应用作用域!
2658
- set :foo, 42
2659
- foo # => 42
2660
-
2661
- get '/foo' do
2662
- # 嘿,我已经不在应用作用域了!
2663
- end
2664
- end
2665
- ```
2666
-
2667
- 下列位置绑定的是应用作用域:
2668
-
2669
- * 应用类内部
2670
- * 通过扩展定义的方法内部
2671
- * 传递给 `helpers` 方法的代码块内部
2672
- * 作为 `set` 值的 procs/blocks 内部
2673
- * 传递给 `Sinatra.new` 的代码块内部
2674
-
2675
- 你可以这样访问变量域对象(应用类):
2676
- * 通过传递给 configure 代码块的对象 (`configure { |c| ... }`)
2677
- * 在请求作用域中使用 `settings`
2678
-
2679
- ### 请求/实例作用域
2680
-
2681
- 对于每个请求,Sinatra 会创建应用类的一个新实例。所有的处理器代码块都在该实例对象的作用域中运行。
2682
- 在该作用域中, 你可以访问 `request` 和 `session` 对象,
2683
- 或调用渲染方法(如 `erb`、`haml`)。你可以在请求作用域中通过 `settings` 辅助方法
2684
- 访问应用作用域:
2685
-
2686
- ```ruby
2687
- class MyApp < Sinatra::Base
2688
- # 嘿,我在应用作用域!
2689
- get '/define_route/:name' do
2690
- # '/define_route/:name' 的请求作用域
2691
- @value = 42
2692
-
2693
- settings.get("/#{params['name']}") do
2694
- # "/#{params['name']}" 的请求作用域
2695
- @value # => nil (并不是同一个请求)
2696
- end
2697
-
2698
- "Route defined!"
2699
- end
2700
- end
2701
- ```
2702
-
2703
- 以下位置绑定的是请求作用域:
2704
-
2705
- * get、head、post、put、delete、options、patch、link 和 unlink 代码块内部
2706
- * before 和 after 过滤器内部
2707
- * 辅助方法内部
2708
- * 模板/视图内部
2709
-
2710
- ### 代理作用域
2711
-
2712
- 代理作用域只是把方法转送到类作用域。
2713
- 然而,它与类作用域的行为并不完全相同, 因为你并不能在代理作用域获得类的绑定。
2714
- 只有显式地标记为供代理使用的方法才是可用的,
2715
- 而且你不能和类作用域共享变量/状态。(解释:你有了一个不同的 `self`)。
2716
- 你可以通过调用 `Sinatra::Delegator.delegate :method_name` 显式地添加方法代理。
2717
-
2718
- 以下位置绑定的是代理变量域:
2719
- * 顶层绑定,如果你执行了 `require "sinatra"`
2720
- * 扩展了 `Sinatra::Delegator` 这一 mixin 的对象内部
2721
-
2722
- 自己在这里看一下源码:[Sinatra::Delegator
2723
- mixin](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633)
2724
- 已经
2725
- [被扩展进了 main 对象](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30)。
2726
-
2727
- ## 命令行
2728
-
2729
- 可以直接运行 Sinatra 应用:
2730
-
2731
- ```shell
2732
- ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER]
2733
- ```
2734
-
2735
- 选项是:
2736
-
2737
- ```
2738
- -h # 显示帮助
2739
- -p # 设置端口号 (默认是 4567)
2740
- -o # 设定主机名 (默认是 0.0.0.0)
2741
- -e # 设置环境 (默认是 development)
2742
- -s # 声明 rack 服务器/处理器 (默认是 thin)
2743
- -x # 打开互斥锁 (默认是 off)
2744
- ```
2745
-
2746
- ### 多线程
2747
-
2748
- _根据 Konstantin 的 [这个 StackOverflow 答案] [so-answer] 改写_
2749
-
2750
- Sinatra 本身并不使用任何并发模型,而是将并发的任务留给底层的
2751
- Rack 处理器(服务器),如 Thin、Puma 或 WEBrick。Sinatra 本身是线程安全的,所以
2752
- Rack 处理器使用多线程并发模型并无任何问题。这意味着在启动服务器时,你必须指定特定
2753
- Rack 处理器的正确调用方法。
2754
- 下面的例子展示了如何启动一个多线程的 Thin 服务器:
2755
-
2756
- ```ruby
2757
- # app.rb
2758
-
2759
- require 'sinatra/base'
2760
-
2761
- class App < Sinatra::Base
2762
- get '/' do
2763
- "Hello, World"
2764
- end
2765
- end
2766
-
2767
- App.run!
2768
-
2769
- ```
2770
-
2771
- 启动服务器的命令是:
2772
-
2773
- ```shell
2774
- thin --threaded start
2775
- ```
2776
-
2777
-
2778
- [so-answer]: http://stackoverflow.com/questions/6278817/is-sinatra-multi-threaded/6282999#6282999)
2779
-
2780
- ## 必要条件
2781
-
2782
- 以下 Ruby 版本受官方支持:
2783
- <dl>
2784
- <dt>Ruby 1.8.7</dt>
2785
- <dd>
2786
- Sinatra 完全支持 1.8.7,但是,除非必要,我们推荐你升级或者切换到
2787
- JRuby 或 Rubinius。Sinatra 2.0 之前都不会取消对 1.8.7
2788
- 的支持。Ruby 1.8.6 目前已不受支持。
2789
- </dd>
2790
-
2791
- <dt>Ruby 1.9.2</dt>
2792
- <dd>
2793
- Sinatra 完全支持 1.9.2。
2794
- 不要使用 1.9.2p0,它在运行 Sinatra 程序时会产生 segmentation faults 错误。
2795
- 至少在 Sinatra 1.5 发布之前,官方对 1.9.2 的支持仍会继续。
2796
- </dd>
2797
-
2798
- <dt>Ruby 1.9.3</dt>
2799
- <dd>
2800
- Sinatra 完全支持并推荐使用 1.9.3。请注意从更早的版本迁移到 1.9.3 会使所有的会话失效。
2801
- 直到 Sinatra 2.0 发布之前,官方仍然会支持 1.9.3。
2802
- </dd>
2803
-
2804
- <dt>Ruby 2.x</dt>
2805
- <dd>
2806
- Sinatra 完全支持并推荐使用 2.x。目前尚无停止支持 2.x 的计划。
2807
- </dd>
2808
-
2809
- <dt>Rubinius</dt>
2810
- <dd>
2811
- Sinatra 官方支持 Rubinius (Rubinius >= 2.x)。推荐 <tt>gem install puma</tt>。
2812
- </dd>
2813
-
2814
- <dt>JRuby</dt>
2815
- <dd>
2816
- Sinatra 官方支持 JRuby 的最新稳定版本,但不推荐在 JRuby 上使用 C 扩展。
2817
- 推荐 <tt>gem install trinidad</tt>。
2818
- </dd>
2819
- </dl>
2820
-
2821
- 我们也在时刻关注新的 Ruby 版本。
2822
-
2823
- 以下 Ruby 实现不受 Sinatra 官方支持,但可以运行 Sinatra:
2824
-
2825
- * 老版本 JRuby 和 Rubinius
2826
- * Ruby 企业版
2827
- * MacRuby、Maglev、IronRuby
2828
- * Ruby 1.9.0 和 1.9.1 (不推荐使用)
2829
-
2830
- 不受官方支持的意思是,如果仅在不受支持的 Ruby 实现上发生错误,我们认为不是我们的问题,而是该实现的问题。
2831
-
2832
- 我们同时也针对 ruby-head (MRI 的未来版本)运行 CI,但由于 ruby-head 一直处在变化之中,
2833
- 我们不能作任何保证。我们期望完全支持未来的 2.x 版本。
2834
-
2835
- Sinatra 应该会运行在任何支持上述 Ruby 实现的操作系统上。
2836
-
2837
- 如果你使用 MacRuby,你应该 `gem install control_tower`。
2838
-
2839
- Sinatra 目前不支持 Cardinal、SmallRuby、BlueRuby 或其它 1.8.7 之前的 Ruby 版本。
2840
-
2841
- ## 紧跟前沿
2842
-
2843
- 如果你想使用 Sinatra 的最新代码,请放心使用 master 分支来运行你的程序,它是相当稳定的。
2844
-
2845
- 我们也会不定期推出 prerelease gems,所以你也可以运行
2846
-
2847
- ```shell
2848
- gem install sinatra --pre
2849
- ```
2850
-
2851
- 来获得最新的特性。
2852
-
2853
- ### 通过 Bundler 使用 Sinatra
2854
-
2855
- 如果你想在应用中使用最新的 Sinatra,推荐使用 [Bundler](http://bundler.io)。
2856
-
2857
- 首先,安装 Bundler,如果你还没有安装的话:
2858
-
2859
- ```shell
2860
- gem install bundler
2861
- ```
2862
-
2863
- 然后,在你的项目目录下创建一个 `Gemfile`:
2864
-
2865
- ```ruby
2866
- source 'https://rubygems.org'
2867
- gem 'sinatra', :github => "sinatra/sinatra"
2868
-
2869
- # 其它依赖
2870
- gem 'haml' # 假如你使用 haml
2871
- gem 'activerecord', '~> 3.0' # 也许你还需要 ActiveRecord 3.x
2872
- ```
2873
-
2874
- 请注意你必须在 `Gemfile` 中列出应用的所有依赖项。
2875
- 然而, Sinatra 的直接依赖项 (Rack 和 Tilt) 则会被 Bundler 自动获取和添加。
2876
-
2877
- 现在你可以这样运行你的应用:
2878
-
2879
- ```shell
2880
- bundle exec ruby myapp.rb
2881
- ```
2882
-
2883
- ### 使用自己本地的 Sinatra
2884
-
2885
- 创建一个本地克隆,并通过 `$LOAD_PATH` 里的 `sinatra/lib` 目录运行你的应用:
2886
-
2887
- ```shell
2888
- cd myapp
2889
- git clone git://github.com/sinatra/sinatra.git
2890
- ruby -I sinatra/lib myapp.rb
2891
- ```
2892
-
2893
- 为了在未来更新 Sinatra 源代码:
2894
-
2895
- ```shell
2896
- cd myapp/sinatra
2897
- git pull
2898
- ```
2899
-
2900
- ### 全局安装
2901
-
2902
- 你可以自行编译 Sinatra gem:
2903
-
2904
- ```shell
2905
- git clone git://github.com/sinatra/sinatra.git
2906
- cd sinatra
2907
- rake sinatra.gemspec
2908
- rake install
2909
- ```
2910
-
2911
- 如果你以 root 身份安装 gems,最后一步应该是:
2912
-
2913
- ```shell
2914
- sudo rake install
2915
- ```
2916
-
2917
- ## 版本
2918
-
2919
- Sinatra 遵循[语义化版本](http://semver.org),无论是 SemVer 还是 SemVerTag。
2920
-
2921
- ## 更多资料
2922
-
2923
- * [项目官网](http://www.sinatrarb.com/) - 更多文档、新闻和其它资源的链接。
2924
- * [贡献](http://www.sinatrarb.com/contributing) - 找到一个 bug?需要帮助?有了一个 patch?
2925
- * [问题追踪](https://github.com/sinatra/sinatra/issues)
2926
- * [Twitter](https://twitter.com/sinatra)
2927
- * [邮件列表](http://groups.google.com/group/sinatrarb/topics)
2928
- * IRC: [#sinatra](irc://chat.freenode.net/#sinatra) on http://freenode.net
2929
- * [Sinatra & Friends](https://sinatrarb.slack.com) on Slack,点击
2930
- [这里](https://sinatra-slack.herokuapp.com/) 获得邀请。
2931
- * [Sinatra Book](https://github.com/sinatra/sinatra-book/) Cookbook 教程
2932
- * [Sinatra Recipes](http://recipes.sinatrarb.com/) 社区贡献的实用技巧
2933
- * http://www.rubydoc.info/ 上[最新版本](http://www.rubydoc.info//gems/sinatra)或[当前 HEAD](http://www.rubydoc.info/github/sinatra/sinatra) 的 API 文档
2934
- * [CI 服务器](https://travis-ci.org/sinatra/sinatra)