devcenter 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. data/CONTRIBUTING.md +14 -0
  2. data/Gemfile +0 -4
  3. data/devcenter.gemspec +2 -1
  4. data/lib/devcenter.rb +0 -1
  5. data/lib/devcenter/previewer/web_app.rb +3 -2
  6. data/lib/devcenter/version.rb +1 -1
  7. metadata +19 -124
  8. data/vendor/sinatra/.gitignore +0 -6
  9. data/vendor/sinatra/.travis.yml +0 -16
  10. data/vendor/sinatra/.yardopts +0 -4
  11. data/vendor/sinatra/AUTHORS +0 -61
  12. data/vendor/sinatra/Gemfile +0 -91
  13. data/vendor/sinatra/LICENSE +0 -22
  14. data/vendor/sinatra/README.de.rdoc +0 -2116
  15. data/vendor/sinatra/README.es.rdoc +0 -2106
  16. data/vendor/sinatra/README.fr.rdoc +0 -2133
  17. data/vendor/sinatra/README.hu.rdoc +0 -608
  18. data/vendor/sinatra/README.jp.rdoc +0 -1056
  19. data/vendor/sinatra/README.ko.rdoc +0 -1932
  20. data/vendor/sinatra/README.pt-br.rdoc +0 -778
  21. data/vendor/sinatra/README.pt-pt.rdoc +0 -647
  22. data/vendor/sinatra/README.rdoc +0 -2049
  23. data/vendor/sinatra/README.ru.rdoc +0 -2033
  24. data/vendor/sinatra/README.zh.rdoc +0 -1816
  25. data/vendor/sinatra/Rakefile +0 -182
  26. data/vendor/sinatra/examples/chat.rb +0 -61
  27. data/vendor/sinatra/examples/simple.rb +0 -3
  28. data/vendor/sinatra/examples/stream.ru +0 -26
  29. data/vendor/sinatra/lib/sinatra.rb +0 -5
  30. data/vendor/sinatra/lib/sinatra/base.rb +0 -1820
  31. data/vendor/sinatra/lib/sinatra/images/404.png +0 -0
  32. data/vendor/sinatra/lib/sinatra/images/500.png +0 -0
  33. data/vendor/sinatra/lib/sinatra/main.rb +0 -30
  34. data/vendor/sinatra/lib/sinatra/showexceptions.rb +0 -345
  35. data/vendor/sinatra/lib/sinatra/version.rb +0 -3
  36. data/vendor/sinatra/sinatra.gemspec +0 -18
  37. data/vendor/sinatra/test/base_test.rb +0 -172
  38. data/vendor/sinatra/test/builder_test.rb +0 -91
  39. data/vendor/sinatra/test/coffee_test.rb +0 -90
  40. data/vendor/sinatra/test/compile_test.rb +0 -139
  41. data/vendor/sinatra/test/contest.rb +0 -98
  42. data/vendor/sinatra/test/creole_test.rb +0 -65
  43. data/vendor/sinatra/test/delegator_test.rb +0 -160
  44. data/vendor/sinatra/test/encoding_test.rb +0 -20
  45. data/vendor/sinatra/test/erb_test.rb +0 -98
  46. data/vendor/sinatra/test/extensions_test.rb +0 -98
  47. data/vendor/sinatra/test/filter_test.rb +0 -437
  48. data/vendor/sinatra/test/haml_test.rb +0 -91
  49. data/vendor/sinatra/test/helper.rb +0 -123
  50. data/vendor/sinatra/test/helpers_test.rb +0 -1768
  51. data/vendor/sinatra/test/integration/app.rb +0 -62
  52. data/vendor/sinatra/test/integration_helper.rb +0 -222
  53. data/vendor/sinatra/test/integration_test.rb +0 -87
  54. data/vendor/sinatra/test/less_test.rb +0 -69
  55. data/vendor/sinatra/test/liquid_test.rb +0 -59
  56. data/vendor/sinatra/test/mapped_error_test.rb +0 -305
  57. data/vendor/sinatra/test/markaby_test.rb +0 -80
  58. data/vendor/sinatra/test/markdown_test.rb +0 -82
  59. data/vendor/sinatra/test/middleware_test.rb +0 -68
  60. data/vendor/sinatra/test/nokogiri_test.rb +0 -67
  61. data/vendor/sinatra/test/public/favicon.ico +0 -0
  62. data/vendor/sinatra/test/rabl_test.rb +0 -89
  63. data/vendor/sinatra/test/rack_test.rb +0 -45
  64. data/vendor/sinatra/test/radius_test.rb +0 -59
  65. data/vendor/sinatra/test/rdoc_test.rb +0 -66
  66. data/vendor/sinatra/test/readme_test.rb +0 -120
  67. data/vendor/sinatra/test/request_test.rb +0 -45
  68. data/vendor/sinatra/test/response_test.rb +0 -64
  69. data/vendor/sinatra/test/result_test.rb +0 -76
  70. data/vendor/sinatra/test/route_added_hook_test.rb +0 -59
  71. data/vendor/sinatra/test/routing_test.rb +0 -1175
  72. data/vendor/sinatra/test/sass_test.rb +0 -116
  73. data/vendor/sinatra/test/scss_test.rb +0 -89
  74. data/vendor/sinatra/test/server_test.rb +0 -48
  75. data/vendor/sinatra/test/settings_test.rb +0 -561
  76. data/vendor/sinatra/test/sinatra_test.rb +0 -12
  77. data/vendor/sinatra/test/slim_test.rb +0 -84
  78. data/vendor/sinatra/test/static_test.rb +0 -219
  79. data/vendor/sinatra/test/streaming_test.rb +0 -149
  80. data/vendor/sinatra/test/templates_test.rb +0 -333
  81. data/vendor/sinatra/test/textile_test.rb +0 -65
  82. data/vendor/sinatra/test/views/a/in_a.str +0 -1
  83. data/vendor/sinatra/test/views/ascii.erb +0 -2
  84. data/vendor/sinatra/test/views/b/in_b.str +0 -1
  85. data/vendor/sinatra/test/views/calc.html.erb +0 -1
  86. data/vendor/sinatra/test/views/error.builder +0 -3
  87. data/vendor/sinatra/test/views/error.erb +0 -3
  88. data/vendor/sinatra/test/views/error.haml +0 -3
  89. data/vendor/sinatra/test/views/error.sass +0 -2
  90. data/vendor/sinatra/test/views/explicitly_nested.str +0 -1
  91. data/vendor/sinatra/test/views/foo/hello.test +0 -1
  92. data/vendor/sinatra/test/views/hello.builder +0 -1
  93. data/vendor/sinatra/test/views/hello.coffee +0 -1
  94. data/vendor/sinatra/test/views/hello.creole +0 -1
  95. data/vendor/sinatra/test/views/hello.erb +0 -1
  96. data/vendor/sinatra/test/views/hello.haml +0 -1
  97. data/vendor/sinatra/test/views/hello.less +0 -5
  98. data/vendor/sinatra/test/views/hello.liquid +0 -1
  99. data/vendor/sinatra/test/views/hello.mab +0 -1
  100. data/vendor/sinatra/test/views/hello.md +0 -1
  101. data/vendor/sinatra/test/views/hello.nokogiri +0 -1
  102. data/vendor/sinatra/test/views/hello.rabl +0 -2
  103. data/vendor/sinatra/test/views/hello.radius +0 -1
  104. data/vendor/sinatra/test/views/hello.rdoc +0 -1
  105. data/vendor/sinatra/test/views/hello.sass +0 -2
  106. data/vendor/sinatra/test/views/hello.scss +0 -3
  107. data/vendor/sinatra/test/views/hello.slim +0 -1
  108. data/vendor/sinatra/test/views/hello.str +0 -1
  109. data/vendor/sinatra/test/views/hello.test +0 -1
  110. data/vendor/sinatra/test/views/hello.textile +0 -1
  111. data/vendor/sinatra/test/views/hello.wlang +0 -1
  112. data/vendor/sinatra/test/views/hello.yajl +0 -1
  113. data/vendor/sinatra/test/views/layout2.builder +0 -3
  114. data/vendor/sinatra/test/views/layout2.erb +0 -2
  115. data/vendor/sinatra/test/views/layout2.haml +0 -2
  116. data/vendor/sinatra/test/views/layout2.liquid +0 -2
  117. data/vendor/sinatra/test/views/layout2.mab +0 -2
  118. data/vendor/sinatra/test/views/layout2.nokogiri +0 -3
  119. data/vendor/sinatra/test/views/layout2.rabl +0 -3
  120. data/vendor/sinatra/test/views/layout2.radius +0 -2
  121. data/vendor/sinatra/test/views/layout2.slim +0 -3
  122. data/vendor/sinatra/test/views/layout2.str +0 -2
  123. data/vendor/sinatra/test/views/layout2.test +0 -1
  124. data/vendor/sinatra/test/views/layout2.wlang +0 -2
  125. data/vendor/sinatra/test/views/nested.str +0 -1
  126. data/vendor/sinatra/test/views/utf8.erb +0 -2
  127. data/vendor/sinatra/test/wlang_test.rb +0 -70
  128. data/vendor/sinatra/test/yajl_test.rb +0 -86
@@ -1,2116 +0,0 @@
1
- = Sinatra
2
-
3
- <i>Wichtig: Dieses Dokument ist eine Übersetzung aus dem Englischen und unter
4
- Umständen nicht auf dem aktuellen Stand.</i>
5
-
6
- Sinatra ist eine
7
- {DSL}[http://de.wikipedia.org/wiki/Domänenspezifische_Sprache], die das
8
- schnelle Erstellen von Webanwendungen in Ruby mit minimalem Aufwand ermöglicht:
9
-
10
- # myapp.rb
11
- require 'sinatra'
12
- get '/' do
13
- 'Hallo Welt!'
14
- end
15
-
16
- Einfach via +rubygems+ installieren und starten:
17
-
18
- gem install sinatra
19
- ruby -rubygems myapp.rb
20
-
21
- Die Seite kann nun unter http://localhost:4567 betrachtet werden.
22
-
23
- Es wird empfohlen, den Thin-Server via <tt>gem install thin</tt> zu
24
- installieren, den Sinatra dann, soweit vorhanden, automatisch verwendet.
25
-
26
- == Routen
27
-
28
- In Sinatra wird eine Route durch eine HTTP-Methode und ein URL-Muster
29
- definiert. Jeder dieser Routen wird ein Ruby-Block zugeordnet:
30
-
31
- get '/' do
32
- .. zeige etwas ..
33
- end
34
-
35
- post '/' do
36
- .. erstelle etwas ..
37
- end
38
-
39
- put '/' do
40
- .. update etwas ..
41
- end
42
-
43
- delete '/' do
44
- .. entferne etwas ..
45
- end
46
-
47
- options '/' do
48
- .. zeige, was wir können ..
49
- end
50
-
51
- Die Routen werden in der Reihenfolge durchlaufen, in der sie definiert wurden.
52
- Das erste Routen-Muster, das mit dem Request übereinstimmt, wird ausgeführt.
53
-
54
- Die Muster der Routen können benannte Parameter beinhalten, die über den
55
- <tt>params</tt>-Hash zugänglich gemacht werden:
56
-
57
- get '/hallo/:name' do
58
- # passt auf "GET /hallo/foo" und "GET /hallo/bar"
59
- # params[:name] ist 'foo' oder 'bar'
60
- "Hallo #{params[:name]}!"
61
- end
62
-
63
- Man kann auf diese auch mit Block-Parametern zugreifen:
64
-
65
- get '/hallo/:name' do |n|
66
- "Hallo #{n}!"
67
- end
68
-
69
- Routen-Muster können auch mit Splat- oder Wildcard-Parametern über das
70
- <tt>params[:splat]</tt>-Array angesprochen werden:
71
-
72
- get '/sag/*/zu/*' do
73
- # passt auf /sag/hallo/zu/welt
74
- params[:splat] # => ["hallo", "welt"]
75
- end
76
-
77
- get '/download/*.*' do
78
- # passt auf /download/pfad/zu/datei.xml
79
- params[:splat] # => ["pfad/zu/datei", "xml"]
80
- end
81
-
82
- Oder mit Block-Parametern:
83
-
84
- get '/download/*.*' do |pfad, endung|
85
- [pfad, endung] # => ["Pfad/zu/Datei", "xml"]
86
- end
87
-
88
- Routen mit regulären Ausdrücken sind auch möglich:
89
-
90
- get %r{/hallo/([\w]+)} do
91
- "Hallo, #{params[:captures].first}!"
92
- end
93
-
94
- Und auch hier können Block-Parameter genutzt werden:
95
-
96
- get %r{/hallo/([\w]+)} do |c|
97
- "Hallo, #{c}!"
98
- end
99
-
100
- Routen-Muster können auch mit optionalen Parametern ausgestattet werden:
101
-
102
- get '/posts.?:format?' do
103
- # passt auf "GET /posts" sowie jegliche Erweiterung
104
- # wie "GET /posts.json", "GET /posts.xml" etc.
105
- end
106
-
107
- Anmerkung: Solange man den sog. Path Traversal Attack-Schutz nicht deaktiviert
108
- (siehe weiter unten), kann es sein, dass der Request-Pfad noch vor dem Abgleich
109
- mit den Routen modifiziert wird.
110
-
111
- === Bedingungen
112
-
113
- An Routen können eine Vielzahl von Bedingungen angehängt werden, die erfüllt
114
- sein müssen, damit der Block ausgeführt wird. Möglich wäre etwa eine
115
- Einschränkung des User-Agents:
116
-
117
- get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
118
- "Du verwendest Songbird Version #{params[:agent][0]}"
119
- end
120
-
121
- get '/foo' do
122
- # passt auf andere Browser
123
- end
124
-
125
- Andere mitgelieferte Bedingungen sind +host_name+ und +provides+:
126
-
127
- get '/', :host_name => /^admin\./ do
128
- "Adminbereich, Zugriff verweigert!"
129
- end
130
-
131
- get '/', :provides => 'html' do
132
- haml :index
133
- end
134
-
135
- get '/', :provides => ['rss', 'atom', 'xml'] do
136
- builder :feed
137
- end
138
-
139
- Es können auch andere Bedingungen relativ einfach hinzugefügt werden:
140
-
141
- set(:probability) { |value| condition { rand <= value } }
142
-
143
- get '/auto_gewinnen', :probability => 0.1 do
144
- "Du hast gewonnen!"
145
- end
146
-
147
- get '/auto_gewinnen' do
148
- "Tut mir leid, verloren."
149
- end
150
-
151
- Bei Bedingungen, die mehrere Werte annehmen können, sollte ein Splat verwendet
152
- werden:
153
-
154
- set(:auth) do |*roles| # <- hier kommt der Splat ins Spiel
155
- condition do
156
- unless logged_in? && roles.any? {|role| current_user.in_role? role }
157
- redirect "/login/", 303
158
- end
159
- end
160
- end
161
-
162
- get "/mein/account/", :auth => [:user, :admin] do
163
- "Mein Account"
164
- end
165
-
166
- get "/nur/admin/", :auth => :admin do
167
- "Nur Admins dürfen hier rein!"
168
- end
169
-
170
- === Rückgabewerte
171
-
172
- Durch den Rückgabewert eines Routen-Blocks wird mindestens der Response-Body
173
- festgelegt, der an den HTTP-Client, bzw. die nächste Rack-Middleware,
174
- weitergegeben wird. Im Normalfall handelt es sich hierbei, wie in den
175
- vorangehenden Beispielen zu sehen war, um einen String. Es werden allerdings
176
- auch andere Werte akzeptiert.
177
-
178
- Es kann jedes gültige Objekt zurückgegeben werden, bei dem es sich entweder um
179
- einen Rack-Rückgabewert, einen Rack-Body oder einen HTTP-Status-Code handelt:
180
-
181
- * Ein Array mit drei Elementen: <tt>[Status (Fixnum), Headers (Hash),
182
- Response-Body (antwortet auf #each)]</tt>.
183
- * Ein Array mit zwei Elementen: <tt>[Status (Fixnum), Response-Body (antwortet
184
- auf #each)]</tt>.
185
- * Ein Objekt, das auf <tt>#each</tt> antwortet und den an diese Methode
186
- übergebenen Block nur mit Strings als Übergabewerte aufruft.
187
- * Ein Fixnum, das den Status-Code festlegt.
188
-
189
- Damit lässt sich relativ einfach Streaming implementieren:
190
-
191
- class Stream
192
- def each
193
- 100.times { |i| yield "#{i}\n" }
194
- end
195
- end
196
-
197
- get('/') { Stream.new }
198
-
199
- Ebenso kann die +stream+-Helfer-Methode (s.u.) verwendet werden, die Streaming
200
- direkt in die Route integriert.
201
-
202
- === Eigene Routen-Muster
203
-
204
- Wie oben schon beschrieben, ist Sinatra von Haus aus mit Unterstützung für
205
- String-Muster und Reguläre Ausdrücke zum Abgleichen von Routen ausgestattet.
206
- Das muss aber noch nicht alles sein, es können ohne großen Aufwand eigene
207
- Routen-Muster erstellt werden:
208
-
209
- class AllButPattern
210
- Match = Struct.new(:captures)
211
-
212
- def initialize(except)
213
- @except = except
214
- @captures = Match.new([])
215
- end
216
-
217
- def match(str)
218
- @captures unless @except === str
219
- end
220
- end
221
-
222
- def all_but(pattern)
223
- AllButPattern.new(pattern)
224
- end
225
-
226
- get all_but("/index") do
227
- # ...
228
- end
229
-
230
- Beachte, dass das obige Beispiel etwas übertrieben wirkt. Es geht auch
231
- einfacher:
232
-
233
- get // do
234
- pass if request.path_info == "/index"
235
- # ...
236
- end
237
-
238
- Oder unter Verwendung eines negativen look ahead:
239
-
240
- get %r{^(?!/index$)} do
241
- # ...
242
- end
243
-
244
- == Statische Dateien
245
-
246
- Statische Dateien werden aus dem <tt>./public</tt>-Ordner ausgeliefert. Es ist
247
- möglich, einen anderen Ort zu definieren, indem man die
248
- <tt>:public_folder</tt>-Option setzt:
249
-
250
- set :public_folder, File.dirname(__FILE__) + '/static'
251
-
252
- Zu beachten ist, dass der Ordnername public nicht Teil der URL ist. Die Datei
253
- <tt>./public/css/style.css</tt> ist unter
254
- <tt>http://example.com/css/style.css</tt> zu finden.
255
-
256
- Um den <tt>Cache-Control</tt>-Header mit Informationen zu versorgen, verwendet
257
- man die <tt>:static_cache_control</tt>-Einstellung (s.u.).
258
-
259
- == Views/Templates
260
-
261
- Alle Templatesprachen verwenden ihre eigene Renderingmethode, die jeweils
262
- einen String zurückgibt:
263
-
264
- get '/' do
265
- erb :index
266
- end
267
-
268
- Dieses Beispiel rendert <tt>views/index.erb</tt>.
269
-
270
- Anstelle eines Templatenamens kann man auch direkt die Templatesprache
271
- verwenden:
272
-
273
- get '/' do
274
- code = "<%= Time.now %>"
275
- erb code
276
- end
277
-
278
- Templates nehmen ein zweite Argument an, den Options-Hash:
279
-
280
- get '/' do
281
- erb :index, :layout => :post
282
- end
283
-
284
- Dieses Beispiel rendert <tt>views/index.erb</tt> eingebettet in
285
- <tt>views/post.erb</tt> (Voreinstellung ist <tt>views/layout.erb</tt>, sofern
286
- es vorhanden ist.)
287
-
288
- Optionen, die Sinatra nicht versteht, werden an das Template weitergereicht:
289
-
290
- get '/' do
291
- haml :index, :format => :html5
292
- end
293
-
294
- Für alle Templates können auch generelle Einstellungen festgelegt werden:
295
-
296
- set :haml, :format => :html5
297
-
298
- get '/' do
299
- haml :index
300
- end
301
-
302
- Optionen, die an die Rendermethode weitergegeben werden, überschreiben die
303
- Einstellungen, die mit +set+ festgelegt wurden.
304
-
305
- Mögliche Einstellungen:
306
-
307
- [locals]
308
- Liste von lokalen Variablen, die and das Dokument weitergegeben werden.
309
- Praktisch für Partials.
310
- Beispiel: <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>
311
-
312
- [default_encoding]
313
- Gibt die Stringkodierung an, die verwendet werden soll. Voreingestellt auf
314
- <tt>settings.default_encoding</tt>.
315
-
316
- [views]
317
- Ordner, aus dem die Templates heraus geladen werden. Voreingestellt auf
318
- <tt>settings.views</tt>.
319
-
320
- [layout]
321
- Legt fest, ob ein Layouttemplate verwendet werden soll oder nicht (+true+
322
- oder +false+). Ist es ein Symbol, dass legt es fest, welches Template als
323
- Layout verwendet wird. Beispiel:
324
- <tt>erb :index, :layout => !request.xhr?</tt>
325
-
326
- [content_type]
327
- Content-Type den das Template ausgibt. Voreinstellung hängt von der
328
- Templatesprache ab.
329
-
330
- [scope]
331
- Scope, in dem das Template gerendert wird. Liegt standardmäßig innerhalb der
332
- App-Instanz. Wird Scope geändert, sind Instanzvariablen und Helfermethoden
333
- nicht verfügbar.
334
-
335
- [layout_engine]
336
- Legt fest, welcher Renderer für das Layout verantwortlich ist.
337
- Hilfreich für Sprachen, die sonst keine Templates unterstützen.
338
- Voreingestellt auf den Renderer, der für das Template verwendet wird.
339
- Beispiel: <tt>set :rdoc, :layout_engine => :erb</tt>
340
-
341
- Sinatra geht davon aus, dass die Templates sich im <tt>./views</tt> Verzeichnis
342
- befinden. Es kann jedoch ein anderer Ordner festgelegt werden:
343
-
344
- set :views, settings.root + '/templates'
345
-
346
- Es ist zu beachten, dass immer mit Symbolen auf Templates verwiesen werden
347
- muss, auch dann, wenn sie sich in einem Unterordner befinden:
348
-
349
- haml :'unterverzeichnis/template'
350
-
351
- Rendering-Methoden rendern jeden String direkt.
352
-
353
- === Verfügbare Templatesprachen
354
-
355
- Einige Sprachen haben mehrere Implementierungen. Um festzulegen, welche
356
- verwendet wird (und dann auch Thread-sicher ist), verwendet man am besten zu
357
- Beginn ein 'require':
358
-
359
- require 'rdiscount' # oder require 'bluecloth'
360
- get('/') { markdown :index }
361
-
362
- === Haml Templates
363
-
364
- Abhängigkeit:: {haml}[http://haml.info/]
365
- Dateierweiterung:: <tt>.haml</tt>
366
- Beispiel:: <tt>haml :index, :format => :html5</tt>
367
-
368
- === Erb Templates
369
-
370
- Abhängigkeit:: {erubis}[http://www.kuwata-lab.com/erubis/] oder
371
- erb (included in Ruby)
372
- Dateierweiterungen:: <tt>.erb</tt>, <tt>.rhtml</tt> oder <tt>.erubis</tt>
373
- (nur Erubis)
374
- Beispiel:: <tt>erb :index</tt>
375
-
376
- === Builder Templates
377
-
378
- Abhängigkeit:: {builder}[http://builder.rubyforge.org/]
379
- Dateierweiterung:: <tt>.builder</tt>
380
- Beispiel:: <tt>builder { |xml| xml.em "Hallo" }</tt>
381
-
382
- Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
383
-
384
- === Nokogiri Templates
385
-
386
- Abhängigkeit:: {nokogiri}[http://nokogiri.org/]
387
- Dateierweiterung:: <tt>.nokogiri</tt>
388
- Beispiel:: <tt>nokogiri { |xml| xml.em "Hallo" }</tt>
389
-
390
- Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
391
-
392
- === Sass Templates
393
-
394
- Abhängigkeit:: {sass}[http://sass-lang.com/]
395
- Dateierweiterung:: <tt>.sass</tt>
396
- Beispiel:: <tt>sass :stylesheet, :style => :expanded</tt>
397
-
398
- === SCSS Templates
399
-
400
- Abhängigkeit:: {sass}[http://sass-lang.com/]
401
- Dateierweiterung:: <tt>.scss</tt>
402
- Beispiel:: <tt>scss :stylesheet, :style => :expanded</tt>
403
-
404
- === Less Templates
405
-
406
- Abhängigkeit:: {less}[http://www.lesscss.org/]
407
- Dateierweiterung:: <tt>.less</tt>
408
- Beispiel:: <tt>less :stylesheet</tt>
409
-
410
- === Liquid Templates
411
-
412
- Abhängigkeit:: {liquid}[http://www.liquidmarkup.org/]
413
- Dateierweiterung:: <tt>.liquid</tt>
414
- Beispiel:: <tt>liquid :index, :locals => { :key => 'Wert' }</tt>
415
-
416
- Da man aus dem Liquid-Template heraus keine Ruby-Methoden aufrufen kann
417
- (ausgenommen +yield+), wird man üblicherweise locals verwenden wollen, mit
418
- denen man Variablen weitergibt.
419
-
420
- === Markdown Templates
421
-
422
- Abhängigkeit:: {rdiscount}[https://github.com/rtomayko/rdiscount],
423
- {redcarpet}[https://github.com/vmg/redcarpet],
424
- {bluecloth}[http://deveiate.org/projects/BlueCloth],
425
- {kramdown}[http://kramdown.rubyforge.org/] *oder*
426
- {maruku}[http://maruku.rubyforge.org/]
427
- Dateierweiterungen:: <tt>.markdown</tt>, <tt>.mkd</tt> und <tt>.md</tt>
428
- Beispiel:: <tt>markdown :index, :layout_engine => :erb</tt>
429
-
430
- Da man aus den Markdown-Templates heraus keine Ruby-Methoden aufrufen und auch
431
- keine locals verwenden kann, wird man Markdown üblicherweise in Kombination mit
432
- anderen Renderern verwenden wollen:
433
-
434
- erb :overview, :locals => { :text => markdown(:einfuehrung) }
435
-
436
- Beachte, dass man die +markdown+-Methode auch aus anderen Templates heraus
437
- aufrufen kann:
438
-
439
- %h1 Gruß von Haml!
440
- %p= markdown(:Grüße)
441
-
442
- Da man Ruby nicht von Markdown heraus aufrufen kann, können auch Layouts nicht
443
- in Markdown geschrieben werden. Es ist aber möglich, einen Renderer für die
444
- Templates zu verwenden und einen anderen für das Layout, indem die
445
- <tt>:layout_engine</tt>-Option verwendet wird.
446
-
447
- === Textile Templates
448
-
449
- Abhängigkeit:: {RedCloth}[http://redcloth.org/]
450
- Dateierweiterung:: <tt>.textile</tt>
451
- Beispiel:: <tt>textile :index, :layout_engine => :erb</tt>
452
-
453
- Da man aus dem Textile-Template heraus keine Ruby-Methoden aufrufen und auch
454
- keine locals verwenden kann, wird man Textile üblicherweise in Kombination mit
455
- anderen Renderern verwenden wollen:
456
-
457
- erb :overview, :locals => { :text => textile(:einfuehrung) }
458
-
459
- Beachte, dass man die +textile+-Methode auch aus anderen Templates heraus
460
- aufrufen kann:
461
-
462
- %h1 Gruß von Haml!
463
- %p= textile(:Grüße)
464
-
465
- Da man Ruby nicht von Textile heraus aufrufen kann, können auch Layouts nicht
466
- in Textile geschrieben werden. Es ist aber möglich, einen Renderer für die
467
- Templates zu verwenden und einen anderen für das Layout, indem die
468
- <tt>:layout_engine</tt>-Option verwendet wird.
469
-
470
- === RDoc Templates
471
-
472
- Abhängigkeit:: {rdoc}[http://rdoc.rubyforge.org/]
473
- Dateierweiterung:: <tt>.rdoc</tt>
474
- Beispiel:: <tt>textile :README, :layout_engine => :erb</tt>
475
-
476
- Da man aus dem RDoc-Template heraus keine Ruby-Methoden aufrufen und auch
477
- keine locals verwenden kann, wird man RDoc üblicherweise in Kombination mit
478
- anderen Renderern verwenden wollen:
479
-
480
- erb :overview, :locals => { :text => rdoc(:einfuehrung) }
481
-
482
- Beachte, dass man die +rdoc+-Methode auch aus anderen Templates heraus
483
- aufrufen kann:
484
-
485
- %h1 Gruß von Haml!
486
- %p= rdoc(:Grüße)
487
-
488
- Da man Ruby nicht von RDoc heraus aufrufen kann, können auch Layouts nicht
489
- in RDoc geschrieben werden. Es ist aber möglich, einen Renderer für die
490
- Templates zu verwenden und einen anderen für das Layout, indem die
491
- <tt>:layout_engine</tt>-Option verwendet wird.
492
-
493
- === Radius Templates
494
-
495
- Abhängigkeit:: {radius}[http://radius.rubyforge.org/]
496
- Dateierweiterung:: <tt>.radius</tt>
497
- Beispiel:: <tt>radius :index, :locals => { :key => 'Wert' }</tt>
498
-
499
- Da man aus dem Radius-Template heraus keine Ruby-Methoden aufrufen kann, wird
500
- man üblicherweise locals verwenden wollen, mit denen man Variablen weitergibt.
501
-
502
- === Markaby Templates
503
-
504
- Abhängigkeit:: {markaby}[http://markaby.github.com/]
505
- Dateierweiterung:: <tt>.mab</tt>
506
- Beispiel:: <tt>markaby { h1 "Willkommen!" }</tt>
507
-
508
- Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
509
-
510
- === RABL Templates
511
-
512
- Abhängigkeit:: {rabl}[https://github.com/nesquena/rabl]
513
- Dateierweiterung:: <tt>.rabl</tt>
514
- Beispiel:: <tt>rabl :index</tt>
515
-
516
- === Slim Templates
517
-
518
- Abhängigkeit:: {slim}[http://slim-lang.com/]
519
- Dateierweiterung:: <tt>.slim</tt>
520
- Beispiel:: <tt>slim :index</tt>
521
-
522
- === Creole Templates
523
-
524
- Abhängigkeit:: {creole}[https://github.com/minad/creole]
525
- Dateierweiterung:: <tt>.creole</tt>
526
- Beispiel:: <tt>creole :wiki, :layout_engine => :erb</tt>
527
-
528
- Da man aus dem Creole-Template heraus keine Ruby-Methoden aufrufen und auch
529
- keine locals verwenden kann, wird man Creole üblicherweise in Kombination mit
530
- anderen Renderern verwenden wollen:
531
-
532
- erb :overview, :locals => { :text => creole(:einfuehrung) }
533
-
534
- Beachte, dass man die +creole+-Methode auch aus anderen Templates heraus
535
- aufrufen kann:
536
-
537
- %h1 Gruß von Haml!
538
- %p= creole(:Grüße)
539
-
540
- Da man Ruby nicht von Creole heraus aufrufen kann, können auch Layouts nicht
541
- in Creole geschrieben werden. Es ist aber möglich, einen Renderer für die
542
- Templates zu verwenden und einen anderen für das Layout, indem die
543
- <tt>:layout_engine</tt>-Option verwendet wird.
544
-
545
- === CoffeeScript Templates
546
-
547
- Abhängigkeit:: {coffee-script}[https://github.com/josh/ruby-coffee-script]
548
- und eine {Möglichkeit JavaScript auszuführen}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
549
- Dateierweiterung:: <tt>.coffee</tt>
550
- Beispiel:: <tt>coffee :index</tt>
551
-
552
- === WLang Templates
553
-
554
- Abhängigkeit:: {wlang}[https://github.com/blambeau/wlang/]
555
- Dateierweiterung:: <tt>.wlang</tt>
556
- Beispiel:: <tt>wlang :index, :locals => { :key => 'value' }</tt>
557
-
558
- Ruby-Methoden in wlang aufzurufen entspricht nicht den idiomatischen Vorgaben
559
- von wlang, es bietet sich deshalb an, <tt>:locals</tt> zu verwenden.
560
- Layouts, die wlang und +yield+ verwenden, werden aber trotzdem unterstützt.
561
-
562
- === Eingebettete Templates
563
-
564
- get '/' do
565
- haml '%div.title Hallo Welt'
566
- end
567
-
568
- Rendert den eingebetteten Template-String.
569
-
570
- === Auf Variablen in Templates zugreifen
571
-
572
- Templates werden in demselben Kontext ausgeführt wie Routen. Instanzvariablen
573
- in Routen sind auch direkt im Template verfügbar:
574
-
575
- get '/:id' do
576
- @foo = Foo.find(params[:id])
577
- haml '%h1= @foo.name'
578
- end
579
-
580
- Oder durch einen expliziten Hash von lokalen Variablen:
581
-
582
- get '/:id' do
583
- foo = Foo.find(params[:id])
584
- haml '%h1= bar.name', :locals => { :bar => foo }
585
- end
586
-
587
- Dies wird typischerweise bei Verwendung von Subtemplates (partials) in anderen
588
- Templates eingesetzt.
589
-
590
- === Inline-Templates
591
-
592
- Templates können auch am Ende der Datei definiert werden:
593
-
594
- require 'sinatra'
595
-
596
- get '/' do
597
- haml :index
598
- end
599
-
600
- __END__
601
-
602
- @@ layout
603
- %html
604
- = yield
605
-
606
- @@ index
607
- %div.title Hallo Welt!!!!!
608
-
609
- Anmerkung: Inline-Templates, die in der Datei definiert sind, die <tt>require
610
- 'sinatra'</tt> aufruft, werden automatisch geladen. Um andere Inline-Templates
611
- in anderen Dateien aufzurufen, muss explizit <tt>enable :inline_templates</tt>
612
- verwendet werden.
613
-
614
- === Benannte Templates
615
-
616
- Templates können auch mit der Top-Level <tt>template</tt>-Methode definiert
617
- werden:
618
-
619
- template :layout do
620
- "%html\n =yield\n"
621
- end
622
-
623
- template :index do
624
- '%div.title Hallo Welt!'
625
- end
626
-
627
- get '/' do
628
- haml :index
629
- end
630
-
631
- Wenn ein Template mit dem Namen "layout" existiert, wird es bei jedem Aufruf
632
- verwendet. Durch <tt>:layout => false</tt> kann das Ausführen verhindert
633
- werden:
634
-
635
- get '/' do
636
- haml :index, :layout => request.xhr?
637
- end
638
-
639
- === Dateiendungen zuordnen
640
-
641
- Um eine Dateiendung einer Template-Engine zuzuordnen, kann
642
- <tt>Tilt.register</tt> genutzt werden. Wenn etwa die Dateiendung +tt+ für
643
- Textile-Templates genutzt werden soll, lässt sich dies wie folgt
644
- bewerkstelligen:
645
-
646
- Tilt.register :tt, Tilt[:textile]
647
-
648
- === Eine eigene Template-Engine hinzufügen
649
-
650
- Zu allererst muss die Engine bei Tilt registriert und danach eine
651
- Rendering-Methode erstellt werden:
652
-
653
- Tilt.register :mtt, MeineTolleTemplateEngine
654
-
655
- helpers do
656
- def mtt(*args) render(:mtt, *args) end
657
- end
658
-
659
- get '/' do
660
- mtt :index
661
- end
662
-
663
- Dieser Code rendert <tt>./views/application.mtt</tt>. Siehe
664
- github.com/rtomayko/tilt[https://github.com/rtomayko/tilt], um mehr über Tilt
665
- zu lernen.
666
-
667
- == Filter
668
-
669
- Before-Filter werden vor jedem Request in demselben Kontext, wie danach die
670
- Routen, ausgeführt. So können etwa Request und Antwort geändert werden.
671
- Gesetzte Instanzvariablen in Filtern können in Routen und Templates verwendet
672
- werden:
673
-
674
- before do
675
- @note = 'Hi!'
676
- request.path_info = '/foo/bar/baz'
677
- end
678
-
679
- get '/foo/*' do
680
- @note #=> 'Hi!'
681
- params[:splat] #=> 'bar/baz'
682
- end
683
-
684
- After-Filter werden nach jedem Request in demselben Kontext ausgeführt und
685
- können ebenfalls Request und Antwort ändern. In Before-Filtern gesetzte
686
- Instanzvariablen können in After-Filtern verwendet werden:
687
-
688
- after do
689
- puts response.status
690
- end
691
-
692
- Filter können optional auch mit einem Muster ausgestattet werden, welches auf
693
- den Request-Pfad passen muss, damit der Filter ausgeführt wird:
694
-
695
- before '/protected/*' do
696
- authenticate!
697
- end
698
-
699
- after '/create/:slug' do |slug|
700
- session[:last_slug] = slug
701
- end
702
-
703
- Ähnlich wie Routen können Filter auch mit weiteren Bedingungen eingeschränkt
704
- werden:
705
-
706
- before :agent => /Songbird/ do
707
- # ...
708
- end
709
-
710
- after '/blog/*', :host_name => 'example.com' do
711
- # ...
712
- end
713
-
714
- == Helfer
715
-
716
- Durch die Top-Level <tt>helpers</tt>-Methode werden sogenannte Helfer-Methoden
717
- definiert, die in Routen und Templates verwendet werden können:
718
-
719
- helpers do
720
- def bar(name)
721
- "#{name}bar"
722
- end
723
- end
724
-
725
- get '/:name' do
726
- bar(params[:name])
727
- end
728
-
729
- === Sessions verwenden
730
- Sessions werden verwendet, um Zustände zwischen den Requests zu speichern.
731
- Sind sie aktiviert, kann ein Session-Hash je Benutzer-Session verwendet werden.
732
-
733
- enable :sessions
734
-
735
- get '/' do
736
- "value = " << session[:value].inspect
737
- end
738
-
739
- get '/:value' do
740
- session[:value] = params[:value]
741
- end
742
-
743
- Beachte, dass <tt>enable :sessions</tt> alle Daten in einem Cookie speichert.
744
- Unter Umständen kann dies negative Effekte haben, z.B. verursachen viele Daten
745
- höheren, teilweise überflüssigen Traffic. Um das zu vermeiden, kann eine Rack-
746
- Session-Middleware verwendet werden. Dabei wird auf <tt>enable :sessions</tt>
747
- verzichtet und die Middleware wie üblich im Programm eingebunden:
748
-
749
- use Rack::Session::Pool, :expire_after => 2592000
750
-
751
- get '/' do
752
- "value = " << session[:value].inspect
753
- end
754
-
755
- get '/:value' do
756
- session[:value] = params[:value]
757
- end
758
-
759
- Um die Sicherheit zu erhöhen, werden Cookies, die Session-Daten führen, mit
760
- einem sogenannten Session-Secret signiert. Da sich dieses Geheimwort bei jedem
761
- Neustart der Applikation automatisch ändert, ist es sinnvoll, ein eigenes zu
762
- wählen, damit sich alle Instanzen der Applikation dasselbe Session-Secret
763
- teilen:
764
-
765
- set :session_secret, 'super secret'
766
-
767
- Zur weiteren Konfiguration kann man einen Hash mit Optionen in den +sessions+
768
- Einstellungen ablegen.
769
-
770
- set :sessions, :domain => 'foo.com'
771
-
772
- == Anhalten
773
-
774
- Zum sofortigen Stoppen eines Request in einem Filter oder einer Route:
775
-
776
- halt
777
-
778
- Der Status kann beim Stoppen auch angegeben werden:
779
-
780
- halt 410
781
-
782
- Oder auch den Response-Body:
783
-
784
- halt 'Hier steht der Body'
785
-
786
- Oder beides:
787
-
788
- halt 401, 'verschwinde!'
789
-
790
- Sogar mit Headern:
791
-
792
- halt 402, {'Content-Type' => 'text/plain'}, 'Rache'
793
-
794
- Natürlich ist es auch möglich, ein Template mit +halt+ zu verwenden:
795
-
796
- halt erb(:error)
797
-
798
- == Weiterspringen
799
-
800
- Eine Route kann mittels <tt>pass</tt> zu der nächsten passenden Route springen:
801
-
802
- get '/raten/:wer' do
803
- pass unless params[:wer] == 'Frank'
804
- 'Du hast mich!'
805
- end
806
-
807
- get '/raten/*' do
808
- 'Du hast mich nicht!'
809
- end
810
-
811
- Der Block wird sofort verlassen und es wird nach der nächsten treffenden Route
812
- gesucht. Ein 404-Fehler wird zurückgegeben, wenn kein treffendes Routen-Muster
813
- gefunden wird.
814
-
815
- === Eine andere Route ansteuern
816
-
817
- Manchmal entspricht +pass+ nicht den Anforderungen, wenn das Ergebnis einer
818
- anderen Route gefordert wird. Um das zu erreichen, lässt sich +call+ nutzen:
819
-
820
- get '/foo' do
821
- status, headers, body = call env.merge("PATH_INFO" => '/bar')
822
- [status, headers, body.map(&:upcase)]
823
- end
824
-
825
- get '/bar' do
826
- "bar"
827
- end
828
-
829
- Beachte, dass in dem oben angegeben Beispiel die Performance erheblich erhöht
830
- werden kann, wenn <tt>"bar"</tt> in eine Helfer-Methode umgewandelt wird, auf
831
- die <tt>/foo</tt> und <tt>/bar</tt> zugreifen können.
832
-
833
- Wenn der Request innerhalb derselben Applikations-Instanz aufgerufen und keine
834
- Kopie der Instanz erzeugt werden soll, kann <tt>call!</tt> anstelle von
835
- +call+ verwendet werden.
836
-
837
- Die Rack-Spezifikationen enthalten weitere Informationen zu +call+.
838
-
839
- === Body, Status-Code und Header setzen
840
-
841
- Es ist möglich und empfohlen, den Status-Code sowie den Response-Body mit einem
842
- Returnwert in der Route zu setzen. In manchen Situationen kann es jedoch sein,
843
- dass der Body an irgendeiner anderen Stelle während der Ausführung gesetzt
844
- wird. Das lässt sich mit der Helfer-Methode +body+ bewerkstelligen. Wird +body+
845
- verwendet, lässt sich der Body jederzeit über diese Methode aufrufen:
846
-
847
- get '/foo' do
848
- body "bar"
849
- end
850
-
851
- after do
852
- puts body
853
- end
854
-
855
- Ebenso ist es möglich, einen Block an +body+ weiterzureichen, der dann vom
856
- Rack-Handler ausgeführt wird (lässt sich z.B. zur Umsetzung von Streaming
857
- einsetzen, siehe auch "Rückgabewerte").
858
-
859
- Vergleichbar mit +body+ lassen sich auch Status-Code und Header setzen:
860
-
861
- get '/foo' do
862
- status 418
863
- headers \
864
- "Allow" => "BREW, POST, GET, PROPFIND, WHEN",
865
- "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
866
- halt "Ich bin ein Teekesselchen"
867
- end
868
-
869
- Genau wie bei +body+ liest ein Aufrufen von +headers+ oder +status+ ohne
870
- Argumente den aktuellen Wert aus.
871
-
872
- === Response-Streams
873
-
874
- In manchen Situationen sollen Daten bereits an den Client zurückgeschickt
875
- werden, bevor ein vollständiger Response bereit steht. Manchmal will man die
876
- Verbindung auch erst dann beenden und Daten so lange an den Client
877
- zurückschicken, bis er die Verbindung abbricht. Für diese Fälle gibt es die
878
- +stream+-Helfer-Methode, die es einem erspart eigene Lösungen zu schreiben:
879
-
880
- get '/' do
881
- stream do |out|
882
- out << "Das ist ja mal wieder fanta -\n"
883
- sleep 0.5
884
- out << " (bitte warten…) \n"
885
- sleep 1
886
- out << "- stisch!\n"
887
- end
888
- end
889
-
890
- Damit lassen sich Streaming-APIs realisieren, sog.
891
- {Server Sent Events}[http://dev.w3.org/html5/eventsource/] die als Basis für
892
- {WebSockets}[http://en.wikipedia.org/wiki/WebSocket] dienen. Ebenso können sie
893
- verwendet werden, um den Durchsatz zu erhöhen, wenn ein Teil der Daten von
894
- langsamen Ressourcen abhängig ist.
895
-
896
- Es ist zu beachten, dass das Verhalten beim Streaming, insbesondere die Anzahl
897
- nebenläufiger Anfragen, stark davon abhängt, welcher Webserver für die
898
- Applikation verwendet wird. Einige Server, z.B. WEBRick, unterstützen Streaming
899
- nicht oder nur teilweise. Sollte der Server Streaming nicht unterstützen, wird
900
- ein vollständiger Response-Body zurückgeschickt, sobald der an +stream+
901
- weitergegebene Block abgearbeitet ist. Mit Shotgun funktioniert Streaming z.B.
902
- überhaupt nicht.
903
-
904
- Ist der optionale Parameter +keep_open+ aktiviert, wird beim gestreamten Objekt
905
- +close+ nicht aufgerufen und es ist einem überlassen dies an einem beliebigen
906
- späteren Zeitpunkt nachholen. Die Funktion ist jedoch nur bei Event-gesteuerten
907
- Serven wie Thin oder Rainbows möglich, andere Server werden trotzdem den Stream
908
- beenden:
909
-
910
- set :server, :thin
911
- connections = []
912
-
913
- get '/' do
914
- # Den Stream offen halten
915
- stream(:keep_open) { |out| connections << out }
916
- end
917
-
918
- post '/' do
919
- # In alle offenen Streams schreiben
920
- connections.each { |out| out << params[:message] << "\n" }
921
- "Nachricht verschickt"
922
- end
923
-
924
- === Logger
925
-
926
- Im Geltungsbereich eines Request stellt die +logger+ Helfer-Methode eine
927
- +Logger+ Instanz zur Verfügung:
928
-
929
- get '/' do
930
- logger.info "es passiert gerade etwas"
931
- # ...
932
- end
933
-
934
- Der Logger übernimmt dabei automatisch alle im Rack-Handler eingestellten Log-
935
- Vorgaben. Ist Loggen ausgeschaltet, gibt die Methode ein Leerobjekt zurück.
936
- In den Routen und Filtern muss man sich also nicht weiter darum kümmern.
937
-
938
- Beachte, dass das Loggen standardmäßig nur für <tt>Sinatra::Application</tt>
939
- voreingestellt ist. Wird über <tt>Sinatra::Base</tt> vererbt, muss es erst
940
- aktiviert werden:
941
-
942
- class MyApp < Sinatra::Base
943
- configure :production, :development do
944
- enable :logging
945
- end
946
- end
947
-
948
- Damit auch keine Middleware das Logging aktivieren kann, muss die +logging+
949
- Einstellung auf +nil+ gesetzt werden. Das heißt aber auch, dass +logger+ in
950
- diesem Fall +nil+ zurückgeben wird. Üblicherweise wird das eingesetzt, wenn ein
951
- eigener Logger eingerichtet werden soll. Sinatra wird dann verwenden, was in
952
- <tt>env['rack.logger']</tt> eingetragen ist.
953
-
954
- == Mime-Types
955
-
956
- Wenn <tt>send_file</tt> oder statische Dateien verwendet werden, kann es
957
- vorkommen, dass Sinatra den Mime-Typ nicht kennt. Registriert wird dieser mit
958
- +mime_type+ per Dateiendung:
959
-
960
- configure do
961
- mime_type :foo, 'text/foo'
962
- end
963
-
964
- Es kann aber auch der +content_type+-Helfer verwendet werden:
965
-
966
- get '/' do
967
- content_type :foo
968
- "foo foo foo"
969
- end
970
-
971
- === URLs generieren
972
-
973
- Zum Generieren von URLs sollte die +url+-Helfer-Methode genutzen werden, so
974
- z.B. beim Einsatz von Haml:
975
-
976
- %a{:href => url('/foo')} foo
977
-
978
- Soweit vorhanden, wird Rücksicht auf Proxys und Rack-Router genommen.
979
-
980
- Diese Methode ist ebenso über das Alias +to+ zu erreichen (siehe Beispiel
981
- unten).
982
-
983
- === Browser-Umleitung
984
-
985
- Eine Browser-Umleitung kann mithilfe der +redirect+-Helfer-Methode erreicht
986
- werden:
987
-
988
- get '/foo' do
989
- redirect to('/bar')
990
- end
991
-
992
- Weitere Parameter werden wie Argumente der +halt+-Methode behandelt:
993
-
994
- redirect to('/bar'), 303
995
- redirect 'http://google.com', 'Hier bist du falsch'
996
-
997
- Ebenso leicht lässt sich ein Schritt zurück mit dem Alias
998
- <tt>redirect back</tt> erreichen:
999
-
1000
- get '/foo' do
1001
- "<a href='/bar'>mach was</a>"
1002
- end
1003
-
1004
- get '/bar' do
1005
- mach_was
1006
- redirect back
1007
- end
1008
-
1009
- Um Argumente an ein Redirect weiterzugeben, können sie entweder dem Query
1010
- übergeben:
1011
-
1012
- redirect to('/bar?summe=42')
1013
-
1014
- oder eine Session verwendet werden:
1015
-
1016
- enable :sessions
1017
-
1018
- get '/foo' do
1019
- session[:secret] = 'foo'
1020
- redirect to('/bar')
1021
- end
1022
-
1023
- get '/bar' do
1024
- session[:secret]
1025
- end
1026
-
1027
-
1028
- === Cache einsetzen
1029
-
1030
- Ein sinnvolles Einstellen von Header-Daten ist die Grundlage für ein
1031
- ordentliches HTTP-Caching.
1032
-
1033
- Der Cache-Control-Header lässt sich ganz einfach einstellen:
1034
-
1035
- get '/' do
1036
- cache_control :public
1037
- "schon gecached!"
1038
- end
1039
-
1040
- Profitipp: Caching im before-Filter aktivieren
1041
-
1042
- before do
1043
- cache_control :public, :must_revalidate, :max_age => 60
1044
- end
1045
-
1046
- Bei Verwendung der +expires+-Helfermethode zum Setzen des gleichnamigen
1047
- Headers, wird <tt>Cache-Control</tt> automatisch eigestellt:
1048
-
1049
- before do
1050
- expires 500, :public, :must_revalidate
1051
- end
1052
-
1053
- Um alles richtig zu machen, sollten auch +etag+ oder +last_modified+ verwendet
1054
- werden. Es wird empfohlen, dass diese Helfer aufgerufen werden *bevor* die
1055
- eigentliche Arbeit anfängt, da sie sofort eine Antwort senden, wenn der
1056
- Client eine aktuelle Version im Cache vorhält:
1057
-
1058
- get '/article/:id' do
1059
- @article = Article.find params[:id]
1060
- last_modified @article.updated_at
1061
- etag @article.sha1
1062
- erb :article
1063
- end
1064
-
1065
- ebenso ist es möglich einen
1066
- {schwachen ETag}[http://de.wikipedia.org/wiki/HTTP_ETag] zu verwenden:
1067
-
1068
- etag @article.sha1, :weak
1069
-
1070
- Diese Helfer führen nicht das eigentliche Caching aus, sondern geben die dafür
1071
- notwendigen Informationen an den Cache weiter. Für schnelle Reverse-Proxy
1072
- Cache-Lösungen bietet sich z.B.
1073
- {rack-cache}[https://github.com/rtomayko/rack-cache] an:
1074
-
1075
- require "rack/cache"
1076
- require "sinatra"
1077
-
1078
- use Rack::Cache
1079
-
1080
- get '/' do
1081
- cache_control :public, :max_age => 36000
1082
- sleep 5
1083
- "hello"
1084
- end
1085
-
1086
- Um den <tt>Cache-Control</tt>-Header mit Informationen zu versorgen, verwendet
1087
- man die <tt>:static_cache_control</tt>-Einstellung (s.u.).
1088
-
1089
- Nach RFC 2616 sollte sich die Anwendung anders verhalten, wenn ein
1090
- If-Match oder ein If-None_match Header auf <tt>*</tt> gesetzt wird in
1091
- Abhängigkeit davon, ob die Resource bereits existiert. Sinatra geht
1092
- davon aus, dass Ressourcen bei sicheren Anfragen (z.B. bei get oder Idempotenten
1093
- Anfragen wie put) bereits existieren, wobei anderen Ressourcen
1094
- (besipielsweise bei post), als neue Ressourcen behandelt werden. Dieses
1095
- Verhalten lässt sich mit der <tt>:new_resource</tt> Option ändern:
1096
-
1097
- get '/create' do
1098
- etag '', :new_resource => true
1099
- Article.create
1100
- erb :new_article
1101
- end
1102
-
1103
- Soll das schwache ETag trotzdem verwendet werden, verwendet man die
1104
- <tt>:kind</tt> Option:
1105
-
1106
- etag '', :new_resource => true, :kind => :weak
1107
-
1108
- === Dateien versenden
1109
-
1110
- Zum Versenden von Dateien kann die <tt>send_file</tt>-Helfer-Methode verwendet
1111
- werden:
1112
-
1113
- get '/' do
1114
- send_file 'foo.png'
1115
- end
1116
-
1117
- Für <tt>send_file</tt> stehen einige Hash-Optionen zur Verfügung:
1118
-
1119
- send_file 'foo.png', :type => :jpg
1120
-
1121
- [filename]
1122
- Dateiname als Response. Standardwert ist der eigentliche Dateiname.
1123
-
1124
- [last_modified]
1125
- Wert für den Last-Modified-Header, Standardwert ist +mtime+ der Datei.
1126
-
1127
- [type]
1128
- Content-Type, der verwendet werden soll. Wird, wenn nicht angegeben, von der
1129
- Dateiendung abgeleitet.
1130
-
1131
- [disposition]
1132
- Verwendet für Content-Disposition. Mögliche Werte sind: +nil+ (Standard),
1133
- <tt>:attachment</tt> und <tt>:inline</tt>.
1134
-
1135
- [length]
1136
- Content-Length-Header. Standardwert ist die Dateigröße.
1137
-
1138
- Soweit vom Rack-Handler unterstützt, werden neben der Übertragung über den
1139
- Ruby-Prozess auch andere Möglichkeiten genutzt. Bei Verwendung der
1140
- <tt>send_file</tt>-Helfer-Methode kümmert sich Sinatra selbstständig um die
1141
- Range-Requests.
1142
-
1143
- == Das Request-Objekt
1144
-
1145
- Auf das +request+-Objekt der eigehenden Anfrage kann vom Anfrage-Scope aus
1146
- zugegriffen werden:
1147
-
1148
- # App läuft unter http://example.com/example
1149
- get '/foo' do
1150
- t = %w[text/css text/html application/javascript]
1151
- request.accept # ['text/html', '*/*']
1152
- request.accept? 'text/xml' # true
1153
- request.preferred_type(t) # 'text/html'
1154
- request.body # Request-Body des Client (siehe unten)
1155
- request.scheme # "http"
1156
- request.script_name # "/example"
1157
- request.path_info # "/foo"
1158
- request.port # 80
1159
- request.request_method # "GET"
1160
- request.query_string # ""
1161
- request.content_length # Länge des request.body
1162
- request.media_type # Medientypus von request.body
1163
- request.host # "example.com"
1164
- request.get? # true (ähnliche Methoden für andere Verben)
1165
- request.form_data? # false
1166
- request["IRGENDEIN_HEADER"] # Wert von IRGENDEIN_HEADER header
1167
- request.referrer # Der Referrer des Clients oder '/'
1168
- request.user_agent # User-Agent (verwendet in der :agent Bedingung)
1169
- request.cookies # Hash des Browser-Cookies
1170
- request.xhr? # Ist das hier ein Ajax-Request?
1171
- request.url # "http://example.com/example/foo"
1172
- request.path # "/example/foo"
1173
- request.ip # IP-Adresse des Clients
1174
- request.secure? # false (true wenn SSL)
1175
- request.forwarded? # true (Wenn es hinter einem Reverse-Proxy verwendet wird)
1176
- request.env # vollständiger env-Hash von Rack übergeben
1177
- end
1178
-
1179
- Manche Optionen, wie etwa <tt>script_name</tt> oder <tt>path_info</tt>, sind
1180
- auch schreibbar:
1181
-
1182
- before { request.path_info = "/" }
1183
-
1184
- get "/" do
1185
- "Alle Anfragen kommen hier an!"
1186
- end
1187
-
1188
- Der <tt>request.body</tt> ist ein IO- oder StringIO-Objekt:
1189
-
1190
- post "/api" do
1191
- request.body.rewind # falls schon jemand davon gelesen hat
1192
- daten = JSON.parse request.body.read
1193
- "Hallo #{daten['name']}!"
1194
- end
1195
-
1196
- === Anhänge
1197
-
1198
- Damit der Browser erkennt, dass ein Response gespeichert und nicht im Browser
1199
- angezeigt werden soll, kann der +attachment+-Helfer verwendet werden:
1200
-
1201
- get '/' do
1202
- attachment
1203
- "Speichern!"
1204
- end
1205
-
1206
- Ebenso kann eine Dateiname als Parameter hinzugefügt werden:
1207
-
1208
- get '/' do
1209
- attachment "info.txt"
1210
- "Speichern!"
1211
- end
1212
-
1213
- === Umgang mit Datum und Zeit
1214
-
1215
- Sinatra bietet eine <tt>time_for</tt>-Helfer-Methode, die aus einem gegebenen
1216
- Wert ein Time-Objekt generiert. Ebenso kann sie nach +DateTime+, +Date+ und
1217
- ähnliche Klassen konvertieren:
1218
-
1219
- get '/' do
1220
- pass if Time.now > time_for('Dec 23, 2012')
1221
- "noch Zeit"
1222
- end
1223
-
1224
- Diese Methode wird intern für +expires, +last_modiefied+ und Freunde verwendet.
1225
- Mit ein paar Handgriffen lässt sich diese Methode also in ihrem Verhalten
1226
- erweitern, indem man +time_for+ in der eigenen Applikation überschreibt:
1227
-
1228
- helpers do
1229
- def time_for(value)
1230
- case value
1231
- when :yesterday then Time.now - 24*60*60
1232
- when :tomorrow then Time.now + 24*60*60
1233
- else super
1234
- end
1235
- end
1236
- end
1237
-
1238
- get '/' do
1239
- last_modified :yesterday
1240
- expires :tomorrow
1241
- "Hallo"
1242
- end
1243
-
1244
- === Nachschlagen von Template-Dateien
1245
-
1246
- Die <tt>find_template</tt>-Helfer-Methode wird genutzt, um Template-Dateien zum
1247
- Rendern aufzufinden:
1248
-
1249
- find_template settings.views, 'foo', Tilt[:haml] do |file|
1250
- puts "könnte diese hier sein: #{file}"
1251
- end
1252
-
1253
- Das ist zwar nicht wirklich brauchbar, aber wenn man sie überschreibt, kann sie
1254
- nützlich werden, um eigene Nachschlage-Mechanismen einzubauen. Zum Beispiel
1255
- dann, wenn mehr als nur ein view-Verzeichnis verwendet werden soll:
1256
-
1257
- set :views, ['views', 'templates']
1258
-
1259
- helpers do
1260
- def find_template(views, name, engine, &block)
1261
- Array(views).each { |v| super(v, name, engine, &block) }
1262
- end
1263
- end
1264
-
1265
- Ein anderes Beispiel wäre, verschiedene Vereichnisse für verschiedene Engines
1266
- zu verwenden:
1267
-
1268
- set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views'
1269
-
1270
- helpers do
1271
- def find_template(views, name, engine, &block)
1272
- _, folder = views.detect { |k,v| engine == Tilt[k] }
1273
- folder ||= views[:default]
1274
- super(folder, name, engine, &block)
1275
- end
1276
- end
1277
-
1278
- Ebensogut könnte eine Extension aber auch geschrieben und mit anderen geteilt
1279
- werden!
1280
-
1281
- Beachte, dass <tt>find_template</tt> nicht prüft, ob eine Datei tatsächlich
1282
- existiert. Es wird lediglich der angegebene Block aufgerufen und nach allen
1283
- möglichen Pfaden gesucht. Das ergibt kein Performance-Problem, da +render+
1284
- +block+ verwendet, sobald eine Datei gefunden wurde. Ebenso werden
1285
- Template-Pfade samt Inhalt gecached, solange nicht im Entwicklungsmodus
1286
- gearbeitet wird. Das sollte im Hinterkopf behalten werden, wenn irgendwelche
1287
- verrückten Methoden zusammenbastelt werden.
1288
-
1289
- == Konfiguration
1290
-
1291
- Wird einmal beim Starten in jedweder Umgebung ausgeführt:
1292
-
1293
- configure do
1294
- # setze eine Option
1295
- set :option, 'wert'
1296
-
1297
- # setze mehrere Optionen
1298
- set :a => 1, :b => 2
1299
-
1300
- # das gleiche wie `set :option, true`
1301
- enable :option
1302
-
1303
- # das gleiche wie `set :option, false`
1304
- disable :option
1305
-
1306
- # dynamische Einstellungen mit Blöcken
1307
- set(:css_dir) { File.join(views, 'css') }
1308
- end
1309
-
1310
- Läuft nur, wenn die Umgebung (RACK_ENV-Umgebungsvariable) auf
1311
- <tt>:production</tt> gesetzt ist:
1312
-
1313
- configure :production do
1314
- ...
1315
- end
1316
-
1317
- Läuft nur, wenn die Umgebung auf <tt>:production</tt> oder auf <tt>:test</tt>
1318
- gesetzt ist:
1319
-
1320
- configure :production, :test do
1321
- ...
1322
- end
1323
-
1324
- Diese Einstellungen sind über +settings+ erreichbar:
1325
-
1326
- configure do
1327
- set :foo, 'bar'
1328
- end
1329
-
1330
- get '/' do
1331
- settings.foo? # => true
1332
- settings.foo # => 'bar'
1333
- ...
1334
- end
1335
-
1336
- === Einstellung des Angriffsschutzes
1337
-
1338
- Sinatra verwendet
1339
- {Rack::Protection}[https://github.com/rkh/rack-protection#readme], um die
1340
- Anwendung vor häufig vorkommenden Angriffen zu schützen. Diese Voreinstellung
1341
- lässt sich selbstverständlich deaktivieren, der damit verbundene
1342
- Geschwindigkeitszuwachs steht aber in keinem Verhätnis zu den möglichen
1343
- Risiken.
1344
-
1345
- disable :protection
1346
-
1347
- Um einen bestimmten Schutzmechanismus zu deaktivieren, fügt man +protection+
1348
- einen Hash mit Optionen hinzu:
1349
-
1350
- set :protection, :except => :path_traversal
1351
-
1352
- Neben Strings akzeptiert <tt>:except</tt> auch Arrays, um gleich mehrere
1353
- Schutzmechanismen zu deaktivieren:
1354
-
1355
- set :protection, :except => [:path_traversal, :session_hijacking]
1356
-
1357
- === Mögliche Einstellungen
1358
-
1359
- [absolute_redirects] Wenn ausgeschaltet, wird Sinatra relative Redirects
1360
- zulassen. Jedoch ist Sinatra dann nicht mehr mit RFC
1361
- 2616 (HTTP 1.1) konform, das nur absolute Redirects
1362
- zulässt.
1363
-
1364
- Sollte eingeschaltet werden, wenn die Applikation
1365
- hinter einem Reverse-Proxy liegt, der nicht ordentlich
1366
- eingerichtet ist. Beachte, dass die
1367
- +url+-Helfer-Methode nach wie vor absolute URLs
1368
- erstellen wird, es sei denn, es wird als zweiter
1369
- Parameter +false+ angegeben.
1370
-
1371
- Standardmäßig nicht aktiviert.
1372
-
1373
- [add_charsets] Mime-Types werden hier automatisch der Helfer-Methode
1374
- <tt>content_type</tt> zugeordnet.
1375
-
1376
- Es empfielt sich, Werte hinzuzufügen statt sie zu
1377
- überschreiben:
1378
-
1379
- settings.add_charsets << "application/foobar"
1380
-
1381
- [app_file] Pfad zur Hauptdatei der Applikation. Wird verwendet, um
1382
- das Wurzel-, Inline-, View- und öffentliche Verzeichnis
1383
- des Projekts festzustellen.
1384
-
1385
- [bind] IP-Address, an die gebunden wird
1386
- (Standardwert: 0.0.0.0). Wird nur für den eingebauten
1387
- Server verwendet.
1388
-
1389
- [default_encoding] Das Encoding, falls keines angegeben wurde.
1390
- Standardwert ist <tt>"utf-8"</tt>.
1391
-
1392
- [dump_errors] Fehler im Log anzeigen.
1393
-
1394
- [environment] Momentane Umgebung. Standardmäßig auf
1395
- <tt>content_type</tt> oder <tt>"development"</tt>
1396
- eingestellt, soweit ersteres nicht vorhanden.
1397
-
1398
- [logging] Den Logger verwenden.
1399
-
1400
- [lock] Jeder Request wird gelocked. Es kann nur ein Request
1401
- pro Ruby-Prozess gleichzeitig verarbeitet werden.
1402
-
1403
- Eingeschaltet, wenn die Applikation threadsicher ist.
1404
- Standardmäßig nicht aktiviert.
1405
-
1406
- [method_override] Verwende <tt>_method</tt>, um put/delete-Formulardaten
1407
- in Browsern zu verwenden, die dies normalerweise nicht
1408
- unterstützen.
1409
-
1410
- [port] Port für die Applikation. Wird nur im internen Server
1411
- verwendet.
1412
-
1413
- [prefixed_redirects] Entscheidet, ob <tt>request.script_name</tt> in
1414
- Redirects eingefügt wird oder nicht, wenn kein
1415
- absoluter Pfad angegeben ist. Auf diese Weise verhält
1416
- sich <tt>redirect '/foo'</tt> so, als wäre es ein
1417
- <tt>redirect to('/foo')</tt>. Standardmäßig nicht
1418
- aktiviert.
1419
-
1420
- [protection] Legt fest, ob der Schutzmechanismus für häufig
1421
- Vorkommende Webangriffe auf Webapplikationen aktiviert
1422
- wird oder nicht. Weitere Informationen im vorhergehenden
1423
- Abschnitt.
1424
-
1425
- [public_folder] Das öffentliche Verzeichnis, aus dem Daten zur
1426
- Verfügung gestellt werden können. Wird nur dann
1427
- verwendet, wenn statische Daten zur Verfügung
1428
- gestellt werden können (s.u. <tt>static</tt>
1429
- Option). Leitet sich von der <tt>app_file</tt>
1430
- Einstellung ab, wenn nicht gesetzt.
1431
-
1432
- [public_dir] Alias für <tt>public_folder</tt>, s.o.
1433
-
1434
- [reload_templates] Im development-Modus aktiviert.
1435
-
1436
- [root] Wurzelverzeichnis des Projekts. Leitet sich von der
1437
- <tt>app_file</tt> Einstellung ab, wenn nicht gesetzt.
1438
-
1439
- [raise_errors] Einen Ausnahmezustand aufrufen. Beendet die
1440
- Applikation. Ist automatisch aktiviert, wenn die
1441
- Umgebung auf <tt>"test"</tt> eingestellt ist.
1442
- Ansonsten ist diese Option deaktiviert.
1443
-
1444
- [run] Wenn aktiviert, wird Sinatra versuchen, den Webserver
1445
- zu starten. Nicht verwenden, wenn Rackup oder anderes
1446
- verwendet werden soll.
1447
-
1448
- [running] Läuft der eingebaute Server? Diese Einstellung nicht
1449
- ändern!
1450
-
1451
- [server] Server oder Liste von Servern, die als eingebaute
1452
- Server zur Verfügung stehen.
1453
- Standardmäßig auf ['thin', 'mongrel', 'webrick']
1454
- voreingestellt. Die Anordnung gibt die Priorität vor.
1455
-
1456
- [sessions] Sessions auf Cookiebasis mittels
1457
- <tt>Rack::Session::Cookie</tt>aktivieren. Für
1458
- weitere Infos bitte in der Sektion 'Sessions
1459
- verwenden' nachschauen.
1460
-
1461
- [show_exceptions] Bei Fehlern einen Stacktrace im Browseranzeigen. Ist
1462
- automatisch aktiviert, wenn die Umgebung auf
1463
- <tt>"development"</tt> eingestellt ist. Ansonsten ist
1464
- diese Option deaktiviert.
1465
- Kann auch auf <tt>:after_handler</tt> gestellt werden,
1466
- um eine anwendungsspezifische Fehlerbehandlung
1467
- auszulösen, bevor der Fehlerverlauf im Browser
1468
- angezeigt wird.
1469
-
1470
- [static] Entscheidet, ob Sinatra statische Dateien zur Verfügung
1471
- stellen soll oder nicht.
1472
- Sollte nicht aktiviert werden, wenn ein Server
1473
- verwendet wird, der dies auch selbstständig erledigen
1474
- kann. Deaktivieren wird die Performance erhöhen.
1475
- Standardmäßig aktiviert.
1476
-
1477
- [static_cache_control] Wenn Sinatra statische Daten zur Verfügung stellt,
1478
- können mit dieser Einstellung die +Cache-Control+
1479
- Header zu den Responses hinzugefügt werden. Die
1480
- Einstellung verwendet dazu die +cache_control+
1481
- Helfer-Methode. Standardmäßig deaktiviert.
1482
- Ein Array wird verwendet, um mehrere Werte gleichzeitig
1483
- zu übergeben:
1484
- <tt>set :static_cache_control, [:public, :max_age => 300]</tt>
1485
-
1486
- [views] Verzeichnis der Views. Leitet sich von der
1487
- <tt>app_file</tt> Einstellung ab, wenn nicht gesetzt.
1488
-
1489
- == Umgebungen
1490
-
1491
- Es gibt drei voreingestellte Umgebungen in Sinatra: <tt>"development"</tt>,
1492
- <tt>"production"</tt> und <tt>"test"</tt>. Umgebungen können über die
1493
- +RACK_ENV+ Umgebungsvariable gesetzt werden. Die Standardeinstellung ist
1494
- <tt>"development"</tt>. In diesem Modus werden alle Templates zwischen
1495
- Requests neu geladen. Dazu gibt es besondere Fehlerseiten für 404 Stati
1496
- und Fehlermeldungen. In <tt>"production"</tt> und <tt>"test"</tt> werden
1497
- Templates automatisch gecached.
1498
-
1499
- Um die Anwendung in einer anderen Umgebung auszuführen kann man die
1500
- <tt>-e</tt> Option verwenden:
1501
-
1502
- ruby my_app.rb -e [ENVIRONMENT]
1503
-
1504
- In der Anwendung kann man die die Methoden +development?+, +test?+ und
1505
- +production?+ verwenden, um die aktuelle Umgebung zu erfahren.
1506
-
1507
- == Fehlerbehandlung
1508
-
1509
- Error-Handler laufen in demselben Kontext wie Routen und Filter, was bedeutet,
1510
- dass alle Goodies wie <tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt>, etc.
1511
- verwendet werden können.
1512
-
1513
- === Nicht gefunden
1514
-
1515
- Wenn eine <tt>Sinatra::NotFound</tt>-Exception geworfen wird oder der
1516
- Statuscode 404 ist, wird der <tt>not_found</tt>-Handler ausgeführt:
1517
-
1518
- not_found do
1519
- 'Seite kann nirgendwo gefunden werden.'
1520
- end
1521
-
1522
- === Fehler
1523
-
1524
- Der +error+-Handler wird immer ausgeführt, wenn eine Exception in einem
1525
- Routen-Block oder in einem Filter geworfen wurde. Die Exception kann über die
1526
- <tt>sinatra.error</tt>-Rack-Variable angesprochen werden:
1527
-
1528
- error do
1529
- 'Entschuldige, es gab einen hässlichen Fehler - ' + env['sinatra.error'].name
1530
- end
1531
-
1532
- Benutzerdefinierte Fehler:
1533
-
1534
- error MeinFehler do
1535
- 'Au weia, ' + env['sinatra.error'].message
1536
- end
1537
-
1538
- Dann, wenn das passiert:
1539
-
1540
- get '/' do
1541
- raise MeinFehler, 'etwas Schlimmes ist passiert'
1542
- end
1543
-
1544
- bekommt man dieses:
1545
-
1546
- Au weia, etwas Schlimmes ist passiert
1547
-
1548
- Alternativ kann ein Error-Handler auch für einen Status-Code definiert werden:
1549
-
1550
- error 403 do
1551
- 'Zugriff verboten'
1552
- end
1553
-
1554
- get '/geheim' do
1555
- 403
1556
- end
1557
-
1558
- Oder ein Status-Code-Bereich:
1559
-
1560
- error 400..510 do
1561
- 'Hallo?'
1562
- end
1563
-
1564
- Sinatra setzt verschiedene <tt>not_found</tt>- und <tt>error</tt>-Handler in
1565
- der Development-Umgebung.
1566
-
1567
- == Rack-Middleware
1568
-
1569
- Sinatra baut auf Rack[http://rack.rubyforge.org/], einem minimalistischen
1570
- Standard-Interface für Ruby-Webframeworks. Eines der interessantesten
1571
- Features für Entwickler ist der Support von Middlewares, die zwischen den
1572
- Server und die Anwendung geschaltet werden und so HTTP-Request und/oder Antwort
1573
- überwachen und/oder manipulieren können.
1574
-
1575
- Sinatra macht das Erstellen von Middleware-Verkettungen mit der
1576
- Top-Level-Methode +use+ zu einem Kinderspiel:
1577
-
1578
- require 'sinatra'
1579
- require 'meine_middleware'
1580
-
1581
- use Rack::Lint
1582
- use MeineMiddleware
1583
-
1584
- get '/hallo' do
1585
- 'Hallo Welt'
1586
- end
1587
-
1588
- Die Semantik von +use+ entspricht der gleichnamigen Methode der
1589
- Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html]-DSL
1590
- (meist verwendet in Rackup-Dateien). Ein Beispiel dafür ist, dass die
1591
- +use+-Methode mehrere/verschiedene Argumente und auch Blöcke entgegennimmt:
1592
-
1593
- use Rack::Auth::Basic do |username, password|
1594
- username == 'admin' && password == 'geheim'
1595
- end
1596
-
1597
- Rack bietet eine Vielzahl von Standard-Middlewares für Logging, Debugging,
1598
- URL-Routing, Authentifizierung und Session-Verarbeitung. Sinatra verwendet
1599
- viele von diesen Komponenten automatisch, abhängig von der Konfiguration. So
1600
- muss +use+ häufig nicht explizit verwendet werden.
1601
-
1602
- Hilfreiche Middleware gibt es z.B. hier:
1603
- {rack}[https://github.com/rack/rack/tree/master/lib/rack],
1604
- {rack-contrib}[https://github.com/rack/rack-contrib#readme],
1605
- mit {CodeRack}[http://coderack.org/] oder im
1606
- {Rack wiki}[https://github.com/rack/rack/wiki/List-of-Middleware].
1607
-
1608
- == Testen
1609
-
1610
- Sinatra-Tests können mit jedem auf Rack aufbauendem Test-Framework geschrieben
1611
- werden. {Rack::Test}[http://rdoc.info/github/brynary/rack-test/master/frames]
1612
- wird empfohlen:
1613
-
1614
- require 'my_sinatra_app'
1615
- require 'test/unit'
1616
- require 'rack/test'
1617
-
1618
- class MyAppTest < Test::Unit::TestCase
1619
- include Rack::Test::Methods
1620
-
1621
- def app
1622
- Sinatra::Application
1623
- end
1624
-
1625
- def test_my_default
1626
- get '/'
1627
- assert_equal 'Hallo Welt!', last_response.body
1628
- end
1629
-
1630
- def test_with_params
1631
- get '/meet', :name => 'Frank'
1632
- assert_equal 'Hallo Frank!', last_response.body
1633
- end
1634
-
1635
- def test_with_rack_env
1636
- get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
1637
- assert_equal "Du verwendest Songbird!", last_response.body
1638
- end
1639
- end
1640
-
1641
- == Sinatra::Base - Middleware, Bibliotheken und modulare Anwendungen
1642
-
1643
- Das Definieren einer Top-Level-Anwendung funktioniert gut für
1644
- Mikro-Anwendungen, hat aber Nachteile, wenn wiederverwendbare Komponenten wie
1645
- Middleware, Rails Metal, einfache Bibliotheken mit Server-Komponenten oder auch
1646
- Sinatra-Erweiterungen geschrieben werden sollen.
1647
-
1648
- Das Top-Level geht von einer Konfiguration für eine Mikro-Anwendung aus
1649
- (wie sie z.B. bei einer einzelnen Anwendungsdatei, <tt>./public</tt>
1650
- und <tt>./views</tt> Ordner, Logging, Exception-Detail-Seite, usw.). Genau
1651
- hier kommt <tt>Sinatra::Base</tt> ins Spiel:
1652
-
1653
- require 'sinatra/base'
1654
-
1655
- class MyApp < Sinatra::Base
1656
- set :sessions, true
1657
- set :foo, 'bar'
1658
-
1659
- get '/' do
1660
- 'Hallo Welt!'
1661
- end
1662
- end
1663
-
1664
- Die MyApp-Klasse ist eine unabhängige Rack-Komponente, die als Middleware,
1665
- Endpunkt oder via Rails Metal verwendet werden kann. Verwendet wird sie durch
1666
- +use+ oder +run+ von einer Rackup-<tt>config.ru</tt>-Datei oder als
1667
- Server-Komponente einer Bibliothek:
1668
-
1669
- MyApp.run! :host => 'localhost', :port => 9090
1670
-
1671
- Die Methoden der <tt>Sinatra::Base</tt>-Subklasse sind genau dieselben wie die
1672
- der Top-Level-DSL. Die meisten Top-Level-Anwendungen können mit nur zwei
1673
- Veränderungen zu <tt>Sinatra::Base</tt> konvertiert werden:
1674
-
1675
- * Die Datei sollte <tt>require 'sinatra/base'</tt> anstelle von
1676
- <tt>require 'sinatra/base'</tt> aufrufen, ansonsten werden alle von
1677
- Sinatras DSL-Methoden in den Top-Level-Namespace importiert.
1678
- * Alle Routen, Error-Handler, Filter und Optionen der Applikation müssen in
1679
- einer Subklasse von <tt>Sinatra::Base</tt> definiert werden.
1680
-
1681
- <tt>Sinatra::Base</tt> ist ein unbeschriebenes Blatt. Die meisten Optionen sind
1682
- per Standard deaktiviert. Das betrifft auch den eingebauten Server. Siehe
1683
- {Optionen und Konfiguration}[http://sinatra.github.com/configuration.html] für
1684
- Details über mögliche Optionen.
1685
-
1686
- === Modularer vs. klassischer Stil
1687
-
1688
- Entgegen häufiger Meinungen gibt es nichts gegen den klassischen Stil
1689
- einzuwenden. Solange es die Applikation nicht beeinträchtigt, besteht kein
1690
- Grund, eine modulare Applikation zu erstellen.
1691
-
1692
- Der größte Nachteil der klassischen Sinatra Anwendung gegenüber einer modularen
1693
- ist die Einschränkung auf eine Sinatra Anwendung pro Ruby-Prozess. Sollen
1694
- mehrere zum Einsatz kommen, muss auf den modularen Stil umgestiegen werden.
1695
- Dabei ist es kein Problem klassische und modulare Anwendungen miteinander zu
1696
- vermischen.
1697
-
1698
- Bei einem Umstieg, sollten einige Unterschiede in den Einstellungen beachtet
1699
- werden:
1700
-
1701
- Szenario Classic Modular
1702
-
1703
- app_file sinatra ladende Datei Sinatra::Base subklassierende Datei
1704
- run $0 == app_file false
1705
- logging true false
1706
- method_override true false
1707
- inline_templates true false
1708
-
1709
- === Eine modulare Applikation bereitstellen
1710
-
1711
- Es gibt zwei übliche Wege, eine modulare Anwendung zu starten. Zum einen über
1712
- <tt>run!</tt>:
1713
-
1714
- # mein_app.rb
1715
- require 'sinatra/base'
1716
-
1717
- class MeinApp < Sinatra::Base
1718
- # ... Anwendungscode hierhin ...
1719
-
1720
- # starte den Server, wenn die Ruby-Datei direkt ausgeführt wird
1721
- run! if app_file == $0
1722
- end
1723
-
1724
- Starte mit:
1725
-
1726
- ruby mein_app.rb
1727
-
1728
- Oder über eine <tt>config.ru</tt>-Datei, die es erlaubt, einen beliebigen
1729
- Rack-Handler zu verwenden:
1730
-
1731
- # config.ru
1732
- require './mein_app'
1733
- run MeineApp
1734
-
1735
- Starte:
1736
-
1737
- rackup -p 4567
1738
-
1739
- === Eine klassische Anwendung mit einer config.ru verwenden
1740
-
1741
- Schreibe eine Anwendungsdatei:
1742
-
1743
- # app.rb
1744
- require 'sinatra'
1745
-
1746
- get '/' do
1747
- 'Hallo Welt!'
1748
- end
1749
-
1750
- sowie eine dazugehörige <tt>config.ru</tt>-Datei:
1751
-
1752
- require './app'
1753
- run Sinatra::Application
1754
-
1755
- === Wann sollte eine config.ru-Datei verwendet werden?
1756
-
1757
- Anzeichen dafür, dass eine <tt>config.ru</tt>-Datei gebraucht wird:
1758
-
1759
- * Es soll ein anderer Rack-Handler verwendet werden (Passenger, Unicorn,
1760
- Heroku, ...).
1761
- * Es gibt mehr als nur eine Subklasse von <tt>Sinatra::Base</tt>.
1762
- * Sinatra soll als Middleware verwendet werden, nicht als Endpunkt.
1763
-
1764
- <b>Es gibt keinen Grund, eine <tt>config.ru</tt>-Datei zu verwenden, nur weil
1765
- eine Anwendung im modularen Stil betrieben werden soll. Ebenso wird keine
1766
- Anwendung mit modularem Stil benötigt, um eine <tt>config.ru</tt>-Datei zu
1767
- verwenden.</b>
1768
-
1769
- === Sinatra als Middleware nutzen
1770
-
1771
- Es ist nicht nur möglich, andere Rack-Middleware mit Sinatra zu nutzen, es kann
1772
- außerdem jede Sinatra-Anwendung selbst als Middleware vor jeden beliebigen
1773
- Rack-Endpunkt gehangen werden. Bei diesem Endpunkt muss es sich nicht um eine
1774
- andere Sinatra-Anwendung handeln, es kann jede andere Rack-Anwendung sein
1775
- (Rails/Ramaze/Camping/...):
1776
-
1777
- require 'sinatra/base'
1778
-
1779
- class LoginScreen < Sinatra::Base
1780
- enable :sessions
1781
-
1782
- get('/login') { haml :login }
1783
-
1784
- post('/login') do
1785
- if params[:name] == 'admin' && params[:password] == 'admin'
1786
- session['user_name'] = params[:name]
1787
- else
1788
- redirect '/login'
1789
- end
1790
- end
1791
- end
1792
-
1793
- class MyApp < Sinatra::Base
1794
- # Middleware wird vor Filtern ausgeführt
1795
- use LoginScreen
1796
-
1797
- before do
1798
- unless session['user_name']
1799
- halt "Zugriff verweigert, bitte <a href='/login'>einloggen</a>."
1800
- end
1801
- end
1802
-
1803
- get('/') { "Hallo #{session['user_name']}." }
1804
- end
1805
-
1806
- === Dynamische Applikationserstellung
1807
-
1808
- Manche Situationen erfordern die Erstellung neuer Applikationen zur Laufzeit,
1809
- ohne dass sie einer Konstanten zugeordnet werden. Dies lässt sich mit
1810
- <tt>Sinatra.new</tt> erreichen:
1811
-
1812
- require 'sinatra/base'
1813
- my_app = Sinatra.new { get('/') { "hallo" } }
1814
- my_app.run!
1815
-
1816
- Die Applikation kann mit Hilfe eines optionalen Parameters erstellt werden:
1817
-
1818
- # config.ru
1819
- require 'sinatra/base'
1820
-
1821
- controller = Sinatra.new do
1822
- enable :logging
1823
- helpers MyHelpers
1824
- end
1825
-
1826
- map('/a') do
1827
- run Sinatra.new(controller) { get('/') { 'a' } }
1828
- end
1829
-
1830
- map('/b') do
1831
- run Sinatra.new(controller) { get('/') { 'b' } }
1832
- end
1833
-
1834
- Das ist besonders dann interessant, wenn Sinatra-Erweiterungen getestet werden
1835
- oder Sinatra in einer Bibliothek Verwendung findet.
1836
-
1837
- Ebenso lassen sich damit hervorragend Sinatra-Middlewares erstellen:
1838
-
1839
- require 'sinatra/base'
1840
-
1841
- use Sinatra do
1842
- get('/') { ... }
1843
- end
1844
-
1845
- run RailsProject::Application
1846
-
1847
- == Geltungsbereich und Bindung
1848
-
1849
- Der Geltungsbereich (Scope) legt fest, welche Methoden und Variablen zur
1850
- Verfügung stehen.
1851
-
1852
- === Anwendungs- oder Klassen-Scope
1853
-
1854
- Jede Sinatra-Anwendung entspricht einer <tt>Sinatra::Base</tt>-Subklasse. Falls
1855
- die Top- Level-DSL verwendet wird (<tt>require 'sinatra'</tt>), handelt es sich
1856
- um <tt>Sinatra::Application</tt>, andernfalls ist es jene Subklasse, die
1857
- explizit angelegt wurde. Auf Klassenebene stehen Methoden wie +get+ oder
1858
- +before+ zur Verfügung, es gibt aber keinen Zugriff auf das +request+-Object
1859
- oder die +session+, da nur eine einzige Klasse für alle eingehenden Anfragen
1860
- genutzt wird.
1861
-
1862
- Optionen, die via +set+ gesetzt werden, sind Methoden auf Klassenebene:
1863
-
1864
- class MyApp < Sinatra::Base
1865
- # Hey, ich bin im Anwendungsscope!
1866
- set :foo, 42
1867
- foo # => 42
1868
-
1869
- get '/foo' do
1870
- # Hey, ich bin nicht mehr im Anwendungs-Scope!
1871
- end
1872
- end
1873
-
1874
- Im Anwendungs-Scope befindet man sich:
1875
-
1876
- * In der Anwendungs-Klasse.
1877
- * In Methoden, die von Erweiterungen definiert werden.
1878
- * Im Block, der an +helpers+ übergeben wird.
1879
- * In Procs und Blöcken, die an +set+ übergeben werden.
1880
- * Der an <tt>Sinatra.new</tt> übergebene Block
1881
-
1882
- Auf das Scope-Objekt (die Klasse) kann wie folgt zugegriffen werden:
1883
-
1884
- * Über das Objekt, das an den +configure+-Block übergeben wird (<tt>configure
1885
- { |c| ... }</tt>).
1886
- * +settings+ aus den anderen Scopes heraus.
1887
-
1888
- === Anfrage- oder Instanz-Scope
1889
-
1890
- Für jede eingehende Anfrage wird eine neue Instanz der Anwendungs-Klasse
1891
- erstellt und alle Handler in diesem Scope ausgeführt. Aus diesem Scope
1892
- heraus kann auf +request+ oder +session+ zugegriffen und Methoden wie +erb+
1893
- oder +haml+ aufgerufen werden. Außerdem kann mit der +settings+-Method auf den
1894
- Anwendungs-Scope zugegriffen werden:
1895
-
1896
- class MyApp < Sinatra::Base
1897
- # Hey, ich bin im Anwendungs-Scope!
1898
- get '/neue_route/:name' do
1899
- # Anfrage-Scope für '/neue_route/:name'
1900
- @value = 42
1901
-
1902
- settings.get "/#{params[:name]}" do
1903
- # Anfrage-Scope für "/#{params[:name]}"
1904
- @value # => nil (nicht dieselbe Anfrage)
1905
- end
1906
-
1907
- "Route definiert!"
1908
- end
1909
- end
1910
-
1911
- Im Anfrage-Scope befindet man sich:
1912
-
1913
- * In get/head/post/put/delete-Blöcken
1914
- * In before/after-Filtern
1915
- * In Helfer-Methoden
1916
- * In Templates
1917
-
1918
- === Delegation-Scope
1919
-
1920
- Vom Delegation-Scope aus werden Methoden einfach an den Klassen-Scope
1921
- weitergeleitet. Dieser verhält sich jedoch nicht 100%ig wie der Klassen-Scope,
1922
- da man nicht die Bindung der Klasse besitzt: Nur Methoden, die explizit als
1923
- delegierbar markiert wurden, stehen hier zur Verfügung und es kann nicht auf
1924
- die Variablen des Klassenscopes zugegriffen werden (mit anderen Worten: es gibt
1925
- ein anderes +self+). Weitere Delegationen können mit
1926
- <tt>Sinatra::Delegator.delegate :methoden_name</tt> hinzugefügt werden.
1927
-
1928
- Im Delegation-Scop befindet man sich:
1929
-
1930
- * Im Top-Level, wenn <tt>require 'sinatra'</tt> aufgerufen wurde.
1931
- * In einem Objekt, das mit dem <tt>Sinatra::Delegator</tt>-Mixin erweitert
1932
- wurde.
1933
-
1934
- Schau am besten im Code nach: Hier ist
1935
- {Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1064]
1936
- definiert und wird in den
1937
- {globalen Namespace eingebunden}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb#L25].
1938
-
1939
- == Kommandozeile
1940
-
1941
- Sinatra-Anwendungen können direkt von der Kommandozeile aus gestartet werden:
1942
-
1943
- ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-h HOST] [-s HANDLER]
1944
-
1945
- Die Optionen sind:
1946
-
1947
- -h # Hilfe
1948
- -p # Port setzen (Standard ist 4567)
1949
- -h # Host setzen (Standard ist 0.0.0.0)
1950
- -e # Umgebung setzen (Standard ist development)
1951
- -s # Rack-Server/Handler setzen (Standard ist thin)
1952
- -x # Mutex-Lock einschalten (Standard ist off)
1953
-
1954
- == Systemanforderungen
1955
-
1956
- Die folgenden Versionen werden offiziell unterstützt:
1957
-
1958
- [ Ruby 1.8.7 ]
1959
- 1.8.7 wird vollständig unterstützt, aber solange nichts dagegen spricht,
1960
- wird ein Update auf 1.9.2 oder ein Umstieg auf JRuby/Rubinius empfohlen.
1961
- Unterstützung für 1.8.7 wird es mindestens bis Sinatra 2.0 und Ruby 2.0 geben,
1962
- es sei denn, dass der unwahrscheinliche Fall eintritt und 1.8.8 rauskommt.
1963
- Doch selbst dann ist es eher wahrscheinlich, dass 1.8.7 weiterhin unterstützt
1964
- wird. <b>Ruby 1.8.6 wird nicht mehr unterstützt.</b> Soll Sinatra unter 1.8.6
1965
- eingesetzt werden, muss Sinatra 1.2 verwendet werden, dass noch bis zum
1966
- Release von Sinatra 1.4.0 fortgeführt wird.
1967
-
1968
- [ Ruby 1.9.2 ]
1969
- 1.9.2 wird voll unterstützt und empfohlen. Version 1.9.2p0 sollte nicht
1970
- verwendet werden, da unter Sinatra immer wieder Segfaults auftreten.
1971
- Unterstützung wird es mindestens bis zum Release von Ruby 1.9.4/2.0 geben und
1972
- das letzte Sinatra Release für 1.9 wird so lange unterstützt, wie das Ruby
1973
- Core-Team 1.9 pflegt.
1974
-
1975
- [ Ruby 1.9.3 ]
1976
- 1.9.3 wird vollständig unterstützt und empfohlen. Achtung, bei einem Wechsel
1977
- zu 1.9.3 werden alle Sessions ungültig.
1978
-
1979
- [ Rubinius ]
1980
- Rubinius (rbx >= 1.2.4) wird offiziell unter Einbezug aller Templates
1981
- unterstützt. Die kommende 2.0 Version wird ebenfalls unterstützt, samt 1.9
1982
- Modus.
1983
-
1984
- [ JRuby ]
1985
- JRuby wird offiziell unterstützt (JRuby >= 1.6.7). Probleme mit Template-
1986
- Bibliotheken Dritter sind nicht bekannt. Falls JRuby zum Einsatz kommt,
1987
- sollte aber darauf geachtet werden, dass ein JRuby-Rack-Handler zum Einsatz
1988
- kommt – die Thin und Mongrel Web-Server werden bisher nicht unterstütz. JRubys
1989
- Unterstützung für C-Erweiterungen sind zur Zeit ebenfalls experimenteller
1990
- Natur, betrifft im Moment aber nur die RDiscount, Redcarpet, RedCloth und
1991
- Yajl Templates.
1992
-
1993
-
1994
- Weiterhin werden wir die kommende Ruby-Versionen im Auge behalten.
1995
-
1996
- Die nachfolgend aufgeführten Ruby-Implementierungen werden offiziell nicht von
1997
- Sinatra unterstützt, funktionieren aber normalerweise:
1998
-
1999
- * Ruby Enterprise Edition
2000
- * Ältere Versionen von JRuby und Rubinius
2001
- * MacRuby, Maglev, IronRuby
2002
- * Ruby 1.9.0 und 1.9.1 (wird jedoch nicht empfohlen, s.o.)
2003
-
2004
- Nicht offiziell unterstützt bedeutet, dass wenn Sachen nicht funktionieren,
2005
- wir davon ausgehen, dass es nicht an Sinatra sondern an der jeweiligen
2006
- Implentierung liegt.
2007
-
2008
- Im Rahmen unserer CI (Kontinuierlichen Integration) wird bereits ruby-head
2009
- (das kommende Ruby 2.0.0) und 1.9.4 mit eingebunden. Da noch alles im Fluss ist,
2010
- kann zur Zeit für nichts garantiert werden. Es kann aber erwartet werden, dass
2011
- Ruby 2.0.0p0 und 1.9.4p0 von Sinatra unterstützt werden wird.
2012
-
2013
- Sinatra sollte auf jedem Betriebssystem laufen, dass den gewählten Ruby-
2014
- Interpreter unterstützt.
2015
-
2016
- Sinatra wird aktuell nicht unter Cardinal, SmallRuby, BleuRuby oder irgendeiner
2017
- Version von Ruby vor 1.8.7 laufen.
2018
-
2019
- == Der neuste Stand (The Bleeding Edge)
2020
-
2021
- Um auf dem neusten Stand zu bleiben, kann der Master-Branch verwendet werden.
2022
- Er sollte recht stabil sein. Ebenso gibt es von Zeit zu Zeit prerelease Gems,
2023
- die so installiert werden:
2024
-
2025
- gem install sinatra --pre
2026
-
2027
- === Mit Bundler
2028
-
2029
- Wenn die Applikation mit der neuesten Version von Sinatra und
2030
- {Bundler}[http://gembundler.com/] genutzt werden soll, empfehlen wir den
2031
- nachfolgenden Weg.
2032
-
2033
- Soweit Bundler noch nicht installiert ist:
2034
-
2035
- gem install bundler
2036
-
2037
- Anschließend wird eine +Gemfile+-Datei im Projektverzeichnis mit folgendem
2038
- Inhalt erstellt:
2039
-
2040
- source :rubygems
2041
- gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
2042
-
2043
- # evtl. andere Abhängigkeiten
2044
- gem 'haml' # z.B. wenn du Haml verwendest...
2045
- gem 'activerecord', '~> 3.0' # ...oder ActiveRecord 3.x
2046
-
2047
- Beachte: Hier sollten alle Abhängigkeiten eingetragen werden. Sinatras eigene,
2048
- direkte Abhängigkeiten (Tilt und Rack) werden von Bundler automatisch aus dem
2049
- Gemfile von Sinatra hinzugefügt.
2050
-
2051
- Jetzt kannst du deine Applikation starten:
2052
-
2053
- bundle exec ruby myapp.rb
2054
-
2055
- === Eigenes Repository
2056
- Um auf dem neuesten Stand von Sinatras Code zu sein, kann eine lokale Kopie
2057
- angelegt werden. Gestartet wird in der Anwendung mit dem <tt>sinatra/lib</tt>-
2058
- Ordner im <tt>LOAD_PATH</tt>:
2059
-
2060
- cd myapp
2061
- git clone git://github.com/sinatra/sinatra.git
2062
- ruby -Isinatra/lib myapp.rb
2063
-
2064
- Alternativ kann der <tt>sinatra/lib</tt>-Ordner zum <tt>LOAD_PATH</tt> in
2065
- der Anwendung hinzugefügt werden:
2066
-
2067
- $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib'
2068
- require 'rubygems'
2069
- require 'sinatra'
2070
-
2071
- get '/ueber' do
2072
- "Ich laufe auf Version " + Sinatra::VERSION
2073
- end
2074
-
2075
- Um Sinatra-Code von Zeit zu Zeit zu aktualisieren:
2076
-
2077
- cd myproject/sinatra
2078
- git pull
2079
-
2080
- === Gem erstellen
2081
-
2082
- Aus der eigenen lokalen Kopie kann nun auch ein globales Gem gebaut werden:
2083
-
2084
- git clone git://github.com/sinatra/sinatra.git
2085
- cd sinatra
2086
- rake sinatra.gemspec
2087
- rake install
2088
-
2089
- Falls Gems als Root installiert werden sollen, sollte die letzte Zeile
2090
- folgendermaßen lauten:
2091
-
2092
- sudo rake install
2093
-
2094
- == Versions-Verfahren
2095
-
2096
- Sinatra folgt dem sogenannten {Semantic Versioning}[http://semver.org/], d.h.
2097
- SemVer und SemVerTag.
2098
-
2099
- == Mehr
2100
-
2101
- * {Projekt-Website}[http://sinatra.github.com/] - Ergänzende Dokumentation,
2102
- News und Links zu anderen Ressourcen.
2103
- * {Mitmachen}[http://sinatra.github.com/contributing.html] - Einen
2104
- Fehler gefunden? Brauchst du Hilfe? Hast du einen Patch?
2105
- * {Issue-Tracker}[http://github.com/sinatra/sinatra/issues]
2106
- * {Twitter}[http://twitter.com/sinatra]
2107
- * {Mailing-Liste}[http://groups.google.com/group/sinatrarb]
2108
- * {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] auf http://freenode.net
2109
- * {Sinatra Book}[http://sinatra-book.gittr.com] Kochbuch Tutorial
2110
- * {Sinatra Recipes}[http://recipes.sinatrarb.com/] Sinatra-Rezepte aus
2111
- der Community
2112
- * API Dokumentation für die {aktuelle Version}[http://rubydoc.info/gems/sinatra]
2113
- oder für {HEAD}[http://rubydoc.info/github/sinatra/sinatra] auf
2114
- http://rubydoc.info
2115
- * {CI Server}[http://travis-ci.org/sinatra/sinatra]
2116
-