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,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/]