sinatra-base 1.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,2116 @@
1
+ = Sinatra
2
+
3
+ <i>Attention : Ce document correspond à la traduction de la version anglaise et
4
+ il n'est peut être plus à jour.</i>
5
+
6
+ Sinatra est un DSL pour créer rapidement et facilement des applications web en
7
+ Ruby :
8
+
9
+ # mon_application.rb
10
+ require 'sinatra'
11
+
12
+ get '/' do
13
+ 'Bonjour le monde !'
14
+ end
15
+
16
+ Installez la gem et lancez avec :
17
+
18
+ gem install sinatra
19
+ ruby -rubygems mon_application.rb
20
+
21
+ Le résultat est visible sur : http://localhost:4567
22
+
23
+ Il est recommandé d'exécuter également <tt>gem install thin</tt>, pour que
24
+ Sinatra utilise le server Thin quand il est disponible.
25
+
26
+ == Routes
27
+
28
+ Dans Sinatra, une route est une méthode HTTP couplée à un masque (pattern)
29
+ URL. Chaque route est associée à un bloc :
30
+
31
+ get '/' do
32
+ .. montrer quelque chose ..
33
+ end
34
+
35
+ post '/' do
36
+ .. créer quelque chose ..
37
+ end
38
+
39
+ put '/' do
40
+ .. remplacer quelque chose ..
41
+ end
42
+
43
+ patch '/' do
44
+ .. changer quelque chose ..
45
+ end
46
+
47
+ delete '/' do
48
+ .. effacer quelque chose ..
49
+ end
50
+
51
+ options '/' do
52
+ .. apaiser quelquechose ..
53
+ end
54
+
55
+ Les routes sont évaluées dans l'ordre où elles ont été définies. La première
56
+ route qui correspond à la requête est appelée.
57
+
58
+ Les masques peuvent inclure des paramètres nommés, accessibles par
59
+ l'intermédiaire du hash <tt>params</tt> :
60
+
61
+ get '/bonjour/:nom' do
62
+ # répond aux requêtes "GET /bonjour/foo" et "GET /bonjour/bar"
63
+ # params[:nom] est 'foo' ou 'bar'
64
+ "Bonjour #{params[:nom]} !"
65
+ end
66
+
67
+ Vous pouvez aussi accéder aux paramètres nommés directement grâce aux
68
+ paramètres du bloc comme ceci :
69
+
70
+ get '/bonjour/:nom' do |n|
71
+ "Bonjour #{n} !"
72
+ end
73
+
74
+ Une route peut contenir un splat (caractère joker), accessible par
75
+ l'intermédiaire du tableau <tt>params[:splat]</tt> :
76
+
77
+ get '/dire/*/a/*' do
78
+ # répond à /dire/bonjour/a/monde
79
+ params[:splat] # => ["bonjour", "monde"]
80
+ end
81
+
82
+ get '/telecharger/*.*' do
83
+ # répond à /telecharger/chemin/vers/fichier.xml
84
+ params[:splat] # => ["chemin/vers/fichier", "xml"]
85
+ end
86
+
87
+ Ou par l'intermédiaire des paramètres du bloc :
88
+
89
+ get '/telecharger/*.*' do |chemin, ext|
90
+ [chemin, ext] # => ["path/to/file", "xml"]
91
+ end
92
+
93
+ Une route peut aussi être définie par une Expression Régulière :
94
+
95
+ get %r{/bonjour/([\w]+)} do
96
+ "Bonjour, #{params[:captures].first} !"
97
+ end
98
+
99
+ Là encore on peut utiliser les paramètres de bloc :
100
+
101
+ get %r{/bonjour/([\w]+)} do |c|
102
+ "Bonjour, #{c} !"
103
+ end
104
+
105
+ Les routes peuvent aussi comporter des paramètres optionnels :
106
+
107
+ get '/posts.?:format?' do
108
+ # répond à "GET /posts" et aussi à "GET /posts.json", "GET /posts.xml" etc...
109
+ end
110
+
111
+ A ce propos, à moins d'avoir désactivé la protection contre les attaques par
112
+ "path transversal" (voir plus loin), l'URL demandée peut avoir été modifiée
113
+ avant d'être comparée à vos routes.
114
+
115
+ === Conditions
116
+
117
+ Les routes peuvent définir toutes sortes de conditions, comme par exemple le
118
+ "user agent" :
119
+
120
+ get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
121
+ "Vous utilisez Songbird version #{params[:agent][0]}"
122
+ end
123
+
124
+ get '/foo' do
125
+ # Correspond à tous les autres navigateurs
126
+ end
127
+
128
+ Les autres conditions disponibles sont +host_name+ et +provides+ :
129
+
130
+ get '/', :host_name => /^admin\./ do
131
+ "Zone Administrateur, Accès refusé !"
132
+ end
133
+
134
+ get '/', :provides => 'html' do
135
+ haml :index
136
+ end
137
+
138
+ get '/', :provides => ['rss', 'atom', 'xml'] do
139
+ builder :feed
140
+ end
141
+
142
+ Vous pouvez facilement définir vos propres conditions :
143
+
144
+ set(:probability) { |value| condition { rand <= value } }
145
+
146
+ get '/gagner_une_voiture', :probability => 0.1 do
147
+ "Vous avez gagné !"
148
+ end
149
+
150
+ get '/gagner_une_voiture' do
151
+ "Désolé, vous avez perdu."
152
+ end
153
+
154
+ Utilisez un splat (caractère joker) dans le cas d'une condition qui prend
155
+ plusieurs valeurs :
156
+
157
+ set(:auth) do |*roles| # <- ici on utilise un splat
158
+ condition do
159
+ unless logged_in? && roles.any? {|role| current_user.in_role? role }
160
+ redirect "/login/", 303
161
+ end
162
+ end
163
+ end
164
+
165
+ get "/mon/compte/", :auth => [:user, :admin] do
166
+ "Informations sur votre compte"
167
+ end
168
+
169
+ get "/reserve/aux/admins/", :auth => :admin do
170
+ "Seuls les administrateurs sont acceptés ici !"
171
+ end
172
+
173
+ === Valeurs de retour
174
+
175
+ La valeur renvoyée par le bloc correspondant à une route constitue le corps de
176
+ la réponse qui sera transmise au client HTTP ou du moins au prochain middleware
177
+ dans la pile Rack. Le plus souvent, il s'agit d'une chaîne de caractères,
178
+ comme dans les exemples précédents. Cependant, d'autres valeurs sont
179
+ acceptées.
180
+
181
+ Vous pouvez renvoyer n'importe quel objet qu'il s'agisse d'une réponse Rack
182
+ valide, d'un corps de réponse Rack ou d'un code statut HTTP :
183
+
184
+ * Un tableau de 3 éléments : <tt>[code statut (Fixnum), entêtes (Hash), corps
185
+ de la réponse (répondant à #each)]</tt>
186
+ * Un tableau de 2 élements : <tt>[code statut (Fixnum), corps de la réponse
187
+ (répondant à #each)]</tt>
188
+ * Un objet qui répond à <tt>#each</tt> et qui ne transmet que des chaînes de
189
+ caractères au bloc fourni
190
+ * Un Fixnum représentant le code statut
191
+
192
+ Avec cela, on peut facilement implémenter un streaming par exemple :
193
+
194
+ class Stream
195
+ def each
196
+ 100.times { |i| yield "#{i}\n" }
197
+ end
198
+ end
199
+
200
+ get('/') { Stream.new }
201
+
202
+ Vous pouvez aussi utiliser le helper +stream+ (présenté un peu plus loin) pour
203
+ éviter la surcharge et intégrer le traitement relatif au streaming dans le bloc
204
+ de code de la route.
205
+
206
+ === Masques de route spécifiques
207
+
208
+ Comme cela a été vu auparavant, Sinatra offre la possibilité d'utiliser des
209
+ masques sous forme de chaines de caractères ou des expressions régulières
210
+ pour définir les routes. Mais il est possible de faire bien plus. Vous pouvez
211
+ facilement définir vos propres masques :
212
+
213
+ class MasqueToutSauf
214
+ Masque = Struct.new(:captures)
215
+
216
+ def initialize(except)
217
+ @except = except
218
+ @captures = Masque.new([])
219
+ end
220
+
221
+ def match(str)
222
+ @caputres unless @except === str
223
+ end
224
+ end
225
+
226
+ def tout_sauf(masque)
227
+ MasqueToutSauf.new(masque)
228
+ end
229
+
230
+ get tout_sauf("/index") do
231
+ # ...
232
+ end
233
+
234
+ Notez que l'exemple ci-dessus est bien trop compliqué et que le même résultat
235
+ peut être obtenu avec :
236
+
237
+ get // do
238
+ pass if request.path_info == "/index"
239
+ # ...
240
+ end
241
+
242
+ Ou bien en utilisant la forme négative :
243
+
244
+ get %r{^(?!/index$)} do
245
+ # ...
246
+ end
247
+
248
+ == Fichiers statiques
249
+
250
+ Les fichiers du dossier <tt>./public</tt> sont servis de façon statique. Vous
251
+ avez la possibilité d'utiliser un autre répertoire en définissant le paramètre
252
+ <tt>:public_folder</tt> :
253
+
254
+ set :public_folder, File.dirname(__FILE__) + '/statique'
255
+
256
+ Notez que le nom du dossier public n'apparait pas dans l'URL. Le fichier
257
+ <tt>./public/css/style.css</tt> sera appelé via l'URL :
258
+ <tt>http://exemple.com/css/style.css</tt>.
259
+
260
+ Utilisez le paramètre <tt>:static_cache_control</tt> pour ajouter l'information
261
+ d'en-tête <tt>Cache-Control</tt> (voir plus loin).
262
+
263
+ == Vues / Templates
264
+
265
+ Chaqie langage de template est disponible via sa propre méthode de rendu,
266
+ lesquelles renvoient tout simplement une chaîne de caractères.
267
+
268
+ get '/' do
269
+ erb :index
270
+ end
271
+
272
+ Ceci effectue le rendu de la vue <tt>views/index.erb</tt>.
273
+
274
+ Plutôt que d'utiliser le nom d'un template, vous pouvez directement passer
275
+ le contenu du template :
276
+
277
+ get '/' do
278
+ code = "<%= Time.now %>"
279
+ erb code
280
+ end
281
+
282
+ Les méthodes de templates acceptent un second paramètre, un hash d'options :
283
+
284
+ get '/' do
285
+ erb :index, :layout => :post
286
+ end
287
+
288
+ Ceci effectuera le rendu de la vue <tt>views/index.erb</tt> en l'intégrant
289
+ au +layout+ <tt>views/post.erb</tt> (les vues Erb sont intégrées par défaut
290
+ au +layout+ <tt>views/layout.erb</tt> quand ce fichier existe).
291
+
292
+ Toute option que Sinatra ne comprend pas sera passée au moteur de rendu :
293
+
294
+ get '/' do
295
+ haml :index, :format => :html5
296
+ end
297
+
298
+ Vous pouvez également définir des options par langage de template de façon
299
+ générale :
300
+
301
+ set :haml, :format => html5
302
+
303
+ get '/' do
304
+ haml :index
305
+ end
306
+
307
+ Les options passées à la méthode de rendu prennent le pas sur les options
308
+ définies au moyen de +set+.
309
+
310
+ Options disponibles :
311
+
312
+ [locals]
313
+ Liste de variables locales passées au document. Pratique pour les vues
314
+ partielles.
315
+ Exemple : <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>.
316
+
317
+ [default_encoding]
318
+ Encodage de caractères à utiliser en cas d'incertitude. Par défaut, c'est
319
+ <tt>settings.default_encoding</tt>.
320
+
321
+ [views]
322
+ Dossier de vues dans lequel chercher les templates. Par défaut
323
+ <tt>settings.views</tt>.
324
+
325
+ [layout]
326
+ S'il faut ou non utiliser un +layout+ (+true+ or +false+). Indique le
327
+ template à utiliser lorsque c'est un symbole. Exemple : <tt>erb :index,
328
+ :layout => !request.xhr?</tt>.
329
+
330
+ [content_type]
331
+ Content-Type que le template produit, dépend par défaut du langage de
332
+ template.
333
+
334
+ [scope]
335
+ Contexte sous lequel effectuer le rendu du template. Par défaut il s'agit
336
+ de l'instance de l'application. Si vous changez cela, les variables
337
+ d'instance et les méthodes utilitaires ne seront pas disponibles.
338
+
339
+ [layout_engine]
340
+ Moteur de rendu à utiliser pour le +layout+. Utile pour les langages ne
341
+ supportant pas les +layouts+. Il s'agit par défaut du moteur utilisé pour
342
+ le rendu du template. Exemple : <tt>set :rdoc, :layout_engine => :erb</tt>
343
+
344
+ Les templates sont supposés se trouver directement dans le dossier
345
+ <tt>./views</tt>. Pour utiliser un dossier de vues différent :
346
+
347
+ set :views, settings.root + '/templates'
348
+
349
+ Il est important de se souvenir que les templates sont toujours référencés
350
+ sous forme de symboles, même lorsqu'ils sont dans un sous-répertoire (dans
351
+ ce cas, utilisez <tt>:'sous_repertoire/template'</tt>). Il faut utiliser
352
+ un symbole car les méthodes de rendu évaluent le contenu des chaînes de
353
+ caractères au lieu de les considérer comme un chemin vers un fichier.
354
+
355
+ === Langages de template disponibles
356
+
357
+ Certains langages ont plusieurs implémentations. Pour préciser l'implémentation
358
+ à utiliser (et garantir l'aspect thread-safe), vous devez simplement l'avoir
359
+ chargée au préalable :
360
+
361
+ require 'rdiscount' # ou require 'bluecloth'
362
+ get('/') { markdown :index }
363
+
364
+ === Templates Haml
365
+
366
+ Dépendances:: {haml}[http://haml-lang.com/]
367
+ Extensions de fichier:: <tt>.haml</tt>
368
+ Exemple:: <tt>haml :index, :format => :html5</tt>
369
+
370
+ === Templates Erb
371
+
372
+ Dépendances:: {erubis}[http://www.kuwata-lab.com/erubis/] ou
373
+ erb (inclus avec Ruby)
374
+ Extensions de fichier:: <tt>.erb</tt>, <tt>.rhtml</tt> ou <tt>.erubis</tt>
375
+ (Erubis seulement)
376
+ Exemple:: <tt>erb :index</tt>
377
+
378
+ === Templates Builder
379
+
380
+ Dépendances:: {builder}[http://builder.rubyforge.org/]
381
+ Extensions de fichier:: <tt>.builder</tt>
382
+ Exemple:: <tt>builder { |xml| xml.em "salut" }</tt>
383
+
384
+ Ce moteur accepte également un bloc pour des templates en ligne (voir exemple).
385
+
386
+ === Templates Nokogiri
387
+
388
+ Dépendances:: {nokogiri}[http://nokogiri.org/]
389
+ Extensions de fichier:: <tt>.nokogiri</tt>
390
+ Exemple:: <tt>nokogiri { |xml| xml.em "salut" }</tt>
391
+
392
+ Ce moteur accepte également un bloc pour des templates en ligne (voir exemple).
393
+
394
+ === Templates Sass
395
+
396
+ Dépendances:: {sass}[http://sass-lang.com/]
397
+ Extensions de fichier:: <tt>.sass</tt>
398
+ Exemple:: <tt>sass :stylesheet, :style => :expanded</tt>
399
+
400
+ === Templates SCSS
401
+
402
+ Dépendances:: {sass}[http://sass-lang.com/]
403
+ Extensions de fichier:: <tt>.scss</tt>
404
+ Exemple:: <tt>scss :stylesheet, :style => :expanded</tt>
405
+
406
+ === Templates Less
407
+
408
+ Dépendances:: {less}[http://www.lesscss.org/]
409
+ Extensions de fichier:: <tt>.less</tt>
410
+ Exemple:: <tt>less :stylesheet</tt>
411
+
412
+ === Templates Liquid
413
+
414
+ Dépendances:: {liquid}[http://www.liquidmarkup.org/]
415
+ Extensions de fichier:: <tt>.liquid</tt>
416
+ Exemple:: <tt>liquid :index, :locals => { :key => 'value' }</tt>
417
+
418
+ Comme vous ne pouvez appeler de méthodes Ruby (autres que +yield+) dans un
419
+ template Liquid, vous aurez sûrement à lui passer des variables locales.
420
+
421
+ === Templates Markdown
422
+
423
+ Dépendances:: {rdiscount}[https://github.com/rtomayko/rdiscount],
424
+ {redcarpet}[https://github.com/tanoku/redcarpet],
425
+ {bluecloth}[http://deveiate.org/projects/BlueCloth],
426
+ {kramdown}[http://kramdown.rubyforge.org/] *ou*
427
+ {maruku}[http://maruku.rubyforge.org/]
428
+ Extensions de fichier:: <tt>.markdown</tt>, <tt>.mkd</tt> et <tt>.md</tt>
429
+ Exemple:: <tt>markdown :index, :layout_engine => :erb</tt>
430
+
431
+ Il n'est pas possible d'appeler des méthodes depuis markdown, ni de lui
432
+ passer des variables locales. Par conséquent, il sera souvent utilisé en
433
+ combinaison avec un autre moteur de rendu :
434
+
435
+ erb :overview, :locals => { :text => markdown(:introduction) }
436
+
437
+ Notez que vous pouvez également appeler la méthode +markdown+ au sein d'autres
438
+ templates :
439
+
440
+ %h1 Hello From Haml !
441
+ %p= markdown(:greetings)
442
+
443
+ Comme vous ne pouvez pas appeler de Ruby au sein de Markdown, vous ne pouvez
444
+ pas utiliser de +layouts+ écrits en Markdown. Toutefois, il est possible
445
+ d'utiliser un moteur de rendu différent pour le template et pour le +layout+
446
+ en utilisant l'option <tt>:layout_engine</tt>.
447
+
448
+ === Templates Textile
449
+
450
+ Dépendances:: {RedCloth}[http://redcloth.org/]
451
+ Extensions de fichier:: <tt>.textile</tt>
452
+ Exemple:: <tt>textile :index, :layout_engine => :erb</tt>
453
+
454
+ Il n'est pas possible d'appeler des méthodes depuis textile, ni de lui
455
+ passer des variables locales. Par conséquent, il sera souvent utilisé en
456
+ combinaison avec un autre moteur de rendu :
457
+
458
+ erb :overview, :locals => { :text => textile(:introduction) }
459
+
460
+ Notez que vous pouvez également appeler la méthode +textile+ au sein d'autres
461
+ templates :
462
+
463
+ %h1 Hello From Haml !
464
+ %p= textile(:greetings)
465
+
466
+ Comme vous ne pouvez pas appeler de Ruby au sein de Textile, vous ne pouvez
467
+ pas utiliser de +layouts+ écrits en Textile. Toutefois, il est possible
468
+ d'utiliser un moteur de rendu différent pour le template et pour le +layout+
469
+ en utilisant l'option <tt>:layout_engine</tt>.
470
+
471
+ === Templates RDoc
472
+
473
+ Dépendances:: {rdoc}[http://rdoc.rubyforge.org/]
474
+ Extensions de fichier:: <tt>.rdoc</tt>
475
+ Exemple:: <tt>rdoc :README, :layout_engine => :erb</tt>
476
+
477
+ Il n'est pas possible d'appeler des méthodes depuis rdoc, ni de lui
478
+ passer des variables locales. Par conséquent, il sera souvent utilisé en
479
+ combinaison avec un autre moteur de rendu :
480
+
481
+ erb :overview, :locals => { :text => rdoc(:introduction) }
482
+
483
+ Notez que vous pouvez également appeler la méthode +rdoc+ au sein d'autres
484
+ templates :
485
+
486
+ %h1 Hello From Haml !
487
+ %p= rdoc(:greetings)
488
+
489
+ Comme vous ne pouvez pas appeler de Ruby au sein de RDoc, vous ne pouvez
490
+ pas utiliser de +layouts+ écrits en RDoc. Toutefois, il est possible
491
+ d'utiliser un moteur de rendu différent pour le template et pour le +layout+
492
+ en utilisant l'option <tt>:layout_engine</tt>.
493
+
494
+ === Templates Radius
495
+
496
+ Dépendances:: {radius}[http://radius.rubyforge.org/]
497
+ Extensions de fichier:: <tt>.radius</tt>
498
+ Exemple:: <tt>radius :index, :locals => { :key => 'value' }</tt>
499
+
500
+ Comme vous ne pouvez pas appeler de méthodes Ruby depuis un template Radius,
501
+ vous aurez sûrement à lui passer des variables locales.
502
+
503
+ === Templates Markaby
504
+
505
+ Dépendances:: {markaby}[http://markaby.github.com/]
506
+ Extensions de fichier:: <tt>.mab</tt>
507
+ Exemple:: <tt>markaby { h1 "Bienvenue !" }</tt>
508
+
509
+ Ce moteur accepte également un bloc pour des templates en ligne (voir exemple).
510
+
511
+ === Templates Slim
512
+
513
+ Dépendances:: {slim}[http://slim-lang.com/]
514
+ Extensions de fichier:: <tt>.slim</tt>
515
+ Exemple:: <tt>slim :index</tt>
516
+
517
+ === Templates Creole
518
+
519
+ Dépendances:: {creole}[https://github.com/minad/creole]
520
+ Extensions de fichier:: <tt>.creole</tt>
521
+ Exemple:: <tt>creole :wiki, :layout_engine => :erb</tt>
522
+
523
+ Il n'est pas possible d'appeler des méthodes depuis creole, ni de lui
524
+ passer des variables locales. Par conséquent, il sera souvent utilisé en
525
+ combinaison avec un autre moteur de rendu :
526
+
527
+ erb :overview, :locals => { :text => creole(:introduction) }
528
+
529
+ Notez que vous pouvez également appeler la méthode +creole+ au sein d'autres
530
+ templates :
531
+
532
+ %h1 Hello From Haml !
533
+ %p= creole(:greetings)
534
+
535
+ Comme vous ne pouvez pas appeler de Ruby au sein de Creole, vous ne pouvez
536
+ pas utiliser de +layouts+ écrits en Creole. Toutefois, il est possible
537
+ d'utiliser un moteur de rendu différent pour le template et pour le +layout+
538
+ en utilisant l'option <tt>:layout_engine</tt>.
539
+
540
+ === Templates CoffeeScript
541
+
542
+ Dépendances:: {coffee-script}[https://github.com/josh/ruby-coffee-script]
543
+ et un {moyen d'exécuter javascript}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
544
+ Extensions de fichier:: <tt>.coffee</tt>
545
+ Exemple:: <tt>coffee :index</tt>
546
+
547
+ === Templates Yajl
548
+
549
+ Dépendances:: {yajl-ruby}[https://github.com/brianmario/yajl-ruby]
550
+ Extensions de fichier:: <tt>.yajl</tt>
551
+ Exemple:: <tt>yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource'</tt>
552
+
553
+ Le source du template est évalué en tant que chaine Ruby, puis la variable json
554
+ obtenue est convertie avec #to_json.
555
+
556
+ json = { :foo => 'bar' }
557
+ json[:baz] = key
558
+
559
+ Les options <tt>:callback</tt> et <tt>:variable</tt> peuvent être utilisées
560
+ pour décorer l'objet retourné.
561
+
562
+ var resource = {"foo":"bar","baz":"qux"}; present(resource);
563
+
564
+ === Templates embarqués
565
+
566
+ get '/' do
567
+ haml '%div.title Bonjour le monde'
568
+ end
569
+
570
+ Générera le code du template spécifié dans la chaîne de caractères.
571
+
572
+ === Accéder aux variables dans un Template
573
+
574
+ Un template est évalué dans le même contexte que l'endroit d'où il a été
575
+ appelé (gestionnaire de route). Les variables d'instance déclarées dans le
576
+ gestionnaire de route sont directement accessibles dans le template :
577
+
578
+ get '/:id' do
579
+ @foo = Foo.find(params[:id])
580
+ haml '%h1= @foo.nom'
581
+ end
582
+
583
+ Alternativement, on peut passer un hash contenant des variables locales :
584
+
585
+ get '/:id' do
586
+ foo = Foo.find(params[:id])
587
+ haml '%h1= foo.nom', :locals => { :foo => foo }
588
+ end
589
+
590
+ Ceci est généralement utilisé lorsque l'on veut utiliser un template comme
591
+ partiel (depuis un autre template) et qu'il est donc nécessaire d'adapter les
592
+ noms de variables.
593
+
594
+ === Templates dans le fichier source
595
+
596
+ Des templates peuvent être définis dans le fichier source comme ceci :
597
+
598
+ require 'sinatra'
599
+
600
+ get '/' do
601
+ haml :index
602
+ end
603
+
604
+ __END__
605
+
606
+ @@ layout
607
+ %html
608
+ = yield
609
+
610
+ @@ index
611
+ %div.title Bonjour le monde !
612
+
613
+ NOTE : Les templates du fichier source qui contient <tt>require 'sinatra'</tt>
614
+ sont automatiquement chargés. Si vous avez des templates dans d'autres
615
+ fichiers source, il faut explicitement les déclarer avec
616
+ <tt>enable :inline_templates</tt>.
617
+
618
+ === Templates nommés
619
+
620
+ Les templates peuvent aussi être définis grâce à la méthode de haut niveau
621
+ <tt>template</tt> :
622
+
623
+ template :layout do
624
+ "%html\n =yield\n"
625
+ end
626
+
627
+ template :index do
628
+ '%div.title Bonjour le monde !'
629
+ end
630
+
631
+ get '/' do
632
+ haml :index
633
+ end
634
+
635
+ Si un template nommé "layout" existe, il sera utilisé à chaque fois qu'un
636
+ template sera affiché. Vous pouvez désactivez les layouts au cas par cas en
637
+ passant <tt>:layout => false</tt> ou bien les désactiver par défaut au moyen
638
+ de <tt>set :haml, :layout => false</tt> :
639
+
640
+ get '/' do
641
+ haml :index, :layout => !request.xhr?
642
+ end
643
+
644
+ === Associer des extensions de fichier
645
+
646
+ Pour associer une extension de fichier avec un moteur de rendu, utilisez
647
+ <tt>Tilt.register</tt>. Par exemple, si vous désirez utiliser l'extension
648
+ de fichier +tt+ pour les templates Textile, vous pouvez faire comme suit :
649
+
650
+ Tilt.register :tt, Tilt[:textile]
651
+
652
+ === Ajouter son propre moteur de rendu
653
+
654
+ En premier lieu, déclarez votre moteur de rendu avec Tilt, ensuite créez
655
+ votre méthode de rendu :
656
+
657
+ Tilt.register :monmoteur, MonMerveilleurMoteurDeRendu
658
+
659
+ helpers do
660
+ def monmoteur(*args) render(:monmoteur, *args) end
661
+ end
662
+
663
+ get '/' do
664
+ monmoteur :index
665
+ end
666
+
667
+ Utilisera <tt>./views/index.monmoteur</tt>. Voir
668
+ https://github.com/rtomayko/tilt pour en savoir plus sur Tilt.
669
+
670
+ == Filtres
671
+
672
+ Les filtres before sont exécutés avant chaque requête, dans le même contexte
673
+ que les routes, et permettent de modifier la requête et sa réponse. Les
674
+ variables d'instance déclarées dans les filtres sont accessibles au niveau
675
+ des routes et des templates :
676
+
677
+ before do
678
+ @note = 'Coucou !'
679
+ request.path_info = '/foo/bar/baz'
680
+ end
681
+
682
+ get '/foo/*' do
683
+ @note #=> 'Coucou !'
684
+ params[:splat] #=> 'bar/baz'
685
+ end
686
+
687
+ Les filtres after sont exécutés après chaque requête à l'intérieur du même
688
+ contexte et permettent de modifier la requête et sa réponse. Les variables
689
+ d'instance déclarées dans les filtres before ou les routes sont accessibles
690
+ au niveau des filtres after :
691
+
692
+ after do
693
+ puts response.status
694
+ end
695
+
696
+ Note : Le corps de la réponse n'est pas disponible au niveau du filtre after
697
+ car il ne sera généré que plus tard (sauf dans le cas où vous utilisez la
698
+ méthode +body+ au lieu de simplement renvoyer une chaine depuis vos routes).
699
+
700
+ Les filtres peuvent être associés à un masque, ce qui permet de limiter leur
701
+ exécution aux cas où la requête correspond à ce masque :
702
+
703
+ before '/secret/*' do
704
+ authentification!
705
+ end
706
+
707
+ after '/faire/:travail' do |travail|
708
+ session[:dernier_travail] = travail
709
+ end
710
+
711
+ Tout comme les routes, les filtres acceptent également des conditions :
712
+
713
+ before :agent => /Songbird/ do
714
+ # ...
715
+ end
716
+
717
+ after '/blog/*', :host_name => 'example.com' do
718
+ # ...
719
+ end
720
+
721
+ == Helpers
722
+
723
+ Utilisez la méthode de haut niveau <tt>helpers</tt> pour définir des routines
724
+ qui seront accessibles dans vos gestionnaires de route et dans vos templates :
725
+
726
+ helpers do
727
+ def bar(nom)
728
+ "#{nom}bar"
729
+ end
730
+ end
731
+
732
+ get '/:nom' do
733
+ bar(params[:nom])
734
+ end
735
+
736
+ Vous pouvez aussi définir les méthodes helper dans un module séparé :
737
+
738
+ module FooUtils
739
+ def foo(nom) "#{nom}foo" end
740
+ end
741
+
742
+ module BarUtils
743
+ def bar(nom) "#{nom}bar" end
744
+ end
745
+
746
+ helpers FooUtils, BarUtils
747
+
748
+ Cela a le même résultat que d'inclure les modules dans la classe de
749
+ l'application.
750
+
751
+ === Utiliser les sessions
752
+
753
+ Une session est utilisée pour conserver un état entre les requêtes. Une fois
754
+ activées, vous avez un +hash+ de session par session utilisateur :
755
+
756
+ enable :sessions
757
+
758
+ get '/' do
759
+ "valeur = " << session[:valeur].inspect
760
+ end
761
+
762
+ get '/:value' do
763
+ session[:valeur] = params[:valeur]
764
+ end
765
+
766
+ Notez que <tt>enable :sessions</tt> enregistre en fait toutes les données dans
767
+ un +cookie+. Ce n'est pas toujours ce que vous voulez (enregistrer beaucoup de
768
+ données va augmenter le traffic par exemple). Vous pouvez utiliser n'importe
769
+ quel +middleware+ Rack de session afin d'éviter cela. N'utiliser *pas*
770
+ <tt>enable :sessions</tt> dans ce cas mais charger le +middleware+ de votre
771
+ choix comme vous le feriez pour n'importe quel autre +middleware+ :
772
+
773
+ use Rack::Session::Pool, :expire_after => 2592000
774
+
775
+ get '/' do
776
+ "valeur = " << session[:valeur].inspect
777
+ end
778
+
779
+ get '/:value' do
780
+ session[:valeur] = params[:valeur]
781
+ end
782
+
783
+ Pour renforcer la sécurité, les données de session dans le cookie sont signées
784
+ avec une clé secrète de session. Une clé secrète est générée pour vous au
785
+ hasard par Sinatra. Toutefois, comme cette clé change à chaque démarrage de
786
+ votre application, vous pouvez définir cette clé vous-même afin que toutes
787
+ les instances de votre application la partage :
788
+
789
+ set :session_secret, 'super secret'
790
+
791
+ Si vous souhaitez avoir plus de contrôle, vous pouvez également enregistrer un
792
+ +hash+ avec des options lors de la configuration de +sessions+ :
793
+
794
+ set :sessions, :domain => 'foo.com'
795
+
796
+ === Halt
797
+
798
+ Pour arrêter immédiatement la requête dans un filtre ou un gestionnaire de
799
+ route :
800
+
801
+ halt
802
+
803
+ Vous pouvez aussi passer le code retour ...
804
+
805
+ halt 410
806
+
807
+ Ou le texte ...
808
+
809
+ halt 'Ceci est le texte'
810
+
811
+ Ou les deux ...
812
+
813
+ halt 401, 'Partez !'
814
+
815
+ Ainsi que les entêtes ...
816
+
817
+ halt 402, {'Content-Type' => 'text/plain'}, 'revanche'
818
+
819
+ Bien sûr il est possible de combiner un template avec +halt+ :
820
+
821
+ halt erb(:erreur)
822
+
823
+ === Passer
824
+
825
+ Une route peut passer le relais aux autres routes qui correspondent également
826
+ avec <tt>pass</tt> :
827
+
828
+ get '/devine/:qui' do
829
+ pass unless params[:qui] == 'Frank'
830
+ "Tu m'as eu !"
831
+ end
832
+
833
+ get '/devine/*' do
834
+ 'Manqué !'
835
+ end
836
+
837
+ On sort donc immédiatement de ce gestionnaire et on continue à chercher,
838
+ dans les masques suivants, le prochain qui correspond à la requête.
839
+ Si aucun des masques suivants ne correspond, un code 404 est retourné.
840
+
841
+ === Déclencher une autre route
842
+
843
+ Parfois, +pass+ n'est pas ce que vous recherchez, au lieu de cela vous
844
+ souhaitez obtenir le résultat d'une autre route. Pour cela, utilisez
845
+ simplement +call+ :
846
+
847
+ get '/foo' do
848
+ status, headers, body = call env.merge("PATH_INFO" => '/bar')
849
+ [status, headers, body.map(&:upcase)]
850
+ end
851
+
852
+ get '/bar' do
853
+ "bar"
854
+ end
855
+
856
+ Notez que dans l'exemple ci-dessus, vous faciliterez les tests et améliorerez
857
+ la performance en déplaçant simplement <tt>"bar"</tt> dans un +helper+
858
+ utilisé à la fois par <tt>/foo</tt> et <tt>/bar</tt>.
859
+
860
+ Si vous souhiatez que la requête soit envoyée à la même instance de
861
+ l'application plutôt qu'à une copie, utilisez <tt>call!</tt> au lieu de
862
+ <tt>call</tt>.
863
+
864
+ Lisez la spécification Rack si vous souhaitez en savoir plus sur
865
+ <tt>call</tt>.
866
+
867
+ === Définir le corps, le code retour et les entêtes
868
+
869
+ Il est possible et recommandé de définir le code retour et le corps de la
870
+ réponse au moyen de la valeur de retour d'un bloc définissant une route.
871
+ Quoiqu'il en soit, dans certains cas vous pourriez avoir besoin de définir
872
+ le coprs de la réponse à un moment arbitraire de l'exécution. Vous pouvez le
873
+ faire au moyen de la méthode +body+. Si vous faites ainsi, vous pouvez alors
874
+ utiliser cette même méthode pour accéder au corps de la réponse :
875
+
876
+ get '/foo' do
877
+ body "bar"
878
+ end
879
+
880
+ after do
881
+ puts body
882
+ end
883
+
884
+ Il est également possible de passer un bloc à +body+, qui sera exécuté par le
885
+ gestionnaire Rack (ceci peut être utilisé pour implémenter un +streaming+,
886
+ voir "Valeurs de retour").
887
+
888
+ Pareillement au corps de la réponse, vous pouvez également définir le code
889
+ retour et les entêtes :
890
+
891
+ get '/foo' do
892
+ status 418
893
+ headers \
894
+ "Allow" => "BREW, POST, GET, PROPFIND, WHEN",
895
+ "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
896
+ body "Je suis une théière !"
897
+ end
898
+
899
+ Comme +body+, +headers+ et +status+ peuvent être utilisés sans arguments
900
+ pour accéder à leurs valeurs.
901
+
902
+ === Faire du streaming
903
+
904
+ Il y a des cas où vous voulez commencer à renvoyer des données pendant que
905
+ vous êtes en train de générer le reste de la réponse. Dans les cas les plus
906
+ extrèmes, vous souhaitez continuer à envoyer des données tant que le client
907
+ n'abandonne pas la connection. Vous pouvez alors utiliser le helper +stream+
908
+ pour éviter de créer votre propre système :
909
+
910
+ get '/' do
911
+ stream do |out|
912
+ out << "Ca va être hallu -\n"
913
+ sleep 0.5
914
+ out << " (attends la suite) \n"
915
+ sleep 1
916
+ out << "- cinant !\n"
917
+ end
918
+ end
919
+
920
+ Cela permet d'implémenter des API de streaming ou de
921
+ {Server Sent Events}[http://dev.w3.org/html5/eventsource/] et peut servir de
922
+ base pour des {WebSockets}[http://en.wikipedia.org/wiki/WebSocket]. Vous
923
+ pouvez aussi l'employer pour augmenter le débit quand une partie du contenu
924
+ provient d'une resource lente.
925
+
926
+ Le fonctionnement du streaming, notamment le nombre de requêtes simultanées,
927
+ dépend énormément du serveur web utilisé. Certains ne prennent pas du tout en
928
+ charge le streaming (WEBRick par exemple). Lorsque le serveur ne gère pas le
929
+ streaming, la partie body de la réponse sera envoyée au client en une seule
930
+ fois, après que l'exécution du bloc passé au helper +stream+ sera terminée. Le
931
+ streaming ne fonctionne pas du tout avec Shotgun.
932
+
933
+ En utilisant le helper +stream+ avec le paramètre +keep_open+, il n'appelera
934
+ pas la méthode +close+ du flux, vous laissant la possibilité de le fermer à
935
+ tout moment au cours de l'exécution. Ceci ne fonctionne qu'avec les serveurs
936
+ evented (ie non threadés) tels que Thin et Rainbows. Les autres serveurs
937
+ fermeront malgré tout le flux :
938
+
939
+ set :server, :thin
940
+ connections = []
941
+
942
+ get '/' do
943
+ # conserve le flux ouvert
944
+ stream(:keep_open) { |out| connections << out }
945
+ end
946
+
947
+ post '/' do
948
+ # écrit dans tous les flux ouverts
949
+ connections.each { |out| out << params[:message] << "\n" }
950
+ "message sent"
951
+ end
952
+
953
+ === Journalisation (Logging)
954
+
955
+ Dans le contexte de la requête, la méthode utilitaire +logger+ expose une
956
+ instance de +logger+ :
957
+
958
+ get '/' do
959
+ logger.info "chargement des données"
960
+ # ...
961
+ end
962
+
963
+ Ce +logger+ va automatiquement prendre en compte les paramètres de
964
+ configuration pour la journalisation de votre gestionnaire Rack. Si la
965
+ journalisation est désactivée, cette méthode renverra un objet factice et
966
+ vous n'avez pas à vous en inquiéter dans vos routes en le filtrant.
967
+
968
+ Notez que la journalisation est seulement activée par défaut pour
969
+ <tt>Sinatra::Application</tt>, donc si vous héritez de <tt>Sinatra::Base</tt>,
970
+ vous aurez à l'activer vous-même :
971
+
972
+ class MonApp < Sinatra::Base
973
+ configure :production, :development do
974
+ enable :logging
975
+ end
976
+ end
977
+
978
+ Si vous souhaitez utiliser votre propre logger, vous devez définir le paramètre
979
+ +logging+ à +nil+ pour être certain qu'aucun middleware de logging ne sera
980
+ installé (notez toutefois que +logger+ renverra alors +nil+). Dans ce cas,
981
+ Sinatra utilisera ce qui sera présent dans <tt>env['rack.logger']</tt>.
982
+
983
+ === Types Mime
984
+
985
+ Quand vous utilisez <tt>send_file</tt> ou des fichiers statiques, vous
986
+ pouvez rencontrer des types mime que Sinatra ne connaît pas. Utilisez
987
+ +mime_type+ pour les déclarer par extension de fichier :
988
+
989
+ configure do
990
+ mime_type :foo, 'text/foo'
991
+ end
992
+
993
+ Vous pouvez également les utiliser avec la méthode +content_type+ :
994
+
995
+ get '/' do
996
+ content_type :foo
997
+ "foo foo foo"
998
+ end
999
+
1000
+ === Former des URLs
1001
+
1002
+ Pour former des URLs, vous devriez utiliser la méthode +url+, par exemple en
1003
+ Haml :
1004
+
1005
+ %a{:href => url('/foo')} foo
1006
+
1007
+ Cela prend en compte les proxy inverse et les routeurs Rack, s'ils existent.
1008
+
1009
+ Cette méthode est également disponible sous l'alias +to+ (voir ci-dessous
1010
+ pour un exemple).
1011
+
1012
+ === Redirection du navigateur
1013
+
1014
+ Vous pouvez déclencher une redirection du navigateur avec la méthode
1015
+ +redirect+ :
1016
+
1017
+ get '/foo' do
1018
+ redirect to('/bar')
1019
+ end
1020
+
1021
+ Tout paramètre additionnel est géré comme des arguments pour la méthode
1022
+ +halt+ :
1023
+
1024
+ redirect to('/bar'), 303
1025
+ redirect 'http://google.com', 'mauvais endroit mon pote'
1026
+
1027
+ Vous pouvez aussi rediriger vers la page dont l'utilisateur venait au moyen de
1028
+ <tt>redirect back</tt> :
1029
+
1030
+ get '/foo' do
1031
+ "<a href='/bar'>faire quelque chose</a>"
1032
+ end
1033
+
1034
+ get '/bar' do
1035
+ faire_quelque_chose
1036
+ redirect back
1037
+ end
1038
+
1039
+ Pour passer des arguments à une redirection, ajoutez-les soit à la requête :
1040
+
1041
+ redirect to('/bar?sum=42')
1042
+
1043
+ Ou bien utilisez une session :
1044
+
1045
+ enable :sessions
1046
+
1047
+ get '/foo' do
1048
+ session[:secret] = 'foo'
1049
+ redirect to('/bar')
1050
+ end
1051
+
1052
+ get '/bar' do
1053
+ session[:secret]
1054
+ end
1055
+
1056
+ === Contrôle du cache
1057
+
1058
+ Définir correctement vos entêtes à la base pour un bon cache HTTP.
1059
+
1060
+ Vous pouvez facilement définir l'entête Cache-Control de la manière suivante :
1061
+
1062
+ get '/' do
1063
+ cache_control :public
1064
+ "met le en cache !"
1065
+ end
1066
+
1067
+ Conseil de pro : définir le cache dans un filtre +before+ :
1068
+
1069
+ before do
1070
+ cache_control :public, :must_revalidate, :max_age => 60
1071
+ end
1072
+
1073
+ Si vous utilisez la méthode +expires+ pour définir l'entête correspondant,
1074
+ <tt>Cache-Control</tt> sera alors défini automatiquement :
1075
+
1076
+ before do
1077
+ expires 500, :public, :must_revalidate
1078
+ end
1079
+
1080
+ Pour utiliser correctement les caches, vous devriez utiliser +etag+ ou
1081
+ +last_modified+. Il est recommandé d'utiliser ces méthodes *avant* de faire
1082
+ d'importantes modifications, car elles vont immédiatement déclencher la réponse
1083
+ si le client a déjà la version courante dans son cache :
1084
+
1085
+ get '/article/:id' do
1086
+ @article = Article.find params[:id]
1087
+ last_modified @article.updated_at
1088
+ etag @article.sha1
1089
+ erb :article
1090
+ end
1091
+
1092
+ Il est également possible d'utiliser un
1093
+ {weak ETag}[http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation] :
1094
+
1095
+ etag @article.sha1, :weak
1096
+
1097
+ Ces méthodes ne sont pas chargées de mettre des données en cache, mais elles
1098
+ fournissent les informations nécessaires pour votre cache. Si vous êtes à la
1099
+ recherche de solutions rapides pour un reverse-proxy de cache, essayez
1100
+ {rack-cache}[http://rtomayko.github.com/rack-cache/] :
1101
+
1102
+ require "rack/cache"
1103
+ require "sinatra"
1104
+
1105
+ use Rack::Cache
1106
+
1107
+ get '/' do
1108
+ cache_control :public, :max_age => 36000
1109
+ sleep 5
1110
+ "hello"
1111
+ end
1112
+
1113
+ Utilisez le paramètre <tt>:static_cache_control</tt> pour ajouter l'information
1114
+ d'en-tête <tt>Cache-Control</tt> (voir plus loin).
1115
+
1116
+ D'après la RFC 2616, votre application devrait se comporter différement lorsque
1117
+ l'en-tête If-Match ou If-None-Match est défini à <tt>*</tt> en tenant compte du
1118
+ fait que la resource demandée existe déjà ou pas. Sinatra considère que les
1119
+ requêtes portant sur des resources sûres (tel que get) ou idempotentes (tel que
1120
+ put) existent déjà et pour les autres resources (par exemple dans le cas
1121
+ de requêtes post) qu'il s'agit de nouvelles resources. Vous pouvez modifier ce
1122
+ comportement en passant une option <tt>:new_resource</tt> :
1123
+
1124
+ get '/create' do
1125
+ etag '', :new_resource => true
1126
+ Article.create
1127
+ erb :new_article
1128
+ end
1129
+
1130
+ Si vous souhaitez utilisez un ETag faible, utilisez l'option <tt>:kind</tt> :
1131
+
1132
+ etag '', :new_resource => true, :kind => :weak
1133
+
1134
+ === Envoyer des fichiers
1135
+
1136
+ Pour envoyer des fichiers, vous pouvez utiliser la méthode
1137
+ <tt>send_file</tt> :
1138
+
1139
+ get '/' do
1140
+ send_file 'foo.png'
1141
+ end
1142
+
1143
+ Quelques options sont également acceptées :
1144
+
1145
+ send_file 'foo.png', :type => :jpg
1146
+
1147
+ Les options sont :
1148
+
1149
+ [filename]
1150
+ le nom du fichier dans la réponse, par défaut le nom du fichier envoyé.
1151
+
1152
+ [last_modified]
1153
+ valeur pour l'entête Last-Modified, par défaut la date de modification du
1154
+ fichier
1155
+
1156
+ [type]
1157
+ type de contenu à utiliser, deviné à partir de l'extension de fichier si
1158
+ absent
1159
+
1160
+ [disposition]
1161
+ utilisé pour Content-Disposition, les valuers possibles étant : +nil+ (par
1162
+ défaut), <tt>:attachment</tt> et <tt>:inline</tt>
1163
+
1164
+ [length]
1165
+ entête Content-Length, par défaut la taille du fichier
1166
+
1167
+ [status]
1168
+ code état à renvoyer. Utile quand un fichier statique sert de page d'erreur.
1169
+
1170
+ Si le gestionnaire Rack le supporte, d'autres moyens que le +streaming+ via le
1171
+ processus Ruby seront utilisés. Si vous utilisez cette méthode, Sinatra gérera
1172
+ automatiquement les requêtes de type +range+.
1173
+
1174
+ === Accéder à l'objet requête
1175
+
1176
+ L'objet correspondant à la requête envoyée peut être récupéré dans le contexte
1177
+ de la requête (filtres, routes, gestionnaires d'erreur) au moyen de la méthode
1178
+ +request+ :
1179
+
1180
+ # application tournant à l'adresse http://exemple.com/exemple
1181
+ get '/foo' do
1182
+ t = %w[text/css text/html application/javascript]
1183
+ request.accept # ['text/html', '*/*']
1184
+ request.accept? 'text/xml' # true
1185
+ request.preferred_type(t) # 'text/html'
1186
+ request.body # corps de la requête envoyée par le client
1187
+ # (voir ci-dessous)
1188
+ request.scheme # "http"
1189
+ request.script_name # "/exemple"
1190
+ request.path_info # "/foo"
1191
+ request.port # 80
1192
+ request.request_method # "GET"
1193
+ request.query_string # ""
1194
+ request.content_length # taille de request.body
1195
+ request.media_type # type de média pour request.body
1196
+ request.host # "exemple.com"
1197
+ request.get? # true (méthodes similaires pour les autres
1198
+ # verbes HTTP)
1199
+ request.form_data? # false
1200
+ request["UN_ENTETE"] # valeur de l'entête UN_ENTETE
1201
+ request.referer # référant du client ou '/'
1202
+ request.user_agent # user agent (utilisé par la condition :agent)
1203
+ request.cookies # tableau contenant les cookies du navigateur
1204
+ request.xhr? # requête AJAX ?
1205
+ request.url # "http://exemple.com/exemple/foo"
1206
+ request.path # "/exemple/foo"
1207
+ request.ip # adresse IP du client
1208
+ request.secure? # false
1209
+ request.forwarded? # vrai (si on est derrière un proxy inverse)
1210
+ request.env # tableau brut de l'environnement fourni par
1211
+ # Rack
1212
+ end
1213
+
1214
+ Certaines options, telles que <tt>script_name</tt> ou <tt>path_info</tt>
1215
+ peuvent également être modifiées :
1216
+
1217
+ before { request.path_info = "/" }
1218
+
1219
+ get "/" do
1220
+ "toutes les requêtes arrivent ici"
1221
+ end
1222
+
1223
+ <tt>request.body</tt> est un objet IO ou StringIO :
1224
+
1225
+ post "/api" do
1226
+ request.body.rewind # au cas où il a déjà été lu
1227
+ donnees = JSON.parse request.body.read
1228
+ "Bonjour #{donnees['nom']} !"
1229
+ end
1230
+
1231
+ === Fichiers joints
1232
+
1233
+ Vous pouvez utiliser la méthode +attachment+ pour indiquer au navigateur que
1234
+ la réponse devrait être stockée sur le disque plutôt qu'affichée :
1235
+
1236
+ get '/' do
1237
+ attachment
1238
+ "enregistre-le !"
1239
+ end
1240
+
1241
+ Vous pouvez également lui passer un nom de fichier :
1242
+
1243
+ get '/' do
1244
+ attachment "info.txt"
1245
+ "enregistre-le !"
1246
+ end
1247
+
1248
+ === Gérer Date et Time
1249
+
1250
+ Sinatra fourni un helper +time_for+ pour convertir une valeur donnée en
1251
+ objet +Time+. Il peut aussi faire la conversion à partir d'objets +DateTime+,
1252
+ +Date+ ou de classes similaires :
1253
+
1254
+ get '/' do
1255
+ pass if Time.now > time_for('Dec 23, 2012')
1256
+ "encore temps"
1257
+ end
1258
+
1259
+ Cette méthode est utilisée en interne par +expires+, +last_modified+ et
1260
+ consorts. Par conséquent, vous pouvez très facilement étendre le
1261
+ fonctionnement de ces méthodes en surchargeant le helper +time_for+ dans
1262
+ votre application :
1263
+
1264
+ helpers do
1265
+ def time_for(value)
1266
+ case value
1267
+ when :yesterday then Time.now - 24*60*60
1268
+ when :tomorrow then Time.now + 24*60*60
1269
+ else super
1270
+ end
1271
+ end
1272
+ end
1273
+
1274
+ get '/' do
1275
+ last_modified :yesterday
1276
+ expires :tomorrow
1277
+ "salut"
1278
+ end
1279
+
1280
+ === Chercher les fichiers de templates
1281
+
1282
+ La méthode <tt>find_template</tt> est utilisée pour trouver les fichiers de
1283
+ templates à générer :
1284
+
1285
+ find_template settings.views, 'foo', Tilt[:haml] do |file|
1286
+ puts "pourrait être #{file}"
1287
+ end
1288
+
1289
+ Ce n'est pas très utilise. En revanche, il est utile de pouvoir surcharger
1290
+ cette méthode afin de définir son propre mécanisme de recherche. Par exemple,
1291
+ vous pouvez utiliser plus d'un répertoire de vues :
1292
+
1293
+ set :views, ['views', 'templates']
1294
+
1295
+ helpers do
1296
+ def find_template(views, name, engine, &block)
1297
+ Array(views).each { |v| super(v, name, engine, &block) }
1298
+ end
1299
+ end
1300
+
1301
+ Un autre exemple est d'utiliser des répertoires différents pour des moteurs
1302
+ de rendu différents :
1303
+
1304
+ set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views'
1305
+
1306
+ helpers do
1307
+ def find_template(views, name, engine, &block)
1308
+ _, folder = views.detect { |k,v| engine == Tilt[k] }
1309
+ folder ||= views[:default]
1310
+ super(folder, name, engine, &block)
1311
+ end
1312
+ end
1313
+
1314
+ Vous pouvez également écrire cela dans une extension et la partager avec
1315
+ d'autres !
1316
+
1317
+ Notez que <tt>find_template</tt> ne vérifie pas que le fichier existe mais
1318
+ va plutôt exécuter le bloc pour tous les chemins possibles. Cela n'induit pas
1319
+ un problème de performance dans le sens où +render+ va utiliser +break+ dès
1320
+ qu'un fichier est trouvé. De plus, l'emplacement des templates (et leur
1321
+ contenu) est mis en cache si vous n'êtes pas en mode développement. Vous
1322
+ devriez garder cela en tête si vous écrivez une méthode vraiment dingue.
1323
+
1324
+ == Configuration
1325
+
1326
+ Lancé une seule fois au démarrage de tous les environnements :
1327
+
1328
+ configure do
1329
+ # définir un paramètre
1330
+ set :option, 'value'
1331
+
1332
+ # définir plusieurs paramètre
1333
+ set :a => 1, :b => 2
1334
+
1335
+ # identique à "set :option, true"
1336
+ enable :option
1337
+
1338
+ # identique à "set :option, false""
1339
+ disable :option
1340
+
1341
+ # vous pouvez également avoir des paramètres dynamiques avec des blocs
1342
+ set(:css_dir) { File.join(views, 'css') }
1343
+ end
1344
+
1345
+ Lancé si l'environnement (variable d'environnement RACK_ENV) est défini comme
1346
+ <tt>:production</tt> :
1347
+
1348
+ configure :production do
1349
+ ...
1350
+ end
1351
+
1352
+ Lancé si l'environnement est <tt>:production</tt> ou
1353
+ <tt>:test</tt> :
1354
+
1355
+ configure :production, :test do
1356
+ ...
1357
+ end
1358
+
1359
+ Vous pouvez accéder à ces paramètres via <tt>settings</tt> :
1360
+
1361
+ configure do
1362
+ set :foo, 'bar'
1363
+ end
1364
+
1365
+ get '/' do
1366
+ settings.foo? # => true
1367
+ settings.foo # => 'bar'
1368
+ ...
1369
+ end
1370
+
1371
+ === Se protéger des attaques
1372
+
1373
+ Sinatra utilise {Rack::Protection}[https://github.com/rkh/rack-protection#readme]
1374
+ pour protéger votre application contre les principales attaques opportunistes.
1375
+ Vous pouvez très simplement désactiver cette fonctionnalité (ce qui devrait
1376
+ améliorer les performances) :
1377
+
1378
+ disable :protection
1379
+
1380
+ Pour désactiver seulement un type de protection, vous pouvez définir +protection+
1381
+ avec un hash d'options :
1382
+
1383
+ set :protection, :except => :path_traversal
1384
+
1385
+ Vous pouvez également lui passer un tableau pour désactiver plusieurs types de
1386
+ protection :
1387
+
1388
+ set :protection, :except => [:path_traversal, :session_hijacking]
1389
+
1390
+ === Paramètres disponibles
1391
+
1392
+ [absolute_redirects] Si désactivé, Sinatra permettra les redirections
1393
+ relatives. Toutefois, Sinatra ne sera plus conforme à la
1394
+ RFC 2616 (HTTP 1.1), qui n'autorise que les redirections
1395
+ absolues.
1396
+
1397
+ Activez si votre application tourne derrière un proxy
1398
+ inverse qui n'a pas été correctement configuré. Notez
1399
+ que la méthode +url+ continuera de produire des URLs
1400
+ absolues, sauf si vous lui passez +false+ comme second
1401
+ argument.
1402
+
1403
+ Désactivé par défaut.
1404
+
1405
+ [add_charsets] types mime pour lesquels la méthode
1406
+ <tt>content_type</tt> va automatiquement ajouter
1407
+ l'information du +charset+.
1408
+
1409
+ Vous devriez lui ajouter des valeurs plutôt que de
1410
+ l'écraser :
1411
+
1412
+ settings.add_charsets << "application/foobar"
1413
+
1414
+ [app_file] chemin pour le fichier de l'application principale,
1415
+ utilisé pour détecter la racine du projet, les dossiers
1416
+ public et vues, et les templates en ligne.
1417
+
1418
+ [bind] adresse IP sur laquelle se brancher
1419
+ (par défaut : 0.0.0.0).
1420
+ Utiliser seulement pour le serveur intégré.
1421
+
1422
+ [default_encoding] encodage à utiliser si inconnu (par défaut
1423
+ <tt>"utf-8"</tt>).
1424
+
1425
+ [dump_errors] afficher les erreurs dans le +log+.
1426
+
1427
+ [environment] environnement courant, par défaut
1428
+ <tt>ENV['RACK_ENV']</tt>, ou
1429
+ <tt>"development"</tt> si absent.
1430
+
1431
+ [logging] utiliser le +logger+.
1432
+
1433
+ [lock] Place un +lock+ autour de chaque requête, n'exécutant
1434
+ donc qu'une seule requête par processus Ruby.
1435
+
1436
+ Activé si votre application n'est pas +thread-safe+.
1437
+ Désactivé par défaut.
1438
+
1439
+ [method_override] utilise la magie de <tt>_method</tt> afin de permettre
1440
+ des formulaires put/delete dans des navigateurs qui ne
1441
+ le permettent pas.
1442
+
1443
+ [port] port à écouter. Utiliser seulement pour le serveur
1444
+ intégré.
1445
+
1446
+ [prefixed_redirects] si oui ou non <tt>request.script_name</tt> doit être
1447
+ inséré dans les redirections si un chemin non absolu
1448
+ est utilisé. Ainsi, <tt>redirect '/foo'</tt> se
1449
+ comportera comme <tt>redirect to('/foo')</tt>.
1450
+ Désactivé par défaut.
1451
+
1452
+ [protection] défini s'il faut activer ou non la protection contre
1453
+ les attaques web. Voir la section protection précédente.
1454
+
1455
+ [public_folder] chemin pour le dossier à partir duquel les fichiers
1456
+ publics sont servis. Utilisé seulement si les fichiers
1457
+ statiques doivent être servis (voir le paramètre
1458
+ <tt>static</tt>). Si non défini, il découle du paramètre
1459
+ <tt>app_file</tt>.
1460
+
1461
+ [reload_templates] si oui ou non les templates doivent être rechargés
1462
+ entre les requêtes. Activé en mode développement.
1463
+
1464
+ [root] chemin pour le dossier racine du projet. Si non défini,
1465
+ il découle du paramètre <tt>app_file</tt>.
1466
+
1467
+ [raise_errors] soulever les erreurs (ce qui arrêtera l'application).
1468
+ Désactivé par défaut sauf lorsque <tt>environment</tt>
1469
+ est défini à <tt>"test"</tt>.
1470
+
1471
+ [run] si activé, Sinatra s'occupera de démarrer le serveur,
1472
+ ne pas activer si vous utiliser rackup ou autres.
1473
+
1474
+ [running] est-ce que le serveur intégré est en marche ?
1475
+ ne changez pas ce paramètre !
1476
+
1477
+ [server] serveur ou liste de serveurs à utiliser pour le serveur
1478
+ intégré.
1479
+ Par défaut ['thin', 'mongrel', 'webrick'], l'ordre
1480
+ indiquant la priorité.
1481
+
1482
+ [sessions] active le support des sessions basées sur les cookies, en
1483
+ utilisant <tt>Rack::Session::Cookie</tt>. Reportez-vous
1484
+ à la section 'Utiliser les sessions' pour plus
1485
+ d'informations.
1486
+
1487
+ [show_exceptions] affiche la trace de l'erreur dans le navigateur lorsqu'une
1488
+ exception se produit. Désactivé par défaut sauf lorsque
1489
+ <tt>environment</tt> est défini à <tt>"development"</tt>.
1490
+
1491
+ [static] Si oui ou non Sinatra doit s'occuper de servir les
1492
+ fichiers statiques.
1493
+ Désactivez si vous utilisez un serveur capable de le
1494
+ gérer lui même. Le désactiver augmentera la performance.
1495
+ Activé par défaut pour le style classique, désactivé pour
1496
+ le style modulaire.
1497
+
1498
+ [static_cache_control] A définir quand Sinatra rend des fichiers statiques pour
1499
+ ajouter les en-têtes <tt>Cache-Control</tt>. Utilise le
1500
+ helper +cache_control+. Désactivé par défaut.
1501
+ Utiliser un array explicite pour définir des plusieurs
1502
+ valeurs :
1503
+ <tt>set :static_cache_control, [:public, :max_age => 300]</tt>
1504
+
1505
+ [threaded] à définir à +true+ pour indiquer à Thin d'utiliser
1506
+ <tt>EventMachine.defer</tt> pour traiter la requête.
1507
+
1508
+ [views] chemin pour le dossier des vues. Si non défini, il
1509
+ découle du paramètre <tt>app_file</tt>.
1510
+
1511
+ == Environements
1512
+
1513
+ Il existe trois environnements prédéfinis : <tt>"development"</tt>,
1514
+ <tt>"production"</tt> et <tt>"test"</tt>. Les environements peuvent être
1515
+ sélectionné via la variable d'environnement +RACK_ENV+. Sa valeur par défaut
1516
+ est <tt>"development"</tt>. Dans ce mode, tous les templates sont rechargés à
1517
+ chaque requête. Des handlers spécifiques pour <tt>not_found</tt> et
1518
+ <tt>error</tt> sont installés pour vous permettre d'avoir une pile de trace
1519
+ dans votre navigateur. En mode <tt>"production"</tt> et <tt>"test"</tt> les
1520
+ templates sont mis en cache par défaut.
1521
+
1522
+ Pour exécuter votre application dans un environnement différent, utilisez
1523
+ l'option <tt>-e</tt> de Ruby :
1524
+
1525
+ ruby mon_application.rb -e [ENVIRONMENT]
1526
+
1527
+ Vous pouvez utiliser une des méthodes +development?+, +test?+ et +production?+
1528
+ pour déterminer quel est l'environnement en cours.
1529
+
1530
+ == Gérer les erreurs
1531
+
1532
+ Les gestionnaires d'erreur s'exécutent dans le même contexte que les routes ou
1533
+ les filtres, ce qui veut dire que vous avez accès (entre autres) aux bons
1534
+ vieux <tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt>, etc.
1535
+
1536
+ === NotFound
1537
+
1538
+ Quand une exception <tt>Sinatra::NotFound</tt> est soulevée, ou que le code
1539
+ retour est 404, le gestionnaire <tt>not_found</tt> est invoqué :
1540
+
1541
+ not_found do
1542
+ 'Pas moyen de trouver ce que vous cherchez'
1543
+ end
1544
+
1545
+ === Error
1546
+
1547
+ Le gestionnaire +error+ est invoqué à chaque fois qu'une exception est
1548
+ soulevée dans une route ou un filtre. L'objet exception est accessible via la
1549
+ variable Rack <tt>sinatra.error</tt> :
1550
+
1551
+ error do
1552
+ 'Désolé mais une méchante erreur est survenue - ' + env['sinatra.error'].name
1553
+ end
1554
+
1555
+ Erreur sur mesure :
1556
+
1557
+ error MonErreurSurMesure do
1558
+ 'Donc il est arrivé ceci...' + env['sinatra.error'].message
1559
+ end
1560
+
1561
+ Donc si ceci arrive :
1562
+
1563
+ get '/' do
1564
+ raise MonErreurSurMesure, 'quelque chose de mal'
1565
+ end
1566
+
1567
+ Vous obtenez ça :
1568
+
1569
+ Donc il est arrivé ceci... quelque chose de mal
1570
+
1571
+ Alternativement, vous pouvez avoir un gestionnaire d'erreur associé à un code
1572
+ particulier :
1573
+
1574
+ error 403 do
1575
+ 'Accès interdit'
1576
+ end
1577
+
1578
+ get '/secret' do
1579
+ 403
1580
+ end
1581
+
1582
+ Ou un intervalle :
1583
+
1584
+ error 400..510 do
1585
+ 'Boom'
1586
+ end
1587
+
1588
+ Sinatra installe pour vous quelques gestionnaires <tt>not_found</tt> et
1589
+ <tt>error</tt> génériques lorsque vous êtes en environnement
1590
+ <tt>development</tt>.
1591
+
1592
+ == Les Middlewares Rack
1593
+
1594
+ Sinatra tourne avec Rack[http://rack.rubyforge.org/], une interface standard
1595
+ et minimale pour les web frameworks Ruby. Un des points forts de Rack est le
1596
+ support de ce que l'on appelle des "middlewares" -- composant qui vient se
1597
+ situer entre le serveur et votre application, et dont le but est de
1598
+ visualiser/manipuler la requête/réponse HTTP, et d'offrir diverses
1599
+ fonctionnalités classiques.
1600
+
1601
+ Sinatra permet de construire facilement des middlewares Rack via la méthode de
1602
+ haut niveau +use+ :
1603
+
1604
+ require 'sinatra'
1605
+ require 'mon_middleware_perso'
1606
+
1607
+ use Rack::Lint
1608
+ use MonMiddlewarePerso
1609
+
1610
+ get '/bonjour' do
1611
+ 'Bonjour le monde'
1612
+ end
1613
+
1614
+ La sémantique de +use+ est identique à celle définie dans le DSL de
1615
+ Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html]
1616
+ (le plus souvent utilisé dans un fichier rackup). Par exemple, la méthode
1617
+ +use+ accepte divers arguments ainsi que des blocs :
1618
+
1619
+ use Rack::Auth::Basic do |login, password|
1620
+ login == 'admin' && password == 'secret'
1621
+ end
1622
+
1623
+ Rack est distribué avec une bonne variété de middlewares standards pour les
1624
+ logs, débuguer, faire du routage URL, de l'authentification, gérer des
1625
+ sessions. Sinatra utilise beaucoup de ces composants automatiquement via la
1626
+ configuration, donc pour ceux-ci vous n'aurez pas à utiliser la méthode +use+.
1627
+
1628
+ == Tester
1629
+
1630
+ Les tests pour Sinatra peuvent être écrit avec n'importe quelle bibliothèque
1631
+ basée sur Rack. {Rack::Test}[http://gitrdoc.com/brynary/rack-test] est
1632
+ recommandé :
1633
+
1634
+ require 'mon_application_sinatra'
1635
+ require 'test/unit'
1636
+ require 'rack/test'
1637
+
1638
+ class MonTest < Test::Unit::TestCase
1639
+ include Rack::Test::Methods
1640
+
1641
+ def app
1642
+ Sinatra::Application
1643
+ end
1644
+
1645
+ def test_ma_racine
1646
+ get '/'
1647
+ assert_equal 'Bonjour le monde !', last_response.body
1648
+ end
1649
+
1650
+ def test_avec_des_parametres
1651
+ get '/rencontrer', :name => 'Frank'
1652
+ assert_equal 'Salut Frank !', last_response.body
1653
+ end
1654
+
1655
+ def test_avec_rack_env
1656
+ get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
1657
+ assert_equal "Vous utilisez Songbird !", last_response.body
1658
+ end
1659
+ end
1660
+
1661
+ == Sinatra::Base - Les Middlewares, Bibliothèques, et Applications Modulaires
1662
+
1663
+ Définir votre application au niveau supérieur fonctionne bien dans le cas des
1664
+ micro-applications mais présente pas mal d'inconvénients pour créer des
1665
+ composants réutilisables sous forme de middlewares Rack, de Rails metal, de
1666
+ simples librairies avec un composant serveur ou même d'extensions Sinatra. Le
1667
+ niveau supérieur suppose une configuration dans le style des micro-applications
1668
+ (une application d'un seul fichier, des répertoires <tt>./public</tt> et
1669
+ <tt>./views</tt>, des logs, une page d'erreur, etc...). C'est là que
1670
+ <tt>Sinatra::Base</tt> prend tout son intérêt :
1671
+
1672
+ require 'sinatra/base'
1673
+
1674
+ class MonApplication < Sinatra::Base
1675
+ set :sessions, true
1676
+ set :foo, 'bar'
1677
+
1678
+ get '/' do
1679
+ 'Bonjour le monde !'
1680
+ end
1681
+ end
1682
+
1683
+ Les méthodes de la classe <tt>Sinatra::Base</tt> sont parfaitement identiques à
1684
+ celles disponibles via le DSL de haut niveau. Il suffit de deux modifications
1685
+ pour transformer la plupart des applications de haut niveau en un composant
1686
+ <tt>Sinatra::Base</tt> :
1687
+
1688
+ * Votre fichier doit charger +sinatra/base+ au lieu de +sinatra+, sinon toutes
1689
+ les méthodes du DSL Sinatra seront importées dans l'espace de nom principal.
1690
+ * Les gestionnaires de routes, la gestion d'erreur, les filtres et les options
1691
+ doivent être placés dans une classe héritant de <tt>Sinatra::Base</tt>.
1692
+
1693
+ <tt>Sinatra::Base</tt> est une page blanche. La plupart des options sont
1694
+ désactivées par défaut, y compris le serveur intégré. Reportez-vous à
1695
+ {Options et Configuration}[http://sinatra.github.com/configuration.html]
1696
+ pour plus d'informations sur les options et leur fonctionnement.
1697
+
1698
+ === Style modulaire vs. style classique
1699
+
1700
+ Contrairement aux idées reçues, il n'y a rien de mal à utiliser le style
1701
+ classique. Si c'est ce qui convient pour votre application, vous n'avez pas
1702
+ aucune raison de passer à une application modulaire.
1703
+
1704
+ Le principal inconvénient du style classique sur le style modulaire est que vous
1705
+ ne pouvez avoir qu'une application Ruby par processus Ruby. Si vous pensez en
1706
+ utiliser plus, passez au style modulaire. Et rien ne vous empêche de mixer style
1707
+ classique et style modulaire.
1708
+
1709
+ Si vous passez d'un style à l'autre, souvenez-vous des quelques différences
1710
+ mineures en ce qui concerne les paramètres par défaut :
1711
+
1712
+ Paramètre Classique Modulaire
1713
+
1714
+ app_file fichier chargeant sinatra fichier héritant de Sinatra::Base
1715
+ run $0 == app_file false
1716
+ logging true false
1717
+ method_override true false
1718
+ inline_templates true false
1719
+ static true false
1720
+
1721
+ === Servir une application modulaire
1722
+
1723
+ Il y a deux façons de faire pour démarrer une application modulaire, démarrez
1724
+ avec <tt>run!</tt> :
1725
+
1726
+ # my_app.rb
1727
+ require 'sinatra/base'
1728
+
1729
+ class MyApp < Sinatra::Base
1730
+ # ... code de l'application ici ...
1731
+
1732
+ # démarre le serveur si ce fichier est directement exécuté
1733
+ run! if app_file == $0
1734
+ end
1735
+
1736
+ Démarrez ensuite avec :
1737
+
1738
+ ruby my_app.rb
1739
+
1740
+ Ou alors avec un fichier <tt>config.ru</tt>, qui permet d'utiliser n'importe
1741
+ quel gestionnaire Rack :
1742
+
1743
+ # config.ru
1744
+ require './my_app'
1745
+ run MyApp
1746
+
1747
+ Exécutez :
1748
+
1749
+ rackup -p 4567
1750
+
1751
+ === Utiliser une application de style classique avec un fichier config.ru
1752
+
1753
+ Ecrivez votre application :
1754
+
1755
+ # app.rb
1756
+ require 'sinatra'
1757
+
1758
+ get '/' do
1759
+ 'Bonjour le monde !'
1760
+ end
1761
+
1762
+ Et un fichier <tt>config.ru</tt> correspondant :
1763
+
1764
+ require './app'
1765
+ run Sinatra::Application
1766
+
1767
+ === Quand utiliser un fichier config.ru ?
1768
+
1769
+ Quelques cas où vous devriez utiliser un fichier <tt>config.ru</tt> :
1770
+
1771
+ * Vous souhaitez déployer avec un autre gestionnaire Rack (Passenger, Unicorn,
1772
+ Heroku, ...).
1773
+ * Vous souhaitez utiliser plus d'une sous-classe de <tt>Sinatra::Base</tt>.
1774
+ * Vous voulez utiliser Sinatra comme un +middleware+, non en tant que
1775
+ +endpoint+.
1776
+
1777
+ <b>Il n'est pas nécessaire de passer par un fichier <tt>config.ru</tt> pour la
1778
+ seule raison que vous êtes passé au style modulaire, et vous n'avez pas besoin
1779
+ de passer au style modulaire pour utiliser un fichier <tt>config.ru</tt>.</b>
1780
+
1781
+ === Utiliser Sinatra comme Middleware
1782
+
1783
+ Non seulement Sinatra peut utiliser d'autres middlewares Rack, il peut
1784
+ également être à son tour utilisé au-dessus de n'importe quel +endpoint+ Rack
1785
+ en tant que middleware. Ce +endpoint+ peut très bien être une autre
1786
+ application Sinatra, ou n'importe quelle application basée sur Rack
1787
+ (Rails/Ramaze/Camping/...) :
1788
+
1789
+ require 'sinatra/base'
1790
+
1791
+ class EcranDeConnexion < Sinatra::Base
1792
+ enable :sessions
1793
+
1794
+ get('/connexion') { haml :connexion }
1795
+
1796
+ post('/connexion') do
1797
+ if params[:nom] = 'admin' && params[:motdepasse] = 'admin'
1798
+ session['nom_utilisateur'] = params[:nom]
1799
+ else
1800
+ redirect '/connexion'
1801
+ end
1802
+ end
1803
+ end
1804
+
1805
+ class MonApp < Sinatra::Base
1806
+ # le middleware sera appelé avant les filtres
1807
+ use EcranDeConnexion
1808
+
1809
+ before do
1810
+ unless session['nom_utilisateur']
1811
+ halt "Accès refusé, merci de vous <a href='/connexion'>connecter</a>."
1812
+ end
1813
+ end
1814
+
1815
+ get('/') { "Bonjour #{session['nom_utilisateur']}." }
1816
+ end
1817
+
1818
+ === Création dynamique d'applications
1819
+
1820
+ Il se peut que vous ayez besoin de créer une nouvelle application à l'exécution
1821
+ sans avoir à les assigner à une constante, vous pouvez le faire grâce à
1822
+ <tt>Sinatra.new</tt> :
1823
+
1824
+ require 'sinatra/base'
1825
+ mon_app = Sinatra.new { get('/') { "salut" } }
1826
+ mon_app.run!
1827
+
1828
+ L'application dont elle hérite peut être passé en argument optionnel :
1829
+
1830
+ # config.ru
1831
+ require 'sinatra/base'
1832
+
1833
+ controleur = Sinatra.new do
1834
+ enable :logging
1835
+ helpers MyHelpers
1836
+ end
1837
+
1838
+ map('/a') do
1839
+ run Sinatra.new(controleur) { get('/') { 'a' } }
1840
+ end
1841
+
1842
+ map('/b') do
1843
+ run Sinatra.new(controleur) { get('/') { 'b' } }
1844
+ end
1845
+
1846
+ C'est notamment utile pour tester des extensions à Sinatra ou bien pour
1847
+ utiliser Sinatra dans votre propre bibliothèque.
1848
+
1849
+ Cela permet également d'utiliser très facilement Sinatra comme middleware :
1850
+
1851
+ require 'sinatra/base'
1852
+
1853
+ use Sinatra do
1854
+ get('/') { ... }
1855
+ end
1856
+
1857
+ run RailsProject::Application
1858
+
1859
+ == Contextes et Binding
1860
+
1861
+ Le contexte dans lequel vous êtes détermine les méthodes et variables
1862
+ disponibles.
1863
+
1864
+ === Contexte de l'application/classe
1865
+
1866
+ Toute application Sinatra correspond à une sous-classe de <tt>Sinatra::Base</tt>.
1867
+ Si vous utilisez le DSL haut niveau (<tt>require 'sinatra'</tt>), alors cette
1868
+ classe est <tt>Sinatra::Application</tt>, sinon il s'agit de la sous-classe que
1869
+ vous avez définie. Dans le contexte de la classe, vous avez accès aux méthodes
1870
+ telles que +get+ ou +before+, mais vous n'avez pas accès aux objets +request+
1871
+ ou +session+ car c'est la même classe d'application qui traitera toutes les
1872
+ requêtes.
1873
+
1874
+ Les options définies au moyen de +set+ deviennent des méthodes de classe :
1875
+
1876
+ class MonApp < Sinatra::Base
1877
+ # Eh, je suis dans le contexte de l'application !
1878
+ set :foo, 42
1879
+ foo # => 42
1880
+
1881
+ get '/foo' do
1882
+ # Eh, je ne suis plus dans le contexte de l'application !
1883
+ end
1884
+ end
1885
+
1886
+ Vous avez le binding du contexte de l'application dans :
1887
+
1888
+ * Le corps de la classe d'application
1889
+ * Les méthodes définies par les extensions
1890
+ * Le bloc passé à +helpers+
1891
+ * Les procs/blocs utilisés comme argument pour +set+
1892
+ * Le bloc passé à <tt>Sinatra.new</tt>
1893
+
1894
+ Vous pouvez atteindre ce contexte (donc la classe) de la façon suivante :
1895
+
1896
+ * Via l'objet passé dans les blocs +configure+ (<tt>configure { |c| ... }</tt>)
1897
+ * En utilisant +settings+ dans le contexte de la requête
1898
+
1899
+ === Contexte de la requête/instance
1900
+
1901
+ Pour tout traitement d'une requête, une nouvelle instance de votre classe
1902
+ d'application est créée et tous vos gestionnaires sont exécutés dans ce
1903
+ contexte. Dans ce dernier, vous pouvez accéder aux objets +request+ et
1904
+ +session+ et faire appel aux fonctions de rendu telles que +erb+ ou +haml+.
1905
+ Vous pouvez accéder au contexte de l'application depuis le contexte de la
1906
+ requête au moyen de +settings+ :
1907
+
1908
+ class MonApp < Sinatra::Base
1909
+ # Eh, je suis dans le contexte de l'application !
1910
+ get '/ajouter_route/:nom' do
1911
+ # Contexte de la requête pour '/ajouter_route/:nom'
1912
+ @value = 42
1913
+
1914
+ settings.get("/#{params[:nom]}") do
1915
+ # Contexte de la requête pour "/#{params[:nom]}"
1916
+ @value # => nil (on est pas au sein de la même requête)
1917
+ end
1918
+
1919
+ "Route ajoutée !"
1920
+ end
1921
+ end
1922
+
1923
+ Vous avez le binding du contexte de la requête dans :
1924
+
1925
+ * les blocs get/head/post/put/delete/options
1926
+ * les filtres before/after
1927
+ * les méthodes utilitaires (définies au moyen de +helpers+)
1928
+ * les vues/templates
1929
+
1930
+ === Le contexte de délégation
1931
+
1932
+ Le contexte de délégation se contente de transmettre les appels de méthodes au
1933
+ contexte de classe. Toutefois, il ne se comporte pas à 100% comme le contexte
1934
+ de classe car vous n'avez pas le binding de la classe : seules les méthodes
1935
+ spécifiquement déclarées pour délégation sont disponibles et il n'est pas
1936
+ possible de partager des variables/états avec le contexte de classe
1937
+ (comprenez : +self+ n'est pas le même). Vous pouvez ajouter des délégation de
1938
+ méthodes en appelant <tt>Sinatra::Delegator.delegate :method_name</tt>.
1939
+
1940
+ Vous avez le binding du contexte de délégation dans :
1941
+
1942
+ * Le binding de haut niveau, si vous avez utilisé <tt>require "sinatra"</tt>
1943
+ * Un objet qui inclut le module +Sinatra::Delegator+
1944
+
1945
+ Jetez un oeil pour vous faire une idée : voici le
1946
+ {mixin Sinatra::Delegator}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633]
1947
+ qui {étend l'objet principal}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30].
1948
+
1949
+ == Ligne de commande
1950
+
1951
+ Les applications en Sinatra peuvent être lancées directement :
1952
+
1953
+ ruby mon_application.rb [-h] [-x] [-e ENVIRONNEMENT] [-p PORT] [-o HOTE] [-s SERVEUR]
1954
+
1955
+ Les options sont :
1956
+
1957
+ -h # aide
1958
+ -p # déclare le port (4567 par défaut)
1959
+ -o # déclare l'hôte (0.0.0.0 par défaut)
1960
+ -e # déclare l'environnement (+development+ par défaut)
1961
+ -s # déclare le serveur/gestionnaire à utiliser (thin par défaut)
1962
+ -x # active le mutex lock (off par défaut)
1963
+
1964
+ == Configuration nécessaire
1965
+
1966
+ Les versions suivantes de Ruby sont officiellement supportées :
1967
+
1968
+ [ Ruby 1.8.7 ]
1969
+ 1.8.7 est complètement supporté, toutefois si rien ne vous en empêche, nous
1970
+ vous recommandons de passer à 1.9.2 ou bien de passer à JRuby ou Rubinius. Le
1971
+ support de Ruby 1.8.7 ne sera pas supprimé avant la sortie de Sinatra 2.0 et
1972
+ de Ruby 2.0, à moins qu'un improbable Ruby 1.8.8 apparaisse. Et même dans ce
1973
+ cas, nous pourrions continuer à le supporter. <b>Ruby 1.8.6 n'est plus
1974
+ supporté.</b> Si vous souhaitez utiliser la version 1.8.6, vous devez revenir
1975
+ à Sinatra 1.2 qui continuera à recevoir des corrections de bugs tant que
1976
+ Sinatra 1.4.0 ne sera pas livré.
1977
+
1978
+ [ Ruby 1.9.2 ]
1979
+ 1.9.2 est totalement supporté et recommandé. N'utilisez pas 1.9.2p0 car il
1980
+ provoque des erreurs de segmentation à l'exécution de Sinatra. Son support
1981
+ continuera au minimum jusqu'à la sortie de Ruby 1.9.4/2.0 et le support de la
1982
+ dernière version 1.9 se poursuivra aussi longtemps que la core team de Ruby la
1983
+ supportera.
1984
+
1985
+ [ Ruby 1.9.3 ]
1986
+ 1.9.3 est totalement supporté et recommandé. Nous vous rappelons que passer à
1987
+ 1.9.3 depuis une version précédente annulera toutes les sessions.
1988
+
1989
+ [ Rubinius ]
1990
+ Rubinius est officiellement supporté (Rubinius >= 1.2.4), tout fonctionne,
1991
+ y compris tous les langages de template. La version 2.0 à venir est
1992
+ également supportée.
1993
+
1994
+ [ JRuby ]
1995
+ JRuby est officiellement supporté (JRuby >= 1.6.5). Aucune anomalie avec
1996
+ des bibliothèques de templates tierces ne sont connues. Toutefois, si vous
1997
+ choisissez JRuby, alors tournez vous vers des gestionnaires Rack JRuby car
1998
+ le serveur Thin n'est pas complètement supporté par JRuby. Le support des
1999
+ extensions C dans JRuby est encore expérimental, ce qui n'affecte que
2000
+ RDiscount, Redcarpet and RedCloth pour l'instant.
2001
+
2002
+ Nous gardons également un oeil sur les versions Ruby à venir.
2003
+
2004
+ Les implémentations Ruby suivantes ne sont pas officiellement supportées mais
2005
+ sont malgré tout connues pour permettre de faire fonctionner Sinatra :
2006
+
2007
+ * Versions plus anciennes de JRuby et Rubinius
2008
+ * Ruby Enterprise Edition
2009
+ * MacRuby, Maglev, IronRuby
2010
+ * Ruby 1.9.0 et 1.9.1 (mais nous déconseillons leur utilisation)
2011
+
2012
+ Le fait de ne pas être officiellement supporté signifie que si quelque chose
2013
+ ne fonctionne pas uniquement sur cette plateforme alors c'est un problème de la
2014
+ plateforme et pas un bug de Sinatra.
2015
+
2016
+ Nous lançons également notre intégration continue (CI) avec ruby-head (la
2017
+ future 2.0.0) et la branche 1.9.4, mais étant donné les évolutions continuelles,
2018
+ nous ne pouvont rien garantir, si ce n'est que les versions 1.9.4p0 et 2.0.0p0
2019
+ seront supportées.
2020
+
2021
+ Sinatra devrait fonctionner sur n'importe quel système d'exploitation
2022
+ supportant l'implémentation Ruby choisie.
2023
+
2024
+ Il n'est pas possible d'utiliser Sinatra sur Cardinal, SmallRuby, Blueuby ou
2025
+ toute version de Ruby antérieure à 1.8.7 à l'heure actuelle.
2026
+
2027
+ == Essuyer les plâtres
2028
+
2029
+ Si vous voulez utiliser la toute dernière version de Sinatra, n'ayez pas peur
2030
+ de faire tourner votre application sur la branche master, cela devrait être
2031
+ stable.
2032
+
2033
+ Nous publions également une gem de +prerelease+ de temps en temps que vous
2034
+ pouvez installer comme suit :
2035
+
2036
+ gem install sinatra --pre
2037
+
2038
+ afin d'avoir les toutes dernières fonctionnalités.
2039
+
2040
+ === Avec Bundler
2041
+
2042
+ Si vous voulez faire tourner votre application avec le tout dernier
2043
+ Sinatra, {Bundler}[http://gembundler.com/] est recommandé.
2044
+
2045
+ Tout d'abord, installer bundler si vous ne l'avez pas :
2046
+
2047
+ gem install bundler
2048
+
2049
+ Ensuite, dans le dossier de votre projet, créez un fichier +Gemfile+ :
2050
+
2051
+ source :rubygems
2052
+ gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
2053
+
2054
+ # autres dépendances
2055
+ gem 'haml' # par exemple, si vous utilisez haml
2056
+ gem 'activerecord', '~> 3.0' # peut-être que vous avez également besoin
2057
+ # de ActiveRecord 3.x
2058
+
2059
+ Notez que vous aurez à lister toutes les dépendances de votre application dans
2060
+ ce fichier. Les dépendances directes de Sinatra (Rack et Tilt) seront
2061
+ automatiquement téléchargées et ajoutées par Bundler.
2062
+
2063
+ Maintenant, vous pouvez faire tourner votre application de la façon suivante :
2064
+
2065
+ bundle exec ruby myapp.rb
2066
+
2067
+ === Faites le vous-même
2068
+
2069
+ Créez un clone local et démarrez votre application avec le dossier
2070
+ <tt>sinatra/lib</tt> dans le <tt>$LOAD_PATH</tt> :
2071
+
2072
+ cd myapp
2073
+ git clone git://github.com/sinatra/sinatra.git
2074
+ ruby -Isinatra/lib myapp.rb
2075
+
2076
+ A l'avenir, pour mettre à jour le code source de Sinatra :
2077
+
2078
+ cd myapp/sinatra
2079
+ git pull
2080
+
2081
+ === Installez globalement
2082
+
2083
+ Vous pouvez construire la gem vous-même :
2084
+
2085
+ git clone git://github.com/sinatra/sinatra.git
2086
+ cd sinatra
2087
+ rake sinatra.gemspec
2088
+ rake install
2089
+
2090
+ Si vous installez les gems en tant que +root+, la dernière étape sera :
2091
+
2092
+ sudo rake install
2093
+
2094
+ == Versions
2095
+
2096
+ Sinatra se conforme aux {versions sémantiques}[http://semver.org/], aussi bien
2097
+ SemVer que SemVerTag.
2098
+
2099
+ == Mais encore
2100
+
2101
+ * {Site internet}[http://www.sinatrarb.com/] - Plus de documentation,
2102
+ de news, et des liens vers d'autres ressources.
2103
+ * {Contribuer}[http://www.sinatrarb.com/contributing] - Vous avez trouvé un
2104
+ bug ? Besoin d'aide ? Vous avez un patch ?
2105
+ * {Suivi des problèmes}[http://github.com/sinatra/sinatra/issues]
2106
+ * {Twitter}[http://twitter.com/sinatra]
2107
+ * {Mailing List}[http://groups.google.com/group/sinatrarb/topics]
2108
+ * {IRC : #sinatra}[irc://chat.freenode.net/#sinatra] sur http://freenode.net
2109
+ * {IRC : #sinatra}[irc://chat.freenode.net/#sinatra] on http://freenode.net
2110
+ * {Sinatra Book}[http://sinatra-book.gittr.com] Tutoriels et recettes
2111
+ * {Sinatra Recipes}[http://recipes.sinatrarb.com/] Recettes contribuées
2112
+ par la communauté
2113
+ * Documentation API de la {dernière version}[http://rubydoc.info/gems/sinatra]
2114
+ ou du {HEAD courant}[http://rubydoc.info/github/sinatra/sinatra] sur
2115
+ http://rubydoc.info
2116
+ * {CI server}[http://ci.rkh.im/view/Sinatra/]