sinatra-acd 1.4.5

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 (128) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +5 -0
  3. data/AUTHORS +61 -0
  4. data/CHANGES +1293 -0
  5. data/Gemfile +76 -0
  6. data/LICENSE +23 -0
  7. data/README.de.md +2864 -0
  8. data/README.es.md +2786 -0
  9. data/README.fr.md +2924 -0
  10. data/README.hu.md +694 -0
  11. data/README.ja.md +2726 -0
  12. data/README.ko.md +2832 -0
  13. data/README.md +2980 -0
  14. data/README.pt-br.md +965 -0
  15. data/README.pt-pt.md +791 -0
  16. data/README.ru.md +2799 -0
  17. data/README.zh.md +2158 -0
  18. data/Rakefile +199 -0
  19. data/examples/chat.rb +61 -0
  20. data/examples/simple.rb +3 -0
  21. data/examples/stream.ru +26 -0
  22. data/lib/sinatra.rb +4 -0
  23. data/lib/sinatra/base.rb +2044 -0
  24. data/lib/sinatra/images/404.png +0 -0
  25. data/lib/sinatra/images/500.png +0 -0
  26. data/lib/sinatra/main.rb +34 -0
  27. data/lib/sinatra/show_exceptions.rb +345 -0
  28. data/lib/sinatra/version.rb +3 -0
  29. data/sinatra.gemspec +19 -0
  30. data/test/asciidoctor_test.rb +72 -0
  31. data/test/base_test.rb +171 -0
  32. data/test/builder_test.rb +91 -0
  33. data/test/coffee_test.rb +90 -0
  34. data/test/compile_test.rb +183 -0
  35. data/test/contest.rb +100 -0
  36. data/test/creole_test.rb +65 -0
  37. data/test/delegator_test.rb +160 -0
  38. data/test/encoding_test.rb +20 -0
  39. data/test/erb_test.rb +116 -0
  40. data/test/extensions_test.rb +98 -0
  41. data/test/filter_test.rb +487 -0
  42. data/test/haml_test.rb +109 -0
  43. data/test/helper.rb +131 -0
  44. data/test/helpers_test.rb +1917 -0
  45. data/test/integration/app.rb +79 -0
  46. data/test/integration_helper.rb +236 -0
  47. data/test/integration_test.rb +104 -0
  48. data/test/less_test.rb +69 -0
  49. data/test/liquid_test.rb +77 -0
  50. data/test/mapped_error_test.rb +285 -0
  51. data/test/markaby_test.rb +80 -0
  52. data/test/markdown_test.rb +82 -0
  53. data/test/mediawiki_test.rb +68 -0
  54. data/test/middleware_test.rb +68 -0
  55. data/test/nokogiri_test.rb +67 -0
  56. data/test/public/favicon.ico +0 -0
  57. data/test/rabl_test.rb +89 -0
  58. data/test/rack_test.rb +45 -0
  59. data/test/radius_test.rb +59 -0
  60. data/test/rdoc_test.rb +66 -0
  61. data/test/readme_test.rb +130 -0
  62. data/test/request_test.rb +97 -0
  63. data/test/response_test.rb +63 -0
  64. data/test/result_test.rb +76 -0
  65. data/test/route_added_hook_test.rb +59 -0
  66. data/test/routing_test.rb +1412 -0
  67. data/test/sass_test.rb +115 -0
  68. data/test/scss_test.rb +88 -0
  69. data/test/server_test.rb +48 -0
  70. data/test/settings_test.rb +582 -0
  71. data/test/sinatra_test.rb +12 -0
  72. data/test/slim_test.rb +102 -0
  73. data/test/static_test.rb +236 -0
  74. data/test/streaming_test.rb +149 -0
  75. data/test/stylus_test.rb +90 -0
  76. data/test/templates_test.rb +382 -0
  77. data/test/textile_test.rb +65 -0
  78. data/test/views/a/in_a.str +1 -0
  79. data/test/views/ascii.erb +2 -0
  80. data/test/views/b/in_b.str +1 -0
  81. data/test/views/calc.html.erb +1 -0
  82. data/test/views/error.builder +3 -0
  83. data/test/views/error.erb +3 -0
  84. data/test/views/error.haml +3 -0
  85. data/test/views/error.sass +2 -0
  86. data/test/views/explicitly_nested.str +1 -0
  87. data/test/views/foo/hello.test +1 -0
  88. data/test/views/hello.asciidoc +1 -0
  89. data/test/views/hello.builder +1 -0
  90. data/test/views/hello.coffee +1 -0
  91. data/test/views/hello.creole +1 -0
  92. data/test/views/hello.erb +1 -0
  93. data/test/views/hello.haml +1 -0
  94. data/test/views/hello.less +5 -0
  95. data/test/views/hello.liquid +1 -0
  96. data/test/views/hello.mab +1 -0
  97. data/test/views/hello.md +1 -0
  98. data/test/views/hello.mediawiki +1 -0
  99. data/test/views/hello.nokogiri +1 -0
  100. data/test/views/hello.rabl +2 -0
  101. data/test/views/hello.radius +1 -0
  102. data/test/views/hello.rdoc +1 -0
  103. data/test/views/hello.sass +2 -0
  104. data/test/views/hello.scss +3 -0
  105. data/test/views/hello.slim +1 -0
  106. data/test/views/hello.str +1 -0
  107. data/test/views/hello.styl +2 -0
  108. data/test/views/hello.test +1 -0
  109. data/test/views/hello.textile +1 -0
  110. data/test/views/hello.wlang +1 -0
  111. data/test/views/hello.yajl +1 -0
  112. data/test/views/layout2.builder +3 -0
  113. data/test/views/layout2.erb +2 -0
  114. data/test/views/layout2.haml +2 -0
  115. data/test/views/layout2.liquid +2 -0
  116. data/test/views/layout2.mab +2 -0
  117. data/test/views/layout2.nokogiri +3 -0
  118. data/test/views/layout2.rabl +3 -0
  119. data/test/views/layout2.radius +2 -0
  120. data/test/views/layout2.slim +3 -0
  121. data/test/views/layout2.str +2 -0
  122. data/test/views/layout2.test +1 -0
  123. data/test/views/layout2.wlang +2 -0
  124. data/test/views/nested.str +1 -0
  125. data/test/views/utf8.erb +2 -0
  126. data/test/wlang_test.rb +87 -0
  127. data/test/yajl_test.rb +86 -0
  128. metadata +280 -0
@@ -0,0 +1,2924 @@
1
+ # Sinatra
2
+ *Attention : Ce document correspond à la traduction de la version anglaise et
3
+ il n'est peut être plus à jour.*
4
+
5
+ Sinatra est un [DSL](http://fr.wikipedia.org/wiki/Langage_dédié) pour
6
+ créer rapidement et facilement des applications web en Ruby :
7
+
8
+ ``` ruby
9
+ # mon_application.rb
10
+ require 'sinatra'
11
+
12
+ get '/' do
13
+ 'Bonjour le monde !'
14
+ end
15
+ ```
16
+
17
+ Installez la gem Sinatra :
18
+
19
+ ``` shell
20
+ gem install sinatra
21
+ ```
22
+
23
+ Puis lancez votre programme :
24
+
25
+ ``` shell
26
+ ruby mon_application.rb
27
+ ```
28
+
29
+ Le résultat est visible sur : http://localhost:4567
30
+
31
+ Il est recommandé d'exécuter également `gem install thin`, pour que
32
+ Sinatra utilise le server Thin quand il est disponible.
33
+
34
+ ## Table des matières
35
+
36
+ * [Sinatra](#sinatra)
37
+ * [Table des matières](#table-des-matières)
38
+ * [Routes](#routes)
39
+ * [Conditions](#conditions)
40
+ * [Valeurs de retour](#valeurs-de-retour)
41
+ * [Masques de route spécifiques](#masques-de-route-spécifiques)
42
+ * [Fichiers statiques](#fichiers-statiques)
43
+ * [Vues / Templates](#vues--templates)
44
+ * [Templates littéraux](#templates-littéraux)
45
+ * [Langages de template disponibles](#langages-de-template-disponibles)
46
+ * [Templates Haml](#templates-haml)
47
+ * [Templates Erb](#templates-erb)
48
+ * [Templates Builder](#templates-builder)
49
+ * [Templates Nokogiri](#templates-nokogiri)
50
+ * [Templates Sass](#templates-sass)
51
+ * [Templates SCSS](#templates-scss)
52
+ * [Templates Less](#templates-less)
53
+ * [Templates Liquid](#templates-liquid)
54
+ * [Templates Markdown](#templates-markdown)
55
+ * [Templates Textile](#templates-textile)
56
+ * [Templates RDoc](#templates-rdoc)
57
+ * [Templates Radius](#templates-radius)
58
+ * [Templates Markaby](#templates-markaby)
59
+ * [Templates RABL](#templates-rabl)
60
+ * [Templates Slim](#templates-slim)
61
+ * [Templates Creole](#templates-creole)
62
+ * [Templates CoffeeScript](#templates-coffeescript)
63
+ * [Templates Stylus](#templates-stylus)
64
+ * [Templates Yajl](#templates-yajl)
65
+ * [Templates WLang](#templates-wlang)
66
+ * [Accéder aux variables dans un Template](#accéder-aux-variables-dans-un-template)
67
+ * [Templates avec `yield` et layouts imbriqués](#templates-avec-yield-et-layouts-imbriqués)
68
+ * [Templates dans le fichier source](#templates-dans-le-fichier-source)
69
+ * [Templates nommés](#templates-nommés)
70
+ * [Associer des extensions de fichier](#associer-des-extensions-de-fichier)
71
+ * [Ajouter son propre moteur de rendu](#ajouter-son-propre-moteur-de-rendu)
72
+ * [Filtres](#filtres)
73
+ * [Helpers](#helpers)
74
+ * [Utiliser les sessions](#utiliser-les-sessions)
75
+ * [Halt](#halt)
76
+ * [Passer](#passer)
77
+ * [Déclencher une autre route](#déclencher-une-autre-route)
78
+ * [Définir le corps, le code retour et les entêtes](#définir-le-corps-le-code-retour-et-les-entêtes)
79
+ * [Faire du streaming](#faire-du-streaming)
80
+ * [Journalisation (Logging)](#journalisation-logging)
81
+ * [Types Mime](#types-mime)
82
+ * [Former des URLs](#former-des-urls)
83
+ * [Redirection du navigateur](#redirection-du-navigateur)
84
+ * [Contrôle du cache](#contrôle-du-cache)
85
+ * [Envoyer des fichiers](#envoyer-des-fichiers)
86
+ * [Accéder à l'objet requête](#accéder-à-lobjet-requête)
87
+ * [Fichiers joints](#fichiers-joints)
88
+ * [Gérer Date et Time](#gérer-date-et-time)
89
+ * [Chercher les fichiers de templates](#chercher-les-fichiers-de-templates)
90
+ * [Configuration](#configuration)
91
+ * [Se protéger des attaques](#se-protéger-des-attaques)
92
+ * [Paramètres disponibles](#paramètres-disponibles)
93
+ * [Environements](#environements)
94
+ * [Gérer les erreurs](#gérer-les-erreurs)
95
+ * [NotFound](#notfound)
96
+ * [Error](#error)
97
+ * [Les Middlewares Rack](#les-middlewares-rack)
98
+ * [Tester](#tester)
99
+ * [Sinatra::Base - Les Middlewares, Bibliothèques, et Applications Modulaires](#sinatrabase---les-middlewares-bibliothèques-et-applications-modulaires)
100
+ * [Style modulaire vs. style classique](#style-modulaire-vs-style-classique)
101
+ * [Servir une application modulaire](#servir-une-application-modulaire)
102
+ * [Utiliser une application de style classique avec un fichier config.ru](#utiliser-une-application-de-style-classique-avec-un-fichier-configru)
103
+ * [Quand utiliser un fichier config.ru ?](#quand-utiliser-un-fichier-configru-)
104
+ * [Utiliser Sinatra comme Middleware](#utiliser-sinatra-comme-middleware)
105
+ * [Création dynamique d'applications](#création-dynamique-dapplications)
106
+ * [Contextes et Binding](#contextes-et-binding)
107
+ * [Contexte de l'application/classe](#contexte-de-lapplicationclasse)
108
+ * [Contexte de la requête/instance](#contexte-de-la-requêteinstance)
109
+ * [Le contexte de délégation](#le-contexte-de-délégation)
110
+ * [Ligne de commande](#ligne-de-commande)
111
+ * [Configuration nécessaire](#configuration-nécessaire)
112
+ * [Essuyer les plâtres](#essuyer-les-plâtres)
113
+ * [Installer avec Bundler](#installer-avec-bundler)
114
+ * [Faire un clone local](#faire-un-clone-local)
115
+ * [Installer globalement](#installer-globalement)
116
+ * [Versions](#versions)
117
+ * [Mais encore](#mais-encore)
118
+
119
+ ## Routes
120
+
121
+ Dans Sinatra, une route est une méthode HTTP couplée à un masque (pattern)
122
+ URL. Chaque route est associée à un bloc :
123
+
124
+ ``` ruby
125
+ get '/' do
126
+ .. montrer quelque chose ..
127
+ end
128
+
129
+ post '/' do
130
+ .. créer quelque chose ..
131
+ end
132
+
133
+ put '/' do
134
+ .. remplacer quelque chose ..
135
+ end
136
+
137
+ patch '/' do
138
+ .. changer quelque chose ..
139
+ end
140
+
141
+ delete '/' do
142
+ .. effacer quelque chose ..
143
+ end
144
+
145
+ options '/' do
146
+ .. paramétrer quelque chose ..
147
+ end
148
+
149
+ link '/' do
150
+ .. relier quelque chose ..
151
+ end
152
+
153
+ unlink '/' do
154
+ .. séparer quelque chose ..
155
+ end
156
+ ```
157
+
158
+ Les routes sont évaluées dans l'ordre où elles ont été définies. La première
159
+ route qui correspond à la requête est appelée.
160
+
161
+ Les masques peuvent inclure des paramètres nommés, accessibles par
162
+ l'intermédiaire du hash `params` :
163
+
164
+ ``` ruby
165
+ get '/bonjour/:nom' do
166
+ # répond aux requêtes "GET /bonjour/foo" et "GET /bonjour/bar"
167
+ # params[:nom] est 'foo' ou 'bar'
168
+ "Bonjour #{params[:nom]} !"
169
+ end
170
+ ```
171
+
172
+ Vous pouvez aussi accéder aux paramètres nommés directement grâce aux
173
+ paramètres du bloc comme ceci :
174
+
175
+ ``` ruby
176
+ get '/bonjour/:nom' do |n|
177
+ # répond aux requêtes "GET /bonjour/foo" et "GET /bonjour/bar"
178
+ # params[:nom] est 'foo' ou 'bar'
179
+ # n contient params[:nom]
180
+ "Bonjour #{n} !"
181
+ end
182
+ ```
183
+
184
+ Une route peut contenir un splat (caractère joker), accessible par
185
+ l'intermédiaire du tableau `params[:splat]` :
186
+
187
+ ``` ruby
188
+ get '/dire/*/a/*' do
189
+ # répond à /dire/bonjour/a/monde
190
+ params[:splat] # => ["bonjour", "monde"]
191
+ end
192
+
193
+ get '/telecharger/*.*' do
194
+ # répond à /telecharger/chemin/vers/fichier.xml
195
+ params[:splat] # => ["chemin/vers/fichier", "xml"]
196
+ end
197
+ ```
198
+
199
+ Ou par l'intermédiaire des paramètres du bloc :
200
+
201
+ ``` ruby
202
+ get '/telecharger/*.*' do |chemin, ext|
203
+ [chemin, ext] # => ["path/to/file", "xml"]
204
+ end
205
+ ```
206
+
207
+ Une route peut aussi être définie par une expression régulière :
208
+
209
+ ``` ruby
210
+ get %r{/bonjour/([\w]+)} do
211
+ "Bonjour, #{params[:captures].first} !"
212
+ end
213
+ ```
214
+
215
+ Là encore on peut utiliser les paramètres de bloc :
216
+
217
+ ``` ruby
218
+ get %r{/bonjour/([\w]+)} do |c|
219
+ "Bonjour, #{c} !"
220
+ end
221
+ ```
222
+
223
+ Les routes peuvent aussi comporter des paramètres optionnels :
224
+
225
+ ``` ruby
226
+ get '/posts.?:format?' do
227
+ # répond à "GET /posts" et aussi à "GET /posts.json", "GET /posts.xml" etc...
228
+ end
229
+ ```
230
+
231
+ A ce propos, à moins d'avoir désactivé la protection contre les attaques par
232
+ "path transversal" (voir plus loin), l'URL demandée peut avoir été modifiée
233
+ avant d'être comparée à vos routes.
234
+
235
+ ## Conditions
236
+
237
+ Les routes peuvent définir toutes sortes de conditions, comme par exemple le
238
+ "user agent" :
239
+
240
+ ``` ruby
241
+ get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
242
+ "Vous utilisez Songbird version #{params[:agent][0]}"
243
+ end
244
+
245
+ get '/foo' do
246
+ # Correspond à tous les autres navigateurs
247
+ end
248
+ ```
249
+
250
+ Les autres conditions disponibles sont `host_name` et `provides` :
251
+
252
+ ``` ruby
253
+ get '/', :host_name => /^admin\./ do
254
+ "Zone Administrateur, Accès refusé !"
255
+ end
256
+
257
+ get '/', :provides => 'html' do
258
+ haml :index
259
+ end
260
+
261
+ get '/', :provides => ['rss', 'atom', 'xml'] do
262
+ builder :feed
263
+ end
264
+ ```
265
+
266
+ Vous pouvez facilement définir vos propres conditions :
267
+
268
+ ``` ruby
269
+ set(:probability) { |value| condition { rand <= value } }
270
+
271
+ get '/gagner_une_voiture', :probability => 0.1 do
272
+ "Vous avez gagné !"
273
+ end
274
+
275
+ get '/gagner_une_voiture' do
276
+ "Désolé, vous avez perdu."
277
+ end
278
+ ```
279
+
280
+ Utilisez un splat (caractère joker) dans le cas d'une condition qui prend
281
+ plusieurs valeurs :
282
+
283
+ ``` ruby
284
+ set(:auth) do |*roles| # <- ici on utilise un splat
285
+ condition do
286
+ unless logged_in? && roles.any? {|role| current_user.in_role? role }
287
+ redirect "/login/", 303
288
+ end
289
+ end
290
+ end
291
+
292
+ get "/mon/compte/", :auth => [:user, :admin] do
293
+ "Informations sur votre compte"
294
+ end
295
+
296
+ get "/reserve/aux/admins/", :auth => :admin do
297
+ "Seuls les administrateurs sont acceptés ici !"
298
+ end
299
+ ```
300
+
301
+ ## Valeurs de retour
302
+
303
+ La valeur renvoyée par le bloc correspondant à une route constitue le corps de
304
+ la réponse qui sera transmise au client HTTP ou du moins au prochain middleware
305
+ dans la pile Rack. Le plus souvent, il s'agit d'une chaîne de caractères,
306
+ comme dans les exemples précédents. Cependant, d'autres valeurs sont
307
+ acceptées.
308
+
309
+ Vous pouvez renvoyer n'importe quel objet qu'il s'agisse d'une réponse Rack
310
+ valide, d'un corps de réponse Rack ou d'un code statut HTTP :
311
+
312
+ * Un tableau de 3 éléments : `[code statut (Fixnum), entêtes (Hash), corps
313
+ de la réponse (répondant à #each)]`
314
+ * Un tableau de 2 élements : `[code statut (Fixnum), corps de la réponse
315
+ (répondant à #each)]`
316
+ * Un objet qui répond à `#each` et qui ne transmet que des chaînes de
317
+ caractères au bloc fourni
318
+ * Un Fixnum représentant le code statut
319
+
320
+ Avec cela, on peut facilement implémenter un streaming par exemple :
321
+
322
+ ``` ruby
323
+ class Stream
324
+ def each
325
+ 100.times { |i| yield "#{i}\n" }
326
+ end
327
+ end
328
+
329
+ get('/') { Stream.new }
330
+ ```
331
+
332
+ Vous pouvez aussi utiliser le helper `stream` (présenté un peu plus loin) pour
333
+ éviter la surcharge et intégrer le traitement relatif au streaming dans le bloc
334
+ de code de la route.
335
+
336
+ ## Masques de route spécifiques
337
+
338
+ Comme cela a été vu auparavant, Sinatra offre la possibilité d'utiliser des
339
+ masques sous forme de chaines de caractères ou des expressions régulières
340
+ pour définir les routes. Mais il est possible de faire bien plus. Vous pouvez
341
+ facilement définir vos propres masques :
342
+
343
+ ``` ruby
344
+ class MasqueToutSauf
345
+ Masque = Struct.new(:captures)
346
+
347
+ def initialize(except)
348
+ @except = except
349
+ @captures = Masque.new([])
350
+ end
351
+
352
+ def match(str)
353
+ @caputres unless @except === str
354
+ end
355
+ end
356
+
357
+ def tout_sauf(masque)
358
+ MasqueToutSauf.new(masque)
359
+ end
360
+
361
+ get tout_sauf("/index") do
362
+ # ...
363
+ end
364
+ ```
365
+
366
+ Notez que l'exemple ci-dessus est bien trop compliqué et que le même résultat
367
+ peut être obtenu avec :
368
+
369
+ ``` ruby
370
+ get // do
371
+ pass if request.path_info == "/index"
372
+ # ...
373
+ end
374
+ ```
375
+
376
+ Ou bien en utilisant la forme négative :
377
+
378
+ ``` ruby
379
+ get %r{^(?!/index$)} do
380
+ # ...
381
+ end
382
+ ```
383
+
384
+ ## Fichiers statiques
385
+
386
+ Les fichiers du dossier `./public` sont servis de façon statique. Vous
387
+ avez la possibilité d'utiliser un autre répertoire en définissant le paramètre
388
+ `:public_folder` :
389
+
390
+ ``` ruby
391
+ set :public_folder, File.dirname(__FILE__) + '/statique'
392
+ ```
393
+
394
+ Notez que le nom du dossier public n'apparait pas dans l'URL. Le fichier
395
+ `./public/css/style.css` sera appelé via l'URL :
396
+ `http://exemple.com/css/style.css`.
397
+
398
+ Utilisez le paramètre `:static_cache_control` pour ajouter l'information
399
+ d'en-tête <tt>Cache-Control</tt> (voir plus loin).
400
+
401
+ ## Vues / Templates
402
+
403
+ Chaqie langage de template est disponible via sa propre méthode de rendu,
404
+ lesquelles renvoient tout simplement une chaîne de caractères.
405
+
406
+ ``` ruby
407
+ get '/' do
408
+ erb :index
409
+ end
410
+ ```
411
+
412
+ Ceci effectue le rendu de la vue `views/index.erb`.
413
+
414
+ Plutôt que d'utiliser le nom d'un template, vous pouvez directement passer
415
+ le contenu du template :
416
+
417
+ ``` ruby
418
+ get '/' do
419
+ code = "<%= Time.now %>"
420
+ erb code
421
+ end
422
+ ```
423
+
424
+ Les méthodes de templates acceptent un second paramètre, un hash d'options :
425
+
426
+ ``` ruby
427
+ get '/' do
428
+ erb :index, :layout => :post
429
+ end
430
+ ```
431
+
432
+ Ceci effectuera le rendu de la vue `views/index.erb` en l'intégrant
433
+ au *layout* `views/post.erb` (les vues Erb sont intégrées par défaut
434
+ au *layout* `views/layout.erb` quand ce fichier existe).
435
+
436
+ Toute option que Sinatra ne comprend pas sera passée au moteur de rendu :
437
+
438
+ ``` ruby
439
+ get '/' do
440
+ haml :index, :format => :html5
441
+ end
442
+ ```
443
+
444
+ Vous pouvez également définir des options par langage de template de façon
445
+ générale :
446
+
447
+ ``` ruby
448
+ set :haml, :format => html5
449
+
450
+ get '/' do
451
+ haml :index
452
+ end
453
+ ```
454
+
455
+ Les options passées à la méthode de rendu prennent le pas sur les options
456
+ définies au moyen de `set`.
457
+
458
+ Options disponibles :
459
+
460
+ <dl>
461
+ <dt>locals</dt>
462
+ <dd>
463
+ Liste de variables locales passées au document. Pratique pour les vues
464
+ partielles.
465
+ Exemple : <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>.
466
+ </dd>
467
+
468
+ <dt>default_encoding</dt>
469
+ <dd>
470
+ Encodage de caractères à utiliser en cas d'incertitude. Par défaut, c'est
471
+ <tt>settings.default_encoding</tt>.
472
+ </dd>
473
+
474
+ <dt>views</dt>
475
+ <dd>
476
+ Dossier de vues dans lequel chercher les templates. Par défaut
477
+ <tt>settings.views</tt>.
478
+ </dd>
479
+
480
+ <dt>layout</dt>
481
+ <dd>
482
+ S'il faut ou non utiliser un layout (<tt>true</tt> ou <tt>false</tt>).
483
+ Indique le template à utiliser lorsque c'est un symbole. Exemple :
484
+ <tt>erb :index, :layout => !request.xhr?</tt>.
485
+ </dd>
486
+
487
+ <dt>content_type</dt>
488
+ <dd>
489
+ Content-Type que le template produit, dépend par défaut du langage de
490
+ template.
491
+ </dd>
492
+
493
+ <dt>scope</dt>
494
+ <dd>
495
+ Contexte sous lequel effectuer le rendu du template. Par défaut il s'agit
496
+ de l'instance de l'application. Si vous changez cela, les variables
497
+ d'instance et les méthodes utilitaires ne seront pas disponibles.
498
+ </dd>
499
+
500
+ <dt>layout_engine</dt>
501
+ <dd>
502
+ Moteur de rendu à utiliser pour le layout. Utile pour les langages ne
503
+ supportant pas les layouts. Il s'agit par défaut du moteur utilisé pour
504
+ le rendu du template. Exemple : <tt>set :rdoc, :layout_engine => :erb</tt>
505
+ </dd>
506
+
507
+ <dt>layout_options</dt>
508
+ <dd>
509
+ Options spécifiques destinées au moteur de rendu. Exemple : <tt>set :rdoc,
510
+ :layout_options => { :views => 'views/layouts' }</tt>
511
+ </dd>
512
+ </dl>
513
+
514
+ Les templates sont supposés se trouver directement dans le dossier
515
+ `./views`. Pour utiliser un dossier de vues différent :
516
+
517
+ ``` ruby
518
+ set :views, settings.root + '/templates'
519
+ ```
520
+
521
+ Il est important de se souvenir que les templates sont toujours référencés
522
+ sous forme de symboles, même lorsqu'ils sont dans un sous-répertoire (dans
523
+ ce cas, utilisez `:'sous_repertoire/template'`). Il faut utiliser
524
+ un symbole car les méthodes de rendu évaluent le contenu des chaînes de
525
+ caractères au lieu de les considérer comme un chemin vers un fichier.
526
+
527
+ ### Templates littéraux
528
+
529
+ ``` ruby
530
+ get '/' do
531
+ haml '%div.title Bonjour le monde'
532
+ end
533
+ ```
534
+
535
+ Générera le code du template spécifié dans la chaîne de caractères.
536
+
537
+ ### Langages de template disponibles
538
+
539
+ Certains langages ont plusieurs implémentations. Pour préciser l'implémentation
540
+ à utiliser (et garantir l'aspect thread-safe), vous devez simplement l'avoir
541
+ chargée au préalable :
542
+
543
+ ``` ruby
544
+ require 'rdiscount' # ou require 'bluecloth'
545
+ get('/') { markdown :index }
546
+ ```
547
+
548
+ #### Templates Haml
549
+
550
+ <table>
551
+ <tr>
552
+ <td>Dépendances</td>
553
+ <td><a href="http://haml.info/" title="haml">haml</a></td>
554
+ </tr>
555
+ <tr>
556
+ <td>Extensions de fichier</td>
557
+ <td><tt>.haml</tt></td>
558
+ </tr>
559
+ <tr>
560
+ <td>Exemple</td>
561
+ <td><tt>haml :index, :format => :html5</tt></td>
562
+ </tr>
563
+ </table>
564
+
565
+ #### Templates Erb
566
+
567
+ <table>
568
+ <tr>
569
+ <td>Dépendances</td>
570
+ <td>
571
+ <a href="http://www.kuwata-lab.com/erubis/" title="erubis">erubis</a>
572
+ ou erb (inclus avec Ruby)
573
+ </td>
574
+ </tr>
575
+ <tr>
576
+ <td>Extensions de fichier</td>
577
+ <td><tt>.erb</tt>, <tt>.rhtml</tt> ou <tt>.erubis</tt> (Erubis seulement)</td>
578
+ </tr>
579
+ <tr>
580
+ <td>Exemple</td>
581
+ <td><tt>erb :index</tt></td>
582
+ </tr>
583
+ </table>
584
+
585
+ #### Templates Builder
586
+
587
+ <table>
588
+ <tr>
589
+ <td>Dépendances</td>
590
+ <td>
591
+ <a href="http://builder.rubyforge.org/" title="builder">builder</a>
592
+ </td>
593
+ </tr>
594
+ <tr>
595
+ <td>Extensions de fichier</td>
596
+ <td><tt>.builder</tt></td>
597
+ </tr>
598
+ <tr>
599
+ <td>Exemple</td>
600
+ <td><tt>builder { |xml| xml.em "salut" }</tt></td>
601
+ </tr>
602
+ </table>
603
+
604
+ Ce moteur accepte également un bloc pour des templates en ligne (voir
605
+ exemple).
606
+
607
+ #### Templates Nokogiri
608
+
609
+ <table>
610
+ <tr>
611
+ <td>Dépendances</td>
612
+ <td><a href="http://nokogiri.org/" title="nokogiri">nokogiri</a></td>
613
+ </tr>
614
+ <tr>
615
+ <td>Extensions de fichier</td>
616
+ <td><tt>.nokogiri</tt></td>
617
+ </tr>
618
+ <tr>
619
+ <td>Exemple</td>
620
+ <td><tt>nokogiri { |xml| xml.em "salut" }</tt>
621
+ </td>
622
+ </tr>
623
+ </table>
624
+
625
+ Ce moteur accepte également un bloc pour des templates en ligne (voir
626
+ exemple).
627
+
628
+ #### Templates Sass
629
+
630
+ <table>
631
+ <tr>
632
+ <td>Dépendances</td>
633
+ <td><a href="http://sass-lang.com/" title="sass">sass</a></td>
634
+ </tr>
635
+ <tr>
636
+ <td>Extensions de fichier</td>
637
+ <td><tt>.sass</tt></td>
638
+ </tr>
639
+ <tr>
640
+ <td>Exemple</td>
641
+ <td><tt>sass :stylesheet, :style => :expanded</tt></td>
642
+ </tr>
643
+ </table>
644
+
645
+ #### Templates SCSS
646
+
647
+ <table>
648
+ <tr>
649
+ <td>Dépendances</td>
650
+ <td><a href="http://sass-lang.com/" title="sass">sass</a></td>
651
+ </tr>
652
+ <tr>
653
+ <td>Extensions de fichier</td>
654
+ <td><tt>.scss</tt></td>
655
+ </tr>
656
+ <tr>
657
+ <td>Exemple</td>
658
+ <td><tt>scss :stylesheet, :style => :expanded</tt></p>
659
+ </td>
660
+ </tr>
661
+ </table>
662
+
663
+ #### Templates Less
664
+
665
+ <table>
666
+ <tr>
667
+ <td>Dépendances</td>
668
+ <td><a href="http://www.lesscss.org/" title="less">less</a></td>
669
+ </tr>
670
+ <tr>
671
+ <td>Extensions de fichier</td>
672
+ <td><tt>.less</tt></td>
673
+ </tr>
674
+ <tr>
675
+ <td>Exemple</td>
676
+ <td><tt>less :stylesheet</tt>
677
+ </td>
678
+ </tr>
679
+ </table>
680
+
681
+ #### Templates Liquid
682
+
683
+ <table>
684
+ <tr>
685
+ <td>Dépendances</td>
686
+ <td><a href="http://www.liquidmarkup.org/" title="liquid">liquid</a></td>
687
+ </tr>
688
+ <tr>
689
+ <td>Extensions de fichier</td>
690
+ <td><tt>.liquid</tt></td>
691
+ </tr>
692
+ <tr>
693
+ <td>Exemple</td>
694
+ <td><tt>liquid :index, :locals => { :key => 'value' }</tt></td>
695
+ </tr>
696
+ </table>
697
+
698
+ Comme vous ne pouvez appeler de méthodes Ruby (autres que `yield`)
699
+ dans un template Liquid, vous aurez sûrement à lui passer des variables
700
+ locales.
701
+
702
+ #### Templates Markdown
703
+
704
+ <table>
705
+ <tr>
706
+ <td><p>Dépendances</p></td>
707
+ <td>
708
+ Au choix :
709
+ <a href="https://github.com/rtomayko/rdiscount" title="RDiscount">RDiscount</a>,
710
+ <a href="https://github.com/vmg/redcarpet" title="RedCarpet">RedCarpet</a>,
711
+ <a href="http://deveiate.org/projects/BlueCloth" title="BlueCloth">BlueCloth</a>,
712
+ <a href="http://kramdown.rubyforge.org/" title="kramdown">kramdown</a>,
713
+ <a href="http://maruku.rubyforge.org/" title="maruku">maruku</a>
714
+ </td>
715
+ </tr>
716
+
717
+ <tr>
718
+ <td>Extensions de fichier</td>
719
+ <td><tt>.markdown</tt>, <tt>.mkd</tt> et <tt>.md</tt></td>
720
+ </tr>
721
+ <tr>
722
+ <td>Exemple</td>
723
+ <td><tt>markdown :index, :layout_engine => :erb</tt></td>
724
+ </tr>
725
+ </table>
726
+
727
+ Il n’est pas possible d’appeler des méthodes depuis markdown, ni de
728
+ lui passer des variables locales. Par conséquent, il sera souvent utilisé
729
+ en combinaison avec un autre moteur de rendu :
730
+
731
+ ``` ruby
732
+ erb :overview, :locals => { :text => markdown(:introduction) }
733
+ ```
734
+
735
+ Notez que vous pouvez également appeler la méthode `markdown` au
736
+ sein d’autres templates :
737
+
738
+ ``` ruby
739
+ %h1 Hello From Haml !
740
+ %p= markdown(:greetings)
741
+ ```
742
+
743
+ Comme vous ne pouvez pas appeler de Ruby au sein de Markdown, vous ne
744
+ pouvez pas utiliser de layouts écrits en Markdown. Toutefois, il
745
+ est possible d’utiliser un moteur de rendu différent pour le template et
746
+ pour le layout en utilisant l’option `:layout_engine`.
747
+
748
+ #### Templates Textile
749
+
750
+ <table>
751
+ <tr>
752
+ <td>Dépendances</td>
753
+ <td><a href="http://redcloth.org/" title="RedCloth">RedCloth</a></td>
754
+ </tr>
755
+ <tr>
756
+ <td>Extensions de fichier</td>
757
+ <td><tt>.textile</tt></td>
758
+ </tr>
759
+ <tr>
760
+ <td>Exemple</td>
761
+ <td><tt>textile :index, :layout_engine => :erb</tt></td>
762
+ </tr>
763
+ </table>
764
+
765
+ Il n’est pas possible d’appeler des méthodes depuis textile, ni de lui
766
+ passer des variables locales. Par conséquent, il sera souvent utilisé en
767
+ combinaison avec un autre moteur de rendu :
768
+
769
+ ``` ruby
770
+ erb :overview, :locals => { :text => textile(:introduction) }
771
+ ```
772
+
773
+ Notez que vous pouvez également appeler la méthode `textile` au
774
+ sein d’autres templates :
775
+
776
+ ``` ruby
777
+ %h1 Hello From Haml !
778
+ %p= textile(:greetings)
779
+ ```
780
+
781
+ Comme vous ne pouvez pas appeler de Ruby au sein de Textile, vous ne pouvez
782
+ pas utiliser de layouts écrits en Textile. Toutefois, il est
783
+ possible d’utiliser un moteur de rendu différent pour le template et
784
+ pour le layout en utilisant l’option `:layout_engine`.
785
+
786
+ #### Templates RDoc
787
+
788
+ <table>
789
+ <tr>
790
+ <td>Dépendances</td>
791
+ <td><a href="http://rdoc.rubyforge.org/" title="RDoc">RDoc</a></td>
792
+ </tr>
793
+ <tr>
794
+ <td>Extensions de fichier</td>
795
+ <td><tt>.rdoc</tt></td>
796
+ </tr>
797
+ <tr>
798
+ <td>Exemple</td>
799
+ <td><tt>rdoc :README, :layout_engine => :erb</tt></td>
800
+ </tr>
801
+ </table>
802
+
803
+ Il n’est pas possible d’appeler des méthodes depuis rdoc, ni de lui
804
+ passer des variables locales. Par conséquent, il sera souvent utilisé en
805
+ combinaison avec un autre moteur de rendu :
806
+
807
+ ``` ruby
808
+ erb :overview, :locals => { :text => rdoc(:introduction) }
809
+ ```
810
+
811
+ Notez que vous pouvez également appeler la méthode `rdoc` au sein
812
+ d’autres templates :
813
+
814
+ ``` ruby
815
+ %h1 Hello From Haml !
816
+ %p= rdoc(:greetings)
817
+ ```
818
+
819
+ Comme vous ne pouvez pas appeler de Ruby au sein de RDoc, vous ne pouvez
820
+ pas utiliser de layouts écrits en RDoc. Toutefois, il est
821
+ possible d’utiliser un moteur de rendu différent pour le template et
822
+ pour le layout en utilisant l’option `:layout_engine`.
823
+
824
+ #### Templates Radius
825
+ <table>
826
+ <tr>
827
+ <td>Dépendances</td>
828
+ <td><a href="http://radius.rubyforge.org/" title="Radius">Radius</a></td>
829
+ </tr>
830
+ <tr>
831
+ <td>Extensions de fichier</td>
832
+ <td><tt>.radius</tt></td>
833
+ </tr>
834
+ <tr>
835
+ <td>Exemple</td>
836
+ <td><tt>radius :index, :locals => { :key => 'value' }</tt></td>
837
+ </tr>
838
+ </table>
839
+
840
+ Comme vous ne pouvez pas appeler de méthodes Ruby depuis un template
841
+ Radius, vous aurez sûrement à lui passer des variables locales.
842
+
843
+ #### Templates Markaby
844
+
845
+ <table>
846
+ <tr>
847
+ <td>Dépendances</td>
848
+ <td><a href="http://markaby.github.com/" title="Markaby">Markaby</a></td>
849
+ </tr>
850
+ <tr>
851
+ <td>Extensions de fichier</td>
852
+ <td><tt>.mab</tt></td>
853
+ </tr>
854
+ <tr>
855
+ <td>Exemple</td>
856
+ <td><tt>markaby { h1 "Bienvenue !" }</tt></td>
857
+ </tr>
858
+ </table>
859
+
860
+ Ce moteur accepte également un bloc pour des templates en ligne (voir
861
+ exemple).
862
+
863
+ #### Templates RABL
864
+
865
+ <table>
866
+ <tr>
867
+ <td>Dépendances</td>
868
+ <td><a href="https://github.com/nesquena/rabl" title="Rabl">Rabl</a></td>
869
+ </tr>
870
+ <tr>
871
+ <td>Extensions de fichier</td>
872
+ <td><tt>.rabl</tt></td>
873
+ </tr>
874
+ <tr>
875
+ <td>Exemple</td>
876
+ <td><tt>rabl :index</tt></td>
877
+ </tr>
878
+ </table>
879
+
880
+ #### Templates Slim
881
+
882
+ <table>
883
+ <tr>
884
+ <td>Dépendances</td>
885
+ <td><a href="http://slim-lang.com/" title="Slim Lang">Slim Lang</a></td>
886
+ </tr>
887
+ <tr>
888
+ <td>Extensions de fichier</td>
889
+ <td><tt>.slim</tt></td>
890
+ </tr>
891
+ <tr>
892
+ <td>Exemple</td>
893
+ <td><tt>slim :index</tt></td>
894
+ </tr>
895
+ </table>
896
+
897
+ #### Templates Creole
898
+
899
+ <table>
900
+ <tr>
901
+ <td>Dépendances</td>
902
+ <td><a href="https://github.com/minad/creole" title="Creole">Creole</a></td>
903
+ </tr>
904
+ <tr>
905
+ <td>Extensions de fichier</td>
906
+ <td><tt>.creole</tt></td>
907
+ </tr>
908
+ <tr>
909
+ <td>Exemple</td>
910
+ <td><tt>creole :wiki, :layout_engine => :erb</tt></td>
911
+ </tr>
912
+ </table>
913
+
914
+ Il n'est pas possible d'appeler des méthodes depuis creole, ni de lui
915
+ passer des variables locales. Par conséquent, il sera souvent utilisé en
916
+ combinaison avec un autre moteur de rendu :
917
+
918
+ ``` ruby
919
+ erb :overview, :locals => { :text => markdown(:introduction) }
920
+ ```
921
+
922
+ Notez que vous pouvez également appeler la méthode `creole` au sein d'autres
923
+ templates :
924
+
925
+ ``` ruby
926
+ %h1 Hello From Haml !
927
+ %p= creole(:greetings)
928
+ ```
929
+
930
+ Comme vous ne pouvez pas appeler de Ruby au sein de Creole, vous ne pouvez
931
+ pas utiliser de layouts écrits en Creole. Toutefois, il est possible
932
+ d'utiliser un moteur de rendu différent pour le template et pour le layout
933
+ en utilisant l'option `:layout_engine`.
934
+
935
+ #### Templates CoffeeScript
936
+
937
+ <table>
938
+ <tr>
939
+ <td>Dépendances</td>
940
+ <td>
941
+ <a href="https://github.com/josh/ruby-coffee-script" title="Ruby CoffeeScript">
942
+ CoffeeScript
943
+ </a>
944
+ et un
945
+ <a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme" title="ExecJS">
946
+ moyen d'exécuter javascript
947
+ </a>
948
+ </td>
949
+ </tr>
950
+ <tr>
951
+ <td>Extensions de fichier</td>
952
+ <td><tt>.coffee</tt></td>
953
+ </tr>
954
+ <tr>
955
+ <td>Exemple</td>
956
+ <td><tt>coffee :index</tt></td>
957
+ </tr>
958
+ </table>
959
+
960
+ #### Templates Stylus
961
+
962
+ <table>
963
+ <tr>
964
+ <td>Dépendances</td>
965
+ <td>
966
+ <a href="https://github.com/lucasmazza/ruby-stylus" title="Ruby Stylus">
967
+ Stylus
968
+ </a>
969
+ et un
970
+ <a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme" title="ExecJS">
971
+ moyen d'exécuter javascript
972
+ </a>
973
+ </td>
974
+ </tr>
975
+ <tr>
976
+ <td>Extensions de fichier</td>
977
+ <td><tt>.styl</tt></td>
978
+ </tr>
979
+ <tr>
980
+ <td>Exemple</td>
981
+ <td><tt>stylus :index</tt></td>
982
+ </tr>
983
+ </table>
984
+
985
+ Avant de pouvoir utiliser des templates Stylus, vous devez auparavant charger
986
+ `stylus` et `stylus/tilt` :
987
+
988
+ ``` ruby
989
+ require 'sinatra'
990
+ require 'stylus'
991
+ require 'stylus/tilt'
992
+
993
+ get '/' do
994
+ stylus :exemple
995
+ end
996
+ ```
997
+
998
+ #### Templates Yajl
999
+
1000
+ <table>
1001
+ <tr>
1002
+ <td>Dépendances</td>
1003
+ <td>
1004
+ <a href="https://github.com/brianmario/yajl-ruby" title="yajl-ruby">yajl-ruby</a>
1005
+ </td>
1006
+ </tr>
1007
+ <tr>
1008
+ <td>Extensions de fichier</td>
1009
+ <td><tt>.yajl</tt></td>
1010
+ </tr>
1011
+ <tr>
1012
+ <td>Exemple</td>
1013
+ <td><tt>yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource'</tt></p>
1014
+ </td>
1015
+ </tr>
1016
+ </table>
1017
+
1018
+ Le source du template est évalué en tant que chaine Ruby, puis la
1019
+ variable json obtenue est convertie avec #to_json.
1020
+
1021
+ ``` ruby
1022
+ json = { :foo => 'bar' }
1023
+ json[:baz] = key
1024
+ ```
1025
+
1026
+ Les options `:callback` et `:variable` peuvent être utilisées pour décorer
1027
+ l’objet retourné.
1028
+
1029
+ ``` ruby
1030
+ var resource = {"foo":"bar","baz":"qux"}; present(resource);</pre>
1031
+ ```
1032
+
1033
+ #### Templates WLang
1034
+
1035
+ <table>
1036
+ <tr>
1037
+ <td>Dépendances</td>
1038
+ <td><a href="https://github.com/blambeau/wlang/" title="wlang">wlang</a></td>
1039
+ </tr>
1040
+ <tr>
1041
+ <td>Extensions de fichier</td>
1042
+ <td><tt>.wlang</tt></td>
1043
+ </tr>
1044
+ <tr>
1045
+ <td>Exemple</td>
1046
+ <td><tt>wlang :index, :locals => { :key => 'value' }</tt></td>
1047
+ </tr>
1048
+ </table>
1049
+
1050
+ L’appel de code ruby au sein des templates n’est pas idiomatique en wlang.
1051
+ L’écriture de templates sans logique est encouragé, via le passage de variables
1052
+ locales. Il est néanmoins possible d’écrire un layout en wlang et d’y utiliser
1053
+ `yield`.
1054
+
1055
+ ### Accéder aux variables dans un Template
1056
+
1057
+ Un template est évalué dans le même contexte que l'endroit d'où il a été
1058
+ appelé (gestionnaire de route). Les variables d'instance déclarées dans le
1059
+ gestionnaire de route sont directement accessibles dans le template :
1060
+
1061
+ ``` ruby
1062
+ get '/:id' do
1063
+ @foo = Foo.find(params[:id])
1064
+ haml '%h1= @foo.nom'
1065
+ end
1066
+ ```
1067
+
1068
+ Alternativement, on peut passer un hash contenant des variables locales :
1069
+
1070
+ ``` ruby
1071
+ get '/:id' do
1072
+ foo = Foo.find(params[:id])
1073
+ haml '%h1= foo.nom', :locals => { :foo => foo }
1074
+ end
1075
+ ```
1076
+
1077
+ Ceci est généralement utilisé lorsque l'on veut utiliser un template comme
1078
+ partiel (depuis un autre template) et qu'il est donc nécessaire d'adapter les
1079
+ noms de variables.
1080
+
1081
+
1082
+ ### Templates avec `yield` et layouts imbriqués
1083
+
1084
+ En général, un layout est un simple template qui appelle `yield`. Ce genre de
1085
+ template peut s'utiliser via l'option `:template` comme décrit précédemment ou
1086
+ peut être rendu depuis un bloc :
1087
+
1088
+ ``` ruby
1089
+ erb :post, :layout => false do
1090
+ erb :index
1091
+ end
1092
+ ```
1093
+
1094
+ Ce code est à globalement équivalent à `erb :index, :layout => :post`.
1095
+
1096
+ Le fait de passer des blocs aux méthodes de rendu est particulièrement utile
1097
+ pour gérer des templates imbriqués :
1098
+
1099
+ ``` ruby
1100
+ erb :main_layout, :layout => false do
1101
+ erb :admin_layout do
1102
+ erb :user
1103
+ end
1104
+ end
1105
+ ```
1106
+
1107
+ Ce qui peut aussi être fait avec un peu moins de code :
1108
+
1109
+ ``` ruby
1110
+ erb :admin_layout, :layout => :main_layout do
1111
+ erb :user
1112
+ end
1113
+ ```
1114
+
1115
+ Actuellement, les méthodes de rendu qui acceptent un bloc sont : `erb`, `haml`,
1116
+ `liquid`, `slim ` et `wlang`. La méthode générale `render` accepte elle aussi
1117
+ un bloc.
1118
+
1119
+
1120
+ ### Templates dans le fichier source
1121
+
1122
+ Des templates peuvent être définis dans le fichier source comme ceci :
1123
+
1124
+ ``` ruby
1125
+ require 'sinatra'
1126
+
1127
+ get '/' do
1128
+ haml :index
1129
+ end
1130
+
1131
+ __END__
1132
+
1133
+ @@ layout
1134
+ %html
1135
+ = yield
1136
+
1137
+ @@ index
1138
+ %div.title Bonjour le monde !
1139
+ ```
1140
+
1141
+ NOTE : Les templates du fichier source qui contient `require 'sinatra'`
1142
+ sont automatiquement chargés. Si vous avez des templates dans d'autres
1143
+ fichiers source, il faut explicitement les déclarer avec
1144
+ `enable :inline_templates`.
1145
+
1146
+
1147
+ ### Templates nommés
1148
+
1149
+ Les templates peuvent aussi être définis grâce à la méthode de haut niveau `template` :
1150
+
1151
+ ``` ruby
1152
+ template :layout do
1153
+ "%html\n =yield\n"
1154
+ end
1155
+
1156
+ template :index do
1157
+ '%div.title Bonjour le monde !'
1158
+ end
1159
+
1160
+ get '/' do
1161
+ haml :index
1162
+ end
1163
+ ```
1164
+
1165
+ Si un template nommé "layout" existe, il sera utilisé à chaque fois qu'un
1166
+ template sera affiché. Vous pouvez désactivez les layouts au cas par cas en
1167
+ passant `:layout => false` ou bien les désactiver par défaut au moyen
1168
+ de `set :haml, :layout => false` :
1169
+
1170
+ ``` ruby
1171
+ get '/' do
1172
+ haml :index, :layout => !request.xhr?
1173
+ end
1174
+ ```
1175
+
1176
+ ### Associer des extensions de fichier
1177
+
1178
+ Pour associer une extension de fichier avec un moteur de rendu, utilisez
1179
+ `Tilt.register`. Par exemple, si vous désirez utiliser l'extension
1180
+ de fichier `tt` pour les templates Textile, vous pouvez faire comme suit :
1181
+
1182
+ ``` ruby
1183
+ Tilt.register :tt, Tilt[:textile]
1184
+ ```
1185
+
1186
+ ### Ajouter son propre moteur de rendu
1187
+
1188
+ En premier lieu, déclarez votre moteur de rendu avec Tilt, ensuite créez
1189
+ votre méthode de rendu :
1190
+
1191
+ ``` ruby
1192
+ Tilt.register :monmoteur, MonMerveilleurMoteurDeRendu
1193
+
1194
+ helpers do
1195
+ def monmoteur(*args) render(:monmoteur, *args) end
1196
+ end
1197
+
1198
+ get '/' do
1199
+ monmoteur :index
1200
+ end
1201
+ ```
1202
+
1203
+ Utilisera `./views/index.monmoteur`. Voir [le dépôt Github](https://github.com/rtomayko/tilt) pour en savoir plus sur Tilt.
1204
+
1205
+ ## Filtres
1206
+
1207
+ Les filtres before sont exécutés avant chaque requête, dans le même contexte
1208
+ que les routes, et permettent de modifier la requête et sa réponse. Les
1209
+ variables d'instance déclarées dans les filtres sont accessibles au niveau
1210
+ des routes et des templates :
1211
+
1212
+ ``` ruby
1213
+ before do
1214
+ @note = 'Coucou !'
1215
+ request.path_info = '/foo/bar/baz'
1216
+ end
1217
+
1218
+ get '/foo/*' do
1219
+ @note #=> 'Coucou !'
1220
+ params[:splat] #=> 'bar/baz'
1221
+ end
1222
+ ```
1223
+
1224
+ Les filtres after sont exécutés après chaque requête à l'intérieur du même
1225
+ contexte et permettent de modifier la requête et sa réponse. Les variables
1226
+ d'instance déclarées dans les filtres before ou les routes sont accessibles
1227
+ au niveau des filtres after :
1228
+
1229
+ ``` ruby
1230
+ after do
1231
+ puts response.status
1232
+ end
1233
+ ```
1234
+
1235
+ Note : Le corps de la réponse n'est pas disponible au niveau du filtre after
1236
+ car il ne sera généré que plus tard (sauf dans le cas où vous utilisez la
1237
+ méthode `body` au lieu de simplement renvoyer une chaine depuis vos routes).
1238
+
1239
+ Les filtres peuvent être associés à un masque, ce qui permet de limiter leur
1240
+ exécution aux cas où la requête correspond à ce masque :
1241
+
1242
+ ``` ruby
1243
+ before '/secret/*' do
1244
+ authentification!
1245
+ end
1246
+
1247
+ after '/faire/:travail' do |travail|
1248
+ session[:dernier_travail] = travail
1249
+ end
1250
+ ```
1251
+
1252
+ Tout comme les routes, les filtres acceptent également des conditions :
1253
+
1254
+ ``` ruby
1255
+ before :agent => /Songbird/ do
1256
+ # ...
1257
+ end
1258
+
1259
+ after '/blog/*', :host_name => 'example.com' do
1260
+ # ...
1261
+ end
1262
+ ```
1263
+
1264
+ ## Helpers
1265
+
1266
+ Utilisez la méthode de haut niveau `helpers` pour définir des routines
1267
+ qui seront accessibles dans vos gestionnaires de route et dans vos templates :
1268
+
1269
+ ``` ruby
1270
+ helpers do
1271
+ def bar(nom)
1272
+ "#{nom}bar"
1273
+ end
1274
+ end
1275
+
1276
+ get '/:nom' do
1277
+ bar(params[:nom])
1278
+ end
1279
+ ```
1280
+
1281
+ Vous pouvez aussi définir les méthodes helper dans un module séparé :
1282
+
1283
+ ``` ruby
1284
+ module FooUtils
1285
+ def foo(nom) "#{nom}foo" end
1286
+ end
1287
+
1288
+ module BarUtils
1289
+ def bar(nom) "#{nom}bar" end
1290
+ end
1291
+
1292
+ helpers FooUtils, BarUtils
1293
+ ```
1294
+
1295
+ Cela a le même résultat que d'inclure les modules dans la classe de
1296
+ l'application.
1297
+
1298
+ ### Utiliser les sessions
1299
+
1300
+ Une session est utilisée pour conserver un état entre les requêtes. Une fois
1301
+ activées, vous avez un hash de session par session utilisateur :
1302
+
1303
+ ``` ruby
1304
+ enable :sessions
1305
+
1306
+ get '/' do
1307
+ "valeur = " << session[:valeur].inspect
1308
+ end
1309
+
1310
+ get '/:value' do
1311
+ session[:valeur] = params[:valeur]
1312
+ end
1313
+ ```
1314
+
1315
+ Notez que `enable :sessions` enregistre en fait toutes les données dans
1316
+ un cookie. Ce n'est pas toujours ce que vous voulez (enregistrer beaucoup de
1317
+ données va augmenter le traffic par exemple). Vous pouvez utiliser n'importe
1318
+ quel middleware Rack de session afin d'éviter cela. N'utilisez **pas**
1319
+ `enable :sessions` dans ce cas mais chargez le middleware de votre
1320
+ choix comme vous le feriez pour n'importe quel autre middleware :
1321
+
1322
+ ``` ruby
1323
+ use Rack::Session::Pool, :expire_after => 2592000
1324
+
1325
+ get '/' do
1326
+ "valeur = " << session[:valeur].inspect
1327
+ end
1328
+
1329
+ get '/:value' do
1330
+ session[:valeur] = params[:valeur]
1331
+ end
1332
+ ```
1333
+
1334
+ Pour renforcer la sécurité, les données de session dans le cookie sont signées
1335
+ avec une clé secrète de session. Une clé secrète est générée pour vous au
1336
+ hasard par Sinatra. Toutefois, comme cette clé change à chaque démarrage de
1337
+ votre application, vous pouvez définir cette clé vous-même afin que toutes
1338
+ les instances de votre application la partage :
1339
+
1340
+ ``` ruby
1341
+ set :session_secret, 'super secret'
1342
+ ```
1343
+
1344
+ Si vous souhaitez avoir plus de contrôle, vous pouvez également enregistrer un
1345
+ hash avec des options lors de la configuration de `sessions` :
1346
+
1347
+ ``` ruby
1348
+ set :sessions, :domain => 'foo.com'
1349
+ ```
1350
+
1351
+ Pour que les différents sous-domaines de foo.com puisse partager une session,
1352
+ vous devez préfixer le domaine par *.* :
1353
+
1354
+ ``` ruby
1355
+ set :sessions, :domain => '.foo.com'
1356
+ ```
1357
+
1358
+
1359
+ ### Halt
1360
+
1361
+ Pour arrêter immédiatement la requête dans un filtre ou un gestionnaire de
1362
+ route :
1363
+
1364
+ ``` ruby
1365
+ halt
1366
+ ```
1367
+
1368
+ Vous pouvez aussi passer le code retour ...
1369
+
1370
+ ``` ruby
1371
+ halt 410
1372
+ ```
1373
+
1374
+ Ou le texte ...
1375
+
1376
+ ``` ruby
1377
+ halt 'Ceci est le texte'
1378
+ ```
1379
+
1380
+ Ou les deux ...
1381
+
1382
+ ``` ruby
1383
+ halt 401, 'Partez !'
1384
+ ```
1385
+
1386
+ Ainsi que les entêtes ...
1387
+
1388
+ ``` ruby
1389
+ halt 402, {'Content-Type' => 'text/plain'}, 'revanche'
1390
+ ```
1391
+
1392
+ Bien sûr il est possible de combiner un template avec `halt` :
1393
+
1394
+ ``` ruby
1395
+ halt erb(:erreur)
1396
+ ```
1397
+
1398
+ ### Passer
1399
+
1400
+ Une route peut passer le relais aux autres routes qui correspondent également
1401
+ avec `pass` :
1402
+
1403
+ ``` ruby
1404
+ get '/devine/:qui' do
1405
+ pass unless params[:qui] == 'Frank'
1406
+ "Tu m'as eu !"
1407
+ end
1408
+
1409
+ get '/devine/*' do
1410
+ 'Manqué !'
1411
+ end
1412
+ ```
1413
+
1414
+ On sort donc immédiatement de ce gestionnaire et on continue à chercher,
1415
+ dans les masques suivants, le prochain qui correspond à la requête.
1416
+ Si aucun des masques suivants ne correspond, un code 404 est retourné.
1417
+
1418
+ ### Déclencher une autre route
1419
+
1420
+ Parfois, `pass` n'est pas ce que vous recherchez, au lieu de cela vous
1421
+ souhaitez obtenir le résultat d'une autre route. Pour cela, utilisez
1422
+ simplement `call` :
1423
+
1424
+ ``` ruby
1425
+ get '/foo' do
1426
+ status, headers, body = call env.merge("PATH_INFO" => '/bar')
1427
+ [status, headers, body.map(&:upcase)]
1428
+ end
1429
+
1430
+ get '/bar' do
1431
+ "bar"
1432
+ end
1433
+ ```
1434
+
1435
+ Notez que dans l'exemple ci-dessus, vous faciliterez les tests et améliorerez
1436
+ la performance en déplaçant simplement `"bar"` dans un helper
1437
+ utilisé à la fois par `/foo` et `/bar`.
1438
+
1439
+ Si vous souhiatez que la requête soit envoyée à la même instance de
1440
+ l'application plutôt qu'à une copie, utilisez `call!` au lieu de
1441
+ `call`.
1442
+
1443
+ Lisez la spécification Rack si vous souhaitez en savoir plus sur
1444
+ `call`.
1445
+
1446
+ ### Définir le corps, le code retour et les entêtes
1447
+
1448
+ Il est possible et recommandé de définir le code retour et le corps de la
1449
+ réponse au moyen de la valeur de retour d'un bloc définissant une route.
1450
+ Quoiqu'il en soit, dans certains cas vous pourriez avoir besoin de définir
1451
+ le coprs de la réponse à un moment arbitraire de l'exécution. Vous pouvez le
1452
+ faire au moyen de la méthode `body`. Si vous faites ainsi, vous pouvez alors
1453
+ utiliser cette même méthode pour accéder au corps de la réponse :
1454
+
1455
+ ``` ruby
1456
+ get '/foo' do
1457
+ body "bar"
1458
+ end
1459
+
1460
+ after do
1461
+ puts body
1462
+ end
1463
+ ```
1464
+
1465
+ Il est également possible de passer un bloc à `body`, qui sera exécuté par le
1466
+ gestionnaire Rack (ceci peut être utilisé pour implémenter un streaming,
1467
+ voir "Valeurs de retour").
1468
+
1469
+ Pareillement au corps de la réponse, vous pouvez également définir le code
1470
+ retour et les entêtes :
1471
+
1472
+ ``` ruby
1473
+ get '/foo' do
1474
+ status 418
1475
+ headers \
1476
+ "Allow" => "BREW, POST, GET, PROPFIND, WHEN",
1477
+ "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
1478
+ body "Je suis une théière !"
1479
+ end
1480
+ ```
1481
+
1482
+ Comme `body` `headers` et `status` peuvent être utilisés sans arguments
1483
+ pour accéder à leurs valeurs.
1484
+
1485
+ ### Faire du streaming
1486
+
1487
+ Il y a des cas où vous voulez commencer à renvoyer des données pendant que
1488
+ vous êtes en train de générer le reste de la réponse. Dans les cas les plus
1489
+ extrèmes, vous souhaitez continuer à envoyer des données tant que le client
1490
+ n'abandonne pas la connection. Vous pouvez alors utiliser le helper `stream`
1491
+ pour éviter de créer votre propre système :
1492
+
1493
+ ``` ruby
1494
+ get '/' do
1495
+ stream do |out|
1496
+ out << "Ca va être hallu -\n"
1497
+ sleep 0.5
1498
+ out << " (attends la suite) \n"
1499
+ sleep 1
1500
+ out << "- cinant !\n"
1501
+ end
1502
+ end
1503
+ ```
1504
+
1505
+ Cela permet d'implémenter des API de streaming ou de
1506
+ [Server Sent Events](http://dev.w3.org/html5/eventsource/) et peut servir de
1507
+ base pour des [WebSockets](http://en.wikipedia.org/wiki/WebSocket). Vous
1508
+ pouvez aussi l'employer pour augmenter le débit quand une partie du contenu
1509
+ provient d'une resource lente.
1510
+
1511
+ Le fonctionnement du streaming, notamment le nombre de requêtes simultanées,
1512
+ dépend énormément du serveur web utilisé. Certains ne prennent pas du tout en
1513
+ charge le streaming (WEBRick par exemple). Lorsque le serveur ne gère pas le
1514
+ streaming, la partie body de la réponse sera envoyée au client en une seule
1515
+ fois, après que l'exécution du bloc passé au helper `stream` sera terminée. Le
1516
+ streaming ne fonctionne pas du tout avec Shotgun.
1517
+
1518
+ En utilisant le helper stream avec le paramètre `keep_open`, il n'appelera
1519
+ pas la méthode `close` du flux, vous laissant la possibilité de le fermer à
1520
+ tout moment au cours de l'exécution. Ceci ne fonctionne qu'avec les serveurs
1521
+ evented (ie non threadés) tels que Thin et Rainbows. Les autres serveurs
1522
+ fermeront malgré tout le flux :
1523
+
1524
+ ``` ruby
1525
+ # interrogation prolongée
1526
+
1527
+ set :server, :thin
1528
+ connexions = []
1529
+
1530
+ get '/souscrire' do
1531
+ # abonne un client aux évènements du serveur
1532
+ stream(:keep_open) { |out| connexions << out }
1533
+
1534
+ # purge les connexions abandonnées
1535
+ connexions.reject!(&:closed?)
1536
+
1537
+ # compte-rendu
1538
+ "abonné"
1539
+ end
1540
+
1541
+ post '/message' do
1542
+ connexions.each do |out|
1543
+ # prévient le client qu'un nouveau message est arrivé
1544
+ out << params[:message] << "\n"
1545
+
1546
+ # indique au client de se connecter à nouveau
1547
+ out.close
1548
+ end
1549
+
1550
+ # compte-rendu
1551
+ "message reçu"
1552
+ end
1553
+ ```
1554
+
1555
+ ### Journalisation (Logging)
1556
+
1557
+ Dans le contexte de la requête, la méthode utilitaire `logger` expose une
1558
+ instance de `Logger` :
1559
+
1560
+ ``` ruby
1561
+ get '/' do
1562
+ logger.info "chargement des données"
1563
+ # ...
1564
+ end
1565
+ ```
1566
+
1567
+ Ce logger va automatiquement prendre en compte les paramètres de
1568
+ configuration pour la journalisation de votre gestionnaire Rack. Si la
1569
+ journalisation est désactivée, cette méthode renverra un objet factice et
1570
+ vous n'avez pas à vous en inquiéter dans vos routes en le filtrant.
1571
+
1572
+ Notez que la journalisation est seulement activée par défaut pour
1573
+ `Sinatra::Application`, donc si vous héritez de `>Sinatra::Base`,
1574
+ vous aurez à l'activer vous-même :
1575
+
1576
+ ``` ruby
1577
+ class MonApp < Sinatra::Base
1578
+ configure :production, :development do
1579
+ enable :logging
1580
+ end
1581
+ end
1582
+ ```
1583
+
1584
+ Si vous souhaitez utiliser votre propre logger, vous devez définir le paramètre
1585
+ `logging` à `nil` pour être certain qu'aucun middleware de logging ne sera
1586
+ installé (notez toutefois que `logger` renverra alors `nil`). Dans ce cas,
1587
+ Sinatra utilisera ce qui sera présent dans `env['rack.logger']`.
1588
+
1589
+ ### Types Mime
1590
+
1591
+ Quand vous utilisez `send_file` ou des fichiers statiques, vous
1592
+ pouvez rencontrer des types mime que Sinatra ne connaît pas. Utilisez
1593
+ `mime_type` pour les déclarer par extension de fichier :
1594
+
1595
+ ``` ruby
1596
+ configure do
1597
+ mime_type :foo, 'text/foo'
1598
+ end
1599
+ ```
1600
+
1601
+ Vous pouvez également les utiliser avec la méthode `content_type` :
1602
+
1603
+ ``` ruby
1604
+ get '/' do
1605
+ content_type :foo
1606
+ "foo foo foo"
1607
+ end
1608
+ ```
1609
+
1610
+ ### Former des URLs
1611
+
1612
+ Pour former des URLs, vous devriez utiliser la méthode `url`, par exemple en
1613
+ Haml :
1614
+
1615
+ ``` ruby
1616
+ %a{:href => url('/foo')} foo
1617
+ ```
1618
+
1619
+ Cela prend en compte les proxy inverse et les routeurs Rack, s'ils existent.
1620
+
1621
+ Cette méthode est également disponible sous l'alias `to` (voir ci-dessous
1622
+ pour un exemple).
1623
+
1624
+ ### Redirection du navigateur
1625
+
1626
+ Vous pouvez déclencher une redirection du navigateur avec la méthode
1627
+ `redirect` :
1628
+
1629
+ ``` ruby
1630
+ get '/foo' do
1631
+ redirect to('/bar')
1632
+ end
1633
+ ```
1634
+
1635
+ Tout paramètre additionnel est géré comme des arguments pour la méthode
1636
+ `halt` :
1637
+
1638
+ ``` ruby
1639
+ redirect to('/bar'), 303
1640
+ redirect 'http://google.com', 'mauvais endroit mon pote'
1641
+ ```
1642
+
1643
+ Vous pouvez aussi rediriger vers la page dont l'utilisateur venait au moyen de
1644
+ `redirect back` :
1645
+
1646
+ ``` ruby
1647
+ get '/foo' do
1648
+ "<a href='/bar'>faire quelque chose</a>"
1649
+ end
1650
+
1651
+ get '/bar' do
1652
+ faire_quelque_chose
1653
+ redirect back
1654
+ end
1655
+ ```
1656
+
1657
+ Pour passer des arguments à une redirection, ajoutez-les soit à la requête :
1658
+
1659
+ ``` ruby
1660
+ redirect to('/bar?sum=42')
1661
+ ```
1662
+
1663
+ Ou bien utilisez une session :
1664
+
1665
+ ``` ruby
1666
+ enable :sessions
1667
+
1668
+ get '/foo' do
1669
+ session[:secret] = 'foo'
1670
+ redirect to('/bar')
1671
+ end
1672
+
1673
+ get '/bar' do
1674
+ session[:secret]
1675
+ end
1676
+ ```
1677
+
1678
+ ### Contrôle du cache
1679
+
1680
+ Définir correctement vos entêtes à la base pour un bon cache HTTP.
1681
+
1682
+ Vous pouvez facilement définir l'entête Cache-Control de la manière suivante :
1683
+
1684
+ ``` ruby
1685
+ get '/' do
1686
+ cache_control :public
1687
+ "met le en cache !"
1688
+ end
1689
+ ```
1690
+
1691
+ Conseil de pro : définir le cache dans un filtre before :
1692
+
1693
+ ``` ruby
1694
+ before do
1695
+ cache_control :public, :must_revalidate, :max_age => 60
1696
+ end
1697
+ ```
1698
+
1699
+ Si vous utilisez la méthode `expires` pour définir l'entête correspondant,
1700
+ `Cache-Control` sera alors défini automatiquement :
1701
+
1702
+ ``` ruby
1703
+ before do
1704
+ expires 500, :public, :must_revalidate
1705
+ end
1706
+ ```
1707
+
1708
+ Pour utiliser correctement les caches, vous devriez utiliser `etag` ou
1709
+ `last_modified`. Il est recommandé d'utiliser ces méthodes *avant* de faire
1710
+ d'importantes modifications, car elles vont immédiatement déclencher la réponse
1711
+ si le client a déjà la version courante dans son cache :
1712
+
1713
+ ``` ruby
1714
+ get '/article/:id' do
1715
+ @article = Article.find params[:id]
1716
+ last_modified @article.updated_at
1717
+ etag @article.sha1
1718
+ erb :article
1719
+ end
1720
+ ```
1721
+
1722
+ Il est également possible d'utiliser un
1723
+ [weak ETag](http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation) :
1724
+
1725
+ ``` ruby
1726
+ etag @article.sha1, :weak
1727
+ ```
1728
+
1729
+ Ces méthodes ne sont pas chargées de mettre des données en cache, mais elles
1730
+ fournissent les informations nécessaires pour votre cache. Si vous êtes à la
1731
+ recherche de solutions rapides pour un reverse-proxy de cache, essayez
1732
+ [rack-cache](https://github.com/rtomayko/rack-cache) :
1733
+
1734
+ ``` ruby
1735
+ require "rack/cache"
1736
+ require "sinatra"
1737
+
1738
+ use Rack::Cache
1739
+
1740
+ get '/' do
1741
+ cache_control :public, :max_age => 36000
1742
+ sleep 5
1743
+ "hello"
1744
+ end
1745
+ ```
1746
+
1747
+ Utilisez le paramètre `:static_cache_control` pour ajouter l'information
1748
+ d'en-tête `Cache-Control` (voir plus loin).
1749
+
1750
+ D'après la RFC 2616, votre application devrait se comporter différement lorsque
1751
+ l'en-tête If-Match ou If-None-Match est défini à `*` en tenant compte du
1752
+ fait que la resource demandée existe déjà ou pas. Sinatra considère que les
1753
+ requêtes portant sur des resources sûres (tel que get) ou idempotentes (tel que
1754
+ put) existent déjà et pour les autres resources (par exemple dans le cas
1755
+ de requêtes post) qu'il s'agit de nouvelles resources. Vous pouvez modifier ce
1756
+ comportement en passant une option `:new_resource` :
1757
+
1758
+ ``` ruby
1759
+ get '/create' do
1760
+ etag '', :new_resource => true
1761
+ Article.create
1762
+ erb :new_article
1763
+ end
1764
+ ```
1765
+
1766
+ Si vous souhaitez utilisez un ETag faible, utilisez l'option <tt>:kind</tt> :
1767
+
1768
+ ``` ruby
1769
+ etag '', :new_resource => true, :kind => :weak
1770
+ ```
1771
+
1772
+ ### Envoyer des fichiers
1773
+
1774
+ Pour envoyer des fichiers, vous pouvez utiliser la méthode `send_file` :
1775
+
1776
+ ``` ruby
1777
+ get '/' do
1778
+ send_file 'foo.png'
1779
+ end
1780
+ ```
1781
+
1782
+ Quelques options sont également acceptées :
1783
+
1784
+ ``` ruby
1785
+ send_file 'foo.png', :type => :jpg
1786
+ ```
1787
+
1788
+ Les options sont :
1789
+
1790
+ <dl>
1791
+ <dt>filename</dt>
1792
+ <dd>
1793
+ le nom du fichier dans la réponse, par défaut le nom du fichier envoyé.
1794
+ </dd>
1795
+
1796
+ <dt>last_modified</dt>
1797
+ <dd>
1798
+ valeur pour l’entête Last-Modified, par défaut la date de modification du
1799
+ fichier
1800
+ </dd>
1801
+
1802
+ <dt>type</dt>
1803
+ <dd>
1804
+ type de contenu à utiliser, deviné à partir de l’extension de fichier si
1805
+ absent
1806
+ </dd>
1807
+
1808
+ <dt>disposition</dt>
1809
+ <dd>
1810
+ utilisé pour Content-Disposition, les valuers possibles étant : <tt>nil</tt>
1811
+ (par défaut), <tt>:attachment</tt> et <tt>:inline</tt>
1812
+ </dd>
1813
+
1814
+ <dt>length</dt>
1815
+ <dd>entête Content-Length, par défaut la taille du fichier</dd>
1816
+
1817
+ <dt>status</dt>
1818
+ <dd>
1819
+ code état à renvoyer. Utile quand un fichier statique sert de page d’erreur.
1820
+ </dd>
1821
+ </dl>
1822
+
1823
+ Si le gestionnaire Rack le supporte, d'autres moyens que le streaming via le
1824
+ processus Ruby seront utilisés. Si vous utilisez cette méthode, Sinatra gérera
1825
+ automatiquement les requêtes de type range.
1826
+
1827
+ ### Accéder à l'objet requête
1828
+
1829
+ L'objet correspondant à la requête envoyée peut être récupéré dans le contexte
1830
+ de la requête (filtres, routes, gestionnaires d'erreur) au moyen de la méthode
1831
+ `request` :
1832
+
1833
+ ``` ruby
1834
+ # application tournant à l'adresse http://exemple.com/exemple
1835
+ get '/foo' do
1836
+ t = %w[text/css text/html application/javascript]
1837
+ request.accept # ['text/html', '*/*']
1838
+ request.accept? 'text/xml' # true
1839
+ request.preferred_type(t) # 'text/html'
1840
+ request.body # corps de la requête envoyée par le client
1841
+ # (voir ci-dessous)
1842
+ request.scheme # "http"
1843
+ request.script_name # "/exemple"
1844
+ request.path_info # "/foo"
1845
+ request.port # 80
1846
+ request.request_method # "GET"
1847
+ request.query_string # ""
1848
+ request.content_length # taille de request.body
1849
+ request.media_type # type de média pour request.body
1850
+ request.host # "exemple.com"
1851
+ request.get? # true (méthodes similaires pour les autres
1852
+ # verbes HTTP)
1853
+ request.form_data? # false
1854
+ request["UN_ENTETE"] # valeur de l'entête UN_ENTETE
1855
+ request.referrer # référant du client ou '/'
1856
+ request.user_agent # user agent (utilisé par la condition :agent)
1857
+ request.cookies # tableau contenant les cookies du navigateur
1858
+ request.xhr? # requête AJAX ?
1859
+ request.url # "http://exemple.com/exemple/foo"
1860
+ request.path # "/exemple/foo"
1861
+ request.ip # adresse IP du client
1862
+ request.secure? # false
1863
+ request.forwarded? # vrai (si on est derrière un proxy inverse)
1864
+ request.env # tableau brut de l'environnement fourni par Rack
1865
+ end
1866
+ ```
1867
+
1868
+ Certaines options, telles que `script_name` ou `path_info`
1869
+ peuvent également être modifiées :
1870
+
1871
+ ``` ruby
1872
+ before { request.path_info = "/" }
1873
+
1874
+ get "/" do
1875
+ "toutes les requêtes arrivent ici"
1876
+ end
1877
+ ```
1878
+
1879
+ `request.body` est un objet IO ou StringIO :
1880
+
1881
+ ``` ruby
1882
+ post "/api" do
1883
+ request.body.rewind # au cas où il a déjà été lu
1884
+ donnees = JSON.parse request.body.read
1885
+ "Bonjour #{donnees['nom']} !"
1886
+ end
1887
+ ```
1888
+
1889
+ ### Fichiers joints
1890
+
1891
+ Vous pouvez utiliser la méthode `attachment` pour indiquer au navigateur que
1892
+ la réponse devrait être stockée sur le disque plutôt qu'affichée :
1893
+
1894
+
1895
+ ``` ruby
1896
+ get '/' do
1897
+ attachment
1898
+ "enregistre-le !"
1899
+ end
1900
+ ```
1901
+
1902
+ Vous pouvez également lui passer un nom de fichier :
1903
+
1904
+ ``` ruby
1905
+ get '/' do
1906
+ attachment "info.txt"
1907
+ "enregistre-le !"
1908
+ end
1909
+ ```
1910
+
1911
+ ### Gérer Date et Time
1912
+
1913
+ Sinatra fourni un helper `time_for` pour convertir une valeur donnée en
1914
+ objet `Time`. Il peut aussi faire la conversion à partir d'objets `DateTime`,
1915
+ `Date` ou de classes similaires :
1916
+
1917
+ ``` ruby
1918
+ get '/' do
1919
+ pass if Time.now > time_for('Dec 23, 2012')
1920
+ "encore temps"
1921
+ end
1922
+ ```
1923
+
1924
+ Cette méthode est utilisée en interne par `expires`, `last_modified` et
1925
+ consorts. Par conséquent, vous pouvez très facilement étendre le
1926
+ fonctionnement de ces méthodes en surchargeant le helper `time_for` dans
1927
+ votre application :
1928
+
1929
+ ``` ruby
1930
+ helpers do
1931
+ def time_for(value)
1932
+ case value
1933
+ when :yesterday then Time.now - 24*60*60
1934
+ when :tomorrow then Time.now + 24*60*60
1935
+ else super
1936
+ end
1937
+ end
1938
+ end
1939
+
1940
+ get '/' do
1941
+ last_modified :yesterday
1942
+ expires :tomorrow
1943
+ "salut"
1944
+ end
1945
+ ```
1946
+
1947
+ ### Chercher les fichiers de templates
1948
+
1949
+ La méthode `find_template` est utilisée pour trouver les fichiers de
1950
+ templates à générer :
1951
+
1952
+ ``` ruby
1953
+ find_template settings.views, 'foo', Tilt[:haml] do |file|
1954
+ puts "pourrait être #{file}"
1955
+ end
1956
+ ```
1957
+
1958
+ Ce n'est pas très utilise. En revanche, il est utile de pouvoir surcharger
1959
+ cette méthode afin de définir son propre mécanisme de recherche. Par exemple,
1960
+ vous pouvez utiliser plus d'un répertoire de vues :
1961
+
1962
+ ``` ruby
1963
+ set :views, ['views', 'templates']
1964
+
1965
+ helpers do
1966
+ def find_template(views, name, engine, &block)
1967
+ Array(views).each { |v| super(v, name, engine, &block) }
1968
+ end
1969
+ end
1970
+ ```
1971
+
1972
+ Un autre exemple est d'utiliser des répertoires différents pour des moteurs
1973
+ de rendu différents :
1974
+
1975
+ ``` ruby
1976
+ set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views'
1977
+
1978
+ helpers do
1979
+ def find_template(views, name, engine, &block)
1980
+ _, folder = views.detect { |k,v| engine == Tilt[k] }
1981
+ folder ||= views[:default]
1982
+ super(folder, name, engine, &block)
1983
+ end
1984
+ end
1985
+ ```
1986
+
1987
+ Vous pouvez également écrire cela dans une extension et la partager avec
1988
+ d'autres !
1989
+
1990
+ Notez que `find_template` ne vérifie pas que le fichier existe mais
1991
+ va plutôt exécuter le bloc pour tous les chemins possibles. Cela n'induit pas
1992
+ un problème de performance dans le sens où `render` va utiliser `break` dès
1993
+ qu'un fichier est trouvé. De plus, l'emplacement des templates (et leur
1994
+ contenu) est mis en cache si vous n'êtes pas en mode développement. Vous
1995
+ devriez garder cela en tête si vous écrivez une méthode vraiment dingue.
1996
+
1997
+ ## Configuration
1998
+
1999
+ Lancé une seule fois au démarrage de tous les environnements :
2000
+
2001
+ ``` ruby
2002
+ configure do
2003
+ # définir un paramètre
2004
+ set :option, 'value'
2005
+
2006
+ # définir plusieurs paramètre
2007
+ set :a => 1, :b => 2
2008
+
2009
+ # identique à "set :option, true"
2010
+ enable :option
2011
+
2012
+ # identique à "set :option, false""
2013
+ disable :option
2014
+
2015
+ # vous pouvez également avoir des paramètres dynamiques avec des blocs
2016
+ set(:css_dir) { File.join(views, 'css') }
2017
+ end
2018
+ ```
2019
+
2020
+ Lancé si l'environnement (variable d'environnement RACK_ENV) est défini comme
2021
+ `:production` :
2022
+
2023
+ ``` ruby
2024
+ configure :production do
2025
+ ...
2026
+ end
2027
+ ```
2028
+
2029
+ Lancé si l'environnement est `:production` ou `:test` :
2030
+
2031
+ ``` ruby
2032
+ configure :production, :test do
2033
+ ...
2034
+ end
2035
+ ```
2036
+
2037
+ Vous pouvez accéder à ces paramètres via `settings` :
2038
+
2039
+ ``` ruby
2040
+ configure do
2041
+ set :foo, 'bar'
2042
+ end
2043
+
2044
+ get '/' do
2045
+ settings.foo? # => true
2046
+ settings.foo # => 'bar'
2047
+ ...
2048
+ end
2049
+ ```
2050
+
2051
+ ### Se protéger des attaques
2052
+
2053
+ Sinatra utilise [Rack::Protection](https://github.com/rkh/rack-protection#readme)
2054
+ pour protéger votre application contre les principales attaques opportunistes.
2055
+ Vous pouvez très simplement désactiver cette fonctionnalité (ce qui exposera
2056
+ votre application à beaucoup de vulnerabilités courantes) :
2057
+
2058
+ ``` ruby
2059
+ disable :protection
2060
+ ```
2061
+
2062
+ Pour désactiver seulement un type de protection, vous pouvez définir `protection`
2063
+ avec un hash d'options :
2064
+
2065
+ ``` ruby
2066
+ set :protection, :except => :path_traversal
2067
+ ```
2068
+
2069
+ Vous pouvez également lui passer un tableau pour désactiver plusieurs types de
2070
+ protection :
2071
+
2072
+ ``` ruby
2073
+ set :protection, :except => [:path_traversal, :session_hijacking]
2074
+ ```
2075
+
2076
+ Par défaut, il faut que `:sessions` soit activé pour que Sinatra mette en place
2077
+ un système de protection au niveau de la session. Dans le cas où vous gérez
2078
+ vous même les sessions, vous devez utiliser l'option `:session` pour que cela
2079
+ soit le cas :
2080
+
2081
+ ``` ruby
2082
+ use Rack::Session::Pool
2083
+ set :protection, :session => true
2084
+ ```
2085
+
2086
+ ### Paramètres disponibles
2087
+
2088
+ <dl>
2089
+ <dt>absolute_redirects</dt>
2090
+ <dd>Si désactivé, Sinatra permettra les redirections relatives. Toutefois,
2091
+ Sinatra ne sera plus conforme à la RFC 2616 (HTTP 1.1), qui n’autorise
2092
+ que les redirections absolues.</p>
2093
+
2094
+ Activez si votre application tourne derrière un proxy inverse qui n’a
2095
+ pas été correctement configuré. Notez que la méthode <tt>url</tt>
2096
+ continuera de produire des URLs absolues, sauf si vous lui passez
2097
+ <tt>false</tt> comme second argument.</p>
2098
+
2099
+ <p>Désactivé par défaut.</p></dd>
2100
+
2101
+ <dt>add_charsets</dt>
2102
+ <dd><p>types mime pour lesquels la méthode <tt>content_type</tt> va
2103
+ automatiquement ajouter l’information du <tt>charset</tt>.</p>
2104
+
2105
+ <p>Vous devriez lui ajouter des valeurs plutôt que de l’écraser :</p>
2106
+
2107
+ <pre>settings.add_charsets >> "application/foobar"</pre></dd>
2108
+
2109
+ <dt>app_file</dt>
2110
+ <dd><p>chemin pour le fichier de l’application principale, utilisé pour
2111
+ détecter la racine du projet, les dossiers public et vues, et les
2112
+ templates en ligne.</p></dd>
2113
+
2114
+ <dt>bind</dt>
2115
+ <dd>adresse IP sur laquelle se brancher (par défaut : 0.0.0.0). Utiliser
2116
+ seulement pour le serveur intégré.</dd>
2117
+
2118
+ <dt>default_encoding</dt>
2119
+ <dd>encodage à utiliser si inconnu (par défaut <tt>"utf-8"</tt>)</dd>
2120
+
2121
+ <dt>dump_errors</dt>
2122
+ <dd>afficher les erreurs dans le <tt>log</tt>.
2123
+ </dd>
2124
+
2125
+ <dt>environment</dt>
2126
+ <dd>environnement courant, par défaut <tt>ENV['RACK_ENV']</tt>, ou
2127
+ <tt>"development"</tt> si absent.</dd>
2128
+
2129
+ <dt>logging</dt>
2130
+ <dd>utiliser le <tt>logger</tt>.</dd>
2131
+
2132
+ <dt>lock</dt>
2133
+ <dd><p>Place un <tt>lock</tt> autour de chaque requête, n’exécutant donc
2134
+ qu’une seule requête par processus Ruby.</p>
2135
+
2136
+ <p>Activé si votre application n’est pas <tt>thread-safe</tt>. Désactivé
2137
+ par défaut.</p></dd>
2138
+
2139
+ <dt>method_override</dt>
2140
+ <dd>utilise la magie de <tt>_method</tt> afin de permettre des formulaires
2141
+ put/delete dans des navigateurs qui ne le permettent pas.
2142
+
2143
+ </dd>
2144
+ <dt>port</dt>
2145
+ <dd>port à écouter. Utiliser seulement pour le serveur intégré.</dd>
2146
+
2147
+ <dt>prefixed_redirects</dt>
2148
+ <dd>si oui ou non <tt>request.script_name</tt> doit être inséré dans les
2149
+ redirections si un chemin non absolu est utilisé. Ainsi, <tt>redirect
2150
+ '/foo'</tt> se comportera comme <tt>redirect to('/foo')</tt>. Désactivé
2151
+ par défaut.</dd>
2152
+
2153
+ <dt>protection</dt>
2154
+ <dd>défini s’il faut activer ou non la protection contre les attaques web.
2155
+ Voir la section protection précédente.</dd>
2156
+
2157
+ <dt>public_dir</dt>
2158
+ <dd>alias pour <tt>public_folder</tt>. Voir ci-dessous.</dd>
2159
+
2160
+ <dt>public_folder</dt>
2161
+ <dd>chemin pour le dossier à partir duquel les fichiers publics sont servis.
2162
+ Utilisé seulement si les fichiers statiques doivent être servis (voir le
2163
+ paramètre <tt>static</tt>). Si non défini, il découle du paramètre
2164
+ <tt>app_file</tt>.</dd>
2165
+
2166
+ <dt>reload_templates</dt>
2167
+ <dd>si oui ou non les templates doivent être rechargés entre les requêtes.
2168
+ Activé en mode développement.</dd>
2169
+
2170
+ <dt>root</dt>
2171
+ <dd>chemin pour le dossier racine du projet. Si non défini, il découle du
2172
+ paramètre <tt>app_file</tt>.</dd>
2173
+
2174
+ <dt>raise_errors</dt>
2175
+ <dd>soulever les erreurs (ce qui arrêtera l’application). Désactivé par
2176
+ défaut sauf lorsque <tt>environment</tt> est défini à
2177
+ <tt>"test"</tt>.</dd>
2178
+
2179
+ <dt>run</dt>
2180
+ <dd>si activé, Sinatra s’occupera de démarrer le serveur, ne pas activer si
2181
+ vous utiliser rackup ou autres.</dd>
2182
+
2183
+ <dt>running</dt>
2184
+ <dd>est-ce que le serveur intégré est en marche ? ne changez pas ce
2185
+ paramètre !</dd>
2186
+
2187
+ <dt>server</dt>
2188
+ <dd>serveur ou liste de serveurs à utiliser pour le serveur intégré. Par
2189
+ défaut [‘thin’, ‘mongrel’, ‘webrick’], l’ordre indiquant la
2190
+ priorité.</dd>
2191
+
2192
+ <dt>sessions</dt>
2193
+ <dd>active le support des sessions basées sur les cookies, en utilisant
2194
+ <tt>Rack::Session::Cookie</tt>. Reportez-vous à la section ‘Utiliser les
2195
+ sessions’ pour plus d’informations.</dd>
2196
+
2197
+ <dt>show_exceptions</dt>
2198
+ <dd>affiche la trace de l’erreur dans le navigateur lorsqu’une exception se
2199
+ produit. Désactivé par défaut sauf lorsque <tt>environment</tt> est
2200
+ défini à <tt>"development"</tt>.</dd>
2201
+
2202
+ <dt>static</dt>
2203
+ <dd>Si oui ou non Sinatra doit s’occuper de servir les fichiers statiques.
2204
+ Désactivez si vous utilisez un serveur capable de le gérer lui même. Le
2205
+ désactiver augmentera la performance. Activé par défaut pour le style
2206
+ classique, désactivé pour le style modulaire.</dd>
2207
+
2208
+ <dt>static_cache_control</dt>
2209
+ <dd>A définir quand Sinatra rend des fichiers statiques pour ajouter les
2210
+ en-têtes <tt>Cache-Control</tt>. Utilise le helper <tt>cache_control</tt>.
2211
+ Désactivé par défaut. Utiliser un array explicite pour définir des
2212
+ plusieurs valeurs : <tt>set :static_cache_control, [:public, :max_age =>
2213
+ 300]</tt></dd>
2214
+
2215
+ <dt>threaded</dt>
2216
+ <dd>à définir à <tt>true</tt> pour indiquer à Thin d’utiliser
2217
+ <tt>EventMachine.defer</tt> pour traiter la requête.</dd>
2218
+
2219
+ <dt>views</dt>
2220
+ <dd>chemin pour le dossier des vues. Si non défini, il découle du paramètre
2221
+ <tt>app_file</tt>.</dd>
2222
+
2223
+ <dt>x_cascade</dt>
2224
+ <dd>
2225
+ Indique s'il faut ou non définir le header X-Cascade lorsqu'aucune route
2226
+ ne correspond. Défini à <tt>true</tt> par défaut.
2227
+ </dd>
2228
+ </dl>
2229
+
2230
+ ## Environements
2231
+
2232
+ Il existe trois environnements prédéfinis : `"development"`,
2233
+ `"production"` et `"test"`. Les environements peuvent être
2234
+ sélectionné via la variable d'environnement `RACK_ENV`. Sa valeur par défaut
2235
+ est `"development"`. Dans ce mode, tous les templates sont rechargés à
2236
+ chaque requête. Des handlers spécifiques pour `not_found` et
2237
+ `error` sont installés pour vous permettre d'avoir une pile de trace
2238
+ dans votre navigateur. En mode `"production"` et `"test"` les
2239
+ templates sont mis en cache par défaut.
2240
+
2241
+ Pour exécuter votre application dans un environnement différent, définissez la
2242
+ variable d'environnement `RACK_ENV` :
2243
+
2244
+ ``` shell
2245
+ RACK_ENV=production ruby my_app.rb
2246
+ ```
2247
+
2248
+ Vous pouvez utiliser une des méthodes `development?`, `test?` et `production?`
2249
+ pour déterminer quel est l'environnement en cours :
2250
+
2251
+ ``` ruby
2252
+ get '/' do
2253
+ if settings.development?
2254
+ "développement !"
2255
+ else
2256
+ "pas en développement !"
2257
+ end
2258
+ end
2259
+ ```
2260
+
2261
+ ## Gérer les erreurs
2262
+
2263
+ Les gestionnaires d'erreur s'exécutent dans le même contexte que les routes ou
2264
+ les filtres, ce qui veut dire que vous avez accès (entre autres) aux bons
2265
+ vieux `haml`, `erb`, `halt`, etc.
2266
+
2267
+ ### NotFound
2268
+
2269
+ Quand une exception <tt>Sinatra::NotFound</tt> est soulevée, ou que le code
2270
+ retour est 404, le gestionnaire <tt>not_found</tt> est invoqué :
2271
+
2272
+ ``` ruby
2273
+ not_found do
2274
+ 'Pas moyen de trouver ce que vous cherchez'
2275
+ end
2276
+ ```
2277
+
2278
+ ### Error
2279
+
2280
+ Le gestionnaire `error` est invoqué à chaque fois qu'une exception est
2281
+ soulevée dans une route ou un filtre. L'objet exception est accessible via la
2282
+ variable Rack `sinatra.error` :
2283
+
2284
+ ``` ruby
2285
+ error do
2286
+ 'Désolé mais une méchante erreur est survenue - ' + env['sinatra.error'].name
2287
+ end
2288
+ ```
2289
+
2290
+ Erreur sur mesure :
2291
+
2292
+ ``` ruby
2293
+ error MonErreurSurMesure do
2294
+ 'Donc il est arrivé ceci...' + env['sinatra.error'].message
2295
+ end
2296
+ ```
2297
+
2298
+ Donc si ceci arrive :
2299
+
2300
+ ``` ruby
2301
+ get '/' do
2302
+ raise MonErreurSurMesure, 'quelque chose de mal'
2303
+ end
2304
+ ```
2305
+
2306
+ Vous obtenez ça :
2307
+
2308
+ Donc il est arrivé ceci... quelque chose de mal
2309
+
2310
+ Alternativement, vous pouvez avoir un gestionnaire d'erreur associé à un code
2311
+ particulier :
2312
+
2313
+ ``` ruby
2314
+ error 403 do
2315
+ 'Accès interdit'
2316
+ end
2317
+
2318
+ get '/secret' do
2319
+ 403
2320
+ end
2321
+ ```
2322
+
2323
+ Ou un intervalle :
2324
+
2325
+ ``` ruby
2326
+ error 400..510 do
2327
+ 'Boom'
2328
+ end
2329
+ ```
2330
+
2331
+ Sinatra installe pour vous quelques gestionnaires `not_found` et
2332
+ `error` génériques lorsque vous êtes en environnement
2333
+ `development`.
2334
+
2335
+ ## Les Middlewares Rack
2336
+
2337
+ Sinatra tourne avec [Rack](http://rack.rubyforge.org/), une interface standard
2338
+ et minimale pour les web frameworks Ruby. Un des points forts de Rack est le
2339
+ support de ce que l'on appelle des "middlewares" -- composant qui vient se
2340
+ situer entre le serveur et votre application, et dont le but est de
2341
+ visualiser/manipuler la requête/réponse HTTP, et d'offrir diverses
2342
+ fonctionnalités classiques.
2343
+
2344
+ Sinatra permet de construire facilement des middlewares Rack via la méthode de
2345
+ haut niveau `use` :
2346
+
2347
+ ``` ruby
2348
+ require 'sinatra'
2349
+ require 'mon_middleware_perso'
2350
+
2351
+ use Rack::Lint
2352
+ use MonMiddlewarePerso
2353
+
2354
+ get '/bonjour' do
2355
+ 'Bonjour le monde'
2356
+ end
2357
+ ```
2358
+
2359
+ La sémantique de `use` est identique à celle définie dans le DSL de
2360
+ [Rack::Builder](http://rack.rubyforge.org/doc/classes/Rack/Builder.html)
2361
+ (le plus souvent utilisé dans un fichier rackup). Par exemple, la méthode
2362
+ `use` accepte divers arguments ainsi que des blocs :
2363
+
2364
+ ``` ruby
2365
+ use Rack::Auth::Basic do |login, password|
2366
+ login == 'admin' && password == 'secret'
2367
+ end
2368
+ ```
2369
+
2370
+ Rack est distribué avec de nombreux middlewares standards pour loguer, débuguer,
2371
+ faire du routage URL, de l'authentification ou gérer des sessions. Sinatra gère
2372
+ plusieurs de ces composants automatiquement via son système de configuration, ce
2373
+ qui vous dispense de faire un `use` en ce qui les concerne.
2374
+
2375
+ Vous trouverez d'autres middlewares intéressants sur
2376
+ [rack](https://github.com/rack/rack/tree/master/lib/rack),
2377
+ [rack-contrib](https://github.com/rack/rack-contrib#readm),
2378
+ ou en consultant le [wiki de Rack](https://github.com/rack/rack/wiki/List-of-Middleware).
2379
+
2380
+ ## Tester
2381
+
2382
+ Les tests pour Sinatra peuvent être écrit avec n'importe quelle bibliothèque
2383
+ basée sur Rack. [Rack::Test](http://gitrdoc.com/brynary/rack-test) est
2384
+ recommandé :
2385
+
2386
+ ``` ruby
2387
+ require 'mon_application_sinatra'
2388
+ require 'test/unit'
2389
+ require 'rack/test'
2390
+
2391
+ class MonTest < Test::Unit::TestCase
2392
+ include Rack::Test::Methods
2393
+
2394
+ def app
2395
+ Sinatra::Application
2396
+ end
2397
+
2398
+ def test_ma_racine
2399
+ get '/'
2400
+ assert_equal 'Bonjour le monde !', last_response.body
2401
+ end
2402
+
2403
+ def test_avec_des_parametres
2404
+ get '/rencontrer', :name => 'Frank'
2405
+ assert_equal 'Salut Frank !', last_response.body
2406
+ end
2407
+
2408
+ def test_avec_rack_env
2409
+ get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
2410
+ assert_equal "Vous utilisez Songbird !", last_response.body
2411
+ end
2412
+ end
2413
+ ```
2414
+
2415
+ ## Sinatra::Base - Les Middlewares, Bibliothèques, et Applications Modulaires
2416
+
2417
+ Définir votre application au niveau supérieur fonctionne bien dans le cas des
2418
+ micro-applications mais présente pas mal d'inconvénients pour créer des
2419
+ composants réutilisables sous forme de middlewares Rack, de Rails metal, de
2420
+ simples librairies avec un composant serveur ou même d'extensions Sinatra. Le
2421
+ niveau supérieur suppose une configuration dans le style des micro-applications
2422
+ (une application d'un seul fichier, des répertoires `./public` et
2423
+ `./views`, des logs, une page d'erreur, etc...). C'est là que
2424
+ `Sinatra::Base` prend tout son intérêt :
2425
+
2426
+ ``` ruby
2427
+ require 'sinatra/base'
2428
+
2429
+ class MonApplication < Sinatra::Base
2430
+ set :sessions, true
2431
+ set :foo, 'bar'
2432
+
2433
+ get '/' do
2434
+ 'Bonjour le monde !'
2435
+ end
2436
+ end
2437
+ ```
2438
+
2439
+ Les méthodes de la classe `Sinatra::Base` sont parfaitement identiques à
2440
+ celles disponibles via le DSL de haut niveau. Il suffit de deux modifications
2441
+ pour transformer la plupart des applications de haut niveau en un composant
2442
+ `Sinatra::Base` :
2443
+
2444
+ * Votre fichier doit charger `sinatra/base` au lieu de `sinatra`, sinon toutes
2445
+ les méthodes du DSL Sinatra seront importées dans l'espace de nom principal.
2446
+ * Les gestionnaires de routes, la gestion d'erreur, les filtres et les options
2447
+ doivent être placés dans une classe héritant de `Sinatra::Base`.
2448
+
2449
+ `Sinatra::Base` est une page blanche. La plupart des options sont
2450
+ désactivées par défaut, y compris le serveur intégré. Reportez-vous à
2451
+ [Options et Configuration](http://sinatra.github.com/configuration.html)
2452
+ pour plus d'informations sur les options et leur fonctionnement.
2453
+
2454
+ ### Style modulaire vs. style classique
2455
+
2456
+ Contrairement aux idées reçues, il n'y a rien de mal à utiliser le style
2457
+ classique. Si c'est ce qui convient pour votre application, vous n'avez pas
2458
+ aucune raison de passer à une application modulaire.
2459
+
2460
+ Le principal inconvénient du style classique sur le style modulaire est que vous
2461
+ ne pouvez avoir qu'une application Ruby par processus Ruby. Si vous pensez en
2462
+ utiliser plus, passez au style modulaire. Et rien ne vous empêche de mixer style
2463
+ classique et style modulaire.
2464
+
2465
+ Si vous passez d'un style à l'autre, souvenez-vous des quelques différences
2466
+ mineures en ce qui concerne les paramètres par défaut :
2467
+
2468
+ Paramètre Classique Modulaire
2469
+
2470
+ app_file fichier chargeant sinatra fichier héritant de Sinatra::Base
2471
+ run $0 == app_file false
2472
+ logging true false
2473
+ method_override true false
2474
+ inline_templates true false
2475
+ static true false
2476
+
2477
+ ### Servir une application modulaire
2478
+
2479
+ Il y a deux façons de faire pour démarrer une application modulaire, démarrez
2480
+ avec `run!` :
2481
+
2482
+ ``` ruby
2483
+ # my_app.rb
2484
+ require 'sinatra/base'
2485
+
2486
+ class MyApp < Sinatra::Base
2487
+ # ... code de l'application ici ...
2488
+
2489
+ # démarre le serveur si ce fichier est directement exécuté
2490
+ run! if app_file == $0
2491
+ end
2492
+ ```
2493
+
2494
+ Démarrez ensuite avec :
2495
+
2496
+ ``` shell
2497
+ ruby my_app.rb
2498
+ ```
2499
+
2500
+ Ou alors avec un fichier `config.ru`, qui permet d'utiliser n'importe
2501
+ quel gestionnaire Rack :
2502
+
2503
+ ``` ruby
2504
+ # config.ru
2505
+ require './my_app'
2506
+ run MyApp
2507
+ ```
2508
+
2509
+ Exécutez :
2510
+
2511
+ ``` shell
2512
+ rackup -p 4567
2513
+ ```
2514
+
2515
+ ### Utiliser une application de style classique avec un fichier config.ru
2516
+
2517
+ Ecrivez votre application :
2518
+
2519
+ ``` ruby
2520
+ # app.rb
2521
+ require 'sinatra'
2522
+
2523
+ get '/' do
2524
+ 'Bonjour le monde !'
2525
+ end
2526
+ ```
2527
+
2528
+ Et un fichier `config.ru` correspondant :
2529
+
2530
+ ``` ruby
2531
+ require './app'
2532
+ run Sinatra::Application
2533
+ ```
2534
+
2535
+ ### Quand utiliser un fichier config.ru ?
2536
+
2537
+ Quelques cas où vous devriez utiliser un fichier `config.ru` :
2538
+
2539
+ * Vous souhaitez déployer avec un autre gestionnaire Rack (Passenger, Unicorn,
2540
+ Heroku, ...).
2541
+ * Vous souhaitez utiliser plus d'une sous-classe de `Sinatra::Base`.
2542
+ * Vous voulez utiliser Sinatra comme un middleware, non en tant que
2543
+ endpoint.
2544
+
2545
+ **Il n'est pas nécessaire de passer par un fichier `config.ru` pour la
2546
+ seule raison que vous êtes passé au style modulaire, et vous n'avez pas besoin
2547
+ de passer au style modulaire pour utiliser un fichier `config.ru`.**
2548
+
2549
+ ### Utiliser Sinatra comme Middleware
2550
+
2551
+ Non seulement Sinatra peut utiliser d'autres middlewares Rack, il peut
2552
+ également être à son tour utilisé au-dessus de n'importe quel endpoint Rack
2553
+ en tant que middleware. Ce endpoint peut très bien être une autre
2554
+ application Sinatra, ou n'importe quelle application basée sur Rack
2555
+ (Rails/Ramaze/Camping/...) :
2556
+
2557
+ ``` ruby
2558
+ require 'sinatra/base'
2559
+
2560
+ class EcranDeConnexion < Sinatra::Base
2561
+ enable :sessions
2562
+
2563
+ get('/connexion') { haml :connexion }
2564
+
2565
+ post('/connexion') do
2566
+ if params[:nom] = 'admin' && params[:motdepasse] = 'admin'
2567
+ session['nom_utilisateur'] = params[:nom]
2568
+ else
2569
+ redirect '/connexion'
2570
+ end
2571
+ end
2572
+ end
2573
+
2574
+ class MonApp < Sinatra::Base
2575
+ # le middleware sera appelé avant les filtres
2576
+ use EcranDeConnexion
2577
+
2578
+ before do
2579
+ unless session['nom_utilisateur']
2580
+ halt "Accès refusé, merci de vous <a href='/connexion'>connecter</a>."
2581
+ end
2582
+ end
2583
+
2584
+ get('/') { "Bonjour #{session['nom_utilisateur']}." }
2585
+ end
2586
+ ```
2587
+
2588
+ ### Création dynamique d'applications
2589
+
2590
+ Il se peut que vous ayez besoin de créer une nouvelle application à l'exécution
2591
+ sans avoir à les assigner à une constante, vous pouvez le faire grâce à
2592
+ `Sinatra.new` :
2593
+
2594
+ ``` ruby
2595
+ require 'sinatra/base'
2596
+ mon_app = Sinatra.new { get('/') { "salut" } }
2597
+ mon_app.run!
2598
+ ```
2599
+
2600
+ L'application dont elle hérite peut être passé en argument optionnel :
2601
+
2602
+ ``` ruby
2603
+ # config.ru
2604
+ require 'sinatra/base'
2605
+
2606
+ controleur = Sinatra.new do
2607
+ enable :logging
2608
+ helpers MyHelpers
2609
+ end
2610
+
2611
+ map('/a') do
2612
+ run Sinatra.new(controleur) { get('/') { 'a' } }
2613
+ end
2614
+
2615
+ map('/b') do
2616
+ run Sinatra.new(controleur) { get('/') { 'b' } }
2617
+ end
2618
+ ```
2619
+
2620
+ C'est notamment utile pour tester des extensions à Sinatra ou bien pour
2621
+ utiliser Sinatra dans votre propre bibliothèque.
2622
+
2623
+ Cela permet également d'utiliser très facilement Sinatra comme middleware :
2624
+
2625
+ ``` ruby
2626
+ require 'sinatra/base'
2627
+
2628
+ use Sinatra do
2629
+ get('/') { ... }
2630
+ end
2631
+
2632
+ run RailsProject::Application
2633
+ ```
2634
+
2635
+ ## Contextes et Binding
2636
+
2637
+ Le contexte dans lequel vous êtes détermine les méthodes et variables
2638
+ disponibles.
2639
+
2640
+ ### Contexte de l'application/classe
2641
+
2642
+ Une application Sinatra correspond à une sous-classe de `Sinatra::Base`. Il
2643
+ s'agit de `Sinatra::Application` si vous utilisez le DSL de haut niveau
2644
+ (`require 'sinatra'`). Sinon c'est la sous-classe que vous avez définie. Dans
2645
+ le contexte de cette classe, vous avez accès aux méthodes telles que `get` ou
2646
+ `before`, mais pas aux objets `request` ou `session` étant donné que toutes
2647
+ les requêtes sont traitées par une seule classe d'application.
2648
+
2649
+ Les options définies au moyen de `set` deviennent des méthodes de classe :
2650
+
2651
+ ``` ruby
2652
+ class MonApp < Sinatra::Base
2653
+ # Eh, je suis dans le contexte de l'application !
2654
+ set :foo, 42
2655
+ foo # => 42
2656
+
2657
+ get '/foo' do
2658
+ # Eh, je ne suis plus dans le contexte de l'application !
2659
+ end
2660
+ end
2661
+ ```
2662
+
2663
+ Vous avez le binding du contexte de l'application dans :
2664
+
2665
+ * Le corps de la classe d'application
2666
+ * Les méthodes définies par les extensions
2667
+ * Le bloc passé à `helpers`
2668
+ * Les procs/blocs utilisés comme argument pour `set`
2669
+ * Le bloc passé à `Sinatra.new`
2670
+
2671
+ Vous pouvez atteindre ce contexte (donc la classe) de la façon suivante :
2672
+
2673
+ * Via l'objet passé dans les blocs `configure` (`configure { |c| ... }`)
2674
+ * En utilisant `settings` dans le contexte de la requête
2675
+
2676
+ ### Contexte de la requête/instance
2677
+
2678
+ Pour chaque requête traitée, une nouvelle instance de votre classe
2679
+ d'application est créée et tous vos gestionnaires sont exécutés dans ce
2680
+ contexte. Depuis celui-ci, vous pouvez accéder aux objets `request` et
2681
+ `session` ou faire appel aux fonctions de rendu telles que `erb` ou `haml`.
2682
+ Vous pouvez accéder au contexte de l'application depuis le contexte de la
2683
+ requête au moyen de `settings` :
2684
+
2685
+ ``` ruby
2686
+ class MonApp < Sinatra::Base
2687
+ # Eh, je suis dans le contexte de l'application !
2688
+ get '/ajouter_route/:nom' do
2689
+ # Contexte de la requête pour '/ajouter_route/:nom'
2690
+ @value = 42
2691
+
2692
+ settings.get("/#{params[:nom]}") do
2693
+ # Contexte de la requête pour "/#{params[:nom]}"
2694
+ @value # => nil (on est pas au sein de la même requête)
2695
+ end
2696
+
2697
+ "Route ajoutée !"
2698
+ end
2699
+ end
2700
+ ```
2701
+
2702
+ Vous avez le binding du contexte de la requête dans :
2703
+
2704
+ * les blocs get, head, post, put, delete, options, patch, link et unlink
2705
+ * les filtres before et after
2706
+ * les méthodes utilitaires (définies au moyen de `helpers`)
2707
+ * les vues et templates
2708
+
2709
+ ### Le contexte de délégation
2710
+
2711
+ Le contexte de délégation se contente de transmettre les appels de méthodes au
2712
+ contexte de classe. Toutefois, il ne se comporte pas à 100% comme le contexte
2713
+ de classe car vous n'avez pas le binding de la classe : seules les méthodes
2714
+ spécifiquement déclarées pour délégation sont disponibles et il n'est pas
2715
+ possible de partager des variables/états avec le contexte de classe
2716
+ (comprenez : `self` n'est pas le même). Vous pouvez ajouter des délégation de
2717
+ méthodes en appelant `Sinatra::Delegator.delegate :method_name`.
2718
+
2719
+ Vous avez le binding du contexte de délégation dans :
2720
+
2721
+ * Le binding de haut niveau, si vous avez utilisé `require "sinatra"`
2722
+ * Un objet qui inclut le module `Sinatra::Delegator`
2723
+
2724
+ Pour vous faire une idée, vous pouvez jeter un coup d'oeil au
2725
+ [mixin Sinatra::Delegator](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633)
2726
+ qui [étend l'objet principal](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30).
2727
+
2728
+ ## Ligne de commande
2729
+
2730
+ Les applications Sinatra peuvent être lancées directement :
2731
+
2732
+ ``` shell
2733
+ ruby mon_application.rb [-h] [-x] [-e ENVIRONNEMENT] [-p PORT] [-o HOTE] [-s SERVEUR]
2734
+ ```
2735
+
2736
+ Avec les options :
2737
+
2738
+ ```
2739
+ -h # aide
2740
+ -p # déclare le port (4567 par défaut)
2741
+ -o # déclare l'hôte (0.0.0.0 par défaut)
2742
+ -e # déclare l'environnement (development par défaut)
2743
+ -s # déclare le serveur/gestionnaire à utiliser (thin par défaut)
2744
+ -x # active le mutex lock (off par défaut)
2745
+ ```
2746
+
2747
+ ## Configuration nécessaire
2748
+
2749
+ Les versions suivantes de Ruby sont officiellement supportées :
2750
+
2751
+ <dl>
2752
+ <dt>Ruby 1.8.7</dt>
2753
+ <dd>
2754
+ 1.8.7 est complètement supporté, toutefois si rien ne vous en empêche,
2755
+ nous vous recommandons de faire une mise à jour ou bien de passer à JRuby
2756
+ ou Rubinius. Le support de Ruby 1.8.7 ne sera pas supprimé avant la sortie
2757
+ de Sinatra 2.0. Ruby 1.8.6 n’est plus supporté.
2758
+ </dd>
2759
+
2760
+ <dt>Ruby 1.9.2</dt>
2761
+ <dd>
2762
+ 1.9.2 est totalement supporté. N’utilisez pas 1.9.2p0 car il provoque des
2763
+ erreurs de segmentation à l’exécution de Sinatra. Son support continuera
2764
+ au minimum jusqu’à la sortie de Sinatra 1.5.
2765
+ </dd>
2766
+
2767
+ <dt>Ruby 1.9.3</dt>
2768
+ <dd>
2769
+ 1.9.3 est totalement supporté et recommandé. Nous vous rappelons que passer
2770
+ à 1.9.3 depuis une version précédente annulera toutes les sessions. 1.9.3
2771
+ sera supporté jusqu'à la sortie de Sinatra 2.0.
2772
+ </dd>
2773
+
2774
+ <dt>Ruby 2.0.0</dt>
2775
+ <dd>
2776
+ 2.0.0 est totalement supporté et recommandé. L'abandon de son support
2777
+ officiel n'est pas à l'ordre du jour.
2778
+ </dd>
2779
+
2780
+ <dt>Rubinius</dt>
2781
+ <dd>
2782
+ Rubinius est officiellement supporté (Rubinius >= 2.x). Un <tt>gem install
2783
+ puma</tt> est recommandé.
2784
+ </dd>
2785
+
2786
+ <dt>JRuby</dt>
2787
+ <dd>
2788
+ La dernière version stable de JRuby est officiellement supportée. Il est
2789
+ déconseillé d'utiliser des extensions C avec JRuby. Un <tt>gem install
2790
+ trinidad</tt> est recommandé.
2791
+ </dd>
2792
+ </dl>
2793
+
2794
+ Nous gardons également un oeil sur les versions Ruby à venir.
2795
+
2796
+ Les implémentations Ruby suivantes ne sont pas officiellement supportées mais
2797
+ sont malgré tout connues pour permettre de faire fonctionner Sinatra :
2798
+
2799
+ * Versions plus anciennes de JRuby et Rubinius
2800
+ * Ruby Enterprise Edition
2801
+ * MacRuby, Maglev, IronRuby
2802
+ * Ruby 1.9.0 et 1.9.1 (mais nous déconseillons leur utilisation)
2803
+
2804
+ Le fait de ne pas être officiellement supporté signifie que si quelque chose
2805
+ ne fonctionne pas sur cette plateforme uniquement alors c'est un problème de la
2806
+ plateforme et pas un bug de Sinatra.
2807
+
2808
+ Nous lançons également notre intégration continue (CI) avec ruby-head (la
2809
+ future 2.1.0), mais nous ne pouvont rien garantir étant donné les évolutions
2810
+ continuelles. La version 2.1.0 devrait être totalement supportée.
2811
+
2812
+ Sinatra devrait fonctionner sur n'importe quel système d'exploitation
2813
+ supporté par l'implémentation Ruby choisie.
2814
+
2815
+ Si vous utilisez MacRuby, vous devriez `gem install control_tower`.
2816
+
2817
+ Il n'est pas possible d'utiliser Sinatra sur Cardinal, SmallRuby, BlueRuby ou
2818
+ toute version de Ruby antérieure à 1.8.7 à l'heure actuelle.
2819
+
2820
+ ## Essuyer les plâtres
2821
+
2822
+ Si vous souhaitez tester la toute dernière version de Sinatra, n'hésitez pas
2823
+ à faire tourner votre application sur la branche master, celle-ci devrait être
2824
+ stable.
2825
+
2826
+ Pour cela, la méthode la plus simple est d'installer une gem de prerelease que
2827
+ nous publions de temps en temps :
2828
+
2829
+ ``` shell
2830
+ gem install sinatra --pre
2831
+ ```
2832
+ Ce qui permet de bénéficier des toutes dernières fonctionnalités.
2833
+
2834
+ ### Installer avec Bundler
2835
+
2836
+ Il est cependant conseillé de passer par [Bundler](http://gembundler.com/) pour
2837
+ faire tourner votre application avec la dernière version de Sinatra.
2838
+
2839
+ Pour commencer, installez bundler si nécessaire :
2840
+
2841
+ ``` shell
2842
+ gem install bundler
2843
+ ```
2844
+
2845
+ Ensuite, créez un fichier `Gemfile` dans le dossier de votre projet :
2846
+
2847
+ ``` ruby
2848
+ source 'https://rubygems.org'
2849
+ gem 'sinatra', :github => "sinatra/sinatra"
2850
+
2851
+ # autres dépendances
2852
+ gem 'haml' # si par exemple vous utilisez haml
2853
+ gem 'activerecord', '~> 3.0' # au cas où vous auriez besoin de ActiveRecord 3.x
2854
+ ```
2855
+
2856
+ Notez que vous devez lister toutes les dépendances de votre application dans
2857
+ ce fichier `Gemfile`. Les dépendances directes de Sinatra (Rack et Tilt) seront
2858
+ automatiquement téléchargées et ajoutées par Bundler.
2859
+
2860
+ Vous pouvez alors lancer votre application de la façon suivante :
2861
+
2862
+ ``` shell
2863
+ bundle exec ruby myapp.rb
2864
+ ```
2865
+
2866
+ ### Faire un clone local
2867
+
2868
+ Si vous ne souhaitez pas employer Bundler, vous pouvez cloner Sinatra en local
2869
+ dans votre projet et démarrez votre application avec le dossier `sinatra/lib`
2870
+ dans le `$LOAD_PATH` :
2871
+
2872
+ ``` shell
2873
+ cd myapp
2874
+ git clone git://github.com/sinatra/sinatra.git
2875
+ ruby -I sinatra/lib myapp.rb
2876
+ ```
2877
+
2878
+ Et de temps en temps, vous devrez récupérer la dernière version du code source
2879
+ de Sinatra :
2880
+
2881
+ ``` shell
2882
+ cd myapp/sinatra
2883
+ git pull
2884
+ ```
2885
+
2886
+ ### Installer globalement
2887
+
2888
+ Une dernière méthode consiste à construire la gem vous-même :
2889
+
2890
+ ``` shell
2891
+ git clone git://github.com/sinatra/sinatra.git
2892
+ cd sinatra
2893
+ rake sinatra.gemspec
2894
+ rake install
2895
+ ```
2896
+
2897
+ Si vous installez les gems en tant que root, vous devez encore faire un :
2898
+
2899
+ ``` shell
2900
+ sudo rake install
2901
+ ```
2902
+
2903
+ ## Versions
2904
+
2905
+ Sinatra se conforme aux [versions sémantiques](http://semver.org/), aussi bien
2906
+ SemVer que SemVerTag.
2907
+
2908
+ ## Mais encore
2909
+
2910
+ * [Site internet](http://www.sinatrarb.com/) - Plus de documentation,
2911
+ de news, et des liens vers d'autres ressources.
2912
+ * [Contribuer](http://www.sinatrarb.com/contributing) - Vous avez trouvé un
2913
+ bug ? Besoin d'aide ? Vous avez un patch ?
2914
+ * [Suivi des problèmes](http://github.com/sinatra/sinatra/issues)
2915
+ * [Twitter](http://twitter.com/sinatra)
2916
+ * [Mailing List](http://groups.google.com/group/sinatrarb/topics)
2917
+ * IRC : [#sinatra](irc://chat.freenode.net/#sinatra) sur http://freenode.net
2918
+ * [Sinatra Book](http://sinatra-book.gittr.com) Tutoriels et recettes
2919
+ * [Sinatra Recipes](http://recipes.sinatrarb.com/) trucs et astuces rédigés par
2920
+ la communauté
2921
+ * Documentation API de la [dernière version](http://rubydoc.info/gems/sinatra)
2922
+ ou du [HEAD courant](http://rubydoc.info/github/sinatra/sinatra) sur
2923
+ http://rubydoc.info
2924
+ * [CI server](http://travis-ci.org/sinatra/sinatra)