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
data/Gemfile ADDED
@@ -0,0 +1,76 @@
1
+ # Why use bundler?
2
+ # Well, not all development dependencies install on all rubies. Moreover, `gem
3
+ # install sinatra --development` doesn't work, as it will also try to install
4
+ # development dependencies of our dependencies, and those are not conflict free.
5
+ # So, here we are, `bundle install`.
6
+ #
7
+ # If you have issues with a gem: `bundle install --without-coffee-script`.
8
+
9
+ RUBY_ENGINE = 'ruby' unless defined? RUBY_ENGINE
10
+ source 'https://rubygems.org' unless ENV['QUICK']
11
+ gemspec
12
+
13
+ gem 'rake'
14
+ gem 'rack-test', '>= 0.5.6'
15
+
16
+ # Allows stuff like `tilt=1.2.2 bundle install` or `tilt=master ...`.
17
+ # Used by the CI.
18
+ github = "git://github.com/%s.git"
19
+ repos = {'tilt' => github % "rtomayko/tilt", 'rack' => github % "rack/rack"}
20
+
21
+ %w[tilt rack].each do |lib|
22
+ dep = case ENV[lib]
23
+ when 'stable', nil then nil
24
+ when /(\d+\.)+\d+/ then "~> " + ENV[lib].sub("#{lib}-", '')
25
+ else {:git => repos[lib], :branch => dep}
26
+ end
27
+ gem lib, dep
28
+ end
29
+
30
+ if RUBY_ENGINE == 'jruby'
31
+ gem 'nokogiri', '!= 1.5.0'
32
+ gem 'jruby-openssl'
33
+ gem 'trinidad'
34
+ end
35
+
36
+ if RUBY_ENGINE == "ruby" and RUBY_VERSION > '1.9.2'
37
+ gem 'less', '~> 2.0'
38
+ gem 'therubyracer'
39
+ gem 'redcarpet'
40
+ gem 'wlang', '>= 2.0.1'
41
+ gem 'bluecloth'
42
+ gem 'rdiscount'
43
+ gem 'RedCloth'
44
+ gem 'puma'
45
+ gem 'net-http-server'
46
+ gem 'yajl-ruby'
47
+ gem 'nokogiri'
48
+ gem 'thin'
49
+ gem 'slim', '~> 1.0'
50
+ gem 'coffee-script', '>= 2.0'
51
+ gem 'rdoc'
52
+ gem 'kramdown'
53
+ gem 'maruku'
54
+ gem 'creole'
55
+ gem 'wikicloth'
56
+ gem 'markaby'
57
+ gem 'radius'
58
+ gem 'asciidoctor'
59
+ gem 'liquid'
60
+ gem 'stylus'
61
+ gem 'rabl'
62
+ gem 'builder'
63
+ gem 'erubis'
64
+ gem 'haml', '>= 3.0'
65
+ gem 'sass'
66
+ end
67
+
68
+ if RUBY_ENGINE == "rbx"
69
+ gem 'json'
70
+ gem 'rubysl'
71
+ gem 'rubysl-test-unit'
72
+ end
73
+
74
+ platforms :ruby_18, :jruby do
75
+ gem 'json' unless RUBY_VERSION > '1.9' # is there a jruby but 1.8 only selector?
76
+ end
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2007, 2008, 2009 Blake Mizerany
2
+ Copyright (c) 2010, 2011, 2012, 2013, 2014 Konstantin Haase
3
+
4
+ Permission is hereby granted, free of charge, to any person
5
+ obtaining a copy of this software and associated documentation
6
+ files (the "Software"), to deal in the Software without
7
+ restriction, including without limitation the rights to use,
8
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the
10
+ Software is furnished to do so, subject to the following
11
+ conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,2864 @@
1
+ # Sinatra
2
+
3
+ *Wichtig: Dieses Dokument ist eine Übersetzung aus dem Englischen und unter
4
+ Umständen nicht auf dem aktuellen Stand (aktuell Sinatra 1.4.2).*
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
9
+ ermöglicht:
10
+
11
+ Sinatra via `rubygems` installieren:
12
+
13
+ ```shell
14
+ gem install sinatra
15
+ ```
16
+
17
+ Eine Datei mit dem Namen `myapp.rb` erstellen:
18
+
19
+ ```ruby
20
+ require 'sinatra'
21
+ get '/' do
22
+ 'Hallo Welt!'
23
+ end
24
+ ```
25
+
26
+ und im gleichen Verzeichnis ausführen:
27
+
28
+ ```shell
29
+ ruby myapp.rb
30
+ ```
31
+
32
+ Die Seite kann nun unter [http://localhost:4567](http://localhost:4567)
33
+ aufgerufen werden.
34
+
35
+ ## Inhalt
36
+
37
+ * [Sinatra](#sinatra)
38
+ * [Routen](#routen)
39
+ * [Bedingungen](#bedingungen)
40
+ * [Rückgabewerte](#rckgabewerte)
41
+ * [Eigene Routen-Muster](#eigene-routen-muster)
42
+ * [Statische Dateien](#statische-dateien)
43
+ * [Views/Templates](#viewstemplates)
44
+ * [Direkte Templates](#direkte-templates)
45
+ * [Verfügbare Templatesprachen](#verfgbare-templatesprachen)
46
+ * [Haml Templates](#haml-templates)
47
+ * [Erb Templates](#erb-templates)
48
+ * [Builder Templates](#builder-templates)
49
+ * [Nokogiri Templates](#nokogiri-templates)
50
+ * [Sass Templates](#sass-templates)
51
+ * [SCSS Templates](#scss-templates)
52
+ * [Less Templates](#less-templates)
53
+ * [Liquid Templates](#liquid-templates)
54
+ * [Markdown Templates](#markdown-templates)
55
+ * [Textile Templates](#textile-templates)
56
+ * [RDoc Templates](#rdoc-templates)
57
+ * [Radius Templates](#radius-templates)
58
+ * [Markaby Templates](#markaby-templates)
59
+ * [RABL Templates](#rabl-templates)
60
+ * [Slim Templates](#slim-templates)
61
+ * [Creole Templates](#creole-templates)
62
+ * [CoffeeScript Templates](#coffeescript-templates)
63
+ * [Stylus Templates](#stylus-templates)
64
+ * [Yajl Templates](#yajl-templates)
65
+ * [WLang Templates](#wlang-templates)
66
+ * [Auf Variablen in Templates zugreifen](#auf-variablen-in-templates-zugreifen)
67
+ * [Templates mit `yield` und verschachtelte Layouts](#templates-mit-yield-und-verschachtelte-layouts)
68
+ * [Inline-Templates](#inline-templates)
69
+ * [Benannte Templates](#benannte-templates)
70
+ * [Dateiendungen zuordnen](#dateiendungen-zuordnen)
71
+ * [Eine eigene Template-Engine hinzufügen](#eine-eigene-template-engine-hinzufgen)
72
+ * [Filter](#filter)
73
+ * [Helfer](#helfer)
74
+ * [Sessions verwenden](#sessions-verwenden)
75
+ * [Anhalten](#anhalten)
76
+ * [Weiterspringen](#weiterspringen)
77
+ * [Eine andere Route ansteuern](#eine-andere-route-ansteuern)
78
+ * [Body, Status-Code und Header setzen](#body-status-code-und-header-setzen)
79
+ * [Response-Streams](#response-streams)
80
+ * [Logger](#logger)
81
+ * [Mime-Types](#mime-types)
82
+ * [URLs generieren](#urls-generieren)
83
+ * [Browser-Umleitung](#browser-umleitung)
84
+ * [Cache einsetzen](#cache-einsetzen)
85
+ * [Dateien versenden](#dateien-versenden)
86
+ * [Das Request-Objekt](#das-request-objekt)
87
+ * [Anhänge](#anhnge)
88
+ * [Umgang mit Datum und Zeit](#umgang-mit-datum-und-zeit)
89
+ * [Nachschlagen von Template-Dateien](#nachschlagen-von-template-dateien)
90
+ * [Konfiguration](#konfiguration)
91
+ * [Einstellung des Angriffsschutzes](#einstellung-des-angriffsschutzes)
92
+ * [Mögliche Einstellungen](#mgliche-einstellungen)
93
+ * [Umgebungen](#umgebungen)
94
+ * [Fehlerbehandlung](#fehlerbehandlung)
95
+ * [Nicht gefunden](#nicht-gefunden)
96
+ * [Fehler](#fehler)
97
+ * [Rack-Middleware](#rack-middleware)
98
+ * [Testen](#testen)
99
+ * [Sinatra::Base - Middleware, Bibliotheken und modulare Anwendungen](#sinatrabase---middleware-bibliotheken-und-modulare-anwendungen)
100
+ * [Modularer vs. klassischer Stil](#modularer-vs-klassischer-stil)
101
+ * [Eine modulare Applikation bereitstellen](#eine-modulare-applikation-bereitstellen)
102
+ * [Eine klassische Anwendung mit einer config.ru verwenden](#eine-klassische-anwendung-mit-einer-configru-verwenden)
103
+ * [Wann sollte eine config.ru-Datei verwendet werden?](#wann-sollte-eine-configru-datei-verwendet-werden)
104
+ * [Sinatra als Middleware nutzen](#sinatra-als-middleware-nutzen)
105
+ * [Dynamische Applikationserstellung](#dynamische-applikationserstellung)
106
+ * [Geltungsbereich und Bindung](#geltungsbereich-und-bindung)
107
+ * [Anwendungs- oder Klassen-Scope](#anwendungs--oder-klassen-scope)
108
+ * [Anfrage- oder Instanz-Scope](#anfrage--oder-instanz-scope)
109
+ * [Delegation-Scope](#delegation-scope)
110
+ * [Kommandozeile](#kommandozeile)
111
+ * [Systemanforderungen](#systemanforderungen)
112
+ * [Der neuste Stand (The Bleeding Edge)](#der-neuste-stand-the-bleeding-edge)
113
+ * [Mit Bundler](#mit-bundler)
114
+ * [Eigenes Repository](#eigenes-repository)
115
+ * [Gem erstellen](#gem-erstellen)
116
+ * [Versions-Verfahren](#versions-verfahren)
117
+ * [Mehr](#mehr)
118
+
119
+ ## Routen
120
+
121
+ In Sinatra wird eine Route durch eine HTTP-Methode und ein URL-Muster definiert.
122
+ Jeder dieser Routen wird ein Ruby-Block zugeordnet:
123
+
124
+ ```ruby
125
+ get '/' do
126
+ .. zeige etwas ..
127
+ end
128
+
129
+ post '/' do
130
+ .. erstelle etwas ..
131
+ end
132
+
133
+ put '/' do
134
+ .. update etwas ..
135
+ end
136
+
137
+ delete '/' do
138
+ .. entferne etwas ..
139
+ end
140
+
141
+ options '/' do
142
+ .. zeige, was wir können ..
143
+ end
144
+
145
+ link '/' do
146
+ .. verbinde etwas ..
147
+ end
148
+
149
+ unlink '/' do
150
+ .. trenne etwas ..
151
+ end
152
+ ```
153
+
154
+ Die Routen werden in der Reihenfolge durchlaufen, in der sie definiert wurden.
155
+ Das erste Routen-Muster, das mit dem Request übereinstimmt, wird ausgeführt.
156
+
157
+ Die Muster der Routen können benannte Parameter beinhalten, die über den
158
+ `params`-Hash zugänglich gemacht werden:
159
+
160
+ ```ruby
161
+ get '/hallo/:name' do
162
+ # passt auf "GET /hallo/foo" und "GET /hallo/bar"
163
+ # params[:name] ist dann 'foo' oder 'bar'
164
+ "Hallo #{params[:name]}!"
165
+ end
166
+ ```
167
+
168
+ Man kann auf diese auch mit Block-Parametern zugreifen:
169
+
170
+ ```ruby
171
+ get '/hallo/:name' do |n|
172
+ # n entspricht hier params[:name]
173
+ "Hallo #{n}!"
174
+ end
175
+ ```
176
+
177
+ Routen-Muster können auch mit sog. Splat- oder Wildcard-Parametern über das
178
+ `params[:splat]`-Array angesprochen werden:
179
+
180
+ ```ruby
181
+ get '/sag/*/zu/*' do
182
+ # passt z.B. auf /sag/hallo/zu/welt
183
+ params[:splat] # => ["hallo", "welt"]
184
+ end
185
+
186
+ get '/download/*.*' do
187
+ # passt auf /download/pfad/zu/datei.xml
188
+ params[:splat] # => ["pfad/zu/datei", "xml"]
189
+ end
190
+ ```
191
+
192
+ Oder mit Block-Parametern:
193
+
194
+ ```ruby
195
+ get '/download/*.*' do |pfad, endung|
196
+ [pfad, endung] # => ["Pfad/zu/Datei", "xml"]
197
+ end
198
+ ```
199
+
200
+ Routen mit regulären Ausdrücken sind auch möglich:
201
+
202
+ ```ruby
203
+ get %r{/hallo/([\w]+)} do
204
+ "Hallo, #{params[:captures].first}!"
205
+ end
206
+ ```
207
+
208
+ Und auch hier können Block-Parameter genutzt werden:
209
+
210
+ ```ruby
211
+ get %r{/hallo/([\w]+)} do |c|
212
+ "Hallo, #{c}!"
213
+ end
214
+ ```
215
+
216
+ Routen-Muster können auch mit optionalen Parametern ausgestattet werden:
217
+
218
+ ```ruby
219
+ get '/posts.?:format?' do
220
+ # passt auf "GET /posts" sowie jegliche Erweiterung
221
+ # wie "GET /posts.json", "GET /posts.xml" etc.
222
+ end
223
+ ```
224
+
225
+ Anmerkung: Solange man den sog. Path Traversal Attack-Schutz nicht deaktiviert
226
+ (siehe weiter unten), kann es sein, dass der Request-Pfad noch vor dem
227
+ Abgleich mit den Routen modifiziert wird.
228
+
229
+ ### Bedingungen
230
+
231
+ An Routen können eine Vielzahl von Bedingungen geknüpft werden, die erfüllt
232
+ sein müssen, damit der Block ausgeführt wird. Möglich wäre etwa eine
233
+ Einschränkung des User-Agents über die interne Bedingung `:agent`:
234
+
235
+ ```ruby
236
+ get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
237
+ "Du verwendest Songbird Version #{params[:agent][0]}"
238
+ end
239
+ ```
240
+
241
+ Wird Songbird als Browser nicht verwendet, springt Sinatra zur nächsten Route:
242
+
243
+ ```ruby
244
+ get '/foo' do
245
+ # passt auf andere Browser
246
+ end
247
+ ```
248
+
249
+ Andere mitgelieferte Bedingungen sind `:host_name` und `:provides`:
250
+
251
+ ```ruby
252
+ get '/', :host_name => /^admin\./ do
253
+ "Adminbereich, Zugriff verweigert!"
254
+ end
255
+
256
+ get '/', :provides => 'html' do
257
+ haml :index
258
+ end
259
+
260
+ get '/', :provides => ['rss', 'atom', 'xml'] do
261
+ builder :feed
262
+ end
263
+ ```
264
+
265
+ Eigene Bedingungen können relativ einfach hinzugefügt werden:
266
+
267
+ ```ruby
268
+ set(:wahrscheinlichkeit) { |value| condition { rand <= value } }
269
+
270
+ get '/auto_gewinnen', :wahrscheinlichkeit => 0.1 do
271
+ "Du hast gewonnen!"
272
+ end
273
+
274
+ get '/auto_gewinnen' do
275
+ "Tut mir leid, verloren."
276
+ end
277
+ ```
278
+
279
+ Bei Bedingungen, die mehrere Werte annehmen können, sollte ein Splat verwendet
280
+ werden:
281
+
282
+ ```ruby
283
+ set(:auth) do |*roles| # <- hier kommt der Splat ins Spiel
284
+ condition do
285
+ unless logged_in? && roles.any? {|role| current_user.in_role? role }
286
+ redirect "/login/", 303
287
+ end
288
+ end
289
+ end
290
+
291
+ get "/mein/account/", :auth => [:user, :admin] do
292
+ "Mein Account"
293
+ end
294
+
295
+ get "/nur/admin/", :auth => :admin do
296
+ "Nur Admins dürfen hier rein!"
297
+ end
298
+ ```
299
+
300
+ ### Rückgabewerte
301
+
302
+ Durch den Rückgabewert eines Routen-Blocks wird mindestens der Response-Body
303
+ festgelegt, der an den HTTP-Client, bzw. die nächste Rack-Middleware,
304
+ weitergegeben wird. Im Normalfall handelt es sich hierbei, wie in den
305
+ vorangehenden Beispielen zu sehen war, um einen String. Es werden allerdings
306
+ auch andere Werte akzeptiert.
307
+
308
+ Es kann jedes gültige Objekt zurückgegeben werden, bei dem es sich entweder um
309
+ einen Rack-Rückgabewert, einen Rack-Body oder einen HTTP-Status-Code handelt:
310
+
311
+ * Ein Array mit drei Elementen: `[Status (Fixnum), Headers (Hash),
312
+ Response-Body (antwortet auf #each)]`.
313
+ * Ein Array mit zwei Elementen: `[Status (Fixnum), Response-Body (antwortet
314
+ auf #each)]`.
315
+ * Ein Objekt, das auf `#each` antwortet und den an diese Methode übergebenen
316
+ Block nur mit Strings als Übergabewerte aufruft.
317
+ * Ein Fixnum, das den Status-Code festlegt.
318
+
319
+ Damit lässt sich relativ einfach Streaming implementieren:
320
+
321
+ ```ruby
322
+ class Stream
323
+ def each
324
+ 100.times { |i| yield "#{i}\n" }
325
+ end
326
+ end
327
+
328
+ get('/') { Stream.new }
329
+ ```
330
+
331
+ Ebenso kann die `stream`-Helfer-Methode (s.u.) verwendet werden, die Streaming
332
+ direkt in die Route integriert.
333
+
334
+ ### Eigene Routen-Muster
335
+
336
+ Wie oben schon beschrieben, ist Sinatra von Haus aus mit Unterstützung für
337
+ String-Muster und Reguläre Ausdrücke zum Abgleichen von Routen ausgestattet.
338
+ Das muss aber noch nicht alles sein, es können ohne großen Aufwand eigene
339
+ Routen-Muster erstellt werden:
340
+
341
+ ```ruby
342
+ class AllButPattern
343
+ Match = Struct.new(:captures)
344
+
345
+ def initialize(except)
346
+ @except = except
347
+ @captures = Match.new([])
348
+ end
349
+
350
+ def match(str)
351
+ @captures unless @except === str
352
+ end
353
+ end
354
+
355
+ def all_but(pattern)
356
+ AllButPattern.new(pattern)
357
+ end
358
+
359
+ get all_but("/index") do
360
+ # ...
361
+ end
362
+ ```
363
+
364
+ Beachte, dass das obige Beispiel etwas übertrieben wirkt. Es geht auch einfacher:
365
+
366
+ ```ruby
367
+ get // do
368
+ pass if request.path_info == "/index"
369
+ # ...
370
+ end
371
+ ```
372
+
373
+ Oder unter Verwendung eines negativen look ahead:
374
+
375
+ ```ruby
376
+ get %r{^(?!/index$)} do
377
+ # ...
378
+ end
379
+ ```
380
+
381
+ ## Statische Dateien
382
+
383
+ Statische Dateien werden im `./public`-Ordner erwartet. Es ist möglich,
384
+ einen anderen Ort zu definieren, indem man die `:public_folder`-Option setzt:
385
+
386
+ ```ruby
387
+ set :public_folder, File.dirname(__FILE__) + '/static'
388
+ ```
389
+
390
+ Zu beachten ist, dass der Ordnername `public` nicht Teil der URL ist. Die Datei
391
+ `./public/css/style.css` ist unter `http://example.com/css/style.css` zu finden.
392
+
393
+ Um den `Cache-Control`-Header mit Informationen zu versorgen, verwendet man
394
+ die `:static_cache_control`-Einstellung (s.u.).
395
+
396
+ ## Views/Templates
397
+
398
+ Alle Templatesprachen verwenden ihre eigene Renderingmethode, die jeweils
399
+ einen String zurückgibt:
400
+
401
+ ```ruby
402
+ get '/' do
403
+ erb :index
404
+ end
405
+ ```
406
+
407
+ Dieses Beispiel rendert `views/index.erb`.
408
+
409
+ Anstelle eines Templatenamens kann man auch direkt die Templatesprache verwenden:
410
+
411
+ ```ruby
412
+ get '/' do
413
+ code = "<%= Time.now %>"
414
+ erb code
415
+ end
416
+ ```
417
+
418
+ Templates nehmen ein zweite Argument an, den Options-Hash:
419
+
420
+ ```ruby
421
+ get '/' do
422
+ erb :index, :layout => :post
423
+ end
424
+ ```
425
+
426
+ Dieses Beispiel rendert `views/index.erb` eingebettet in `views/post.erb`
427
+ (Voreinstellung ist `views/layout.erb`, sofern es vorhanden ist.)
428
+
429
+ Optionen, die Sinatra nicht versteht, werden an das Template weitergereicht:
430
+
431
+ ```ruby
432
+ get '/' do
433
+ haml :index, :format => :html5
434
+ end
435
+ ```
436
+
437
+ Für alle Templates können auch Einstellungen, die für alle Routen gelten,
438
+ festgelegt werden:
439
+
440
+ ```ruby
441
+ set :haml, :format => :html5
442
+
443
+ get '/' do
444
+ haml :index
445
+ end
446
+ ```
447
+
448
+ Optionen, die an die Rendermethode weitergegeben werden, überschreiben die
449
+ Einstellungen, die mit `set` festgelegt wurden.
450
+
451
+ Einstellungen:
452
+
453
+ <dl>
454
+ <dt>locals</dt>
455
+ <dd>Liste von lokalen Variablen, die an das Dokument weitergegeben werden.
456
+ Praktisch für Partials:
457
+
458
+ <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt></dd>
459
+
460
+ <dt>default_encoding</dt>
461
+ <dd>Gibt die Stringkodierung an, die verwendet werden soll. Voreingestellt
462
+ auf <tt>settings.default_encoding</tt>.</dd>
463
+
464
+ <dt>views</dt>
465
+ <dd>Ordner, aus dem die Templates geladen werden. Voreingestellt auf
466
+ <tt>settings.views</tt>.</dd>
467
+
468
+ <dt>layout</dt>
469
+ <dd>Legt fest, ob ein Layouttemplate verwendet werden soll oder nicht
470
+ (<tt>true</tt> oder<tt>false</tt>). Ist es ein Symbol, dann legt es fest,
471
+ welches Template als Layout verwendet wird:
472
+
473
+ <tt>erb :index, :layout => !request.xhr?</tt></dd>
474
+
475
+ <dt>content_type</dt>
476
+ <dd>Content-Typ den das Template ausgibt. Voreinstellung hängt von der
477
+ Templatesprache ab.</dd>
478
+
479
+ <dt>scope</dt>
480
+ <dd>Scope, in dem das Template gerendert wird. Liegt standardmäßig innerhalb
481
+ der App-Instanz. Wird Scope geändert, sind Instanzvariablen und
482
+ Helfermethoden nicht verfügbar.</dd>
483
+
484
+ <dt>layout_engine</dt>
485
+ <dd>Legt fest, welcher Renderer für das Layout verantwortlich ist. Hilfreich
486
+ für Sprachen, die sonst keine Templates unterstützen. Voreingestellt auf
487
+ den Renderer, der für das Template verwendet wird:
488
+
489
+ <tt>set :rdoc, :layout_engine => :erb</tt></dd>
490
+
491
+ <dt>layout_options</dt>
492
+ <dd>Besondere Einstellungen, die nur für das Rendering verwendet werden:
493
+
494
+ <tt>set :rdoc, :layout_options => { :views => 'views/layouts' }</tt></dd>
495
+ </dl>
496
+
497
+ Sinatra geht davon aus, dass die Templates sich im `./views` Verzeichnis
498
+ befinden. Es kann jedoch ein anderer Ordner festgelegt werden:
499
+
500
+ ```ruby
501
+ set :views, settings.root + '/templates'
502
+ ```
503
+
504
+ Es ist zu beachten, dass immer mit Symbolen auf Templates verwiesen werden muss,
505
+ auch dann, wenn sie sich in einem Unterordner befinden:
506
+
507
+ ```ruby
508
+ haml :'unterverzeichnis/template'
509
+ ```
510
+
511
+ Rendering-Methoden rendern jeden String direkt.
512
+
513
+ ### Direkte Templates
514
+
515
+ ``` ruby
516
+ get '/' do
517
+ haml '%div.title Hallo Welt'
518
+ end
519
+ ```
520
+
521
+ Hier wird der String direkt gerendert.
522
+
523
+ ### Verfügbare Templatesprachen
524
+
525
+ Einige Sprachen haben mehrere Implementierungen. Um festzulegen, welche
526
+ verwendet wird (und dann auch Thread-sicher ist), verwendet man am besten zu
527
+ Beginn ein `'require'`:
528
+
529
+ ```ruby
530
+ require 'rdiscount' # oder require 'bluecloth'
531
+ get('/') { markdown :index }
532
+ ```
533
+
534
+ #### Haml Templates
535
+
536
+ <table>
537
+ <tr>
538
+ <td>Abhängigkeit</td>
539
+ <td><a href="http://haml.info/">haml</a></td>
540
+ </tr>
541
+ <tr>
542
+ <td>Dateierweiterung</td>
543
+ <td><tt>.haml</tt></td>
544
+ </tr>
545
+ <tr>
546
+ <td>Beispiel</td>
547
+ <td><tt>haml :index, :format => :html5</tt></td>
548
+ </tr>
549
+ </table>
550
+
551
+
552
+ #### Erb Templates
553
+
554
+ <table>
555
+ <tr>
556
+ <td>Abhängigkeit</td>
557
+ <td><a href="http://www.kuwata-lab.com/erubis/">erubis</a> oder erb
558
+ (Standardbibliothek von Ruby)</td>
559
+ </tr>
560
+ <tr>
561
+ <td>Dateierweiterungen</td>
562
+ <td><tt>.erb</tt>, <tt>.rhtml</tt> oder <tt>.erubis</tt> (nur Erubis)</td>
563
+ </tr>
564
+ <tr>
565
+ <td>Beispiel</td>
566
+ <td><tt>erb :index</tt></td>
567
+ </tr>
568
+ </table>
569
+
570
+
571
+ #### Builder Templates
572
+
573
+ <table>
574
+ <tr>
575
+ <td>Abhängigkeit</td>
576
+ <td><a href="http://builder.rubyforge.org/">builder</a></td>
577
+ </tr>
578
+ <tr>
579
+ <td>Dateierweiterung</td>
580
+ <td><tt>.builder</tt></td>
581
+ </tr>
582
+ <tr>
583
+ <td>Beispiel</td>
584
+ <td><tt>builder { |xml| xml.em "Hallo" }</tt></td>
585
+ </tr>
586
+ </table>
587
+
588
+ Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
589
+
590
+ #### Nokogiri Templates
591
+
592
+ <table>
593
+ <tr>
594
+ <td>Abhängigkeit</td>
595
+ <td><a href="http://nokogiri.org/">nokogiri</a></td>
596
+ </tr>
597
+ <tr>
598
+ <td>Dateierweiterung</td>
599
+ <td><tt>.nokogiri</tt></td>
600
+ </tr>
601
+ <tr>
602
+ <td>Beispiel</td>
603
+ <td><tt>nokogiri { |xml| xml.em "Hallo" }</tt></td>
604
+ </tr>
605
+ </table>
606
+
607
+ Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
608
+
609
+ #### Sass Templates
610
+
611
+ <table>
612
+ <tr>
613
+ <td>Abhängigkeit</td>
614
+ <td><a href="http://sass-lang.com/">sass</a></td>
615
+ </tr>
616
+ <tr>
617
+ <td>Dateierweiterung</td>
618
+ <td><tt>.sass</tt></td>
619
+ </tr>
620
+ <tr>
621
+ <td>Beispiel</td>
622
+ <td><tt>sass :stylesheet, :style => :expanded</tt></td>
623
+ </tr>
624
+ </table>
625
+
626
+
627
+ #### SCSS Templates
628
+
629
+ <table>
630
+ <tr>
631
+ <td>Abhängigkeit</td>
632
+ <td><a href="http://sass-lang.com/">sass</a></td>
633
+ </tr>
634
+ <tr>
635
+ <td>Dateierweiterung</td>
636
+ <td><tt>.scss</tt></td>
637
+ </tr>
638
+ <tr>
639
+ <td>Beispiel</td>
640
+ <td><tt>scss :stylesheet, :style => :expanded</tt></td>
641
+ </tr>
642
+ </table>
643
+
644
+
645
+ #### Less Templates
646
+
647
+ <table>
648
+ <tr>
649
+ <td>Abhängigkeit</td>
650
+ <td><a href="http://www.lesscss.org/">less</a></td>
651
+ </tr>
652
+ <tr>
653
+ <td>Dateierweiterung</td>
654
+ <td><tt>.less</tt></td>
655
+ </tr>
656
+ <tr>
657
+ <td>Beispiel</td>
658
+ <td><tt>less :stylesheet</tt></td>
659
+ </tr>
660
+ </table>
661
+
662
+
663
+ #### Liquid Templates
664
+
665
+ <table>
666
+ <tr>
667
+ <td>Abhängigkeit</td>
668
+ <td><a href="http://www.liquidmarkup.org/">liquid</a></td>
669
+ </tr>
670
+ <tr>
671
+ <td>Dateierweiterung</td>
672
+ <td><tt>.liquid</tt></td>
673
+ </tr>
674
+ <tr>
675
+ <td>Beispiel</td>
676
+ <td><tt>liquid :index, :locals => { :key => 'Wert' }</tt></td>
677
+ </tr>
678
+ </table>
679
+
680
+ Da man aus dem Liquid-Template heraus keine Ruby-Methoden aufrufen kann
681
+ (ausgenommen `yield`), wird man üblicherweise locals verwenden wollen, mit
682
+ denen man Variablen weitergibt.
683
+
684
+ #### Markdown Templates
685
+
686
+ <table>
687
+ <tr>
688
+ <td>Abhängigkeit</td>
689
+ <td>Eine der folgenden Bibliotheken:
690
+ <a href="https://github.com/rtomayko/rdiscount" title="RDiscount">RDiscount</a>,
691
+ <a href="https://github.com/vmg/redcarpet" title="RedCarpet">RedCarpet</a>,
692
+ <a href="http://deveiate.org/projects/BlueCloth" title="BlueCloth">BlueCloth</a>,
693
+ <a href="http://kramdown.rubyforge.org/" title="kramdown">kramdown</a> oder
694
+ <a href="http://maruku.rubyforge.org/" title="maruku">maruku</a>
695
+ </td>
696
+ </tr>
697
+ <tr>
698
+ <td>Dateierweiterungen</td>
699
+ <td><tt>.markdown</tt>, <tt>.mkd</tt> und <tt>.md</tt></td>
700
+ </tr>
701
+ <tr>
702
+ <td>Beispiel</td>
703
+ <td><tt>markdown :index, :layout_engine => :erb</tt></td>
704
+ </tr>
705
+ </table>
706
+
707
+ Da man aus den Markdown-Templates heraus keine Ruby-Methoden aufrufen und auch
708
+ keine locals verwenden kann, wird man Markdown üblicherweise in Kombination
709
+ mit anderen Renderern verwenden wollen:
710
+
711
+ ```ruby
712
+ erb :overview, :locals => { :text => markdown(:einfuehrung) }
713
+ ```
714
+
715
+ Beachte, dass man die `markdown`-Methode auch aus anderen Templates heraus
716
+ aufrufen kann:
717
+
718
+ ```ruby
719
+ %h1 Gruß von Haml!
720
+ %p= markdown(:Grüße)
721
+ ```
722
+
723
+ Da man Ruby nicht von Markdown heraus aufrufen kann, können auch Layouts nicht
724
+ in Markdown geschrieben werden. Es ist aber möglich, einen Renderer für die
725
+ Templates zu verwenden und einen anderen für das Layout, indem die
726
+ `:layout_engine`-Option verwendet wird.
727
+
728
+ #### Textile Templates
729
+
730
+ <table>
731
+ <tr>
732
+ <td>Abhängigkeit</td>
733
+ <td><a href="http://redcloth.org/">RedCloth</a></td>
734
+ </tr>
735
+ <tr>
736
+ <td>Dateierweiterung</td>
737
+ <td><tt>.textile</tt></td>
738
+ </tr>
739
+ <tr>
740
+ <td>Beispiel</td>
741
+ <td><tt>textile :index, :layout_engine => :erb</tt></td>
742
+ </tr>
743
+ </table>
744
+
745
+ Da man aus dem Textile-Template heraus keine Ruby-Methoden aufrufen und auch
746
+ keine locals verwenden kann, wird man Textile üblicherweise in Kombination mit
747
+ anderen Renderern verwenden wollen:
748
+
749
+ ```ruby
750
+ erb :overview, :locals => { :text => textile(:einfuehrung) }
751
+ ```
752
+
753
+ Beachte, dass man die `textile`-Methode auch aus anderen Templates heraus
754
+ aufrufen kann:
755
+
756
+ ```ruby
757
+ %h1 Gruß von Haml!
758
+ %p= textile(:Grüße)
759
+ ```
760
+
761
+ Da man Ruby nicht von Textile heraus aufrufen kann, können auch Layouts nicht
762
+ in Textile geschrieben werden. Es ist aber möglich, einen Renderer für die
763
+ Templates zu verwenden und einen anderen für das Layout, indem die
764
+ `:layout_engine`-Option verwendet wird.
765
+
766
+ #### RDoc Templates
767
+
768
+ <table>
769
+ <tr>
770
+ <td>Abhängigkeit</td>
771
+ <td><a href="http://rdoc.rubyforge.org/">rdoc</a></td>
772
+ </tr>
773
+ <tr>
774
+ <td>Dateierweiterung</td>
775
+ <td><tt>.rdoc</tt></td>
776
+ </tr>
777
+ <tr>
778
+ <td>Beispiel</td>
779
+ <td><tt>textile :README, :layout_engine => :erb</tt></td>
780
+ </tr>
781
+ </table>
782
+
783
+ Da man aus dem RDoc-Template heraus keine Ruby-Methoden aufrufen und auch
784
+ keine locals verwenden kann, wird man RDoc üblicherweise in Kombination mit
785
+ anderen Renderern verwenden wollen:
786
+
787
+ ```ruby
788
+ erb :overview, :locals => { :text => rdoc(:einfuehrung) }
789
+ ```
790
+
791
+ Beachte, dass man die `rdoc`-Methode auch aus anderen Templates heraus
792
+ aufrufen kann:
793
+
794
+ ```ruby
795
+ %h1 Gruß von Haml!
796
+ %p= rdoc(:Grüße)
797
+ ```
798
+
799
+ Da man Ruby nicht von RDoc heraus aufrufen kann, können auch Layouts nicht in
800
+ RDoc geschrieben werden. Es ist aber möglich, einen Renderer für die Templates
801
+ zu verwenden und einen anderen für das Layout, indem die
802
+ `:layout_engine`-Option verwendet wird.
803
+
804
+ #### Radius Templates
805
+
806
+ <table>
807
+ <tr>
808
+ <td>Abhängigkeit</td>
809
+ <td><a href="http://radius.rubyforge.org/">radius</a></td>
810
+ </tr>
811
+ <tr>
812
+ <td>Dateierweiterung</td>
813
+ <td><tt>.radius</tt></td>
814
+ </tr>
815
+ <tr>
816
+ <td>Beispiel</td>
817
+ <td><tt>radius :index, :locals => { :key => 'Wert' }</tt></td>
818
+ </tr>
819
+ </table>
820
+
821
+ Da man aus dem Radius-Template heraus keine Ruby-Methoden aufrufen kann, wird
822
+ man üblicherweise locals verwenden wollen, mit denen man Variablen weitergibt.
823
+
824
+ #### Markaby Templates
825
+
826
+ <table>
827
+ <tr>
828
+ <td>Abhängigkeit</td>
829
+ <td><a href="http://markaby.github.com/">markaby</a></td>
830
+ </tr>
831
+ <tr>
832
+ <td>Dateierweiterung</td>
833
+ <td><tt>.mab</tt></td>
834
+ </tr>
835
+ <tr>
836
+ <td>Beispiel</td>
837
+ <td><tt>markaby { h1 "Willkommen!" }</tt></td>
838
+ </tr>
839
+ </table>
840
+
841
+ Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
842
+
843
+ #### RABL Templates
844
+
845
+ <table>
846
+ <tr>
847
+ <td>Abhängigkeit</td>
848
+ <td><a href="https://github.com/nesquena/rabl">rabl</a></td>
849
+ </tr>
850
+ <tr>
851
+ <td>Dateierweiterung</td>
852
+ <td><tt>.rabl</tt></td>
853
+ </tr>
854
+ <tr>
855
+ <td>Beispiel</td>
856
+ <td><tt>rabl :index</tt></td>
857
+ </tr>
858
+ </table>
859
+
860
+ #### Slim Templates
861
+
862
+ <table>
863
+ <tr>
864
+ <td>Abhängigkeit</td>
865
+ <td><a href="http://slim-lang.com/">slim</a></td>
866
+ </tr>
867
+ <tr>
868
+ <td>Dateierweiterung</td>
869
+ <td><tt>.slim</tt></td>
870
+ </tr>
871
+ <tr>
872
+ <td>Beispiel</td>
873
+ <td><tt>slim :index</tt></td>
874
+ </tr>
875
+ </table>
876
+
877
+ #### Creole Templates
878
+
879
+ <table>
880
+ <tr>
881
+ <td>Abhängigkeit</td>
882
+ <td><a href="https://github.com/minad/creole">creole</a></td>
883
+ </tr>
884
+ <tr>
885
+ <td>Dateierweiterung</td>
886
+ <td><tt>.creole</tt></td>
887
+ </tr>
888
+ <tr>
889
+ <td>Beispiel</td>
890
+ <td><tt>creole :wiki, :layout_engine => :erb</tt></td>
891
+ </tr>
892
+ </table>
893
+
894
+ Da man aus dem Creole-Template heraus keine Ruby-Methoden aufrufen und auch
895
+ keine locals verwenden kann, wird man Creole üblicherweise in Kombination mit
896
+ anderen Renderern verwenden wollen:
897
+
898
+ ```ruby
899
+ erb :overview, :locals => { :text => creole(:einfuehrung) }
900
+ ```
901
+
902
+ Beachte, dass man die `creole`-Methode auch aus anderen Templates heraus
903
+ aufrufen kann:
904
+
905
+ ```ruby
906
+ %h1 Gruß von Haml!
907
+ %p= creole(:Grüße)
908
+ ```
909
+
910
+ Da man Ruby nicht von Creole heraus aufrufen kann, können auch Layouts nicht in
911
+ Creole geschrieben werden. Es ist aber möglich, einen Renderer für die Templates
912
+ zu verwenden und einen anderen für das Layout, indem die `:layout_engine`-Option
913
+ verwendet wird.
914
+
915
+ #### CoffeeScript Templates
916
+
917
+ <table>
918
+ <tr>
919
+ <td>Abhängigkeit</td>
920
+ <td><a href="https://github.com/josh/ruby-coffee-script">coffee-script</a>
921
+ und eine <a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme">Möglichkeit JavaScript auszuführen</a>.
922
+ </td>
923
+ </tr>
924
+ <td>Dateierweiterung</td>
925
+ <td><tt>.coffee</tt></td>
926
+ </tr>
927
+ <tr>
928
+ <td>Beispiel</td>
929
+ <td><tt>coffee :index</tt></td>
930
+ </tr>
931
+ </table>
932
+
933
+ #### Stylus Templates
934
+
935
+ <table>
936
+ <tr>
937
+ <td>Abhängigkeit</td>
938
+ <td>
939
+ <a href="https://github.com/lucasmazza/ruby-stylus" title="Ruby Stylus">
940
+ Stylus
941
+ </a> und eine Möglichkeit
942
+ <a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme" title="ExecJS">
943
+ JavaScript auszuführen
944
+ </a>.
945
+ </td>
946
+ </tr>
947
+ <tr>
948
+ <td>Dateierweiterung</td>
949
+ <td><tt>.styl</tt></td>
950
+ </tr>
951
+ <tr>
952
+ <td>Beispiel</td>
953
+ <td><tt>stylus :index</tt></td>
954
+ </tr>
955
+ </table>
956
+
957
+ Um Stylus-Templates ausführen zu können, müssen `stylus` und `stylus/tilt`
958
+ zuerst geladen werden:
959
+
960
+ ``` ruby
961
+ require 'sinatra'
962
+ require 'stylus'
963
+ require 'stylus/tilt'
964
+
965
+ get '/' do
966
+ stylus :example
967
+ end
968
+ ```
969
+
970
+ #### Yajl Templates
971
+
972
+ <table>
973
+ <tr>
974
+ <td>Abhängigkeit</td>
975
+ <td><a href="https://github.com/brianmario/yajl-ruby" title="yajl-ruby">yajl-ruby</a></td>
976
+ </tr>
977
+ <tr>
978
+ <td>Dateierweiterung</td>
979
+ <td><tt>.yajl</tt></td>
980
+ </tr>
981
+ <tr>
982
+ <td>Beispiel</td>
983
+ <td>
984
+ <tt>
985
+ yajl :index,
986
+ :locals => { :key => 'qux' },
987
+ :callback => 'present',
988
+ :variable => 'resource'
989
+ </tt>
990
+ </td>
991
+ </tr>
992
+ </table>
993
+
994
+ Die Template-Quelle wird als Ruby-String evaluiert. Die daraus resultierende
995
+ json Variable wird mit Hilfe von `#to_json` umgewandelt:
996
+
997
+ ``` ruby
998
+ json = { :foo => 'bar' }
999
+ json[:baz] = key
1000
+ ```
1001
+
1002
+ Die `:callback` und `:variable` Optionen können mit dem gerenderten Objekt
1003
+ verwendet werden:
1004
+
1005
+ ``` ruby
1006
+ var resource = {"foo":"bar","baz":"qux"}; present(resource);
1007
+ ```
1008
+
1009
+ #### WLang Templates
1010
+
1011
+ <table>
1012
+ <tr>
1013
+ <td>Abhängigkeit</td>
1014
+ <td><a href="https://github.com/blambeau/wlang/">wlang</a></td>
1015
+ </tr>
1016
+ <tr>
1017
+ <td>Dateierweiterung</td>
1018
+ <td><tt>.wlang</tt></td>
1019
+ </tr>
1020
+ <tr>
1021
+ <td>Beispiel</td>
1022
+ <td><tt>wlang :index, :locals => { :key => 'value' }</tt></td>
1023
+ </tr>
1024
+ </table>
1025
+
1026
+ Ruby-Methoden in wlang aufzurufen entspricht nicht den idiomatischen Vorgaben
1027
+ von wlang, es bietet sich deshalb an, `:locals` zu verwenden. Layouts, die
1028
+ wlang und `yield` verwenden, werden aber trotzdem unterstützt.
1029
+
1030
+ Rendert den eingebetteten Template-String.
1031
+
1032
+ ### Auf Variablen in Templates zugreifen
1033
+
1034
+ Templates werden in demselben Kontext ausgeführt wie Routen. Instanzvariablen
1035
+ in Routen sind auch direkt im Template verfügbar:
1036
+
1037
+ ```ruby
1038
+ get '/:id' do
1039
+ @foo = Foo.find(params[:id])
1040
+ haml '%h1= @foo.name'
1041
+ end
1042
+ ```
1043
+
1044
+ Oder durch einen expliziten Hash von lokalen Variablen:
1045
+
1046
+ ```ruby
1047
+ get '/:id' do
1048
+ foo = Foo.find(params[:id])
1049
+ haml '%h1= bar.name', :locals => { :bar => foo }
1050
+ end
1051
+ ```
1052
+
1053
+ Dies wird typischerweise bei Verwendung von Subtemplates (partials) in anderen
1054
+ Templates eingesetzt.
1055
+
1056
+ ### Templates mit `yield` und verschachtelte Layouts
1057
+
1058
+ Ein Layout ist üblicherweise ein Template, das ein `yield` aufruft. Ein solches
1059
+ Template kann entweder wie oben beschrieben über die `:template` Option
1060
+ verwendet werden oder mit einem Block gerendert werden:
1061
+
1062
+ ``` ruby
1063
+ erb :post, :layout => false do
1064
+ erb :index
1065
+ end
1066
+ ```
1067
+
1068
+ Dieser Code entspricht weitestgehend `erb :index, :layout => :post`.
1069
+
1070
+ Blöcke an Render-Methoden weiterzugeben ist besonders bei verschachtelten
1071
+ Layouts hilfreich:
1072
+
1073
+ ``` ruby
1074
+ erb :main_layout, :layout => false do
1075
+ erb :admin_layout do
1076
+ erb :user
1077
+ end
1078
+ end
1079
+ ```
1080
+
1081
+ Der gleiche Effekt kann auch mit weniger Code erreicht werden:
1082
+
1083
+ ``` ruby
1084
+ erb :admin_layout, :layout => :main_layout do
1085
+ erb :user
1086
+ end
1087
+ ```
1088
+
1089
+ Zur Zeit nehmen folgende Renderer Blöcke an: `erb`, `haml`, `liquid`, `slim `
1090
+ und `wlang`.
1091
+
1092
+ Das gleich gilt auch für die allgemeine `render` Methode.
1093
+
1094
+ ### Inline-Templates
1095
+
1096
+ Templates können auch am Ende der Datei definiert werden:
1097
+
1098
+ ```ruby
1099
+ require 'sinatra'
1100
+
1101
+ get '/' do
1102
+ haml :index
1103
+ end
1104
+
1105
+ __END__
1106
+
1107
+ @@ layout
1108
+ %html
1109
+ = yield
1110
+
1111
+ @@ index
1112
+ %div.title Hallo Welt!!!!!
1113
+ ```
1114
+
1115
+ Anmerkung: Inline-Templates, die in der Datei definiert sind, die `require
1116
+ 'sinatra'` aufruft, werden automatisch geladen. Um andere Inline-Templates in
1117
+ anderen Dateien aufzurufen, muss explizit `enable :inline_templates` verwendet
1118
+ werden.
1119
+
1120
+ ### Benannte Templates
1121
+
1122
+ Templates können auch mit der Top-Level `template`-Methode definiert werden:
1123
+
1124
+ ```ruby
1125
+ template :layout do
1126
+ "%html\n =yield\n"
1127
+ end
1128
+
1129
+ template :index do
1130
+ '%div.title Hallo Welt!'
1131
+ end
1132
+
1133
+ get '/' do
1134
+ haml :index
1135
+ end
1136
+ ```
1137
+
1138
+ Wenn ein Template mit dem Namen "layout" existiert, wird es bei jedem Aufruf
1139
+ verwendet. Durch `:layout => false` kann das Ausführen verhindert werden:
1140
+
1141
+ ```ruby
1142
+ get '/' do
1143
+ haml :index, :layout => !request.xhr?
1144
+ # !request.xhr? prüft, ob es sich um einen asynchronen Request handelt.
1145
+ # wenn nicht, dann verwende ein Layout (negiert durch !)
1146
+ end
1147
+ ```
1148
+
1149
+ ### Dateiendungen zuordnen
1150
+
1151
+ Um eine Dateiendung einer Template-Engine zuzuordnen, kann `Tilt.register`
1152
+ genutzt werden. Wenn etwa die Dateiendung `tt` für Textile-Templates genutzt
1153
+ werden soll, lässt sich dies wie folgt bewerkstelligen:
1154
+
1155
+ ```ruby
1156
+ Tilt.register :tt, Tilt[:textile]
1157
+ ```
1158
+
1159
+ ### Eine eigene Template-Engine hinzufügen
1160
+
1161
+ Zu allererst muss die Engine bei Tilt registriert und danach eine
1162
+ Rendering-Methode erstellt werden:
1163
+
1164
+ ```ruby
1165
+ Tilt.register :mtt, MeineTolleTemplateEngine
1166
+
1167
+ helpers do
1168
+ def mtt(*args) render(:mtt, *args) end
1169
+ end
1170
+
1171
+ get '/' do
1172
+ mtt :index
1173
+ end
1174
+ ```
1175
+
1176
+ Dieser Code rendert `./views/application.mtt`. Siehe
1177
+ [github.com/rtomayko/tilt](https://github.com/rtomayko/tilt), um mehr über
1178
+ Tilt zu erfahren.
1179
+
1180
+ ## Filter
1181
+
1182
+ Before-Filter werden vor jedem Request in demselben Kontext, wie danach die
1183
+ Routen, ausgeführt. So können etwa Request und Antwort geändert werden.
1184
+ Gesetzte Instanzvariablen in Filtern können in Routen und Templates verwendet
1185
+ werden:
1186
+
1187
+ ```ruby
1188
+ before do
1189
+ @note = 'Hi!'
1190
+ request.path_info = '/foo/bar/baz'
1191
+ end
1192
+
1193
+ get '/foo/*' do
1194
+ @note #=> 'Hi!'
1195
+ params[:splat] #=> 'bar/baz'
1196
+ end
1197
+ ```
1198
+
1199
+ After-Filter werden nach jedem Request in demselben Kontext ausgeführt und
1200
+ können ebenfalls Request und Antwort ändern. In Before-Filtern gesetzte
1201
+ Instanzvariablen können in After-Filtern verwendet werden:
1202
+
1203
+ ```ruby
1204
+ after do
1205
+ puts response.status
1206
+ end
1207
+ ```
1208
+
1209
+ Filter können optional auch mit einem Muster ausgestattet werden, das auf den
1210
+ Request-Pfad passen muss, damit der Filter ausgeführt wird:
1211
+
1212
+ ```ruby
1213
+ before '/protected/*' do
1214
+ authenticate!
1215
+ end
1216
+
1217
+ after '/create/:slug' do |slug|
1218
+ session[:last_slug] = slug
1219
+ end
1220
+ ```
1221
+
1222
+ Ähnlich wie Routen können Filter auch mit weiteren Bedingungen eingeschränkt
1223
+ werden:
1224
+
1225
+ ```ruby
1226
+ before :agent => /Songbird/ do
1227
+ # ...
1228
+ end
1229
+
1230
+ after '/blog/*', :host_name => 'example.com' do
1231
+ # ...
1232
+ end
1233
+ ```
1234
+
1235
+ ## Helfer
1236
+
1237
+ Durch die Top-Level `helpers`-Methode werden sogenannte Helfer-Methoden
1238
+ definiert, die in Routen und Templates verwendet werden können:
1239
+
1240
+ ```ruby
1241
+ helpers do
1242
+ def bar(name)
1243
+ "#{name}bar"
1244
+ end
1245
+ end
1246
+
1247
+ get '/:name' do
1248
+ bar(params[:name])
1249
+ end
1250
+ ```
1251
+
1252
+ ### Sessions verwenden
1253
+ Sessions werden verwendet, um Zustände zwischen den Requests zu speichern. Sind
1254
+ sie aktiviert, kann ein Session-Hash je Benutzer-Session verwendet werden:
1255
+
1256
+ ```ruby
1257
+ enable :sessions
1258
+
1259
+ get '/' do
1260
+ "value = " << session[:value].inspect
1261
+ end
1262
+
1263
+ get '/:value' do
1264
+ session[:value] = params[:value]
1265
+ end
1266
+ ```
1267
+
1268
+ Beachte, dass `enable :sessions` alle Daten in einem Cookie speichert. Unter
1269
+ Umständen kann dies negative Effekte haben, z.B. verursachen viele Daten
1270
+ höheren, teilweise überflüssigen Traffic. Um das zu vermeiden, kann eine Rack-
1271
+ Session-Middleware verwendet werden. Dabei wird auf `enable :sessions`
1272
+ verzichtet und die Middleware wie üblich im Programm eingebunden:
1273
+
1274
+ ```ruby
1275
+ use Rack::Session::Pool, :expire_after => 2592000
1276
+
1277
+ get '/' do
1278
+ "value = " << session[:value].inspect
1279
+ end
1280
+
1281
+ get '/:value' do
1282
+ session[:value] = params[:value]
1283
+ end
1284
+ ```
1285
+
1286
+ Um die Sicherheit zu erhöhen, werden Cookies, die Session-Daten führen, mit
1287
+ einem sogenannten Session-Secret signiert. Da sich dieses Geheimwort bei jedem
1288
+ Neustart der Applikation automatisch ändert, ist es sinnvoll, ein eigenes zu
1289
+ wählen, damit sich alle Instanzen der Applikation dasselbe Session-Secret
1290
+ teilen:
1291
+
1292
+ ```ruby
1293
+ set :session_secret, 'super_geheimes_Gegeimnis'
1294
+ ```
1295
+
1296
+ Zur weiteren Konfiguration kann man einen Hash mit Optionen in den `sessions`
1297
+ Einstellungen ablegen.
1298
+
1299
+ ```ruby
1300
+ set :sessions, :domain => 'foo.com'
1301
+ ```
1302
+
1303
+ ### Anhalten
1304
+
1305
+ Zum sofortigen Stoppen eines Request in einem Filter oder einer Route:
1306
+
1307
+ ```ruby
1308
+ halt
1309
+ ```
1310
+
1311
+ Der Status kann beim Stoppen mit angegeben werden:
1312
+
1313
+ ```ruby
1314
+ halt 410
1315
+ ```
1316
+
1317
+ Oder auch den Response-Body:
1318
+
1319
+ ```ruby
1320
+ halt 'Hier steht der Body'
1321
+ ```
1322
+
1323
+ Oder beides:
1324
+
1325
+ ```ruby
1326
+ halt 401, 'verschwinde!'
1327
+ ```
1328
+
1329
+ Sogar mit Headern:
1330
+
1331
+ ```ruby
1332
+ halt 402, {'Content-Type' => 'text/plain'}, 'Rache'
1333
+ ```
1334
+
1335
+ Natürlich ist es auch möglich, ein Template mit `halt` zu verwenden:
1336
+
1337
+ ```ruby
1338
+ halt erb(:error)
1339
+ ```
1340
+
1341
+ ### Weiterspringen
1342
+
1343
+ Eine Route kann mittels `pass` zu der nächsten passenden Route springen:
1344
+
1345
+ ```ruby
1346
+ get '/raten/:wer' do
1347
+ pass unless params[:wer] == 'Frank'
1348
+ 'Du hast mich!'
1349
+ end
1350
+
1351
+ get '/raten/*' do
1352
+ 'Du hast mich nicht!'
1353
+ end
1354
+ ```
1355
+
1356
+ Der Block wird sofort verlassen und es wird nach der nächsten treffenden Route
1357
+ gesucht. Ein 404-Fehler wird zurückgegeben, wenn kein treffendes Routen-Muster
1358
+ gefunden wird.
1359
+
1360
+ ### Eine andere Route ansteuern
1361
+
1362
+ Wenn nicht zu einer anderen Route gesprungen werden soll, sondern nur das
1363
+ Ergebnis einer anderen Route gefordert wird, kann `call` für einen internen
1364
+ Request verwendet werden:
1365
+
1366
+ ```ruby
1367
+ get '/foo' do
1368
+ status, headers, body = call env.merge("PATH_INFO" => '/bar')
1369
+ [status, headers, body.map(&:upcase)]
1370
+ end
1371
+
1372
+ get '/bar' do
1373
+ "bar"
1374
+ end
1375
+ ```
1376
+
1377
+ Beachte, dass in dem oben angegeben Beispiel die Performance erheblich erhöht
1378
+ werden kann, wenn `"bar"` in eine Helfer-Methode umgewandelt wird, auf die
1379
+ `/foo` und `/bar` zugreifen können.
1380
+
1381
+ Wenn der Request innerhalb derselben Applikations-Instanz aufgerufen und keine
1382
+ Kopie der Instanz erzeugt werden soll, kann `call!` anstelle von `call`
1383
+ verwendet werden.
1384
+
1385
+ ### Body, Status-Code und Header setzen
1386
+
1387
+ Es ist möglich und empfohlen, den Status-Code sowie den Response-Body mit einem
1388
+ Returnwert in der Route zu setzen. In manchen Situationen kann es jedoch sein,
1389
+ dass der Body an anderer Stelle während der Ausführung gesetzt werden soll.
1390
+ Dafür kann man die Helfer-Methode `body` einsetzen. Ist sie gesetzt, kann sie zu
1391
+ einem späteren Zeitpunkt aufgerufen werden:
1392
+
1393
+ ```ruby
1394
+ get '/foo' do
1395
+ body "bar"
1396
+ end
1397
+
1398
+ after do
1399
+ puts body
1400
+ end
1401
+ ```
1402
+
1403
+ Ebenso ist es möglich, einen Block an `body` weiterzureichen, der dann vom
1404
+ Rack-Handler ausgeführt wird (lässt sich z.B. zur Umsetzung von Streaming
1405
+ einsetzen, siehe auch "Rückgabewerte").
1406
+
1407
+ Vergleichbar mit `body` lassen sich auch Status-Code und Header setzen:
1408
+
1409
+ ```ruby
1410
+ get '/foo' do
1411
+ status 418
1412
+ headers \
1413
+ "Allow" => "BREW, POST, GET, PROPFIND, WHEN",
1414
+ "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
1415
+ halt "Ich bin ein Teekesselchen"
1416
+ end
1417
+ ```
1418
+
1419
+ Genau wie bei `body` liest ein Aufrufen von `headers` oder `status` ohne
1420
+ Argumente den aktuellen Wert aus.
1421
+
1422
+ ### Response-Streams
1423
+
1424
+ In manchen Situationen sollen Daten bereits an den Client zurückgeschickt
1425
+ werden, bevor ein vollständiger Response bereit steht. Manchmal will man die
1426
+ Verbindung auch erst dann beenden und Daten so lange an den Client
1427
+ zurückschicken, bis er die Verbindung abbricht. Für diese Fälle gibt es die
1428
+ `stream`-Helfer-Methode, die es einem erspart eigene Lösungen zu schreiben:
1429
+
1430
+ ```ruby
1431
+ get '/' do
1432
+ stream do |out|
1433
+ out << "Das ist ja mal wieder fanta -\n"
1434
+ sleep 0.5
1435
+ out << " (bitte warten…) \n"
1436
+ sleep 1
1437
+ out << "- stisch!\n"
1438
+ end
1439
+ end
1440
+ ```
1441
+
1442
+ Damit lassen sich Streaming-APIs realisieren, sog.
1443
+ [Server Sent Events](http://dev.w3.org/html5/eventsource/), die als Basis für
1444
+ [WebSockets](http://en.wikipedia.org/wiki/WebSocket) dienen. Ebenso können sie
1445
+ verwendet werden, um den Durchsatz zu erhöhen, wenn ein Teil der Daten von
1446
+ langsamen Ressourcen abhängig ist.
1447
+
1448
+ Es ist zu beachten, dass das Verhalten beim Streaming, insbesondere die Anzahl
1449
+ nebenläufiger Anfragen, stark davon abhängt, welcher Webserver für die
1450
+ Applikation verwendet wird. Einige Server, z.B. WEBRick, unterstützen
1451
+ Streaming nicht oder nur teilweise. Sollte der Server Streaming nicht
1452
+ unterstützen, wird ein vollständiger Response-Body zurückgeschickt, sobald der
1453
+ an `stream` weitergegebene Block abgearbeitet ist. Mit Shotgun funktioniert
1454
+ Streaming z.B. überhaupt nicht.
1455
+
1456
+ Ist der optionale Parameter `keep_open` aktiviert, wird beim gestreamten Objekt
1457
+ `close` nicht aufgerufen und es ist einem überlassen dies an einem beliebigen
1458
+ späteren Zeitpunkt nachholen. Die Funktion ist jedoch nur bei Event-gesteuerten
1459
+ Serven wie Thin oder Rainbows möglich, andere Server werden trotzdem den Stream
1460
+ beenden:
1461
+
1462
+ ```ruby
1463
+ # Durchgehende Anfrage (long polling)
1464
+
1465
+ set :server, :thin
1466
+ connections = []
1467
+
1468
+ get '/subscribe' do
1469
+ # Client-Registrierung beim Server, damit Events mitgeteilt werden können
1470
+ stream(:keep_open) { |out| connections << out }
1471
+
1472
+ # tote Verbindungen entfernen
1473
+ connections.reject!(&:closed?)
1474
+
1475
+ # Rückmeldung
1476
+ "Angemeldet"
1477
+ end
1478
+
1479
+ post '/message' do
1480
+ connections.each do |out|
1481
+ # Den Client über eine neue Nachricht in Kenntnis setzen
1482
+ # notify client that a new message has arrived
1483
+ out << params[:message] << "\n"
1484
+
1485
+ # Den Client zur erneuten Verbindung auffordern
1486
+ out.close
1487
+ end
1488
+
1489
+ # Rückmeldung
1490
+ "Mitteiling erhalten"
1491
+ end
1492
+ ```
1493
+
1494
+ ### Logger
1495
+
1496
+ Im Geltungsbereich eines Request stellt die `logger` Helfer-Methode eine `Logger`
1497
+ Instanz zur Verfügung:
1498
+
1499
+ ```ruby
1500
+ get '/' do
1501
+ logger.info "es passiert gerade etwas"
1502
+ # ...
1503
+ end
1504
+ ```
1505
+
1506
+ Der Logger übernimmt dabei automatisch alle im Rack-Handler eingestellten
1507
+ Log-Vorgaben. Ist Loggen ausgeschaltet, gibt die Methode ein Leerobjekt zurück.
1508
+ In den Routen und Filtern muss man sich also nicht weiter darum kümmern.
1509
+
1510
+ Beachte, dass das Loggen standardmäßig nur für `Sinatra::Application`
1511
+ voreingestellt ist. Wird über `Sinatra::Base` vererbt, muss es erst aktiviert
1512
+ werden:
1513
+
1514
+ ```ruby
1515
+ class MyApp < Sinatra::Base
1516
+ configure :production, :development do
1517
+ enable :logging
1518
+ end
1519
+ end
1520
+ ```
1521
+
1522
+ Damit auch keine Middleware das Logging aktivieren kann, muss die `logging`
1523
+ Einstellung auf `nil` gesetzt werden. Das heißt aber auch, dass `logger` in
1524
+ diesem Fall `nil` zurückgeben wird. Üblicherweise wird das eingesetzt, wenn ein
1525
+ eigener Logger eingerichtet werden soll. Sinatra wird dann verwenden, was in
1526
+ `env['rack.logger']` eingetragen ist.
1527
+
1528
+ ### Mime-Types
1529
+
1530
+ Wenn `send_file` oder statische Dateien verwendet werden, kann es vorkommen,
1531
+ dass Sinatra den Mime-Typ nicht kennt. Registriert wird dieser mit `mime_type`
1532
+ per Dateiendung:
1533
+
1534
+ ```ruby
1535
+ configure do
1536
+ mime_type :foo, 'text/foo'
1537
+ end
1538
+ ```
1539
+
1540
+ Es kann aber auch der `content_type`-Helfer verwendet werden:
1541
+
1542
+ ```ruby
1543
+ get '/' do
1544
+ content_type :foo
1545
+ "foo foo foo"
1546
+ end
1547
+ ```
1548
+
1549
+ ### URLs generieren
1550
+
1551
+ Zum Generieren von URLs sollte die `url`-Helfer-Methode genutzen werden, so z.B.
1552
+ beim Einsatz von Haml:
1553
+
1554
+ ```ruby
1555
+ %a{:href => url('/foo')} foo
1556
+ ```
1557
+
1558
+ Soweit vorhanden, wird Rücksicht auf Proxys und Rack-Router genommen.
1559
+
1560
+ Diese Methode ist ebenso über das Alias `to` zu erreichen (siehe Beispiel unten).
1561
+
1562
+ ### Browser-Umleitung
1563
+
1564
+ Eine Browser-Umleitung kann mithilfe der `redirect`-Helfer-Methode erreicht
1565
+ werden:
1566
+
1567
+ ```ruby
1568
+ get '/foo' do
1569
+ redirect to('/bar')
1570
+ end
1571
+ ```
1572
+
1573
+ Weitere Parameter werden wie Argumente der `halt`-Methode behandelt:
1574
+
1575
+ ```ruby
1576
+ redirect to('/bar'), 303
1577
+ redirect 'http://google.com', 'Hier bist du falsch'
1578
+ ```
1579
+
1580
+ Ebenso leicht lässt sich ein Schritt zurück mit dem Alias `redirect back`
1581
+ erreichen:
1582
+
1583
+ ```ruby
1584
+ get '/foo' do
1585
+ "<a href='/bar'>mach was</a>"
1586
+ end
1587
+
1588
+ get '/bar' do
1589
+ mach_was
1590
+ redirect back
1591
+ end
1592
+ ```
1593
+
1594
+ Um Argumente an ein Redirect weiterzugeben, können sie entweder dem Query
1595
+ übergeben:
1596
+
1597
+ ```ruby
1598
+ redirect to('/bar?summe=42')
1599
+ ```
1600
+
1601
+ oder eine Session verwendet werden:
1602
+
1603
+ ```ruby
1604
+ enable :sessions
1605
+
1606
+ get '/foo' do
1607
+ session[:secret] = 'foo'
1608
+ redirect to('/bar')
1609
+ end
1610
+
1611
+ get '/bar' do
1612
+ session[:secret]
1613
+ end
1614
+ ```
1615
+
1616
+ ### Cache einsetzen
1617
+
1618
+ Ein sinnvolles Einstellen von Header-Daten ist die Grundlage für ein
1619
+ ordentliches HTTP-Caching.
1620
+
1621
+ Der Cache-Control-Header lässt sich ganz einfach einstellen:
1622
+
1623
+ ```ruby
1624
+ get '/' do
1625
+ cache_control :public
1626
+ "schon gecached!"
1627
+ end
1628
+ ```
1629
+
1630
+ Profitipp: Caching im before-Filter aktivieren
1631
+
1632
+ ```ruby
1633
+ before do
1634
+ cache_control :public, :must_revalidate, :max_age => 60
1635
+ end
1636
+ ```
1637
+
1638
+ Bei Verwendung der `expires`-Helfermethode zum Setzen des gleichnamigen Headers,
1639
+ wird `Cache-Control` automatisch eigestellt:
1640
+
1641
+ ```ruby
1642
+ before do
1643
+ expires 500, :public, :must_revalidate
1644
+ end
1645
+ ```
1646
+
1647
+ Um alles richtig zu machen, sollten auch `etag` oder `last_modified` verwendet
1648
+ werden. Es wird empfohlen, dass diese Helfer aufgerufen werden **bevor** die
1649
+ eigentliche Arbeit anfängt, da sie sofort eine Antwort senden, wenn der Client
1650
+ eine aktuelle Version im Cache vorhält:
1651
+
1652
+ ```ruby
1653
+ get '/article/:id' do
1654
+ @article = Article.find params[:id]
1655
+ last_modified @article.updated_at
1656
+ etag @article.sha1
1657
+ erb :article
1658
+ end
1659
+ ```
1660
+
1661
+ ebenso ist es möglich einen
1662
+ [schwachen ETag](http://de.wikipedia.org/wiki/HTTP_ETag) zu verwenden:
1663
+
1664
+ ```ruby
1665
+ etag @article.sha1, :weak
1666
+ ```
1667
+
1668
+ Diese Helfer führen nicht das eigentliche Caching aus, sondern geben die dafür
1669
+ notwendigen Informationen an den Cache weiter. Für schnelle Reverse-Proxy
1670
+ Cache-Lösungen bietet sich z.B.
1671
+ [rack-cache](https://github.com/rtomayko/rack-cache) an:
1672
+
1673
+ ```ruby
1674
+ require "rack/cache"
1675
+ require "sinatra"
1676
+
1677
+ use Rack::Cache
1678
+
1679
+ get '/' do
1680
+ cache_control :public, :max_age => 36000
1681
+ sleep 5
1682
+ "hello"
1683
+ end
1684
+ ```
1685
+
1686
+ Um den `Cache-Control`-Header mit Informationen zu versorgen, verwendet man die
1687
+ `:static_cache_control`-Einstellung (s.u.).
1688
+
1689
+ Nach RFC 2616 sollte sich die Anwendung anders verhalten, wenn ein If-Match oder
1690
+ ein If-None_match Header auf `*` gesetzt wird in Abhängigkeit davon, ob die
1691
+ Resource bereits existiert. Sinatra geht davon aus, dass Ressourcen bei sicheren
1692
+ Anfragen (z.B. bei get oder Idempotenten Anfragen wie put) bereits existieren,
1693
+ wobei anderen Ressourcen (besipielsweise bei post), als neue Ressourcen
1694
+ behandelt werden. Dieses Verhalten lässt sich mit der `:new_resource` Option
1695
+ ändern:
1696
+
1697
+ ```ruby
1698
+ get '/create' do
1699
+ etag '', :new_resource => true
1700
+ Article.create
1701
+ erb :new_article
1702
+ end
1703
+ ```
1704
+
1705
+ Soll das schwache ETag trotzdem verwendet werden, verwendet man die `:kind`
1706
+ Option:
1707
+
1708
+ ```ruby
1709
+ etag '', :new_resource => true, :kind => :weak
1710
+ ```
1711
+
1712
+ ### Dateien versenden
1713
+
1714
+ Zum Versenden von Dateien kann die `send_file`-Helfer-Methode verwendet werden:
1715
+
1716
+ ```ruby
1717
+ get '/' do
1718
+ send_file 'foo.png'
1719
+ end
1720
+ ```
1721
+
1722
+ Für `send_file` stehen einige Hash-Optionen zur Verfügung:
1723
+
1724
+ ```ruby
1725
+ send_file 'foo.png', :type => :jpg
1726
+ ```
1727
+
1728
+ <dl>
1729
+ <dt>filename</dt>
1730
+ <dd>Dateiname als Response. Standardwert ist der eigentliche Dateiname.</dd>
1731
+
1732
+ <dt>last_modified</dt>
1733
+ <dd>Wert für den Last-Modified-Header, Standardwert ist <tt>mtime</tt> der
1734
+ Datei.</dd>
1735
+
1736
+ <dt>type</dt>
1737
+ <dd>Content-Type, der verwendet werden soll. Wird, wenn nicht angegeben, von
1738
+ der Dateiendung abgeleitet.</dd>
1739
+
1740
+ <dt>disposition</dt>
1741
+ <dd>Verwendet für Content-Disposition. Mögliche Werte sind: <tt>nil</tt>
1742
+ (Standard), <tt>:attachment</tt> und <tt>:inline</tt>.</dd>
1743
+
1744
+ <dt>length</dt>
1745
+ <dd>Content-Length-Header. Standardwert ist die Dateigröße.</dd>
1746
+ </dl>
1747
+
1748
+ Soweit vom Rack-Handler unterstützt, werden neben der Übertragung über den
1749
+ Ruby-Prozess auch andere Möglichkeiten genutzt. Bei Verwendung der
1750
+ `send_file`-Helfer-Methode kümmert sich Sinatra selbstständig um die
1751
+ Range-Requests.
1752
+
1753
+ ### Das Request-Objekt
1754
+
1755
+ Auf das `request`-Objekt der eigehenden Anfrage kann vom Anfrage-Scope aus
1756
+ zugegriffen werden:
1757
+
1758
+ ```ruby
1759
+ # App läuft unter http://example.com/example
1760
+ get '/foo' do
1761
+ t = %w[text/css text/html application/javascript]
1762
+ request.accept # ['text/html', '*/*']
1763
+ request.accept? 'text/xml' # true
1764
+ request.preferred_type(t) # 'text/html'
1765
+ request.body # Request-Body des Client (siehe unten)
1766
+ request.scheme # "http"
1767
+ request.script_name # "/example"
1768
+ request.path_info # "/foo"
1769
+ request.port # 80
1770
+ request.request_method # "GET"
1771
+ request.query_string # ""
1772
+ request.content_length # Länge des request.body
1773
+ request.media_type # Medientypus von request.body
1774
+ request.host # "example.com"
1775
+ request.get? # true (ähnliche Methoden für andere Verben)
1776
+ request.form_data? # false
1777
+ request["irgendein_param"] # Wert von einem Parameter; [] ist die Kurzform für den params Hash
1778
+ request.referrer # Der Referrer des Clients oder '/'
1779
+ request.user_agent # User-Agent (verwendet in der :agent Bedingung)
1780
+ request.cookies # Hash des Browser-Cookies
1781
+ request.xhr? # Ist das hier ein Ajax-Request?
1782
+ request.url # "http://example.com/example/foo"
1783
+ request.path # "/example/foo"
1784
+ request.ip # IP-Adresse des Clients
1785
+ request.secure? # false (true wenn SSL)
1786
+ request.forwarded? # true (Wenn es hinter einem Reverse-Proxy verwendet wird)
1787
+ request.env # vollständiger env-Hash von Rack übergeben
1788
+ end
1789
+ ```
1790
+
1791
+ Manche Optionen, wie etwa `script_name` oder `path_info`, sind auch
1792
+ schreibbar:
1793
+
1794
+ ```ruby
1795
+ before { request.path_info = "/" }
1796
+
1797
+ get "/" do
1798
+ "Alle Anfragen kommen hier an!"
1799
+ end
1800
+ ```
1801
+
1802
+ Der `request.body` ist ein IO- oder StringIO-Objekt:
1803
+
1804
+ ```ruby
1805
+ post "/api" do
1806
+ request.body.rewind # falls schon jemand davon gelesen hat
1807
+ daten = JSON.parse request.body.read
1808
+ "Hallo #{daten['name']}!"
1809
+ end
1810
+ ```
1811
+
1812
+ ### Anhänge
1813
+
1814
+ Damit der Browser erkennt, dass ein Response gespeichert und nicht im Browser
1815
+ angezeigt werden soll, kann der `attachment`-Helfer verwendet werden:
1816
+
1817
+ ```ruby
1818
+ get '/' do
1819
+ attachment
1820
+ "Speichern!"
1821
+ end
1822
+ ```
1823
+
1824
+ Ebenso kann eine Dateiname als Parameter hinzugefügt werden:
1825
+
1826
+ ```ruby
1827
+ get '/' do
1828
+ attachment "info.txt"
1829
+ "Speichern!"
1830
+ end
1831
+ ```
1832
+
1833
+ ### Umgang mit Datum und Zeit
1834
+
1835
+ Sinatra bietet eine `time_for`-Helfer-Methode, die aus einem gegebenen Wert ein
1836
+ Time-Objekt generiert. Ebenso kann sie nach `DateTime`, `Date` und ähnliche
1837
+ Klassen konvertieren:
1838
+
1839
+ ```ruby
1840
+ get '/' do
1841
+ pass if Time.now > time_for('Dec 23, 2012')
1842
+ "noch Zeit"
1843
+ end
1844
+ ```
1845
+
1846
+ Diese Methode wird intern für +expires, `last_modiefied` und ihresgleichen
1847
+ verwendet. Mit ein paar Handgriffen lässt sich diese Methode also in ihrem
1848
+ Verhalten erweitern, indem man `time_for` in der eigenen Applikation
1849
+ überschreibt:
1850
+
1851
+ ```ruby
1852
+ helpers do
1853
+ def time_for(value)
1854
+ case value
1855
+ when :yesterday then Time.now - 24*60*60
1856
+ when :tomorrow then Time.now + 24*60*60
1857
+ else super
1858
+ end
1859
+ end
1860
+ end
1861
+
1862
+ get '/' do
1863
+ last_modified :yesterday
1864
+ expires :tomorrow
1865
+ "Hallo"
1866
+ end
1867
+ ```
1868
+
1869
+ ### Nachschlagen von Template-Dateien
1870
+
1871
+ Die `find_template`-Helfer-Methode wird genutzt, um Template-Dateien zum Rendern
1872
+ aufzufinden:
1873
+
1874
+ ```ruby
1875
+ find_template settings.views, 'foo', Tilt[:haml] do |file|
1876
+ puts "könnte diese hier sein: #{file}"
1877
+ end
1878
+ ```
1879
+
1880
+ Das ist zwar nicht wirklich brauchbar, aber wenn man sie überschreibt, kann sie
1881
+ nützlich werden, um eigene Nachschlage-Mechanismen einzubauen. Zum Beispiel
1882
+ dann, wenn mehr als nur ein view-Verzeichnis verwendet werden soll:
1883
+
1884
+ ```ruby
1885
+ set :views, ['views', 'templates']
1886
+
1887
+ helpers do
1888
+ def find_template(views, name, engine, &block)
1889
+ Array(views).each { |v| super(v, name, engine, &block) }
1890
+ end
1891
+ end
1892
+ ```
1893
+
1894
+ Ein anderes Beispiel wäre, verschiedene Vereichnisse für verschiedene Engines
1895
+ zu verwenden:
1896
+
1897
+ ```ruby
1898
+ set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views'
1899
+
1900
+ helpers do
1901
+ def find_template(views, name, engine, &block)
1902
+ _, folder = views.detect { |k,v| engine == Tilt[k] }
1903
+ folder ||= views[:default]
1904
+ super(folder, name, engine, &block)
1905
+ end
1906
+ end
1907
+ ```
1908
+
1909
+ Ebensogut könnte eine Extension aber auch geschrieben und mit anderen geteilt
1910
+ werden!
1911
+
1912
+ Beachte, dass `find_template` nicht prüft, ob eine Datei tatsächlich existiert.
1913
+ Es wird lediglich der angegebene Block aufgerufen und nach allen möglichen
1914
+ Pfaden gesucht. Das ergibt kein Performance-Problem, da `render` `block`
1915
+ verwendet, sobald eine Datei gefunden wurde. Ebenso werden Template-Pfade samt
1916
+ Inhalt gecached, solange nicht im Entwicklungsmodus gearbeitet wird. Das sollte
1917
+ im Hinterkopf behalten werden, wenn irgendwelche verrückten Methoden
1918
+ zusammenbastelt werden.
1919
+
1920
+ ### Konfiguration
1921
+
1922
+ Wird einmal beim Starten in jedweder Umgebung ausgeführt:
1923
+
1924
+ ```ruby
1925
+ configure do
1926
+ # setze eine Option
1927
+ set :option, 'wert'
1928
+
1929
+ # setze mehrere Optionen
1930
+ set :a => 1, :b => 2
1931
+
1932
+ # das gleiche wie `set :option, true`
1933
+ enable :option
1934
+
1935
+ # das gleiche wie `set :option, false`
1936
+ disable :option
1937
+
1938
+ # dynamische Einstellungen mit Blöcken
1939
+ set(:css_dir) { File.join(views, 'css') }
1940
+ end
1941
+ ```
1942
+
1943
+ Läuft nur, wenn die Umgebung (RACK_ENV-Umgebungsvariable) auf `:production`
1944
+ gesetzt ist:
1945
+
1946
+ ```ruby
1947
+ configure :production do
1948
+ ...
1949
+ end
1950
+ ```
1951
+
1952
+ Läuft nur, wenn die Umgebung auf `:production` oder auf `:test` gesetzt ist:
1953
+
1954
+ ```ruby
1955
+ configure :production, :test do
1956
+ ...
1957
+ end
1958
+ ```
1959
+
1960
+ Diese Einstellungen sind über `settings` erreichbar:
1961
+
1962
+ ```ruby
1963
+ configure do
1964
+ set :foo, 'bar'
1965
+ end
1966
+
1967
+ get '/' do
1968
+ settings.foo? # => true
1969
+ settings.foo # => 'bar'
1970
+ ...
1971
+ end
1972
+ ```
1973
+
1974
+ #### Einstellung des Angriffsschutzes
1975
+
1976
+ Sinatra verwendet
1977
+ [Rack::Protection](https://github.com/rkh/rack-protection#readme), um die
1978
+ Anwendung vor häufig vorkommenden Angriffen zu schützen. Diese Voreinstellung
1979
+ lässt sich selbstverständlich deaktivieren, der damit verbundene
1980
+ Geschwindigkeitszuwachs steht aber in keinem Verhätnis zu den möglichen
1981
+ Risiken.
1982
+
1983
+ ```ruby
1984
+ disable :protection
1985
+ ```
1986
+
1987
+ Um einen bestimmten Schutzmechanismus zu deaktivieren, fügt man `protection`
1988
+ einen Hash mit Optionen hinzu:
1989
+
1990
+ ```ruby
1991
+ set :protection, :except => :path_traversal
1992
+ ```
1993
+
1994
+ Neben Strings akzeptiert `:except` auch Arrays, um gleich mehrere
1995
+ Schutzmechanismen zu deaktivieren:
1996
+
1997
+ ```ruby
1998
+ set :protection, :except => [:path_traversal, :session_hijacking]
1999
+ ```
2000
+
2001
+ #### Mögliche Einstellungen
2002
+
2003
+ <dl>
2004
+ <dt>absolute_redirects</dt>
2005
+ <dd>Wenn ausgeschaltet, wird Sinatra relative Redirects zulassen. Jedoch ist
2006
+ Sinatra dann nicht mehr mit RFC 2616 (HTTP 1.1) konform, das nur absolute
2007
+ Redirects zulässt. Sollte eingeschaltet werden, wenn die Applikation hinter
2008
+ einem Reverse-Proxy liegt, der nicht ordentlich eingerichtet ist. Beachte,
2009
+ dass die <tt>url</tt>-Helfer-Methode nach wie vor absolute URLs erstellen
2010
+ wird, es sei denn, es wird als zweiter Parameter <tt>false</tt> angegeben.
2011
+ Standardmäßig nicht aktiviert.</dd>
2012
+
2013
+ <dt>add_charsets</dt>
2014
+ <dd>Mime-Types werden hier automatisch der Helfer-Methode
2015
+ <tt>content_type</tt> zugeordnet. Es empfielt sich, Werte hinzuzufügen statt
2016
+ sie zu überschreiben: <tt>settings.add_charsets << "application/foobar"</tt>
2017
+ </dd>
2018
+
2019
+ <dt>app_file</dt>
2020
+ <dd>Pfad zur Hauptdatei der Applikation. Wird verwendet, um das Wurzel-,
2021
+ Inline-, View- und öffentliche Verzeichnis des Projekts festzustellen.</dd>
2022
+
2023
+ <dt>bind</dt>
2024
+ <dd>IP-Address, an die gebunden wird (Standardwert: <tt>0.0.0.0</tt>
2025
+ <em>oder</em> <tt>localhost</tt>). Wird nur für den eingebauten Server
2026
+ verwendet.</dd>
2027
+
2028
+ <dt>default_encoding</dt>
2029
+ <dd>Das Encoding, falls keines angegeben wurde. Standardwert ist
2030
+ <tt>"utf-8"</tt>.</dd>
2031
+
2032
+ <dt>dump_errors</dt>
2033
+ <dd>Fehler im Log anzeigen.</dd>
2034
+
2035
+ <dt>environment</dt>
2036
+ <dd>Momentane Umgebung. Standardmäßig auf <tt>content_type</tt> oder
2037
+ <tt>"development"</tt> eingestellt, soweit ersteres nicht vorhanden.</dd>
2038
+
2039
+ <dt>logging</dt>
2040
+ <dd>Den Logger verwenden.</dd>
2041
+
2042
+ <dt>lock</dt>
2043
+ <dd>Jeder Request wird gelocked. Es kann nur ein Request pro Ruby-Prozess
2044
+ gleichzeitig verarbeitet werden. Eingeschaltet, wenn die Applikation
2045
+ threadsicher ist. Standardmäßig nicht aktiviert.</dd>
2046
+
2047
+ <dt>method_override</dt>
2048
+ <dd>Verwende <tt>_method</tt>, um put/delete-Formulardaten in Browsern zu
2049
+ verwenden, die dies normalerweise nicht unterstützen.</dd>
2050
+
2051
+ <dt>port</dt>
2052
+ <dd>Port für die Applikation. Wird nur im internen Server verwendet.</dd>
2053
+
2054
+ <dt>prefixed_redirects</dt>
2055
+ <dd>Entscheidet, ob <tt>request.script_name</tt> in Redirects eingefügt wird
2056
+ oder nicht, wenn kein absoluter Pfad angegeben ist. Auf diese Weise verhält
2057
+ sich <tt>redirect '/foo'</tt> so, als wäre es ein <tt>redirect
2058
+ to('/foo')</tt>. Standardmäßig nicht aktiviert.</dd>
2059
+
2060
+ <dt>protection</dt>
2061
+ <dd>Legt fest, ob der Schutzmechanismus für häufig Vorkommende Webangriffe
2062
+ auf Webapplikationen aktiviert wird oder nicht. Weitere Informationen im
2063
+ vorhergehenden Abschnitt.</dd>
2064
+
2065
+ <dt>public_folder</dt>
2066
+ <dd>Das öffentliche Verzeichnis, aus dem Daten zur Verfügung gestellt werden
2067
+ können. Wird nur dann verwendet, wenn statische Daten zur Verfügung gestellt
2068
+ werden können (s.u. <tt>static</tt> Option). Leitet sich von der
2069
+ <tt>app_file</tt> Einstellung ab, wenn nicht gesetzt.</dd>
2070
+
2071
+ <dt>public_dir</dt>
2072
+ <dd>Alias für <tt>public_folder</tt>, s.o.</dd>
2073
+
2074
+ <dt>reload_templates</dt>
2075
+ <dd>Im development-Modus aktiviert.</dd>
2076
+
2077
+ <dt>root</dt>
2078
+ <dd>Wurzelverzeichnis des Projekts. Leitet sich von der <tt>app_file</tt>
2079
+ Einstellung ab, wenn nicht gesetzt.</dd>
2080
+
2081
+ <dt>raise_errors</dt>
2082
+ <dd>Einen Ausnahmezustand aufrufen. Beendet die Applikation. Ist automatisch
2083
+ aktiviert, wenn die Umgebung auf <tt>"test"</tt> eingestellt ist. Ansonsten
2084
+ ist diese Option deaktiviert.</dd>
2085
+
2086
+ <dt>run</dt>
2087
+ <dd>Wenn aktiviert, wird Sinatra versuchen, den Webserver zu starten. Nicht
2088
+ verwenden, wenn Rackup oder anderes verwendet werden soll.</dd>
2089
+
2090
+ <dt>running</dt>
2091
+ <dd>Läuft der eingebaute Server? Diese Einstellung nicht ändern!</dd>
2092
+
2093
+ <dt>server</dt>
2094
+ <dd>Server oder Liste von Servern, die als eingebaute Server zur Verfügung
2095
+ stehen. Die Reihenfolge gibt die Priorität vor, die Voreinstellung hängt von
2096
+ der verwendenten Ruby Implementierung ab.</dd>
2097
+
2098
+ <dt>sessions</dt>
2099
+ <dd>Sessions auf Cookiebasis mittels
2100
+ <tt>Rack::Session::Cookie</tt>aktivieren. Für weitere Infos bitte in der
2101
+ Sektion ‘Sessions verwenden’ nachschauen.</dd>
2102
+
2103
+ <dt>show_exceptions</dt>
2104
+ <dd>Bei Fehlern einen Stacktrace im Browseranzeigen. Ist automatisch
2105
+ aktiviert, wenn die Umgebung auf <tt>"development"</tt> eingestellt ist.
2106
+ Ansonsten ist diese Option deaktiviert. Kann auch auf <tt>:after_handler</tt>
2107
+ gestellt werden, um eine anwendungsspezifische Fehlerbehandlung auszulösen,
2108
+ bevor der Fehlerverlauf im Browser angezeigt wird.</dd>
2109
+
2110
+ <dt>static</dt>
2111
+ <dd>Entscheidet, ob Sinatra statische Dateien zur Verfügung stellen soll oder
2112
+ nicht. Sollte nicht aktiviert werden, wenn ein Server verwendet wird, der
2113
+ dies auch selbstständig erledigen kann. Deaktivieren wird die Performance
2114
+ erhöhen. Standardmäßig aktiviert.</dd>
2115
+
2116
+ <dt>static_cache_control</dt>
2117
+ <dd>Wenn Sinatra statische Daten zur Verfügung stellt, können mit dieser
2118
+ Einstellung die <tt>Cache-Control</tt> Header zu den Responses hinzugefügt
2119
+ werden. Die Einstellung verwendet dazu die <tt>cache_control</tt>
2120
+ Helfer-Methode. Standardmäßig deaktiviert. Ein Array wird verwendet, um
2121
+ mehrere Werte gleichzeitig zu übergeben: <tt>set :static_cache_control,
2122
+ [:public, :max_age => 300]</tt></dd>
2123
+
2124
+ <dt>threaded</dt>
2125
+ <dd>Wird es auf <tt>true</tt> gesetzt, wird Thin aufgefordert
2126
+ <tt>EventMachine.defer</tt> zur Verarbeitung des Requests einzusetzen.</dd>
2127
+
2128
+ <dt>views</dt>
2129
+ <dd>Verzeichnis der Views. Leitet sich von der <tt>app_file</tt> Einstellung
2130
+ ab, wenn nicht gesetzt.</dd>
2131
+
2132
+ <dt>x_cascade</dt>
2133
+ <dd>Einstellung, ob der X-Cascade Header bei fehlender Route gesetzt wird oder
2134
+ nicht. Standardeinstellung ist <tt>true</tt>.</dd>
2135
+ </dl>
2136
+
2137
+ ## Umgebungen
2138
+
2139
+ Es gibt drei voreingestellte Umgebungen in Sinatra: `"development"`,
2140
+ `"production"` und `"test"`. Umgebungen können über die `RACK_ENV`
2141
+ Umgebungsvariable gesetzt werden. Die Standardeinstellung ist `"development"`.
2142
+ In diesem Modus werden alle Templates zwischen Requests neu geladen. Dazu gibt
2143
+ es besondere Fehlerseiten für 404 Stati und Fehlermeldungen. In `"production"`
2144
+ und `"test"` werden Templates automatisch gecached.
2145
+
2146
+ Um die Anwendung in einer anderen Umgebung auszuführen kann man die `-e`
2147
+ Option verwenden:
2148
+
2149
+ ```shell
2150
+ ruby my_app.rb -e [ENVIRONMENT]
2151
+ ```
2152
+
2153
+ In der Anwendung kann man die die Methoden `development?`, `test?` und
2154
+ `production?` verwenden, um die aktuelle Umgebung zu erfahren.
2155
+
2156
+ ## Fehlerbehandlung
2157
+
2158
+ Error-Handler laufen in demselben Kontext wie Routen und Filter, was bedeutet,
2159
+ dass alle Goodies wie `haml`, `erb`, `halt`, etc. verwendet werden können.
2160
+
2161
+ ### Nicht gefunden
2162
+
2163
+ Wenn eine `Sinatra::NotFound`-Exception geworfen wird oder der Statuscode 404
2164
+ ist, wird der `not_found`-Handler ausgeführt:
2165
+
2166
+ ```ruby
2167
+ not_found do
2168
+ 'Seite kann nirgendwo gefunden werden.'
2169
+ end
2170
+ ```
2171
+
2172
+ ### Fehler
2173
+
2174
+ Der `error`-Handler wird immer ausgeführt, wenn eine Exception in einem
2175
+ Routen-Block oder in einem Filter geworfen wurde. Die Exception kann über die
2176
+ `sinatra.error`-Rack-Variable angesprochen werden:
2177
+
2178
+ ```ruby
2179
+ error do
2180
+ 'Entschuldige, es gab einen hässlichen Fehler - ' + env['sinatra.error'].name
2181
+ end
2182
+ ```
2183
+
2184
+ Benutzerdefinierte Fehler:
2185
+
2186
+ ```ruby
2187
+ error MeinFehler do
2188
+ 'Au weia, ' + env['sinatra.error'].message
2189
+ end
2190
+ ```
2191
+
2192
+ Dann, wenn das passiert:
2193
+
2194
+ ```ruby
2195
+ get '/' do
2196
+ raise MeinFehler, 'etwas Schlimmes ist passiert'
2197
+ end
2198
+ ```
2199
+
2200
+ bekommt man dieses:
2201
+
2202
+ ```shell
2203
+ Au weia, etwas Schlimmes ist passiert
2204
+ ```
2205
+
2206
+ Alternativ kann ein Error-Handler auch für einen Status-Code definiert werden:
2207
+
2208
+ ```ruby
2209
+ error 403 do
2210
+ 'Zugriff verboten'
2211
+ end
2212
+
2213
+ get '/geheim' do
2214
+ 403
2215
+ end
2216
+ ```
2217
+
2218
+ Oder ein Status-Code-Bereich:
2219
+
2220
+ ```ruby
2221
+ error 400..510 do
2222
+ 'Hallo?'
2223
+ end
2224
+ ```
2225
+
2226
+ Sinatra setzt verschiedene `not_found`- und `error`-Handler in der
2227
+ Development-Umgebung ein, um hilfreiche Debugging Informationen und Stack Traces
2228
+ anzuzeigen.
2229
+
2230
+ ## Rack-Middleware
2231
+
2232
+ Sinatra baut auf [Rack](http://rack.rubyforge.org/), einem minimalistischen
2233
+ Standard-Interface für Ruby-Webframeworks. Eines der interessantesten Features
2234
+ für Entwickler ist der Support von Middlewares, die zwischen den Server und
2235
+ die Anwendung geschaltet werden und so HTTP-Request und/oder Antwort
2236
+ überwachen und/oder manipulieren können.
2237
+
2238
+ Sinatra macht das Erstellen von Middleware-Verkettungen mit der
2239
+ Top-Level-Methode `use` zu einem Kinderspiel:
2240
+
2241
+ ```ruby
2242
+ require 'sinatra'
2243
+ require 'meine_middleware'
2244
+
2245
+ use Rack::Lint
2246
+ use MeineMiddleware
2247
+
2248
+ get '/hallo' do
2249
+ 'Hallo Welt'
2250
+ end
2251
+ ```
2252
+
2253
+ Die Semantik von `use` entspricht der gleichnamigen Methode der
2254
+ [Rack::Builder](http://rack.rubyforge.org/doc/classes/Rack/Builder.html)-DSL
2255
+ (meist verwendet in Rackup-Dateien). Ein Beispiel dafür ist, dass die
2256
+ `use`-Methode mehrere/verschiedene Argumente und auch Blöcke entgegennimmt:
2257
+
2258
+ ```ruby
2259
+ use Rack::Auth::Basic do |username, password|
2260
+ username == 'admin' && password == 'geheim'
2261
+ end
2262
+ ```
2263
+
2264
+ Rack bietet eine Vielzahl von Standard-Middlewares für Logging, Debugging,
2265
+ URL-Routing, Authentifizierung und Session-Verarbeitung. Sinatra verwendet
2266
+ viele von diesen Komponenten automatisch, abhängig von der Konfiguration. So
2267
+ muss `use` häufig nicht explizit verwendet werden.
2268
+
2269
+ Hilfreiche Middleware gibt es z.B. hier:
2270
+ [rack](https://github.com/rack/rack/tree/master/lib/rack),
2271
+ [rack-contrib](https://github.com/rack/rack-contrib#readme),
2272
+ oder im [Rack wiki](https://github.com/rack/rack/wiki/List-of-Middleware).
2273
+
2274
+ ## Testen
2275
+
2276
+ Sinatra-Tests können mit jedem auf Rack aufbauendem Test-Framework geschrieben
2277
+ werden. [Rack::Test](http://rdoc.info/github/brynary/rack-test/master/frames)
2278
+ wird empfohlen:
2279
+
2280
+ ```ruby
2281
+ require 'my_sinatra_app'
2282
+ require 'test/unit'
2283
+ require 'rack/test'
2284
+
2285
+ class MyAppTest < Test::Unit::TestCase
2286
+ include Rack::Test::Methods
2287
+
2288
+ def app
2289
+ Sinatra::Application
2290
+ end
2291
+
2292
+ def test_my_default
2293
+ get '/'
2294
+ assert_equal 'Hallo Welt!', last_response.body
2295
+ end
2296
+
2297
+ def test_with_params
2298
+ get '/meet', :name => 'Frank'
2299
+ assert_equal 'Hallo Frank!', last_response.body
2300
+ end
2301
+
2302
+ def test_with_rack_env
2303
+ get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
2304
+ assert_equal "Du verwendest Songbird!", last_response.body
2305
+ end
2306
+ end
2307
+ ```
2308
+
2309
+ Hinweis: Wird Sinatra modular verwendet, muss <tt>Sinatra::Application</tt> mit
2310
+ dem Namen der Applikations-Klasse ersetzt werden.
2311
+
2312
+ ## Sinatra::Base - Middleware, Bibliotheken und modulare Anwendungen
2313
+
2314
+ Das Definieren einer Top-Level-Anwendung funktioniert gut für
2315
+ Mikro-Anwendungen, hat aber Nachteile, wenn wiederverwendbare Komponenten wie
2316
+ Middleware, Rails Metal, einfache Bibliotheken mit Server-Komponenten oder
2317
+ auch Sinatra-Erweiterungen geschrieben werden sollen.
2318
+
2319
+ Das Top-Level geht von einer Konfiguration für eine Mikro-Anwendung aus (wie
2320
+ sie z.B. bei einer einzelnen Anwendungsdatei, `./public` und `./views` Ordner,
2321
+ Logging, Exception-Detail-Seite, usw.). Genau hier kommt `Sinatra::Base` ins
2322
+ Spiel:
2323
+
2324
+ ```ruby
2325
+ require 'sinatra/base'
2326
+
2327
+ class MyApp < Sinatra::Base
2328
+ set :sessions, true
2329
+ set :foo, 'bar'
2330
+
2331
+ get '/' do
2332
+ 'Hallo Welt!'
2333
+ end
2334
+ end
2335
+ ```
2336
+
2337
+ Die MyApp-Klasse ist eine unabhängige Rack-Komponente, die als Middleware,
2338
+ Endpunkt oder via Rails Metal verwendet werden kann. Verwendet wird sie durch
2339
+ `use` oder `run` von einer Rackup-`config.ru`-Datei oder als Server-Komponente
2340
+ einer Bibliothek:
2341
+
2342
+ ```ruby
2343
+ MyApp.run! :host => 'localhost', :port => 9090
2344
+ ```
2345
+
2346
+ Die Methoden der `Sinatra::Base`-Subklasse sind genau dieselben wie die der
2347
+ Top-Level-DSL. Die meisten Top-Level-Anwendungen können mit nur zwei
2348
+ Veränderungen zu `Sinatra::Base` konvertiert werden:
2349
+
2350
+ * Die Datei sollte `require 'sinatra/base'` anstelle von `require
2351
+ 'sinatra/base'` aufrufen, ansonsten werden alle von Sinatras DSL-Methoden
2352
+ in den Top-Level-Namespace importiert.
2353
+ * Alle Routen, Error-Handler, Filter und Optionen der Applikation müssen in
2354
+ einer Subklasse von `Sinatra::Base` definiert werden.
2355
+
2356
+
2357
+ `Sinatra::Base` ist ein unbeschriebenes Blatt. Die meisten Optionen sind per
2358
+ Standard deaktiviert. Das betrifft auch den eingebauten Server. Siehe
2359
+ [Optionen und Konfiguration](http://sinatra.github.com/configuration.html) für
2360
+ Details über mögliche Optionen.
2361
+
2362
+ ### Modularer vs. klassischer Stil
2363
+
2364
+ Entgegen häufiger Meinungen gibt es nichts gegen den klassischen Stil
2365
+ einzuwenden. Solange es die Applikation nicht beeinträchtigt, besteht kein
2366
+ Grund, eine modulare Applikation zu erstellen.
2367
+
2368
+ Der größte Nachteil der klassischen Sinatra Anwendung gegenüber einer
2369
+ modularen ist die Einschränkung auf eine Sinatra Anwendung pro Ruby-Prozess.
2370
+ Sollen mehrere zum Einsatz kommen, muss auf den modularen Stil umgestiegen
2371
+ werden. Dabei ist es kein Problem klassische und modulare Anwendungen
2372
+ miteinander zu vermischen.
2373
+
2374
+ Bei einem Umstieg, sollten einige Unterschiede in den Einstellungen beachtet
2375
+ werden:
2376
+
2377
+ <table>
2378
+ <tr>
2379
+ <th>Szenario</th>
2380
+ <th>Classic</th>
2381
+ <th>Modular</th>
2382
+ </tr>
2383
+
2384
+ <tr>
2385
+ <td>app_file</td>
2386
+ <td>Sinatra ladende Datei</td>
2387
+ <td>Sinatra::Base subklassierende Datei</td>
2388
+ </tr>
2389
+
2390
+ <tr>
2391
+ <td>run</td>
2392
+ <td>$0 == app_file</td>
2393
+ <td>false</td>
2394
+ </tr>
2395
+
2396
+ <tr>
2397
+ <td>logging</td>
2398
+ <td>true</td>
2399
+ <td>false</td>
2400
+ </tr>
2401
+
2402
+ <tr>
2403
+ <td>method_override</td>
2404
+ <td>true</td>
2405
+ <td>false</td>
2406
+ </tr>
2407
+
2408
+ <tr>
2409
+ <td>inline_templates</td>
2410
+ <td>true</td>
2411
+ <td>false</td>
2412
+ </tr>
2413
+
2414
+ <tr>
2415
+ <td>static</td>
2416
+ <td>true</td>
2417
+ <td>false</td>
2418
+ </tr>
2419
+ </table>
2420
+
2421
+ ### Eine modulare Applikation bereitstellen
2422
+
2423
+ Es gibt zwei übliche Wege, eine modulare Anwendung zu starten. Zum einen über
2424
+ `run!`:
2425
+
2426
+ ```ruby
2427
+ # mein_app.rb
2428
+ require 'sinatra/base'
2429
+
2430
+ class MeinApp < Sinatra::Base
2431
+ # ... Anwendungscode hierhin ...
2432
+
2433
+ # starte den Server, wenn die Ruby-Datei direkt ausgeführt wird
2434
+ run! if app_file == $0
2435
+ end
2436
+ ```
2437
+
2438
+ Starte mit:
2439
+
2440
+ ```shell
2441
+ ruby mein_app.rb
2442
+ ```
2443
+
2444
+ Oder über eine `config.ru`-Datei, die es erlaubt, einen beliebigen
2445
+ Rack-Handler zu verwenden:
2446
+
2447
+ ```ruby
2448
+ # config.ru (mit rackup starten)
2449
+ require './mein_app'
2450
+ run MeineApp
2451
+ ```
2452
+
2453
+ Starte:
2454
+
2455
+ ```shell
2456
+ rackup -p 4567
2457
+ ```
2458
+
2459
+ ### Eine klassische Anwendung mit einer config.ru verwenden
2460
+
2461
+ Schreibe eine Anwendungsdatei:
2462
+
2463
+ ```ruby
2464
+ # app.rb
2465
+ require 'sinatra'
2466
+
2467
+ get '/' do
2468
+ 'Hallo Welt!'
2469
+ end
2470
+ ```
2471
+
2472
+ sowie eine dazugehörige `config.ru`-Datei:
2473
+
2474
+ ```ruby
2475
+ require './app'
2476
+ run Sinatra::Application
2477
+ ```
2478
+
2479
+ ### Wann sollte eine config.ru-Datei verwendet werden?
2480
+
2481
+ Anzeichen dafür, dass eine `config.ru`-Datei gebraucht wird:
2482
+
2483
+ * Es soll ein anderer Rack-Handler verwendet werden (Passenger, Unicorn,
2484
+ Heroku, ...).
2485
+ * Es gibt mehr als nur eine Subklasse von `Sinatra::Base`.
2486
+ * Sinatra soll als Middleware verwendet werden, nicht als Endpunkt.
2487
+
2488
+
2489
+ **Es gibt keinen Grund, eine `config.ru`-Datei zu verwenden, nur weil eine
2490
+ Anwendung im modularen Stil betrieben werden soll. Ebenso wird keine Anwendung
2491
+ mit modularem Stil benötigt, um eine `config.ru`-Datei zu verwenden.**
2492
+
2493
+ ### Sinatra als Middleware nutzen
2494
+
2495
+ Es ist nicht nur möglich, andere Rack-Middleware mit Sinatra zu nutzen, es
2496
+ kann außerdem jede Sinatra-Anwendung selbst als Middleware vor jeden
2497
+ beliebigen Rack-Endpunkt gehangen werden. Bei diesem Endpunkt muss es sich
2498
+ nicht um eine andere Sinatra-Anwendung handeln, es kann jede andere
2499
+ Rack-Anwendung sein (Rails/Ramaze/Camping/...):
2500
+
2501
+ ```ruby
2502
+ require 'sinatra/base'
2503
+
2504
+ class LoginScreen < Sinatra::Base
2505
+ enable :sessions
2506
+
2507
+ get('/login') { haml :login }
2508
+
2509
+ post('/login') do
2510
+ if params[:name] == 'admin' && params[:password] == 'admin'
2511
+ session['user_name'] = params[:name]
2512
+ else
2513
+ redirect '/login'
2514
+ end
2515
+ end
2516
+ end
2517
+
2518
+ class MyApp < Sinatra::Base
2519
+ # Middleware wird vor Filtern ausgeführt
2520
+ use LoginScreen
2521
+
2522
+ before do
2523
+ unless session['user_name']
2524
+ halt "Zugriff verweigert, bitte <a href='/login'>einloggen</a>."
2525
+ end
2526
+ end
2527
+
2528
+ get('/') { "Hallo #{session['user_name']}." }
2529
+ end
2530
+ ```
2531
+
2532
+ ### Dynamische Applikationserstellung
2533
+
2534
+ Manche Situationen erfordern die Erstellung neuer Applikationen zur Laufzeit,
2535
+ ohne dass sie einer Konstanten zugeordnet werden. Dies lässt sich mit
2536
+ `Sinatra.new` erreichen:
2537
+
2538
+ ```ruby
2539
+ require 'sinatra/base'
2540
+ my_app = Sinatra.new { get('/') { "hallo" } }
2541
+ my_app.run!
2542
+ ```
2543
+
2544
+ Die Applikation kann mit Hilfe eines optionalen Parameters erstellt werden:
2545
+
2546
+ ```ruby
2547
+ # config.ru
2548
+ require 'sinatra/base'
2549
+
2550
+ controller = Sinatra.new do
2551
+ enable :logging
2552
+ helpers MyHelpers
2553
+ end
2554
+
2555
+ map('/a') do
2556
+ run Sinatra.new(controller) { get('/') { 'a' } }
2557
+ end
2558
+
2559
+ map('/b') do
2560
+ run Sinatra.new(controller) { get('/') { 'b' } }
2561
+ end
2562
+ ```
2563
+
2564
+ Das ist besonders dann interessant, wenn Sinatra-Erweiterungen getestet werden
2565
+ oder Sinatra in einer Bibliothek Verwendung findet.
2566
+
2567
+ Ebenso lassen sich damit hervorragend Sinatra-Middlewares erstellen:
2568
+
2569
+ ```ruby
2570
+ require 'sinatra/base'
2571
+
2572
+ use Sinatra do
2573
+ get('/') { ... }
2574
+ end
2575
+
2576
+ run RailsProject::Application
2577
+ ```
2578
+
2579
+ ## Geltungsbereich und Bindung
2580
+
2581
+ Der Geltungsbereich (Scope) legt fest, welche Methoden und Variablen zur
2582
+ Verfügung stehen.
2583
+
2584
+ ### Anwendungs- oder Klassen-Scope
2585
+
2586
+ Jede Sinatra-Anwendung entspricht einer `Sinatra::Base`-Subklasse. Falls die
2587
+ Top- Level-DSL verwendet wird (`require 'sinatra'`), handelt es sich um
2588
+ `Sinatra::Application`, andernfalls ist es jene Subklasse, die explizit
2589
+ angelegt wurde. Auf Klassenebene stehen Methoden wie `get` oder `before` zur
2590
+ Verfügung, es gibt aber keinen Zugriff auf das `request`-Object oder die
2591
+ `session`, da nur eine einzige Klasse für alle eingehenden Anfragen genutzt
2592
+ wird.
2593
+
2594
+ Optionen, die via `set` gesetzt werden, sind Methoden auf Klassenebene:
2595
+
2596
+ ```ruby
2597
+ class MyApp < Sinatra::Base
2598
+ # Hey, ich bin im Anwendungsscope!
2599
+ set :foo, 42
2600
+ foo # => 42
2601
+
2602
+ get '/foo' do
2603
+ # Hey, ich bin nicht mehr im Anwendungs-Scope!
2604
+ end
2605
+ end
2606
+ ```
2607
+
2608
+ Im Anwendungs-Scope befindet man sich:
2609
+
2610
+ * In der Anwendungs-Klasse.
2611
+ * In Methoden, die von Erweiterungen definiert werden.
2612
+ * Im Block, der an `helpers` übergeben wird.
2613
+ * In Procs und Blöcken, die an `set` übergeben werden.
2614
+ * Der an `Sinatra.new` übergebene Block
2615
+
2616
+
2617
+ Auf das Scope-Objekt (die Klasse) kann wie folgt zugegriffen werden:
2618
+
2619
+ * Über das Objekt, das an den `configure`-Block übergeben wird (`configure {
2620
+ |c| ... }`).
2621
+ * `settings` aus den anderen Scopes heraus.
2622
+
2623
+
2624
+ ### Anfrage- oder Instanz-Scope
2625
+
2626
+ Für jede eingehende Anfrage wird eine neue Instanz der Anwendungs-Klasse
2627
+ erstellt und alle Handler in diesem Scope ausgeführt. Aus diesem Scope heraus
2628
+ kann auf `request` oder `session` zugegriffen und Methoden wie `erb` oder
2629
+ `haml` aufgerufen werden. Außerdem kann mit der `settings`-Method auf den
2630
+ Anwendungs-Scope zugegriffen werden:
2631
+
2632
+ ```ruby
2633
+ class MyApp < Sinatra::Base
2634
+ # Hey, ich bin im Anwendungs-Scope!
2635
+ get '/neue_route/:name' do
2636
+ # Anfrage-Scope für '/neue_route/:name'
2637
+ @value = 42
2638
+
2639
+ settings.get "/#{params[:name]}" do
2640
+ # Anfrage-Scope für "/#{params[:name]}"
2641
+ @value # => nil (nicht dieselbe Anfrage)
2642
+ end
2643
+
2644
+ "Route definiert!"
2645
+ end
2646
+ end
2647
+ ```
2648
+
2649
+ Im Anfrage-Scope befindet man sich:
2650
+
2651
+ * In get, head, post, put, delete, options, patch, link und unlink Blöcken
2652
+ * In before und after Filtern
2653
+ * In Helfer-Methoden
2654
+ * In Templates
2655
+
2656
+
2657
+ ### Delegation-Scope
2658
+
2659
+ Vom Delegation-Scope aus werden Methoden einfach an den Klassen-Scope
2660
+ weitergeleitet. Dieser verhält sich jedoch nicht 100%ig wie der Klassen-Scope,
2661
+ da man nicht die Bindung der Klasse besitzt: Nur Methoden, die explizit als
2662
+ delegierbar markiert wurden, stehen hier zur Verfügung und es kann nicht auf
2663
+ die Variablen des Klassenscopes zugegriffen werden (mit anderen Worten: es
2664
+ gibt ein anderes `self`). Weitere Delegationen können mit
2665
+ `Sinatra::Delegator.delegate :methoden_name` hinzugefügt werden.
2666
+
2667
+ Im Delegation-Scop befindet man sich:
2668
+
2669
+ * Im Top-Level, wenn `require 'sinatra'` aufgerufen wurde.
2670
+ * In einem Objekt, das mit dem `Sinatra::Delegator`-Mixin erweitert wurde.
2671
+
2672
+
2673
+ Schau am besten im Code nach: Hier ist [Sinatra::Delegator
2674
+ mixin](http://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1064
2675
+ ) definiert und wird in den [globalen Namespace
2676
+ eingebunden](http://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb
2677
+
2678
+ ## Kommandozeile
2679
+
2680
+ Sinatra-Anwendungen können direkt von der Kommandozeile aus gestartet werden:
2681
+
2682
+ ```shell
2683
+ ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-h HOST] [-s HANDLER]
2684
+ ```
2685
+
2686
+ Die Optionen sind:
2687
+
2688
+ ```
2689
+ -h # Hilfe
2690
+ -p # Port setzen (Standard ist 4567)
2691
+ -h # Host setzen (Standard ist 0.0.0.0)
2692
+ -e # Umgebung setzen (Standard ist development)
2693
+ -s # Rack-Server/Handler setzen (Standard ist thin)
2694
+ -x # Mutex-Lock einschalten (Standard ist off)
2695
+ ```
2696
+
2697
+ ## Systemanforderungen
2698
+
2699
+ Die folgenden Versionen werden offiziell unterstützt:
2700
+
2701
+ <dl>
2702
+ <dt>Ruby 1.8.7</dt>
2703
+ <dd>1.8.7 wird vollständig unterstützt, ein Wechsel zu JRuby oder Rubinius wird
2704
+ aber empfohlen. Ruby 1.8.7 wird noch bis Sinatra 2.0 unterstützt werden. Frühere
2705
+ Versionen von Ruby sind nicht kompatibel mit Sinatra.</dd>
2706
+
2707
+ <dt>Ruby 1.9.2</dt>
2708
+ <dd>1.9.2 wird mindestens bis Sinatra 1.5 voll unterstützt. Version 1.9.2p0
2709
+ sollte nicht verwendet werden, da unter Sinatra immer wieder Segfaults
2710
+ auftreten.</dd>
2711
+
2712
+ <dt>Ruby 1.9.3</dt>
2713
+ <dd>1.9.3 wird vollständig unterstützt und empfohlen. Achtung, bei einem
2714
+ Upgrade von einer früheren Version von Ruby zu Ruby 1.9.3 werden alle Sessions
2715
+ ungültig. Ruby 1.9.3 wird bis Sinatra 2.0 unterstützt werden.</dd>
2716
+
2717
+ <dt>Rubinius</dt>
2718
+ <dd>Rubinius (Version >= 2.x) wird offiziell unterstützt. Es wird empfohlen, den
2719
+ <a href="http://puma.io">Puma Server</a> zu installieren (<tt>gem install puma
2720
+ </tt>)</dd>
2721
+
2722
+ <dt>JRuby</dt>
2723
+ <dd>Aktuelle JRuby Versionen werden offiziell unterstützt. Es wird empfohlen,
2724
+ keine C-Erweiterungen zu verwenden und als Server Trinidad zu verwenden
2725
+ (<tt>gem install trinidad</tt>).</dd>
2726
+ </dl>
2727
+
2728
+ Die nachfolgend aufgeführten Ruby-Implementierungen werden offiziell nicht von
2729
+ Sinatra unterstützt, funktionieren aber normalerweise:
2730
+
2731
+ * Ruby Enterprise Edition
2732
+ * Ältere Versionen von JRuby und Rubinius
2733
+ * MacRuby (<tt>gem install control_tower</tt> wird empfohlen), Maglev, IronRuby
2734
+ * Ruby 1.9.0 und 1.9.1
2735
+
2736
+ Nicht offiziell unterstützt bedeutet, dass wenn Sachen nicht funktionieren,
2737
+ wir davon ausgehen, dass es nicht an Sinatra sondern an der jeweiligen
2738
+ Implementierung liegt.
2739
+
2740
+ Im Rahmen unserer CI (Kontinuierlichen Integration) wird bereits ruby-head
2741
+ (das kommende Ruby 2.1.0) mit eingebunden. Es kann davon ausgegangen
2742
+ werden, dass Sinatra Ruby 2.1.0 vollständig unterstützen wird.
2743
+
2744
+ Sinatra sollte auf jedem Betriebssystem laufen, dass einen funktionierenden
2745
+ Ruby-Interpreter aufweist.
2746
+
2747
+ Sinatra läuft aktuell nicht unter Cardinal, SmallRuby, BlueRuby oder Ruby <= 1.8.7.
2748
+
2749
+ ## Der neuste Stand (The Bleeding Edge)
2750
+
2751
+ Um auf dem neusten Stand zu bleiben, kann der Master-Branch verwendet werden.
2752
+ Er sollte recht stabil sein. Ebenso gibt es von Zeit zu Zeit prerelease Gems,
2753
+ die so installiert werden:
2754
+
2755
+ ```shell
2756
+ gem install sinatra --pre
2757
+ ```
2758
+
2759
+ ### Mit Bundler
2760
+
2761
+ Wenn die Applikation mit der neuesten Version von Sinatra und
2762
+ [Bundler](http://gembundler.com/) genutzt werden soll, empfehlen wir den
2763
+ nachfolgenden Weg.
2764
+
2765
+ Soweit Bundler noch nicht installiert ist:
2766
+
2767
+ ```shell
2768
+ gem install bundler
2769
+ ```
2770
+
2771
+ Anschließend wird eine `Gemfile`-Datei im Projektverzeichnis mit folgendem
2772
+ Inhalt erstellt:
2773
+
2774
+ ```ruby
2775
+ source :rubygems
2776
+ gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
2777
+
2778
+ # evtl. andere Abhängigkeiten
2779
+ gem 'haml' # z.B. wenn du Haml verwendest...
2780
+ gem 'activerecord', '~> 3.0' # ...oder ActiveRecord 3.x
2781
+ ```
2782
+
2783
+ Beachte: Hier sollten alle Abhängigkeiten eingetragen werden. Sinatras eigene,
2784
+ direkte Abhängigkeiten (Tilt und Rack) werden von Bundler automatisch aus dem
2785
+ Gemfile von Sinatra hinzugefügt.
2786
+
2787
+ Jetzt kannst du deine Applikation starten:
2788
+
2789
+ ```shell
2790
+ bundle exec ruby myapp.rb
2791
+ ```
2792
+
2793
+ ### Eigenes Repository
2794
+ Um auf dem neuesten Stand von Sinatras Code zu sein, kann eine lokale Kopie
2795
+ angelegt werden. Gestartet wird in der Anwendung mit dem `sinatra/lib`-Ordner
2796
+ im `LOAD_PATH`:
2797
+
2798
+ ```shell
2799
+ cd myapp
2800
+ git clone git://github.com/sinatra/sinatra.git
2801
+ ruby -Isinatra/lib myapp.rb
2802
+ ```
2803
+
2804
+ Alternativ kann der `sinatra/lib`-Ordner zum `LOAD_PATH` in der Anwendung
2805
+ hinzugefügt werden:
2806
+
2807
+ ```ruby
2808
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib'
2809
+ require 'rubygems'
2810
+ require 'sinatra'
2811
+
2812
+ get '/ueber' do
2813
+ "Ich laufe auf Version " + Sinatra::VERSION
2814
+ end
2815
+ ```
2816
+
2817
+ Um Sinatra-Code von Zeit zu Zeit zu aktualisieren:
2818
+
2819
+ ```shell
2820
+ cd myproject/sinatra
2821
+ git pull
2822
+ ```
2823
+
2824
+ ### Gem erstellen
2825
+
2826
+ Aus der eigenen lokalen Kopie kann nun auch ein globales Gem gebaut werden:
2827
+
2828
+ ```shell
2829
+ git clone git://github.com/sinatra/sinatra.git
2830
+ cd sinatra
2831
+ rake sinatra.gemspec
2832
+ rake install
2833
+ ```
2834
+
2835
+ Falls Gems als Root installiert werden sollen, sollte die letzte Zeile
2836
+ folgendermaßen lauten:
2837
+
2838
+ ```shell
2839
+ sudo rake install
2840
+ ```
2841
+
2842
+ ## Versions-Verfahren
2843
+
2844
+ Sinatra folgt dem sogenannten [Semantic Versioning](http://semver.org/), d.h.
2845
+ SemVer und SemVerTag.
2846
+
2847
+ ## Mehr
2848
+
2849
+ * [Projekt-Website](http://sinatra.github.com/) - Ergänzende Dokumentation,
2850
+ News und Links zu anderen Ressourcen.
2851
+ * [Mitmachen](http://sinatra.github.com/contributing.html) - Einen Fehler
2852
+ gefunden? Brauchst du Hilfe? Hast du einen Patch?
2853
+ * [Issue-Tracker](http://github.com/sinatra/sinatra/issues)
2854
+ * [Twitter](http://twitter.com/sinatra)
2855
+ * [Mailing-Liste](http://groups.google.com/group/sinatrarb)
2856
+ * [#sinatra](irc://chat.freenode.net/#sinatra) auf http://freenode.net Es
2857
+ gibt dort auch immer wieder deutschsprachige Entwickler, die gerne weiterhelfen.
2858
+ * [Sinatra Book](http://sinatra-book.gittr.com) Kochbuch Tutorial
2859
+ * [Sinatra Recipes](http://recipes.sinatrarb.com/) Sinatra-Rezepte aus der
2860
+ Community
2861
+ * API Dokumentation für die [aktuelle
2862
+ Version](http://rubydoc.info/gems/sinatra) oder für
2863
+ [HEAD](http://rubydoc.info/github/sinatra/sinatra) auf http://rubydoc.info
2864
+ * [CI Server](http://travis-ci.org/sinatra/sinatra)