sinatra-base 1.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/.yardopts +4 -0
  2. data/AUTHORS +15 -0
  3. data/CHANGES +524 -1
  4. data/Gemfile +82 -0
  5. data/LICENSE +1 -1
  6. data/README.de.rdoc +2093 -0
  7. data/README.es.rdoc +2091 -0
  8. data/README.fr.rdoc +2116 -0
  9. data/README.hu.rdoc +607 -0
  10. data/README.jp.rdoc +514 -23
  11. data/README.pt-br.rdoc +647 -0
  12. data/README.pt-pt.rdoc +646 -0
  13. data/README.rdoc +1580 -205
  14. data/README.ru.rdoc +2015 -0
  15. data/README.zh.rdoc +1816 -0
  16. data/Rakefile +110 -44
  17. data/examples/chat.rb +61 -0
  18. data/examples/simple.rb +3 -0
  19. data/examples/stream.ru +26 -0
  20. data/lib/sinatra.rb +0 -3
  21. data/lib/sinatra/base.rb +923 -393
  22. data/lib/sinatra/main.rb +9 -7
  23. data/lib/sinatra/showexceptions.rb +37 -4
  24. data/lib/sinatra/version.rb +3 -0
  25. data/sinatra-base.gemspec +15 -91
  26. data/test/base_test.rb +2 -2
  27. data/test/builder_test.rb +32 -2
  28. data/test/coffee_test.rb +92 -0
  29. data/test/contest.rb +62 -28
  30. data/test/creole_test.rb +65 -0
  31. data/test/delegator_test.rb +162 -0
  32. data/test/encoding_test.rb +20 -0
  33. data/test/erb_test.rb +25 -2
  34. data/test/extensions_test.rb +1 -1
  35. data/test/filter_test.rb +226 -8
  36. data/test/haml_test.rb +8 -2
  37. data/test/helper.rb +47 -0
  38. data/test/helpers_test.rb +1287 -80
  39. data/test/integration/app.rb +62 -0
  40. data/test/integration_helper.rb +208 -0
  41. data/test/integration_test.rb +82 -0
  42. data/test/less_test.rb +36 -6
  43. data/test/liquid_test.rb +59 -0
  44. data/test/mapped_error_test.rb +84 -7
  45. data/test/markaby_test.rb +80 -0
  46. data/test/markdown_test.rb +81 -0
  47. data/test/middleware_test.rb +1 -1
  48. data/test/nokogiri_test.rb +69 -0
  49. data/test/rack_test.rb +45 -0
  50. data/test/radius_test.rb +59 -0
  51. data/test/rdoc_test.rb +66 -0
  52. data/test/readme_test.rb +136 -0
  53. data/test/request_test.rb +13 -1
  54. data/test/response_test.rb +21 -2
  55. data/test/result_test.rb +5 -5
  56. data/test/route_added_hook_test.rb +1 -1
  57. data/test/routing_test.rb +328 -13
  58. data/test/sass_test.rb +48 -18
  59. data/test/scss_test.rb +88 -0
  60. data/test/server_test.rb +4 -3
  61. data/test/settings_test.rb +191 -21
  62. data/test/sinatra_test.rb +5 -1
  63. data/test/slim_test.rb +88 -0
  64. data/test/static_test.rb +89 -5
  65. data/test/streaming_test.rb +140 -0
  66. data/test/templates_test.rb +143 -4
  67. data/test/textile_test.rb +65 -0
  68. data/test/views/a/in_a.str +1 -0
  69. data/test/views/ascii.erb +2 -0
  70. data/test/views/b/in_b.str +1 -0
  71. data/test/views/calc.html.erb +1 -0
  72. data/test/views/explicitly_nested.str +1 -0
  73. data/test/views/hello.coffee +1 -0
  74. data/test/views/hello.creole +1 -0
  75. data/test/views/hello.liquid +1 -0
  76. data/test/views/hello.mab +1 -0
  77. data/test/views/hello.md +1 -0
  78. data/test/views/hello.nokogiri +1 -0
  79. data/test/views/hello.radius +1 -0
  80. data/test/views/hello.rdoc +1 -0
  81. data/test/views/hello.sass +1 -1
  82. data/test/views/hello.scss +3 -0
  83. data/test/views/hello.slim +1 -0
  84. data/test/views/hello.str +1 -0
  85. data/test/views/hello.textile +1 -0
  86. data/test/views/hello.yajl +1 -0
  87. data/test/views/layout2.liquid +2 -0
  88. data/test/views/layout2.mab +2 -0
  89. data/test/views/layout2.nokogiri +3 -0
  90. data/test/views/layout2.radius +2 -0
  91. data/test/views/layout2.slim +3 -0
  92. data/test/views/layout2.str +2 -0
  93. data/test/views/nested.str +1 -0
  94. data/test/views/utf8.erb +2 -0
  95. data/test/yajl_test.rb +80 -0
  96. metadata +126 -91
  97. data/lib/sinatra/tilt.rb +0 -746
  98. data/test/erubis_test.rb +0 -82
  99. data/test/views/error.erubis +0 -3
  100. data/test/views/hello.erubis +0 -1
  101. data/test/views/layout2.erubis +0 -2
@@ -0,0 +1,2015 @@
1
+ = Sinatra
2
+ <i>Внимание: Этот документ является переводом английской версии и может быть устаревшим</i>
3
+
4
+ Sinatra — это предметно-ориентированный каркас (DSL) для быстрого создания функциональных
5
+ веб-приложений на Ruby с минимумом усилий:
6
+
7
+ # myapp.rb
8
+ require 'sinatra'
9
+
10
+ get '/' do
11
+ 'Hello world!'
12
+ end
13
+
14
+ Установите gem:
15
+
16
+ gem install sinatra
17
+
18
+ и запустите приложение с помощью:
19
+
20
+ ruby -rubygems myapp.rb
21
+
22
+ Оцените результат: http://localhost:4567
23
+
24
+ Рекомендуется также установить Thin, сделать это можно командой: <tt>gem install thin</tt>.
25
+ Thin — это более производительный и функциональный сервер для разработки приложений на Sinatra.
26
+
27
+ == Маршруты
28
+
29
+ В Sinatra маршрут — это пара: <HTTP метод> и <шаблон URL>.
30
+ Каждый маршрут связан с блоком кода:
31
+
32
+ get '/' do
33
+ .. что-то показать ..
34
+ end
35
+
36
+ post '/' do
37
+ .. что-то создать ..
38
+ end
39
+
40
+ put '/' do
41
+ .. что-то заменить ..
42
+ end
43
+
44
+ patch '/' do
45
+ .. что-то изменить ..
46
+ end
47
+
48
+ delete '/' do
49
+ .. что-то удалить ..
50
+ end
51
+
52
+ options '/' do
53
+ .. что-то ответить ..
54
+ end
55
+
56
+ Маршруты сверяются с запросом в порядке очередности их записи в файле приложения.
57
+ Первый же совпавший с запросом маршрут и будет вызван.
58
+
59
+ Шаблоны маршрутов могут включать в себя именные параметры доступные
60
+ в xэше <tt>params</tt>:
61
+
62
+ get '/hello/:name' do
63
+ # соответствует "GET /hello/foo" и "GET /hello/bar",
64
+ # где params[:name] 'foo' или 'bar'
65
+ "Hello #{params[:name]}!"
66
+ end
67
+
68
+ Также можно использовать именные параметры в качестве переменных
69
+ блока:
70
+
71
+ get '/hello/:name' do |n|
72
+ "Hello #{n}!"
73
+ end
74
+
75
+ Шаблоны маршрутов также могут включать в себя splat (или '*'
76
+ маску, обозначающую любой символ) параметры доступные в массиве
77
+ <tt>params[:splat]</tt>:
78
+
79
+ get '/say/*/to/*' do
80
+ # соответствует /say/hello/to/world
81
+ params[:splat] # => ["hello", "world"]
82
+ end
83
+
84
+ get '/download/*.*' do
85
+ # соответствует /download/path/to/file.xml
86
+ params[:splat] # => ["path/to/file", "xml"]
87
+ end
88
+
89
+ Или с параметрами блока:
90
+
91
+ get '/download/*.*' do |path, ext|
92
+ [path, ext] # => ["path/to/file", "xml"]
93
+ end
94
+
95
+ Регулярные выражения в качестве шаблонов маршрутов:
96
+
97
+ get %r{/hello/([\w]+)} do
98
+ "Hello, #{params[:captures].first}!"
99
+ end
100
+
101
+ Или с параметром блока:
102
+
103
+ get %r{/hello/([\w]+)} do |c|
104
+ "Hello, #{c}!"
105
+ end
106
+
107
+ Шаблоны маршрутов могут иметь необязательные параметры:
108
+
109
+ get '/posts.?:format?' do
110
+ # соответствует "GET /posts", "GET /posts.json", "GET /posts.xml" и т.д.
111
+ end
112
+
113
+ Кстати, если вы не отключите защиту от обратного пути в директориях
114
+ (path traversal, см. ниже), путь запроса может быть изменен до начала
115
+ поиска подходящего маршрута.
116
+
117
+ === Условия
118
+
119
+ Маршруты могут включать различные условия совпадений, например,
120
+ клиентской приложение (user agent):
121
+
122
+ get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
123
+ "You're using Songbird version #{params[:agent][0]}"
124
+ end
125
+
126
+ get '/foo' do
127
+ # соответствует не-songbird браузерам
128
+ end
129
+
130
+ Другими доступными условиями являются +host_name+ и +provides+:
131
+
132
+ get '/', :host_name => /^admin\./ do
133
+ "Admin Area, Access denied!"
134
+ end
135
+
136
+ get '/', :provides => 'html' do
137
+ haml :index
138
+ end
139
+
140
+ get '/', :provides => ['rss', 'atom', 'xml'] do
141
+ builder :feed
142
+ end
143
+
144
+ Вы можете задать собственные условия:
145
+
146
+ set(:probability) { |value| condition { rand <= value } }
147
+
148
+ get '/win_a_car', :probability => 0.1 do
149
+ "You won!"
150
+ end
151
+
152
+ get '/win_a_car' do
153
+ "Sorry, you lost."
154
+ end
155
+
156
+ Для условия, которое принимает несколько параметров, используйте
157
+ звездочку:
158
+
159
+ set(:auth) do |*roles| # <- обратите внимание на звездочку
160
+ condition do
161
+ unless logged_in? && roles.any? {|role| current_user.in_role? role }
162
+ redirect "/login/", 303
163
+ end
164
+ end
165
+ end
166
+
167
+ get "/my/account/", :auth => [:user, :admin] do
168
+ "Your Account Details"
169
+ end
170
+
171
+ get "/only/admin/", :auth => :admin do
172
+ "Only admins are allowed here!"
173
+ end
174
+
175
+ === Возвращаемые значения
176
+
177
+ Возвращаемое значение блока маршрута ограничивается телом ответа, которое будет передано HTTP клиенту,
178
+ или следующей "прослойкой" (middleware) в Rack стеке. Чаще всего это строка, как в примерах выше.
179
+ Но и другие значения также приемлемы.
180
+
181
+ Вы можете вернуть любой объект, который будет либо корректным Rack ответом, объектом Rack body,
182
+ либо кодом состояния HTTP:
183
+
184
+ * массив с тремя переменными: <tt>[код (Fixnum), заголовки (Hash), тело ответа (должно отвечать на #each)]</tt>;
185
+ * массив с двумя переменными: <tt>[код (Fixnum), тело ответа (должно отвечать на #each)]</tt>;
186
+ * объект, отвечающий на <tt>#each</tt>, который передает только строковые типы данных в этот блок;
187
+ * Fixnum, представляющий код состояния HTTP.
188
+
189
+ Таким образом, легко можно реализовать, например, поточный пример:
190
+
191
+ class Stream
192
+ def each
193
+ 100.times { |i| yield "#{i}\n" }
194
+ end
195
+ end
196
+
197
+ get('/') { Stream.new }
198
+
199
+ Вы также можете использовать метод +stream+ (описываемый ниже), чтобы
200
+ уменьшить количество дублируемого кода и держать логику стриминга прямо в маршруте.
201
+
202
+ === Собственные детекторы совпадений для маршрутов
203
+
204
+ Как показано выше, Sinatra поставляется со встроенной поддержкой строк и
205
+ регулярных выражений в качестве шаблонов URL. Но и это еще не все. Вы можете
206
+ легко определить свои собственные детекторы совпадений (matchers) для маршрутов:
207
+
208
+ class AllButPattern
209
+ Match = Struct.new(:captures)
210
+
211
+ def initialize(except)
212
+ @except = except
213
+ @captures = Match.new([])
214
+ end
215
+
216
+ def match(str)
217
+ @captures unless @except === str
218
+ end
219
+ end
220
+
221
+ def all_but(pattern)
222
+ AllButPattern.new(pattern)
223
+ end
224
+
225
+ get all_but("/index") do
226
+ # ...
227
+ end
228
+
229
+ Заметьте, что предыдущий пример возможно чересчур усложнен, потому что
230
+ он может быть реализован так:
231
+
232
+ get // do
233
+ pass if request.path_info == "/index"
234
+ # ...
235
+ end
236
+
237
+ Или с использованием негативного просмотра вперед:
238
+
239
+ get %r{^(?!/index$)} do
240
+ # ...
241
+ end
242
+
243
+ == Статические файлы
244
+
245
+ Статические файлы отдаются из <tt>./public</tt> директории. Вы можете
246
+ указать другое место, используя опцию <tt>:public_folder</tt>:
247
+
248
+ set :public_folder, File.dirname(__FILE__) + '/static'
249
+
250
+ Учтите, что имя директории со статическими файлами не включено в URL. Например, файл
251
+ <tt>./public/css/style.css</tt> будет доступен как
252
+ <tt>http://example.com/css/style.css</tt>.
253
+
254
+ Используйте опцию <tt>:static_cache_control</tt> (см. ниже), чтобы
255
+ добавить заголовок <tt>Cache-Control</tt>.
256
+
257
+ == Представления / Шаблоны
258
+
259
+ Каждый шаблонизатор представлен своим собственным методом. Эти методы
260
+ попросту возвращают строку:
261
+
262
+ get '/' do
263
+ erb :index
264
+ end
265
+
266
+ Отобразит <tt>views/index.erb</tt>.
267
+
268
+ Вместо имени шаблона вы так же можете передавать непосредственно само
269
+ содержимое шаблона:
270
+
271
+ get '/' do
272
+ code = "<%= Time.now %>"
273
+ erb code
274
+ end
275
+
276
+ Эти методы принимают второй аргумент, хеш с опциями:
277
+
278
+ get '/' do
279
+ erb :index, :layout => :post
280
+ end
281
+
282
+ Отобразит <tt>views/index.erb</tt>, вложенным в
283
+ <tt>views/post.erb</tt> (по умолчанию: <tt>views/layout.erb</tt>, если существует).
284
+
285
+ Любые опции, не понимаемые Sinatra, будут переданы в шаблонизатор:
286
+
287
+ get '/' do
288
+ haml :index, :format => :html5
289
+ end
290
+
291
+ Вы также можете задавать опции для шаблонизаторов в общем:
292
+
293
+ set :haml, :format => :html5
294
+
295
+ get '/' do
296
+ haml :index
297
+ end
298
+
299
+ Опции, переданные в метод, переопределяют опции, заданные с помощью
300
+ +set+.
301
+
302
+ Доступные опции:
303
+
304
+ [locals]
305
+ Список локальных переменных, передаваемых в документ.
306
+ Например: <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>
307
+
308
+ [default_encoding]
309
+ Кодировка, которую следует использовать, если не удалось определить
310
+ оригинальную. По умолчанию: <tt>settings.default_encoding</tt>.
311
+
312
+ [views]
313
+ Директория с шаблонами. По умолчанию: <tt>settings.views</tt>.
314
+
315
+ [layout]
316
+ Использовать или нет лэйаут (+true+ или +false+). Если же значение Symbol,
317
+ то указывает, какой шаблон использовать в качестве лэйаута. Например:
318
+ <tt>erb :index, :layout => !request.xhr?</tt>
319
+
320
+ [content_type]
321
+ Content-Type отображенного шаблона. По умолчанию: задается шаблонизатором.
322
+
323
+ [scope]
324
+ Область видимости, в которой рендерятся шаблоны. По умолчанию: экземпляр
325
+ приложения. Если вы измените эту опцию, то переменные экземпляра и
326
+ методы-помощники станут недоступными в ваших шаблонах.
327
+
328
+ [layout_engine]
329
+ Шаблонизатор, который следует использовать для отображения лэйаута. Полезная
330
+ опция для шаблонизаторов, в которых нет никакой поддержки лэйаутов. По
331
+ умолчанию: тот же шаблонизатор, что используется и для самого шаблона.
332
+ Пример: <tt>set :rdoc, :layout_engine => :erb</tt>
333
+
334
+ По умолчанию считается, что шаблоны находятся в директории <tt>./views</tt>.
335
+ Чтобы использовать другую директорию с шаблонами:
336
+
337
+ set :views, settings.root + '/templates'
338
+
339
+ Важное замечание: вы всегда должны ссылаться на шаблоны с помощью символов
340
+ (Symbol), даже когда они в поддиректории (в этом случае используйте
341
+ <tt>:'subdir/template'</tt>). Вы должны использовать символы, потому что
342
+ иначе шаблонизаторы попросту отображают любые строки, переданные им.
343
+
344
+ === Доступные шаблонизаторы
345
+
346
+ Некоторые языки шаблонов имеют несколько реализаций. Чтобы указать, какую
347
+ реализацию использовать, вам следует просто подключить нужную
348
+ библиотеку:
349
+
350
+ require 'rdiscount' # или require 'bluecloth'
351
+ get('/') { markdown :index }
352
+
353
+ === Haml шаблоны
354
+
355
+ Зависимости:: {haml}[http://haml-lang.com/]
356
+ Расширения файлов:: <tt>.haml</tt>
357
+ Пример:: <tt>haml :index, :format => :html5</tt>
358
+
359
+ === Erb шаблоны
360
+
361
+ Зависимости:: {erubis}[http://www.kuwata-lab.com/erubis/] или erb (включен в Ruby)
362
+ Расширения файлов:: <tt>.erb</tt>, <tt>.rhtml</tt> или <tt>.erubis</tt> (только Erubis)
363
+ Пример:: <tt>erb :index</tt>
364
+
365
+ === Builder шаблоны
366
+
367
+ Зависимости:: {builder}[http://builder.rubyforge.org/]
368
+ Расширения файлов:: <tt>.builder</tt>
369
+ Пример:: <tt>builder { |xml| xml.em "hi" }</tt>
370
+
371
+ Блок также используется и для встроенных шаблонов (см. пример).
372
+
373
+ === Nokogiri шаблоны
374
+
375
+ Зависимости:: {nokogiri}[http://nokogiri.org/]
376
+ Расширения файлов:: <tt>.nokogiri</tt>
377
+ Пример:: <tt>nokogiri { |xml| xml.em "hi" }</tt>
378
+
379
+ Блок также используется и для встроенных шаблонов (см. пример).
380
+
381
+ === Sass шаблоны
382
+
383
+ Зависимости:: {sass}[http://sass-lang.com/]
384
+ Расширения файлов:: <tt>.sass</tt>
385
+ Пример:: <tt>sass :stylesheet, :style => :expanded</tt>
386
+
387
+ === SCSS шаблоны
388
+
389
+ Зависимости:: {sass}[http://sass-lang.com/]
390
+ Расширения файлов:: <tt>.scss</tt>
391
+ Пример:: <tt>scss :stylesheet, :style => :expanded</tt>
392
+
393
+ === Less шаблоны
394
+
395
+ Зависимости:: {less}[http://www.lesscss.org/]
396
+ Расширения файлов:: <tt>.less</tt>
397
+ Пример:: <tt>less :stylesheet</tt>
398
+
399
+ === Liquid шаблоны
400
+
401
+ Зависимости:: {liquid}[http://www.liquidmarkup.org/]
402
+ Расширения файлов:: <tt>.liquid</tt>
403
+ Пример:: <tt>liquid :index, :locals => { :key => 'value' }</tt>
404
+
405
+ Так как в Liquid шаблонах невозможно вызывать методы из Ruby (кроме yield), то
406
+ вы почти всегда будете передавать в шаблон локальные переменные.
407
+
408
+ === Markdown шаблоны
409
+
410
+ Зависимости:: {rdiscount}[https://github.com/rtomayko/rdiscount], {redcarpet}[https://github.com/tanoku/redcarpet], {bluecloth}[http://deveiate.org/projects/BlueCloth], {kramdown}[http://kramdown.rubyforge.org/] или {maruku}[http://maruku.rubyforge.org/]
411
+ Расширения файлов:: <tt>.markdown</tt>, <tt>.mkd</tt> and <tt>.md</tt>
412
+ Пример:: <tt>markdown :index, :layout_engine => :erb</tt>
413
+
414
+ В Markdown невозможно вызывать методы или передавать локальные переменные.
415
+ Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
416
+ с другим шаблонизатором:
417
+
418
+ erb :overview, :locals => { :text => markdown(:introduction) }
419
+
420
+ Заметьте, что вы можете вызывать метод +markdown+ из других шаблонов:
421
+
422
+ %h1 Hello From Haml!
423
+ %p= markdown(:greetings)
424
+
425
+ Вы не можете вызывать Ruby из Markdown, соответственно, вы не можете
426
+ использовать лэйауты на Markdown. Тем не менее, есть возможность использовать
427
+ один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
428
+ опции <tt>:layout_engine</tt>.
429
+
430
+ === Textile шаблоны
431
+
432
+ Зависимости:: {RedCloth}[http://redcloth.org/]
433
+ Расширения файлов:: <tt>.textile</tt>
434
+ Пример:: <tt>textile :index, :layout_engine => :erb</tt>
435
+
436
+ В Textile невозможно вызывать методы или передавать локальные переменные.
437
+ Следовательно, вам, скорее всего, придется использовать этот шаблон совместно с
438
+ другим шаблонизатором:
439
+
440
+ erb :overview, :locals => { :text => textile(:introduction) }
441
+
442
+ Заметьте, что вы можете вызывать метод +textile+ из других шаблонов:
443
+
444
+ %h1 Hello From Haml!
445
+ %p= textile(:greetings)
446
+
447
+ Вы не можете вызывать Ruby из Textile, соответственно, вы не можете
448
+ использовать лэйауты на Textile. Тем не менее, есть возможность использовать
449
+ один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
450
+ опции <tt>:layout_engine</tt>.
451
+
452
+ === RDoc шаблоны
453
+
454
+ Зависимости:: {rdoc}[http://rdoc.rubyforge.org/]
455
+ Расширения файлов:: <tt>.rdoc</tt>
456
+ Пример:: <tt>rdoc :README, :layout_engine => :erb</tt>
457
+
458
+ В RDoc невозможно вызывать методы или передавать локальные переменные.
459
+ Следовательно, вам, скорее всего, придется использовать этот шаблон совместно с
460
+ другим шаблонизатором:
461
+
462
+ erb :overview, :locals => { :text => rdoc(:introduction) }
463
+
464
+ Заметьте, что вы можете вызывать метод +rdoc+ из других шаблонов:
465
+
466
+ %h1 Hello From Haml!
467
+ %p= rdoc(:greetings)
468
+
469
+ Вы не можете вызывать Ruby из RDoc, соответственно, вы не можете
470
+ использовать лэйауты на RDoc. Тем не менее, есть возможность использовать
471
+ один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
472
+ опции <tt>:layout_engine</tt>.
473
+
474
+ === Radius шаблоны
475
+
476
+ Зависимости:: {radius}[http://radius.rubyforge.org/]
477
+ Расширения файлов:: <tt>.radius</tt>
478
+ Пример:: <tt>radius :index, :locals => { :key => 'value' }</tt>
479
+
480
+ Так как в Radius шаблонах невозможно вызывать методы из Ruby напрямую, то
481
+ вы почти всегда будете передавать в шаблон локальные переменные.
482
+
483
+ === Markaby шаблоны
484
+
485
+ Зависимости:: {markaby}[http://markaby.github.com/]
486
+ Расширения файлов:: <tt>.mab</tt>
487
+ Пример:: <tt>markaby { h1 "Welcome!" }</tt>
488
+
489
+ Блок также используется и для встроенных шаблонов (см. пример).
490
+
491
+ === Slim шаблоны
492
+
493
+ Зависимости:: {slim}[http://slim-lang.com/]
494
+ Расширения файлов:: <tt>.slim</tt>
495
+ Пример:: <tt>slim :index</tt>
496
+
497
+ === Creole шаблоны
498
+
499
+ Зависимости:: {creole}[https://github.com/minad/creole]
500
+ Расширения файлов:: <tt>.creole</tt>
501
+ Пример:: <tt>creole :wiki, :layout_engine => :erb</tt>
502
+
503
+ В Creole невозможно вызывать методы или передавать локальные переменные.
504
+ Следовательно, вам, скорее всего, придется использовать этот шаблон совместно с
505
+ другим шаблонизатором:
506
+
507
+ erb :overview, :locals => { :text => creole(:introduction) }
508
+
509
+ Заметьте, что вы можете вызывать метод +creole+ из других шаблонов:
510
+
511
+ %h1 Hello From Haml!
512
+ %p= creole(:greetings)
513
+
514
+ Вы не можете вызывать Ruby из Creole, соответственно, вы не можете
515
+ использовать лэйауты на Creole. Тем не менее, есть возможность использовать
516
+ один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
517
+ опции <tt>:layout_engine</tt>.
518
+
519
+ === CoffeeScript шаблоны
520
+
521
+ Зависимости:: {coffee-script}[https://github.com/josh/ruby-coffee-script] и {способ запускать javascript}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
522
+ Расширения файлов:: <tt>.coffee</tt>
523
+ Пример:: <tt>coffee :index</tt>
524
+
525
+ === Yajl шаблоны
526
+
527
+ Зависимости:: {yajl-ruby}[https://github.com/brianmario/yajl-ruby]
528
+ Расширения файлов:: <tt>.yajl</tt>
529
+ Пример:: <tt>yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource' </tt>
530
+
531
+ Содержимое шаблона интерпретируется как код на Ruby, а результирующая переменная json затем
532
+ конвертируется с помощью #to_json.
533
+
534
+ json = { :foo => 'bar' }
535
+ json[:baz] = key
536
+
537
+ Опции <tt>:callback</tt> и <tt>:variable</tt> используются для "декорирования" итогового объекта.
538
+
539
+ var resource = {"foo":"bar","baz":"qux"}; present(resource);
540
+
541
+ === Встроенные шаблоны
542
+
543
+ get '/' do
544
+ haml '%div.title Hello World'
545
+ end
546
+
547
+ Отобразит встроенный шаблон, переданный строкой.
548
+
549
+ === Доступ к переменным в шаблонах
550
+
551
+ Шаблоны интерпретируются в том же контексте, что и обработчики маршрутов. Переменные экземпляра,
552
+ установленные в процессе обработки маршрутов, будут доступны напрямую в шаблонах:
553
+
554
+ get '/:id' do
555
+ @foo = Foo.find(params[:id])
556
+ haml '%h1= @foo.name'
557
+ end
558
+
559
+ Либо установите их через хеш локальных переменных:
560
+
561
+ get '/:id' do
562
+ foo = Foo.find(params[:id])
563
+ haml '%h1= bar.name', :locals => { :bar => foo }
564
+ end
565
+
566
+ Это обычный подход, когда шаблоны рендерятся как части других шаблонов.
567
+
568
+ === Вложенные шаблоны
569
+
570
+ Шаблоны также могут быть определены в конце исходного файла:
571
+
572
+ require 'sinatra'
573
+
574
+ get '/' do
575
+ haml :index
576
+ end
577
+
578
+ __END__
579
+
580
+ @@ layout
581
+ %html
582
+ = yield
583
+
584
+ @@ index
585
+ %div.title Hello world.
586
+
587
+ Заметьте: вложенные шаблоны, определенные в исходном файле, который подключила Sinatra, будут
588
+ загружены автоматически. Вызовите <tt>enable :inline_templates</tt> напрямую, если
589
+ используете вложенные шаблоны в других файлах.
590
+
591
+ === Именные шаблоны
592
+
593
+ Шаблоны также могут быть определены при помощи <tt>template</tt> метода:
594
+
595
+ template :layout do
596
+ "%html\n =yield\n"
597
+ end
598
+
599
+ template :index do
600
+ '%div.title Hello World!'
601
+ end
602
+
603
+ get '/' do
604
+ haml :index
605
+ end
606
+
607
+ Если шаблон с именем "layout" существует, то он будет использоваться каждый раз
608
+ при рендеринге. Вы можете отключать лэйаут в каждом конкретном случае с помощью
609
+ <tt>:layout => false</tt> или отключить его для всего приложения:
610
+ <tt>set :haml, :layout => false</tt>:
611
+
612
+ get '/' do
613
+ haml :index, :layout => !request.xhr?
614
+ end
615
+
616
+ === Привязка файловых расширений
617
+
618
+ Чтобы связать расширение файла с движком рендеринга, используйте
619
+ <tt>Tilt.register</tt>. Например, если вы хотите использовать расширение +tt+
620
+ для шаблонов Textile:
621
+
622
+ Tilt.register :tt, Tilt[:textile]
623
+
624
+ === Добавление собственного движка рендеринга
625
+
626
+ Сначала зарегистрируйте свой движок в Tilt, а затем создайте метод,
627
+ отвечающий за рендеринг:
628
+
629
+ Tilt.register :myat, MyAwesomeTemplateEngine
630
+
631
+ helpers do
632
+ def myat(*args) render(:myat, *args) end
633
+ end
634
+
635
+ get '/' do
636
+ myat :index
637
+ end
638
+
639
+ Отобразит <tt>./views/index.myat</tt>. Чтобы узнать больше о Tilt,
640
+ смотрите https://github.com/rtomayko/tilt
641
+
642
+ == Фильтры
643
+
644
+ +before+-фильтры выполняются перед каждым запросом в том же контексте, что и маршруты. Фильтры могут изменять
645
+ как запрос, так и ответ на него. Переменные экземпляра, установленные в фильтрах, доступны в маршрутах и шаблонах:
646
+
647
+ before do
648
+ @note = 'Hi!'
649
+ request.path_info = '/foo/bar/baz'
650
+ end
651
+
652
+ get '/foo/*' do
653
+ @note #=> 'Hi!'
654
+ params[:splat] #=> 'bar/baz'
655
+ end
656
+
657
+ +after+-фильтры выполняются после каждого запроса в том же контексте, что и пути. Фильтры могут изменять
658
+ как запрос, так и ответ на него. Переменные экземпляра, установленные в +before+-фильтрах и маршрутах,
659
+ будут доступны в +after+-фильтрах:
660
+
661
+ after do
662
+ puts response.status
663
+ end
664
+
665
+ Заметьте: если вы используете метод +body+, а не просто возвращаете строку из
666
+ маршрута, то тело ответа не будет доступно в +after+-фильтрах, так как оно будет сгенерировано позднее.
667
+
668
+ Фильтры могут использовать шаблоны URL и будут интерпретированы только если путь запроса совпадет с этим шаблоном:
669
+
670
+ before '/protected/*' do
671
+ authenticate!
672
+ end
673
+
674
+ after '/create/:slug' do |slug|
675
+ session[:last_slug] = slug
676
+ end
677
+
678
+ Как и маршруты, фильтры могут использовать условия:
679
+
680
+ before :agent => /Songbird/ do
681
+ # ...
682
+ end
683
+
684
+ after '/blog/*', :host_name => 'example.com' do
685
+ # ...
686
+ end
687
+
688
+ == Методы-помощники
689
+
690
+ Используйте метод <tt>helpers</tt>, чтобы определить методы-помощники, которые
691
+ в дальнейшем можно будет использовать в обработчиках маршрутов и шаблонах:
692
+
693
+ helpers do
694
+ def bar(name)
695
+ "#{name}bar"
696
+ end
697
+ end
698
+
699
+ get '/:name' do
700
+ bar(params[:name])
701
+ end
702
+
703
+ Также методы-помощники могут быть заданы в отдельных модулях:
704
+
705
+ module FooUtils
706
+ def foo(name) "#{name}foo" end
707
+ end
708
+
709
+ module BarUtils
710
+ def bar(name) "#{name}bar" end
711
+ end
712
+
713
+ helpers FooUtils, BarUtils
714
+
715
+ Эффект равносилен включению модулей в класс приложения.
716
+
717
+ === Использование сессий
718
+
719
+ Сессия используется, чтобы сохранять состояние между запросами. Если эта опция
720
+ включена, то у вас будет один хеш сессии на одну пользовательскую сессию:
721
+
722
+ enable :sessions
723
+
724
+ get '/' do
725
+ "value = " << session[:value].inspect
726
+ end
727
+
728
+ get '/:value' do
729
+ session[:value] = params[:value]
730
+ end
731
+
732
+ Заметьте, что при использовании <tt>enable :sessions</tt> все данные
733
+ сохраняются в куках (cookies). Это может быть не совсем то, что вы хотите
734
+ (например, сохранение больших объемов данных увеличит ваш трафик). В таком случае
735
+ вы можете использовать альтернативную Rack "прослойку" (middleware), реализующую
736
+ механизм сессий. Для этого *не надо* вызывать <tt>enable :sessions</tt>,
737
+ вместо этого следует подключить ее так же, как и любую другую "прослойку":
738
+
739
+ use Rack::Session::Pool, :expire_after => 2592000
740
+
741
+ get '/' do
742
+ "value = " << session[:value].inspect
743
+ end
744
+
745
+ get '/:value' do
746
+ session[:value] = params[:value]
747
+ end
748
+
749
+ Для повышения безопасности данные сессии в куках подписываются секретным
750
+ ключом. Секретный ключ генерируется Sinatra. Тем не менее, так как этот
751
+ ключ будет меняться с каждым запуском приложения, вы, возможно, захотите
752
+ установить ключ вручную, чтобы у всех экземпляров вашего приложения
753
+ был один и тот же ключ:
754
+
755
+ set :session_secret, 'super secret'
756
+
757
+ Если вы хотите больше настроек для сессий, вы можете задать их, передав хеш опций в параметр +sessions+:
758
+
759
+ set :sessions, :domain => 'foo.com'
760
+
761
+ === Прерывание
762
+
763
+ Чтобы незамедлительно прервать обработку запроса внутри фильтра или маршрута, используйте:
764
+
765
+ halt
766
+
767
+ Можно также указать статус при прерывании:
768
+
769
+ halt 410
770
+
771
+ Тело:
772
+
773
+ halt 'this will be the body'
774
+
775
+ И то, и другое:
776
+
777
+ halt 401, 'go away!'
778
+
779
+ Можно указать заголовки:
780
+
781
+ halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
782
+
783
+ И, конечно, можно использовать шаблоны с +halt+:
784
+
785
+ halt erb(:error)
786
+
787
+ === Передача
788
+
789
+ Маршрут может передать обработку запроса следующему совпадающему маршруту, используя <tt>pass</tt>:
790
+
791
+ get '/guess/:who' do
792
+ pass unless params[:who] == 'Frank'
793
+ 'You got me!'
794
+ end
795
+
796
+ get '/guess/*' do
797
+ 'You missed!'
798
+ end
799
+
800
+ Блок маршрута сразу же прерывается, и контроль переходит к следующему совпадающему маршруту.
801
+ Если соответствующий маршрут не найден, то ответом на запрос будет 404.
802
+
803
+ === Вызов другого маршрута
804
+
805
+ Иногда +pass+ не подходит, например, если вы хотите получить результат
806
+ вызова другого обработчика маршрута. В таком случае просто используйте +call+:
807
+
808
+ get '/foo' do
809
+ status, headers, body = call env.merge("PATH_INFO" => '/bar')
810
+ [status, headers, body.map(&:upcase)]
811
+ end
812
+
813
+ get '/bar' do
814
+ "bar"
815
+ end
816
+
817
+ Заметьте, что в предыдущем примере можно облегчить тестирование и повысить
818
+ производительность, перенеся <tt>"bar"</tt> в метод-помощник, используемый
819
+ и в <tt>/foo</tt>, и в <tt>/bar</tt>.
820
+
821
+ Если вы хотите, чтобы запрос был отправлен в тот же экземпляр приложения, а не
822
+ в его копию, используйте <tt>call!</tt> вместо <tt>call</tt>.
823
+
824
+ Если хотите узнать больше о <tt>call</tt>, смотрите спецификацию Rack.
825
+
826
+ === Задание тела, кода и заголовков ответа
827
+
828
+ Хорошим тоном является установка кода состояния HTTP и тела ответа в возвращаемом
829
+ значении обработчика маршрута. Тем не менее, в некоторых ситуациях вам, возможно,
830
+ понадобится задать тело ответа в произвольной точке потока исполнения. Вы можете
831
+ сделать это с помощью метода-помощника +body+. Если вы задействуете метод +body+,
832
+ то вы можете использовать его и в дальнейшем, чтобы получить доступ к телу ответа.
833
+
834
+ get '/foo' do
835
+ body "bar"
836
+ end
837
+
838
+ after do
839
+ puts body
840
+ end
841
+
842
+ Также можно передать блок в метод +body+, который затем будет вызван
843
+ обработчиком Rack (такой подход может быть использован для реализации поточного
844
+ ответа, см. "Возвращаемые значения").
845
+
846
+ Аналогично вы можете установить код ответа и его заголовки:
847
+
848
+ get '/foo' do
849
+ status 418
850
+ headers \
851
+ "Allow" => "BREW, POST, GET, PROPFIND, WHEN",
852
+ "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
853
+ body "I'm a tea pot!"
854
+ end
855
+
856
+ Как и +body+, методы +headers+ и +status+, вызванные без аргументов, возвращают
857
+ свои текущие значения.
858
+
859
+ === Стриминг ответов
860
+
861
+ Иногда требуется начать отправлять данные клиенту прямо в процессе генерирования
862
+ частей этих данных. В особых случаях требуется постоянно отправлять данные до тех
863
+ пор, пока клиент не закроет соединение. Вы можете использовать метод +stream+
864
+ вместо написания собственных "оберток".
865
+
866
+ get '/' do
867
+ stream do |out|
868
+ out << "It's gonna be legen -\n"
869
+ sleep 0.5
870
+ out << " (wait for it) \n"
871
+ sleep 1
872
+ out << "- dary!\n"
873
+ end
874
+ end
875
+
876
+ Что позволяет вам реализовать стриминговые API,
877
+ {Server Sent Events}[http://dev.w3.org/html5/eventsource/],
878
+ и может служить основой для {WebSockets}[http://en.wikipedia.org/wiki/WebSocket].
879
+ Также такой подход можно использовать для увеличения производительности в случае,
880
+ когда какая-то часть контента зависит от медленного ресурса.
881
+
882
+ Заметьте, что возможности стриминга, особенно количество одновременно обслуживаемых
883
+ запросов, очень сильно зависят от используемого веб-сервера. Некоторые сервера,
884
+ например, WEBRick, могут и вовсе не поддерживать стриминг. Если сервер не
885
+ поддерживает стриминг, то все данные будут отправлены за один раз сразу после того,
886
+ как блок, переданный в +stream+, завершится. Стриминг вообще не работает при
887
+ использовании Shotgun.
888
+
889
+ Если метод используется с параметром +keep_open+, то он не будет вызывать +close+
890
+ у объекта потока, что позволит вам закрыть его позже в любом другом месте. Это
891
+ работает только с событийными серверами, например, с Thin и Rainbows.
892
+ Другие же сервера все равно будут закрывать поток:
893
+
894
+ set :server, :thin
895
+ connections = []
896
+
897
+ get '/' do
898
+ # держать все потоки открытыми
899
+ stream(:keep_open) { |out| connections << out }
900
+ end
901
+
902
+ post '/' do
903
+ # написать во все открытые потоки
904
+ connections.each { |out| out << params[:message] << "\n" }
905
+ "message sent"
906
+ end
907
+
908
+ === Логирование
909
+
910
+ В области видимости запроса метод +logger+ предоставляет доступ к экземпляру +Logger+:
911
+
912
+ get '/' do
913
+ logger.info "loading data"
914
+ # ...
915
+ end
916
+
917
+ Этот логер автоматически учитывает ваши настройки логирования в Rack. Если
918
+ логирование выключено, то этот метод вернет пустой (dummy) объект, поэтому вы можете
919
+ смело использовать его в маршрутах и фильтрах.
920
+
921
+ Заметьте, что логирование включено по умолчанию только для <tt>Sinatra::Application</tt>,
922
+ а если ваше приложение -- подкласс <tt>Sinatra::Base</tt>, то вы, наверное, захотите включить
923
+ его вручную:
924
+
925
+ class MyApp < Sinatra::Base
926
+ configure :production, :development do
927
+ enable :logging
928
+ end
929
+ end
930
+
931
+ Чтобы избежать использования любой логирующей "прослойки", задайте опции
932
+ +logging+ значение +nil+. Тем не менее, не забывайте, что в такой ситуации
933
+ +logger+ вернет +nil+. Чаще всего так делают, когда задают свой собственный
934
+ логер. Sinatra будет использовать то, что находится в <tt>env['rack.logger']</tt>.
935
+
936
+ === Mime-типы
937
+
938
+ Когда вы используете <tt>send_file</tt> или статические файлы, у вас могут быть mime-типы, которые Sinatra
939
+ не понимает по умолчанию. Используйте +mime_type+ для их регистрации по расширению файла:
940
+
941
+ configure do
942
+ mime_type :foo, 'text/foo'
943
+ end
944
+
945
+ Вы также можете использовать это в +content_type+ методе-помощнике:
946
+
947
+ get '/' do
948
+ content_type :foo
949
+ "foo foo foo"
950
+ end
951
+
952
+ === Генерирование URL
953
+
954
+ Чтобы сформировать URL вам следует использовать метод +url+, например, в Haml:
955
+
956
+ %a{:href => url('/foo')} foo
957
+
958
+ Этот метод учитывает обратные прокси и маршрутизаторы Rack, если они присутствуют.
959
+
960
+ Наряду с +url+ вы можете использовать +to+ (смотрите пример ниже).
961
+
962
+ === Перенаправление (редирект)
963
+
964
+ Вы можете перенаправить браузер пользователя с помощью метода +redirect+:
965
+
966
+ get '/foo' do
967
+ redirect to('/bar')
968
+ end
969
+
970
+ Любые дополнительные параметры используются по аналогии с аргументами метода +halt+:
971
+
972
+ redirect to('/bar'), 303
973
+ redirect 'http://google.com', 'wrong place, buddy'
974
+
975
+ Вы также можете перенаправить пользователя обратно, на страницу с которой он пришел,
976
+ с помощью <tt>redirect back</tt>:
977
+
978
+ get '/foo' do
979
+ "<a href='/bar'>do something</a>"
980
+ end
981
+
982
+ get '/bar' do
983
+ do_something
984
+ redirect back
985
+ end
986
+
987
+ Чтобы передать какие-либо параметры вместе с перенаправлением, либо добавьте их в строку запроса:
988
+
989
+ redirect to('/bar?sum=42')
990
+
991
+ либо используйте сессию:
992
+
993
+ enable :sessions
994
+
995
+ get '/foo' do
996
+ session[:secret] = 'foo'
997
+ redirect to('/bar')
998
+ end
999
+
1000
+ get '/bar' do
1001
+ session[:secret]
1002
+ end
1003
+
1004
+ === Управление кэшированием
1005
+
1006
+ Установка корректных заголовков — основа правильного HTTP кэширования.
1007
+
1008
+ Вы можете легко выставить заголовок Cache-Control таким образом:
1009
+
1010
+ get '/' do
1011
+ cache_control :public
1012
+ "cache it!"
1013
+ end
1014
+
1015
+ Совет: задавайте кэширование в +before+-фильтре:
1016
+
1017
+ before do
1018
+ cache_control :public, :must_revalidate, :max_age => 60
1019
+ end
1020
+
1021
+ Если вы используете метод +expires+ для задания соответствующего заголовка,
1022
+ то <tt>Cache-Control</tt> будет выставлен автоматически:
1023
+
1024
+ before do
1025
+ expires 500, :public, :must_revalidate
1026
+ end
1027
+
1028
+ Чтобы как следует использовать кэширование, вам следует подумать об использовании
1029
+ +etag+ или +last_modified+. Рекомендуется использовать эти методы-помощники *до*
1030
+ выполнения ресурсоемких вычислений, так как они немедленно отправят ответ клиенту,
1031
+ если текущая версия уже есть в их кэше:
1032
+
1033
+ get '/article/:id' do
1034
+ @article = Article.find params[:id]
1035
+ last_modified @article.updated_at
1036
+ etag @article.sha1
1037
+ erb :article
1038
+ end
1039
+
1040
+ Также вы можете использовать
1041
+ {weak ETag}[http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation]:
1042
+
1043
+ etag @article.sha1, :weak
1044
+
1045
+ Эти методы-помощники не станут ничего кэшировать для вас, но они дадут
1046
+ необходимую информацию для вашего кэша. Если вы ищите легкое решение для
1047
+ кэширования, попробуйте {rack-cache}[http://rtomayko.github.com/rack-cache/]:
1048
+
1049
+ require 'rack/cache'
1050
+ require 'sinatra'
1051
+
1052
+ use Rack::Cache
1053
+
1054
+ get '/' do
1055
+ cache_control :public, :max_age => 36000
1056
+ sleep 5
1057
+ "hello"
1058
+ end
1059
+
1060
+ Используйте опцию <tt>:static_cache_control</tt> (см. ниже), чтобы
1061
+ добавить заголовок <tt>Cache-Control</tt> к статическим файлам.
1062
+
1063
+ В соответствии с RFC 2616 ваше приложение должно вести себя по-разному,
1064
+ когда заголовки If-Match или If-None-Match имеют значение <tt>*</tt>, в
1065
+ зависимости от того, существует или нет запрашиваемый ресурс. Sinatra
1066
+ предполагает, что ресурсы, к которым обращаются с помощью безопасных
1067
+ (GET) и идемпотентных (PUT) методов, уже существуют, а остальные ресурсы
1068
+ (к которым обращаются, например, с помощью POST) считает новыми. Вы
1069
+ можете изменить данное поведение с помощью опции <tt>:new_resource</tt>:
1070
+
1071
+ get '/create' do
1072
+ etag '', :new_resource => true
1073
+ Article.create
1074
+ erb :new_article
1075
+ end
1076
+
1077
+ Если вы хотите использовать weak ETag, задайте опцию <tt>:kind</tt>:
1078
+
1079
+ etag '', :new_resource => true, :kind => :weak
1080
+
1081
+ === Отправка файлов
1082
+
1083
+ Для отправки файлов пользователю вы можете использовать метод <tt>send_file</tt>:
1084
+
1085
+ get '/' do
1086
+ send_file 'foo.png'
1087
+ end
1088
+
1089
+ Этот метод имеет несколько опций:
1090
+
1091
+ send_file 'foo.png', :type => :jpg
1092
+
1093
+ Возможные опции:
1094
+
1095
+ [filename]
1096
+ имя файла, по умолчанию: реальное имя файла.
1097
+
1098
+ [last_modified]
1099
+ значение для заголовка Last-Modified, по умолчанию: mtime (время изменения) файла.
1100
+
1101
+ [type]
1102
+ тип файла, по умолчанию: предполагается по расширению файла.
1103
+
1104
+ [disposition]
1105
+ используется для заголовка Content-Disposition, возможные значения:
1106
+ +nil+ (по умолчанию), <tt>:attachment</tt> и <tt>:inline</tt>.
1107
+
1108
+ [length]
1109
+ значения для заголовка Content-Length, по умолчанию: размер файла.
1110
+
1111
+ [status]
1112
+ Код ответа. Полезно, когда отдается статический файл в качестве страницы
1113
+ с сообщением об ошибке.
1114
+
1115
+ Этот метод будет использовать возможности Rack сервера для отправки файлов, если они
1116
+ доступны, а в противном случае, будет напрямую отдавать файл из Ruby процесса.
1117
+ Метод <tt>send_file</tt> также обеспечивает автоматическую обработку частичных (range)
1118
+ запросов с помощью Sinatra.
1119
+
1120
+ === Доступ к объекту запроса
1121
+
1122
+ Объект входящего запроса доступен на уровне обработки запроса (в фильтрах, маршрутах,
1123
+ обработчиках ошибок) с помощью <tt>request</tt> метода:
1124
+
1125
+ # приложение запущено на http://example.com/example
1126
+ get '/foo' do
1127
+ t = %w[text/css text/html application/javascript]
1128
+ request.accept # ['text/html', '*/*']
1129
+ request.accept? 'text/xml' # true
1130
+ request.preferred_type(t) # 'text/html'
1131
+ request.body # тело запроса, посланное клиентом (см. ниже)
1132
+ request.scheme # "http"
1133
+ request.script_name # "/example"
1134
+ request.path_info # "/foo"
1135
+ request.port # 80
1136
+ request.request_method # "GET"
1137
+ request.query_string # ""
1138
+ request.content_length # длина тела запроса
1139
+ request.media_type # медиатип тела запроса
1140
+ request.host # "example.com"
1141
+ request.get? # true (есть аналоги для других методов HTTP)
1142
+ request.form_data? # false
1143
+ request["SOME_HEADER"] # значение заголовка SOME_HEADER
1144
+ request.referrer # источник запроса клиента либо '/'
1145
+ request.user_agent # user agent (используется для :agent условия)
1146
+ request.cookies # хеш, содержащий cookies браузера
1147
+ request.xhr? # является ли запрос ajax запросом?
1148
+ request.url # "http://example.com/example/foo"
1149
+ request.path # "/example/foo"
1150
+ request.ip # IP-адрес клиента
1151
+ request.secure? # false (true, если запрос сделан через SSL)
1152
+ request.forwarded? # true (если сервер работает за обратным прокси)
1153
+ request.env # "сырой" env хеш, полученный Rack
1154
+ end
1155
+
1156
+ Некоторые опции, такие как <tt>script_name</tt> или <tt>path_info</tt> доступны для изменения:
1157
+
1158
+ before { request.path_info = "/" }
1159
+
1160
+ get "/" do
1161
+ "all requests end up here"
1162
+ end
1163
+
1164
+ <tt>request.body</tt> является IO или StringIO объектом:
1165
+
1166
+ post "/api" do
1167
+ request.body.rewind # в случае, если кто-то уже прочитал тело запроса
1168
+ data = JSON.parse request.body.read
1169
+ "Hello #{data['name']}!"
1170
+ end
1171
+
1172
+ === Вложения
1173
+
1174
+ Вы можете использовать метод +attachment+, чтобы сказать браузеру, что ответ
1175
+ сервера должен быть сохранен на диск, а не отображен:
1176
+
1177
+ get '/' do
1178
+ attachment
1179
+ "store it!"
1180
+ end
1181
+
1182
+ Вы также можете указать имя файла:
1183
+
1184
+ get '/' do
1185
+ attachment "info.txt"
1186
+ "store it!"
1187
+ end
1188
+
1189
+ === Работа с временем и датами
1190
+
1191
+ Sinatra предлагает метод-помощник +time_for+, который из заданного значения
1192
+ создает объект Time. Он также может конвертировать +DateTime+, +Date+ и
1193
+ подобные классы:
1194
+
1195
+ get '/' do
1196
+ pass if Time.now > time_for('Dec 23, 2012')
1197
+ "still time"
1198
+ end
1199
+
1200
+ Этот метод используется внутри Sinatra методами +expires+, +last_modified+ и им
1201
+ подобными. Поэтому вы легко можете расширить функционал этих методов,
1202
+ переопределив +time_for+ в своем приложении:
1203
+
1204
+ helpers do
1205
+ def time_for(value)
1206
+ case value
1207
+ when :yesterday then Time.now - 24*60*60
1208
+ when :tomorrow then Time.now + 24*60*60
1209
+ else super
1210
+ end
1211
+ end
1212
+ end
1213
+
1214
+ get '/' do
1215
+ last_modified :yesterday
1216
+ expires :tomorrow
1217
+ "hello"
1218
+ end
1219
+
1220
+ === Поиск шаблонов
1221
+
1222
+ Для поиска шаблонов и их последующего рендеринга используется метод <tt>find_template</tt>:
1223
+
1224
+ find_template settings.views, 'foo', Tilt[:haml] do |file|
1225
+ puts "could be #{file}"
1226
+ end
1227
+
1228
+ Это не слишком полезный пример. Зато полезен тот факт, что вы можете переопределить
1229
+ этот метод, чтобы использовать свой собственный механизм поиска. Например, если вы
1230
+ хотите, чтобы можно было использовать несколько директорий с шаблонами:
1231
+
1232
+ set :views, ['views', 'templates']
1233
+
1234
+ helpers do
1235
+ def find_template(views, name, engine, &block)
1236
+ Array(views).each { |v| super(v, name, engine, &block) }
1237
+ end
1238
+ end
1239
+
1240
+ Другой пример, в котором используются разные директории для движков рендеринга:
1241
+
1242
+ set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views'
1243
+
1244
+ helpers do
1245
+ def find_template(views, name, engine, &block)
1246
+ _, folder = views.detect { |k,v| engine == Tilt[k] }
1247
+ folder ||= views[:default]
1248
+ super(folder, name, engine, &block)
1249
+ end
1250
+ end
1251
+
1252
+ Вы можете легко вынести этот код в расширение и поделиться им с остальными!
1253
+
1254
+ Заметьте, что <tt>find_template</tt> не проверяет, существует ли файл на самом деле,
1255
+ а вызывает заданный блок для всех возможных путей. Дело тут не в производительности,
1256
+ дело в том, что +render+ вызовет +break+, как только файл не будет найден.
1257
+ Содержимое и местонахождение шаблонов будет закэшировано, если приложение запущено не
1258
+ в режиме разработки (set :environment, :development). Вы должны помнить об этих нюансах,
1259
+ если пишите по-настоящему "сумасшедший" метод.
1260
+
1261
+ == Конфигурация
1262
+
1263
+ Этот блок исполняется один раз при старте в любом окружении, режиме (environment):
1264
+
1265
+ configure do
1266
+ # задание одной опции
1267
+ set :option, 'value'
1268
+
1269
+ # устанавливаем несколько опций
1270
+ set :a => 1, :b => 2
1271
+
1272
+ # то же самое, что и `set :option, true`
1273
+ enable :option
1274
+
1275
+ # то же самое, что и `set :option, false`
1276
+ disable :option
1277
+
1278
+ # у вас могут быть "динамические" опции с блоками
1279
+ set(:css_dir) { File.join(views, 'css') }
1280
+ end
1281
+
1282
+ Будет запущено, когда окружение (RACK_ENV переменная) <tt>:production</tt>:
1283
+
1284
+ configure :production do
1285
+ ...
1286
+ end
1287
+
1288
+ Будет запущено, когда окружение <tt>:production</tt> или <tt>:test</tt>:
1289
+
1290
+ configure :production, :test do
1291
+ ...
1292
+ end
1293
+
1294
+ Вы можете получить доступ к этим опциям с помощью <tt>settings</tt>:
1295
+
1296
+ configure do
1297
+ set :foo, 'bar'
1298
+ end
1299
+
1300
+ get '/' do
1301
+ settings.foo? # => true
1302
+ settings.foo # => 'bar'
1303
+ ...
1304
+ end
1305
+
1306
+ === Настройка защиты от атак
1307
+
1308
+ Sinatra использует
1309
+ {Rack::Protection}[https://github.com/rkh/rack-protection#readme] для
1310
+ защиты приложения от простых атак. Вы можете легко выключить эту
1311
+ защиту (что даст прирост в производительности):
1312
+
1313
+ disable :protection
1314
+
1315
+ Чтобы пропустить какой-либо уровень защиты, передайте хеш опций
1316
+ в параметр +protection+:
1317
+
1318
+ set :protection, :except => :path_traversal
1319
+
1320
+ Вы также можете отключить сразу несколько уровней защиты:
1321
+
1322
+ set :protection, :except => [:path_traversal, :session_hijacking]
1323
+
1324
+ === Доступные настройки
1325
+
1326
+ [absolute_redirects] если отключено, то Sinatra будет позволять использование
1327
+ относительных перенаправлений, но при этом перестанет
1328
+ соответствовать RFC 2616 (HTTP 1.1), который разрешает только
1329
+ абсолютные перенаправления.
1330
+
1331
+ Включайте эту опцию, если ваше приложение работает за обратным прокси,
1332
+ который настроен не совсем корректно. Обратите внимание, метод +url+
1333
+ все равно будет генерировать абсолютные URL, если вы не передадите
1334
+ +false+ вторым аргументом.
1335
+
1336
+ Отключено по умолчанию.
1337
+
1338
+ [add_charsets] mime-типы, к которым метод <tt>content_type</tt> будет автоматически
1339
+ добавлять информацию о кодировке.
1340
+
1341
+ Вам следует добавлять значения к этой опции вместо ее переопределения:
1342
+
1343
+ settings.add_charsets << "application/foobar"
1344
+
1345
+ [app_file] путь к главному файлу приложения, используется для нахождения корневой
1346
+ директории проекта, директорий с шаблонами и статическими файлами,
1347
+ вложенных шаблонов.
1348
+
1349
+ [bind] используемый IP-адрес (по умолчанию: 0.0.0.0). Используется только
1350
+ встроенным сервером.
1351
+
1352
+ [default_encoding] кодировка, если неизвестна (по умолчанию: <tt>"utf-8"</tt>).
1353
+
1354
+ [dump_errors] отображать ошибки в логе.
1355
+
1356
+ [environment] текущее окружение, по умолчанию, значение <tt>ENV['RACK_ENV']</tt>
1357
+ или <tt>"development"</tt>, если <tt>ENV['RACK_ENV']</tt> не доступна.
1358
+
1359
+ [logging] использовать логер.
1360
+
1361
+ [lock] создает блокировку для каждого запроса, которая гарантирует обработку
1362
+ только одного запроса в текущий момент времени в Ruby процессе.
1363
+
1364
+ Включайте, если ваше приложение не потоко-безопасно (thread-safe).
1365
+ Отключено по умолчанию.
1366
+
1367
+ [method_override] использовать "магический" параметр <tt>_method</tt>, чтобы позволить
1368
+ использование PUT/DELETE форм в браузерах, которые не поддерживают
1369
+ эти методы.
1370
+
1371
+ [port] порт, на котором будет работать сервер. Используется только
1372
+ встроенным сервером.
1373
+
1374
+ [prefixed_redirects] добавлять или нет параметр <tt>request.script_name</tt> к редиректам,
1375
+ если не задан абсолютный путь. Таким образом, <tt>redirect '/foo'</tt>
1376
+ будет вести себя как <tt>redirect to('/foo')</tt>. Отключено по умолчанию.
1377
+
1378
+ [protection] включена или нет защита от атак. Смотрите секцию выше.
1379
+
1380
+ [public_folder] путь к директории, откуда будут раздаваться статические файлы.
1381
+ Используется только, если включена раздача статических файлов
1382
+ (см. опцию <tt>static</tt> ниже).
1383
+
1384
+ [reload_templates] перезагружать или нет шаблоны на каждый запрос.
1385
+ Включено в режиме разработки.
1386
+
1387
+ [root] путь к корневой директории проекта.
1388
+
1389
+ [raise_errors] выбрасывать исключения (будет останавливать приложение). По умолчанию
1390
+ включено только в окружении <tt>test</tt>.
1391
+
1392
+ [run] если включено, Sinatra будет самостоятельно запускать веб-сервер.
1393
+ Не включайте, если используете rackup или аналогичные средства.
1394
+
1395
+ [running] работает ли сейчас встроенный сервер?
1396
+ Не меняйте эту опцию!
1397
+
1398
+ [server] сервер или список серверов, которые следует использовать в качестве
1399
+ встроенного сервера. По умолчанию: ['thin', 'mongrel', 'webrick'],
1400
+ порядок задает приоритет.
1401
+
1402
+ [sessions] включить сессии на основе кук (cookie) на базе
1403
+ <tt>Rack::Session::Cookie</tt>. Смотрите секцию "Использование сессий"
1404
+ выше.
1405
+
1406
+ [show_exceptions] показывать исключения/стек вызовов (stack trace) в браузере.
1407
+ По умолчанию включено только в окружении <tt>development</tt>.
1408
+
1409
+ [static] должна ли Sinatra осуществлять раздачу статических файлов.
1410
+ Отключите, когда используете какой-либо веб-сервер для этой цели.
1411
+ Отключение значительно улучшит производительность приложения.
1412
+ По умолчанию включено в классических и отключено в модульных
1413
+ приложениях.
1414
+
1415
+ [static_cache_control] когда Sinatra отдает статические файлы, используйте эту опцию,
1416
+ чтобы добавить им заголовок <tt>Cache-Control</tt>. Для этого
1417
+ используется метод-помощник +cache_control+. По умолчанию отключено.
1418
+ Используйте массив, когда надо задать несколько значений:
1419
+ <tt>set :static_cache_control, [:public, :max_age => 300]</tt>
1420
+
1421
+ [threaded] если включено, то Thin будет использовать
1422
+ <tt>EventMachine.defer</tt> для обработки запросов.
1423
+
1424
+ [views] путь к директории с шаблонами.
1425
+
1426
+ == Режим, окружение
1427
+
1428
+ Есть 3 предопределенных режима, окружения: <tt>"development"</tt>,
1429
+ <tt>"production"</tt> и <tt>"test"</tt>. Режим может быть
1430
+ задан через переменную окружения +RACK_ENV+. Значение по умолчанию
1431
+ — <tt>"development"</tt>. В этом режиме работы, все шаблоны
1432
+ перезагружаются между запросами. А также задаются специальные
1433
+ обработчики <tt>not_found</tt> и <tt>error</tt>, чтобы вы могли
1434
+ увидеть стек вызовов. В окружениях <tt>"production"</tt> и
1435
+ <tt>"test"</tt> шаблоны по умолчанию кэшируются.
1436
+
1437
+ Для запуска приложения в определенном окружении, используйте
1438
+ ключ <tt>-e</tt>
1439
+
1440
+ ruby my_app.rb -e [ENVIRONMENT]
1441
+
1442
+ Вы можете использовать предопределенные методы +development?+, +test?+
1443
+ и +production?, чтобы определить текущее окружение.
1444
+
1445
+ == Обработка ошибок
1446
+
1447
+ Обработчики ошибок исполняются в том же контексте, что и маршруты, и +before+-фильтры, а это означает, что всякие
1448
+ прелести вроде <tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt> и т.д. доступны и им.
1449
+
1450
+ === Not Found
1451
+
1452
+ Когда выброшено исключение <tt>Sinatra::NotFound</tt>, или кодом ответа является 404,
1453
+ то будет вызван <tt>not_found</tt> обработчик:
1454
+
1455
+ not_found do
1456
+ 'This is nowhere to be found.'
1457
+ end
1458
+
1459
+ === Ошибки
1460
+
1461
+ Обработчик ошибок +error+ будет вызван, когда исключение выброшено из блока маршрута, либо из фильтра.
1462
+ Объект-исключение доступен как переменная <tt>sinatra.error</tt> в Rack:
1463
+
1464
+ error do
1465
+ 'Sorry there was a nasty error - ' + env['sinatra.error'].name
1466
+ end
1467
+
1468
+ Частные ошибки:
1469
+
1470
+ error MyCustomError do
1471
+ 'So what happened was...' + env['sinatra.error'].message
1472
+ end
1473
+
1474
+ Тогда, если это произошло:
1475
+
1476
+ get '/' do
1477
+ raise MyCustomError, 'something bad'
1478
+ end
1479
+
1480
+ То вы получите:
1481
+
1482
+ So what happened was... something bad
1483
+
1484
+ Также вы можете установить обработчик ошибок для кода состояния HTTP:
1485
+
1486
+ error 403 do
1487
+ 'Access forbidden'
1488
+ end
1489
+
1490
+ get '/secret' do
1491
+ 403
1492
+ end
1493
+
1494
+ Либо набора кодов:
1495
+
1496
+ error 400..510 do
1497
+ 'Boom'
1498
+ end
1499
+
1500
+ Sinatra устанавливает специальные <tt>not_found</tt> и <tt>error</tt> обработчики, когда приложение запущено в режиме
1501
+ разработки (окружение <tt>:development</tt>).
1502
+
1503
+ == Rack "прослойки"
1504
+
1505
+ Sinatra использует Rack[http://rack.rubyforge.org/], минимальный стандартный
1506
+ интерфейс для веб-фреймворков на Ruby. Одной из самых интересных для разработчиков возможностей Rack
1507
+ является поддержка "прослоек" ("middleware") — компонентов,
1508
+ находящихся "между" сервером и вашим приложением, которые отслеживают и/или манипулируют
1509
+ HTTP запросами/ответами для предоставления различной функциональности.
1510
+
1511
+ В Sinatra очень просто использовать такие "прослойки" с помощью метода +use+:
1512
+
1513
+ require 'sinatra'
1514
+ require 'my_custom_middleware'
1515
+
1516
+ use Rack::Lint
1517
+ use MyCustomMiddleware
1518
+
1519
+ get '/hello' do
1520
+ 'Hello World'
1521
+ end
1522
+
1523
+ Семантика +use+ идентична той, что определена для
1524
+ Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSL
1525
+ (чаще всего используется в rackup файлах). Например, метод +use+ принимает
1526
+ как множественные переменные, так и блоки:
1527
+
1528
+ use Rack::Auth::Basic do |username, password|
1529
+ username == 'admin' && password == 'secret'
1530
+ end
1531
+
1532
+ Rack распространяется с различными стандартными "прослойками"
1533
+ для логирования, отладки, маршрутизации URL, аутентификации, обработки сессий. Sinatra использует
1534
+ многие из этих компонентов автоматически, основываясь на конфигурации, чтобы вам не приходилось
1535
+ подключать (+use+) их вручную.
1536
+
1537
+ Вы можете найти полезные прослойки в
1538
+ {rack}[https://github.com/rack/rack/tree/master/lib/rack],
1539
+ {rack-contrib}[https://github.com/rack/rack-contrib#readme],
1540
+ {CodeRack}[http://coderack.org/] или в
1541
+ {Rack wiki}[https://github.com/rack/rack/wiki/List-of-Middleware].
1542
+
1543
+ == Тестирование
1544
+
1545
+ Тесты для Sinatra приложений могут быть написаны с помощью библиотек, фреймворков, поддерживающих
1546
+ тестирование Rack. {Rack::Test}[http://rdoc.info/github/brynary/rack-test/master/frames] рекомендован:
1547
+
1548
+ require 'my_sinatra_app'
1549
+ require 'test/unit'
1550
+ require 'rack/test'
1551
+
1552
+ class MyAppTest < Test::Unit::TestCase
1553
+ include Rack::Test::Methods
1554
+
1555
+ def app
1556
+ Sinatra::Application
1557
+ end
1558
+
1559
+ def test_my_default
1560
+ get '/'
1561
+ assert_equal 'Hello World!', last_response.body
1562
+ end
1563
+
1564
+ def test_with_params
1565
+ get '/meet', :name => 'Frank'
1566
+ assert_equal 'Hello Frank!', last_response.body
1567
+ end
1568
+
1569
+ def test_with_rack_env
1570
+ get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
1571
+ assert_equal "You're using Songbird!", last_response.body
1572
+ end
1573
+ end
1574
+
1575
+ == Sinatra::Base — "прослойки", библиотеки и модульные приложения
1576
+
1577
+ Описание своего приложения самым простейшим способом (с помощью DSL верхнего уровня,
1578
+ классический стиль) отлично работает для крохотных приложений. В таких случаях
1579
+ используется конфигурация, рассчитанная на микро-приложения
1580
+ (единственный файл приложения, <tt>./public</tt> и <tt>./views</tt> директории,
1581
+ логирование, страница информации об исключении и т.д.). Тем не менее,
1582
+ такой метод имеет множество недостатков при создании компонентов, таких как
1583
+ Rack middleware ("прослоек"), Rails metal, простых библиотек с серверными компонентами,
1584
+ расширений Sinatra. И тут на помощь приходит <tt>Sinatra::Base</tt>:
1585
+
1586
+ require 'sinatra/base'
1587
+
1588
+ class MyApp < Sinatra::Base
1589
+ set :sessions, true
1590
+ set :foo, 'bar'
1591
+
1592
+ get '/' do
1593
+ 'Hello world!'
1594
+ end
1595
+ end
1596
+
1597
+ Методы, доступные <tt>Sinatra::Base</tt> подклассам идентичны тем, что доступны
1598
+ приложениям в DSL верхнего уровня. Большинство таких приложений могут быть
1599
+ конвертированы в <tt>Sinatra::Base</tt> компоненты с помощью двух модификаций:
1600
+
1601
+ * Вы должны подключать <tt>sinatra/base</tt> вместо +sinatra+,
1602
+ иначе все методы, предоставляемые Sinatra, будут импортированы в глобальное пространство имен.
1603
+ * Поместите все маршруты, обработчики ошибок, фильтры и опции в подкласс <tt>Sinatra::Base</tt>.
1604
+
1605
+ <tt>Sinatra::Base</tt> — это чистый лист. Большинство опций, включая встроенный сервер, по умолчанию отключены.
1606
+ Смотрите {Опции и конфигурация}[http://www.sinatrarb.com/configuration.html] для детальной информации
1607
+ об опциях и их поведении.
1608
+
1609
+ === Модульные приложения против классических
1610
+
1611
+ Вопреки всеобщему убеждению, в классическом стиле (самом простом) нет ничего плохого.
1612
+ Если этот стиль подходит вашему приложению, вы не обязаны переписывать его в модульное
1613
+ приложение.
1614
+
1615
+ Основным недостатком классического стиля является тот факт, что у вас может
1616
+ быть только одно приложение Sinatra на один процесс Ruby. Если вы планируете
1617
+ использовать больше, переключайтесь на модульный стиль. Вы можете смело
1618
+ смешивать модульный и классический стили.
1619
+
1620
+ Переходя с одного стиля на другой, примите во внимание следующие изменения в настройках:
1621
+
1622
+ Опция Классический Модульный
1623
+
1624
+ app_file файл с приложением файл с подклассом Sinatra::Base
1625
+ run $0 == app_file false
1626
+ logging true false
1627
+ method_override true false
1628
+ inline_templates true false
1629
+ static true false
1630
+
1631
+
1632
+ === Запуск модульных приложений
1633
+
1634
+ Есть два общепринятых способа запускать модульные приложения: запуск напрямую с помощью <tt>run!</tt>:
1635
+
1636
+ # my_app.rb
1637
+ require 'sinatra/base'
1638
+
1639
+ class MyApp < Sinatra::Base
1640
+ # ... здесь код приложения ...
1641
+
1642
+ # запускаем сервер, если исполняется текущий файл
1643
+ run! if app_file == $0
1644
+ end
1645
+
1646
+ И запускаем с помощью:
1647
+
1648
+ ruby my_app.rb
1649
+
1650
+ Или с помощью конфигурационного файла <tt>config.ru</tt>, который позволяет использовать любой
1651
+ Rack-совместимый сервер приложений.
1652
+
1653
+ # config.ru
1654
+ require './my_app'
1655
+ run MyApp
1656
+
1657
+ Запускаем:
1658
+
1659
+ rackup -p 4567
1660
+
1661
+ === Запуск классических приложений с config.ru
1662
+
1663
+ Файл приложения:
1664
+
1665
+ # app.rb
1666
+ require 'sinatra'
1667
+
1668
+ get '/' do
1669
+ 'Hello world!'
1670
+ end
1671
+
1672
+ И соответствующий <tt>config.ru</tt>:
1673
+
1674
+ require './app'
1675
+ run Sinatra::Application
1676
+
1677
+ === Когда использовать config.ru?
1678
+
1679
+ Вот несколько причин, по которым вы, возможно, захотите использовать <tt>config.ru</tt>:
1680
+
1681
+ * вы хотите разворачивать свое приложение на различных Rack-совместимых серверах (Passenger, Unicorn,
1682
+ Heroku, ...);
1683
+ * вы хотите использовать более одного подкласса <tt>Sinatra::Base</tt>;
1684
+ * вы хотите использовать Sinatra только в качестве "прослойки" Rack.
1685
+
1686
+ <b>Совсем необязательно переходить на использование <tt>config.ru</tt> лишь потому, что вы стали
1687
+ использовать модульный стиль приложения. И необязательно использовать модульный стиль, чтобы
1688
+ запускать приложение с помощью <tt>config.ru</tt>.</b>
1689
+
1690
+ === Использование Sinatra в качестве "прослойки"
1691
+
1692
+ Не только сама Sinatra может использовать "прослойки" Rack, но и любое Sinatra приложение
1693
+ само может быть добавлено к любому Rack endpoint в качестве "прослойки". Этим endpoint (конечной точкой)
1694
+ может быть другое Sinatra приложение, или приложение, основанное на Rack (Rails/Ramaze/Camping/...):
1695
+
1696
+ require 'sinatra/base'
1697
+
1698
+ class LoginScreen < Sinatra::Base
1699
+ enable :sessions
1700
+
1701
+ get('/login') { haml :login }
1702
+
1703
+ post('/login') do
1704
+ if params[:name] == 'admin' && params[:password] == 'admin'
1705
+ session['user_name'] = params[:name]
1706
+ else
1707
+ redirect '/login'
1708
+ end
1709
+ end
1710
+ end
1711
+
1712
+ class MyApp < Sinatra::Base
1713
+ # "прослойка" будет запущена перед фильтрами
1714
+ use LoginScreen
1715
+
1716
+ before do
1717
+ unless session['user_name']
1718
+ halt "Access denied, please <a href='/login'>login</a>."
1719
+ end
1720
+ end
1721
+
1722
+ get('/') { "Hello #{session['user_name']}." }
1723
+ end
1724
+
1725
+ === Создание приложений "на лету"
1726
+
1727
+ Иногда требуется создавать Sinatra приложения "на лету" (например,
1728
+ из другого приложения). Это возможно с помощью <tt>Sinatra.new</tt>:
1729
+
1730
+ require 'sinatra/base'
1731
+ my_app = Sinatra.new { get('/') { "hi" } }
1732
+ my_app.run!
1733
+
1734
+ Этот метод может принимать аргументом приложение, от которого
1735
+ следует наследоваться:
1736
+
1737
+ # config.ru
1738
+ require 'sinatra/base'
1739
+
1740
+ controller = Sinatra.new do
1741
+ enable :logging
1742
+ helpers MyHelpers
1743
+ end
1744
+
1745
+ map('/a') do
1746
+ run Sinatra.new(controller) { get('/') { 'a' } }
1747
+ end
1748
+
1749
+ map('/b') do
1750
+ run Sinatra.new(controller) { get('/') { 'b' } }
1751
+ end
1752
+
1753
+ Это особенно полезно для тестирования расширений Sinatra и при
1754
+ использовании Sinatra внутри вашей библиотеки.
1755
+
1756
+ Благодаря этому, использовать Sinatra как "прослойку" очень просто:
1757
+
1758
+ require 'sinatra/base'
1759
+
1760
+ use Sinatra do
1761
+ get('/') { ... }
1762
+ end
1763
+
1764
+ run RailsProject::Application
1765
+
1766
+
1767
+ == Области видимости и привязка
1768
+
1769
+ Текущая область видимости определяет методы и переменные, доступные
1770
+ в данный момент.
1771
+
1772
+ === Область видимости приложения / класса
1773
+
1774
+ Любое Sinatra приложение соответствует подклассу <tt>Sinatra::Base</tt>. Если вы
1775
+ используете DSL верхнего уровня (<tt>require 'sinatra'</tt>), то этим классом будет
1776
+ <tt>Sinatra::Application</tt>, иначе это будет подкласс, который вы создали вручную.
1777
+ На уровне класса вам будут доступны такие методы, как +get+ или +before+, но вы
1778
+ не сможете получить доступ к объектам +request+ или +session+, так как существует
1779
+ только один класс приложения для всех запросов.
1780
+
1781
+ Опции, созданные с помощью +set+, являются методами уровня класса:
1782
+
1783
+ class MyApp < Sinatra::Base
1784
+ # Я в области видимости приложения!
1785
+ set :foo, 42
1786
+ foo # => 42
1787
+
1788
+ get '/foo' do
1789
+ # Я больше не в области видимости приложения!
1790
+ end
1791
+ end
1792
+
1793
+ У вас будет область видимости приложения внутри:
1794
+
1795
+ * тела вашего класса приложения;
1796
+ * методов, определенных расширениями;
1797
+ * блока, переданного в +helpers+;
1798
+ * блоков, использованных как значения для +set+;
1799
+ * блока, переданного в <tt>Sinatra.new</tt>.
1800
+
1801
+ Вы можете получить доступ к объекту области видимости (классу приложения) следующими способами:
1802
+
1803
+ * через объект, переданный блокам конфигурации (<tt>configure { |c| ... }</tt>);
1804
+ * +settings+ внутри области видимости запроса.
1805
+
1806
+ === Область видимости запроса/экземпляра
1807
+
1808
+ Для каждого входящего запроса будет создан новый экземпляр вашего приложения,
1809
+ и все блоки обработчика будут запущены в этом контексте. В этой области
1810
+ видимости вам доступны +request+ и +session+ объекты, вызовы методов
1811
+ рендеринга, такие как +erb+ или +haml+. Вы можете получить доступ к
1812
+ области видимости приложения из контекста запроса, используя метод-помощник +settings+:
1813
+
1814
+ class MyApp < Sinatra::Base
1815
+ # Я в области видимости приложения!
1816
+ get '/define_route/:name' do
1817
+ # Область видимости запроса '/define_route/:name'
1818
+ @value = 42
1819
+
1820
+ settings.get("/#{params[:name]}") do
1821
+ # Область видимости запроса "/#{params[:name]}"
1822
+ @value # => nil (другой запрос)
1823
+ end
1824
+
1825
+ "Route defined!"
1826
+ end
1827
+ end
1828
+
1829
+ У вас будет область видимости запроса в:
1830
+
1831
+ * get/head/post/put/delete/options блоках;
1832
+ * before/after фильтрах;
1833
+ * методах-помощниках;
1834
+ * шаблонах/отображениях.
1835
+
1836
+ === Область видимости делегирования
1837
+
1838
+ Область видимости делегирования просто перенаправляет методы в область видимости класса.
1839
+ Однако, она не полностью ведет себя как область видимости класса, так как у вас нет
1840
+ привязки к классу. Только методы, явно помеченные для делегирования, будут доступны,
1841
+ а переменных/состояний области видимости класса не будет (иначе говоря,
1842
+ у вас будет другой +self+ объект). Вы можете
1843
+ непосредственно добавить методы делегирования, используя
1844
+ <tt>Sinatra::Delegator.delegate :method_name</tt>.
1845
+
1846
+ У вас будет контекст делегирования внутри:
1847
+
1848
+ * привязки верхнего уровня, если вы сделали <tt>require 'sinatra'</tt>;
1849
+ * объекта, расширенного с помощью <tt>Sinatra::Delegator</tt>.
1850
+
1851
+ Посмотрите сами в код: вот
1852
+ {Sinatra::Delegator примесь}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633]
1853
+ {расширяет главный объект}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30].
1854
+
1855
+ == Командная строка
1856
+
1857
+ Sinatra приложения могут быть запущены напрямую:
1858
+
1859
+ ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER]
1860
+
1861
+ Опции включают:
1862
+
1863
+ -h # раздел помощи
1864
+ -p # указание порта (по умолчанию 4567)
1865
+ -o # указание хоста (по умолчанию 0.0.0.0)
1866
+ -e # указание окружения, режима (по умолчанию development)
1867
+ -s # указание rack сервера/обработчика (по умолчанию thin)
1868
+ -x # включить мьютекс-блокировку (по умолчанию выключена)
1869
+
1870
+ == Системные требования
1871
+
1872
+ Следующие версии Ruby официально поддерживаются:
1873
+
1874
+ [ Ruby 1.8.7 ]
1875
+ 1.8.7 полностью поддерживается, тем не менее, если вас ничто не держит на
1876
+ этой версии, рекомендуем обновиться до 1.9.2 или перейти на JRuby или Rubinius.
1877
+ Поддержка 1.8.7 не будет прекращена до выхода Sinatra 2.0 и Ruby 2.0,
1878
+ разве что в случае релиза 1.8.8 (что мало вероятно). Но даже тогда, возможно,
1879
+ поддержка не будет прекращена. <b>Ruby 1.8.6 больше не поддерживается.</b>
1880
+ Если вы хотите использовать 1.8.6, откатитесь до Sinatra 1.2, которая будет
1881
+ получать все исправления ошибок до тех пор, пока не будет выпущена
1882
+ Sinatra 1.4.0.
1883
+
1884
+ [ Ruby 1.9.2 ]
1885
+ 1.9.2 полностью поддерживается и рекомендована к использованию. Заметьте,
1886
+ что Radius и Markaby пока несовместимы с 1.9. Не используйте 1.9.2p0,
1887
+ известно, что эта версия очень нестабильна при использовании Sinatra.
1888
+ Эта версия будет поддерживаться по крайней мере до выхода Ruby 1.9.4/2.0,
1889
+ а поддержка последней версии 1.9 будет осуществляться до тех пор, пока
1890
+ она поддерживается командой разработчиков Ruby.
1891
+
1892
+ [ Ruby 1.9.3 ]
1893
+ 1.9.3 полностью поддерживается. Рекомендуем подождать релиза версии с
1894
+ большим уровнем патчсета (текущий — p0) перед тем, как использовать ее
1895
+ в рабочем окружении. Заметьте, что переход на 1.9.3 с ранних версий
1896
+ сделает недействительными все сессии.
1897
+
1898
+ [ Rubinius ]
1899
+ Rubinius официально поддерживается (Rubinius >= 1.2.4), всё, включая все
1900
+ языки шаблонов, работает. Предстоящий релиз 2.0 также поддерживается.
1901
+
1902
+ [ JRuby ]
1903
+ JRuby официально поддерживается (JRuby >= 1.6.5). Нет никаких проблем с
1904
+ использованием альтернативных шаблонов. Тем не менее, если вы выбираете
1905
+ JRuby, то, пожалуйста, посмотрите на JRuby Rack-сервера, так как Thin не
1906
+ поддерживается полностью на JRuby. Поддержка расширений на C в JRuby все
1907
+ еще экспериментальная, что на данный момент затрагивает только RDiscount,
1908
+ Redcarpet и RedCloth.
1909
+
1910
+ Мы также следим за предстоящими к выходу версиями Ruby.
1911
+
1912
+ Следующие реализации Ruby не поддерживаются официально, но известно, что на
1913
+ них запускается Sinatra:
1914
+
1915
+ * старые версии JRuby и Rubinius;
1916
+ * Ruby Enterprise Edition;
1917
+ * MacRuby, Maglev, IronRuby;
1918
+ * Ruby 1.9.0 и 1.9.1 (настоятельно не рекомендуются к использованию).
1919
+
1920
+ То, что версия официально не поддерживается, означает, что, если что-то не
1921
+ работает на этой версии, а на поддерживаемой работает — это не наша проблема, а их.
1922
+
1923
+ Мы также запускаем наши CI-тесты на версии Ruby, находящейся в разработке
1924
+ (предстоящей 2.0.0), и на 1.9.4, но мы не можем ничего гарантировать, так как
1925
+ они находятся в разработке. Предполагается, что 1.9.4p0 и 2.0.0p0 будут поддерживаться.
1926
+
1927
+ Sinatra должна работать на любой операционной системе, в которой есть одна из указанных выше версий Ruby.
1928
+
1929
+ Пока невозможно запустить Sinatra на Cardinal, SmallRuby, BlueRuby и на любой
1930
+ версии Ruby до 1.8.7.
1931
+
1932
+ == На острие
1933
+
1934
+ Если вы хотите использовать самый последний код Sinatra, не бойтесь запускать
1935
+ свое приложение вместе с кодом из master ветки Sinatra, она весьма стабильна.
1936
+
1937
+ Мы также время от времени выпускаем предварительные версии, так что вы можете делать так:
1938
+
1939
+ gem install sinatra --pre
1940
+
1941
+ Чтобы воспользоваться некоторыми самыми последними возможностями.
1942
+
1943
+ === С помощью Bundler
1944
+
1945
+ Если вы хотите запускать свое приложение с последней версией Sinatra, то
1946
+ рекомендуем использовать {Bundler}[http://gembundler.com/].
1947
+
1948
+ Сначала установите Bundler, если у вас его еще нет:
1949
+
1950
+ gem install bundler
1951
+
1952
+ Затем создайте файл +Gemfile+ в директории вашего проекта:
1953
+
1954
+ source :rubygems
1955
+ gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
1956
+
1957
+ # другие зависимости
1958
+ gem 'haml' # например, если используете haml
1959
+ gem 'activerecord', '~> 3.0' # может быть, вам нужен и ActiveRecord 3.x
1960
+
1961
+ Обратите внимание, вам нужно будет указывать все зависимости вашего приложения
1962
+ в этом файле. Однако, непосредственные зависимости Sinatra (Rack и Tilt) Bundler
1963
+ автоматически скачает и добавит.
1964
+
1965
+ Теперь вы можете запускать свое приложение так:
1966
+
1967
+ bundle exec ruby myapp.rb
1968
+
1969
+ === Вручную
1970
+
1971
+ Создайте локальный клон репозитория и запускайте свое приложение с <tt>sinatra/lib</tt>
1972
+ директорией в <tt>$LOAD_PATH</tt>:
1973
+
1974
+ cd myapp
1975
+ git clone git://github.com/sinatra/sinatra.git
1976
+ ruby -Isinatra/lib myapp.rb
1977
+
1978
+ Чтобы обновить исходники Sinatra:
1979
+
1980
+ cd myapp/sinatra
1981
+ git pull
1982
+
1983
+ === Установка глобально
1984
+
1985
+ Вы можете самостоятельно собрать gem:
1986
+
1987
+ git clone git://github.com/sinatra/sinatra.git
1988
+ cd sinatra
1989
+ rake sinatra.gemspec
1990
+ rake install
1991
+
1992
+ Если вы устанавливаете пакеты (gem) от пользователя root, то вашим следующим шагом должна быть команда
1993
+
1994
+ sudo rake install
1995
+
1996
+ == Версии
1997
+
1998
+ Sinatra использует {Semantic Versioning}[http://semver.org/], SemVer и
1999
+ SemVerTag.
2000
+
2001
+ == Дальнейшее чтение
2002
+
2003
+ * {Веб-сайт проекта}[http://www.sinatrarb.com/] — Дополнительная документация,
2004
+ новости и ссылки на другие ресурсы.
2005
+ * {Участие в проекте}[http://www.sinatrarb.com/contributing] — Обнаружили баг? Нужна помощь? Написали патч?
2006
+ * {Слежение за проблемами/ошибками}[http://github.com/sinatra/sinatra/issues]
2007
+ * {Twitter}[http://twitter.com/sinatra]
2008
+ * {Группы рассылки}[http://groups.google.com/group/sinatrarb/topics]
2009
+ * {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] на http://freenode.net
2010
+ * {Sinatra Book}[http://sinatra-book.gittr.com] учебник и сборник рецептов
2011
+ * {Sinatra Recipes}[http://recipes.sinatrarb.com/] сборник рецептов
2012
+ * API документация к {последнему релизу}[http://rubydoc.info/gems/sinatra]
2013
+ или {текущему HEAD}[http://rubydoc.info/github/sinatra/sinatra] на
2014
+ http://rubydoc.info
2015
+ * {Сервер непрерывной интеграции}[http://ci.rkh.im/view/Sinatra/]