File: README.de.rdoc

package info (click to toggle)
ruby-sinatra 1.3.2-2
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 1,200 kB
  • sloc: ruby: 7,675; makefile: 5
file content (1961 lines) | stat: -rw-r--r-- 60,746 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
= Sinatra

<i>Wichtig: Dieses Dokument ist eine Übersetzung aus dem Englischen und unter
Umständen nicht auf dem aktuellen Stand.</i>

Sinatra ist eine DSL, die das schnelle Erstellen von Webanwendungen in Ruby
mit minimalem Aufwand ermöglicht:

  # myapp.rb
  require 'sinatra'
  get '/' do
    'Hallo Welt!'
  end

Einfach via +rubygems+ installieren und starten:

  gem install sinatra
  ruby -rubygems myapp.rb

Die Seite kann nun unter http://localhost:4567 betrachtet werden.

Es wird empfohlen, den Thin-Server via <tt>gem install thin</tt> zu 
installieren, den Sinatra dann, soweit vorhanden, automatisch verwendet.

== Routen

In Sinatra wird eine Route durch eine HTTP-Methode und ein URL-Muster
definiert. Jeder dieser Routen wird ein Ruby-Block zugeordnet:

  get '/' do
    .. zeige etwas ..
  end

  post '/' do
    .. erstelle etwas ..
  end

  put '/' do
    .. update etwas ..
  end

  delete '/' do
    .. entferne etwas ..
  end
  
  options '/' do
    .. zeige, was wir können ..
  end

Die Routen werden in der Reihenfolge durchlaufen, in der sie definiert wurden.
Das erste Routen-Muster, das mit dem Request übereinstimmt, wird ausgeführt.

Die Muster der Routen können benannte Parameter beinhalten, die über den
<tt>params</tt>-Hash zugänglich gemacht werden:

  get '/hallo/:name' do
    # passt auf "GET /hallo/foo" und "GET /hallo/bar"
    # params[:name] ist 'foo' oder 'bar'
    "Hallo #{params[:name]}!"
  end

Man kann auf diese auch mit Block-Parametern zugreifen:

  get '/hallo/:name' do |n|
    "Hallo #{n}!"
  end

Routen-Muster können auch mit Splat- oder Wildcard-Parametern über das
<tt>params[:splat]</tt>-Array angesprochen werden:

  get '/sag/*/zu/*' do
    # passt auf /sag/hallo/zu/welt
    params[:splat] # => ["hallo", "welt"]
  end

  get '/download/*.*' do
    # passt auf /download/pfad/zu/datei.xml
    params[:splat] # => ["pfad/zu/datei", "xml"]
  end

Oder mit Block-Parametern:

  get '/download/*.*' do |pfad, endung|
    [pfad, endung] # => ["Pfad/zu/Datei", "xml"]
  end
  
Routen mit regulären Ausdrücken sind auch möglich:

  get %r{/hallo/([\w]+)} do
    "Hallo, #{params[:captures].first}!"
  end

Und auch hier können Block-Parameter genutzt werden:

  get %r{/hallo/([\w]+)} do |c|
    "Hallo, #{c}!"
  end

Routen-Muster können auch mit optionalen Parametern ausgestattet werden:

  get '/posts.?:format?' do
    # passt auf "GET /posts" sowie jegliche Erweiterung 
    # wie "GET /posts.json", "GET /posts.xml" etc.
  end

Anmerkung: Solange man den sog. Path Traversal Attack-Schutz nicht deaktiviert
(siehe weiter unten), kann es sein, dass der Request-Pfad noch vor dem Abgleich
mit den Routen modifiziert wird.

=== Bedingungen

An Routen können eine Vielzahl von Bedingungen angehängt werden, die erfüllt
sein müssen, damit der Block ausgeführt wird. Möglich wäre etwa eine
Einschränkung des User-Agents:

  get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
    "Du verwendest Songbird Version #{params[:agent][0]}"
  end

  get '/foo' do
    # passt auf andere Browser
  end

Andere mitgelieferte Bedingungen sind +host_name+ und +provides+:

  get '/', :host_name => /^admin\./ do
    "Adminbereich, Zugriff verweigert!"
  end

  get '/', :provides => 'html' do
    haml :index
  end

  get '/', :provides => ['rss', 'atom', 'xml'] do
    builder :feed
  end

Es können auch andere Bedingungen relativ einfach hinzugefügt werden:

  set(:probability) { |value| condition { rand <= value } }

  get '/auto_gewinnen', :probability => 0.1 do
    "Du hast gewonnen!"
  end

  get '/auto_gewinnen' do
    "Tut mir leid, verloren."
  end

Bei Bedingungen, die mehrere Werte annehmen können, sollte ein Splat verwendet 
werden:

  set(:auth) do |*roles|   # <- hier kommt der Splat ins Spiel
    condition do
      unless logged_in? && roles.any? {|role| current_user.in_role? role }
        redirect "/login/", 303 
      end
    end
  end

  get "/mein/account/", :auth => [:user, :admin] do
    "Mein Account"
  end
  
  get "/nur/admin/", :auth => :admin do
    "Nur Admins dürfen hier rein!"
  end

=== Rückgabewerte

Durch den Rückgabewert eines Routen-Blocks wird mindestens der Response-Body
festgelegt, der an den HTTP-Client, bzw. die nächste Rack-Middleware,
weitergegeben wird. Im Normalfall handelt es sich hierbei, wie in den 
vorangehenden Beispielen zu sehen war, um einen String. Es werden allerdings 
auch andere Werte akzeptiert.

Es kann jedes gültige Objekt zurückgegeben werden, bei dem es sich entweder um
einen Rack-Rückgabewert, einen Rack-Body oder einen HTTP-Status-Code handelt:

* Ein Array mit drei Elementen: <tt>[Status (Fixnum), Headers (Hash), 
  Response-Body (antwortet auf #each)]</tt>.
* Ein Array mit zwei Elementen: <tt>[Status (Fixnum), Response-Body (antwortet
  auf #each)]</tt>.
* Ein Objekt, das auf <tt>#each</tt> antwortet und den an diese Methode 
  übergebenen Block nur mit Strings als Übergabewerte aufruft.
* Ein Fixnum, das den Status-Code festlegt.

Damit lässt sich relativ einfach Streaming implementieren:

    class Stream
      def each
        100.times { |i| yield "#{i}\n" }
      end
    end

    get('/') { Stream.new }

Ebenso kann die +stream+-Helfer-Methode (s.u.) verwendet werden, die Streaming
direkt in die Route integriert.

=== Eigene Routen-Muster 

Wie oben schon beschrieben, ist Sinatra von Haus aus mit Unterstützung für 
String-Muster und Reguläre Ausdrücke zum Abgleichen von Routen ausgestattet.
Das muss aber noch nicht alles sein, es können ohne großen Aufwand eigene
Routen-Muster erstellt werden:

  class AllButPattern
    Match = Struct.new(:captures)

    def initialize(except)
      @except   = except
      @captures = Match.new([])
    end

    def match(str)
      @captures unless @except === str
    end
  end

  def all_but(pattern)
    AllButPattern.new(pattern)
  end

  get all_but("/index") do
    # ...
  end

Beachte, dass das obige Beispiel etwas übertrieben wirkt. Es geht auch 
einfacher:

  get // do
    pass if request.path_info == "/index"
    # ...
  end

Oder unter Verwendung eines negativen look ahead:

  get %r{^(?!/index$)} do
    # ...
  end

== Statische Dateien

Statische Dateien werden aus dem <tt>./public</tt>-Ordner ausgeliefert. Es ist
möglich, einen anderen Ort zu definieren, indem man die
<tt>:public_folder</tt>-Option setzt:

  set :public_folder, File.dirname(__FILE__) + '/static'

Zu beachten ist, dass der Ordnername public nicht Teil der URL ist. Die Datei
<tt>./public/css/style.css</tt> ist unter
<tt>http://example.com/css/style.css</tt> zu finden.

Um den <tt>Cache-Control</tt>-Header mit Informationen zu versorgen, verwendet
man die <tt>:static_cache_control</tt>-Einstellung (s.u.).

== Views/Templates

Standardmäßig wird davon ausgegangen, dass sich Templates im
<tt>./views</tt>-Ordner befinden. Es kann jedoch ein anderer Ordner festgelegt
werden:

  set :views, File.dirname(__FILE__) + '/templates'

Es ist zu beachten, dass immer mit Symbolen auf Templates verwiesen werden 
muss, auch dann, wenn sie sich in einem Unterordner befinden:

  haml :'unterverzeichnis/template'

Rendering-Methoden rendern jeden String direkt.

=== Verfügbare Templatesprachen

Einige Sprachen haben mehrere Implementierungen. Um festzulegen, welche
verwendet wird (und dann auch Thread-sicher ist), verwendet man am besten zu
Beginn ein 'require':

  require 'rdiscount' # oder require 'bluecloth'
  get('/') { markdown :index }

=== Haml Templates

Abhängigkeit::        {haml}[http://haml-lang.com/]
Dateierweiterungs::   <tt>.haml</tt>
Beispiel::           <tt>haml :index, :format => :html5</tt>

=== Erb Templates

Abhängigkeit::        {erubis}[http://www.kuwata-lab.com/erubis/] oder
                    erb (included in Ruby)
Dateierweiterungs::   <tt>.erb</tt>, <tt>.rhtml</tt> oder <tt>.erubis</tt> 
                    (nur Erubis)
Beispiel::           <tt>erb :index</tt>

=== Builder Templates

Abhängigkeit::        {builder}[http://builder.rubyforge.org/]
Dateierweiterungs::   <tt>.builder</tt>
Beispiel::           <tt>builder { |xml| xml.em "Hallo" }</tt>

Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).

=== Nokogiri Templates

Abhängigkeit::        {nokogiri}[http://nokogiri.org/]
Dateierweiterungs::   <tt>.nokogiri</tt>
Beispiel::           <tt>nokogiri { |xml| xml.em "Hallo" }</tt>

Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).

=== Sass Templates

Abhängigkeit::        {sass}[http://sass-lang.com/]
Dateierweiterungs::   <tt>.sass</tt>
Beispiel::           <tt>sass :stylesheet, :style => :expanded</tt>

=== SCSS Templates

Abhängigkeit::        {sass}[http://sass-lang.com/]
Dateierweiterungs::   <tt>.scss</tt>
Beispiel::           <tt>scss :stylesheet, :style => :expanded</tt>

=== Less Templates

Abhängigkeit::        {less}[http://www.lesscss.org/]
Dateierweiterungs::   <tt>.less</tt>
Beispiel::           <tt>less :stylesheet</tt>

=== Liquid Templates

Abhängigkeit::        {liquid}[http://www.liquidmarkup.org/]
Dateierweiterungs::   <tt>.liquid</tt>
Beispiel::           <tt>liquid :index, :locals => { :key => 'Wert' }</tt>

Da man aus dem Liquid-Template heraus keine Ruby-Methoden aufrufen kann 
(ausgenommen +yield+), wird man üblicherweise locals verwenden wollen, mit 
denen man Variablen weitergibt.

=== Markdown Templates

Abhängigkeit::        {rdiscount}[https://github.com/rtomayko/rdiscount],
                    {redcarpet}[https://github.com/tanoku/redcarpet],
                    {bluecloth}[http://deveiate.org/projects/BlueCloth],
                    {kramdown}[http://kramdown.rubyforge.org/] *oder*
                    {maruku}[http://maruku.rubyforge.org/]
Dateierweiterungs::   <tt>.markdown</tt>, <tt>.mkd</tt> und <tt>.md</tt>
Beispiel::           <tt>markdown :index, :layout_engine => :erb</tt>

Da man aus den Markdown-Templates heraus keine Ruby-Methoden aufrufen und auch
keine locals verwenden kann, wird man Markdown üblicherweise in Kombination mit
anderen Renderern verwenden wollen:

  erb :overview, :locals => { :text => markdown(:einfuehrung) }

Beachte, dass man die +markdown+-Methode auch aus anderen Templates heraus
aufrufen kann:

  %h1 Gruß von Haml!
  %p= markdown(:Grüsse)

Da man Ruby nicht von Markdown heraus aufrufen kann, können auch Layouts nicht
in Markdown geschrieben werden. Es ist aber möglich, einen Renderer für die 
Templates zu verwenden und einen anderen für das Layout, indem die 
<tt>:layout_engine</tt>-Option verwendet wird.

=== Textile Templates

Abhängigkeit::        {RedCloth}[http://redcloth.org/]
Dateierweiterungs::   <tt>.textile</tt>
Beispiel::           <tt>textile :index, :layout_engine => :erb</tt>

Da man aus dem Textile-Template heraus keine Ruby-Methoden aufrufen und auch
keine locals verwenden kann, wird man Textile üblicherweise in Kombination mit
anderen Renderern verwenden wollen:

  erb :overview, :locals => { :text => textile(:einfuehrung) }

Beachte, dass man die +textile+-Methode auch aus anderen Templates heraus
aufrufen kann:

  %h1 Gruß von Haml!
  %p= textile(:Grüsse)

Da man Ruby nicht von Textile heraus aufrufen kann, können auch Layouts nicht
in Textile geschrieben werden. Es ist aber möglich, einen Renderer für die 
Templates zu verwenden und einen anderen für das Layout, indem die 
<tt>:layout_engine</tt>-Option verwendet wird.

=== RDoc Templates

Abhängigkeit::        {rdoc}[http://rdoc.rubyforge.org/]
Dateierweiterungs::   <tt>.rdoc</tt>
Beispiel::           <tt>textile :README, :layout_engine => :erb</tt>

Da man aus dem RDoc-Template heraus keine Ruby-Methoden aufrufen und auch
keine locals verwenden kann, wird man RDoc üblicherweise in Kombination mit
anderen Renderern verwenden wollen:

  erb :overview, :locals => { :text => rdoc(:einfuehrung) }

Beachte, dass man die +rdoc+-Methode auch aus anderen Templates heraus
aufrufen kann:

  %h1 Gruß von Haml!
  %p= rdoc(:Grüße)

Da man Ruby nicht von RDoc heraus aufrufen kann, können auch Layouts nicht
in RDoc geschrieben werden. Es ist aber möglich, einen Renderer für die 
Templates zu verwenden und einen anderen für das Layout, indem die 
<tt>:layout_engine</tt>-Option verwendet wird.

=== Radius Templates

Abhängigkeit::        {radius}[http://radius.rubyforge.org/]
Dateierweiterungs::   <tt>.radius</tt>
Beispiel::           <tt>radius :index, :locals => { :key => 'Wert' }</tt>

Da man aus dem Radius-Template heraus keine Ruby-Methoden aufrufen kann, wird
man üblicherweise locals verwenden wollen, mit denen man Variablen weitergibt.

=== Markaby Templates

Abhängigkeit::        {markaby}[http://markaby.github.com/]
Dateierweiterungs::   <tt>.mab</tt>
Beispiel::           <tt>markaby { h1 "Willkommen!" }</tt>

Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).

=== Slim Templates

Abhängigkeit::        {slim}[http://slim-lang.com/]
Dateierweiterungs::   <tt>.slim</tt>
Beispiel::           <tt>slim :index</tt>

=== Creole Templates

Abhängigkeit::        {creole}[https://github.com/minad/creole]
Dateierweiterungs::   <tt>.creole</tt>
Beispiel::           <tt>creole :wiki, :layout_engine => :erb</tt>

Da man aus dem Creole-Template heraus keine Ruby-Methoden aufrufen und auch
keine locals verwenden kann, wird man Creole üblicherweise in Kombination mit
anderen Renderern verwenden wollen:

  erb :overview, :locals => { :text => creole(:einfuehrung) }

Beachte, dass man die +creole+-Methode auch aus anderen Templates heraus
aufrufen kann:

  %h1 Gruß von Haml!
  %p= creole(:Grüße)

Da man Ruby nicht von Creole heraus aufrufen kann, können auch Layouts nicht
in Creole geschrieben werden. Es ist aber möglich, einen Renderer für die 
Templates zu verwenden und einen anderen für das Layout, indem die 
<tt>:layout_engine</tt>-Option verwendet wird.

=== CoffeeScript Templates

Abhängigkeit::        {coffee-script}[https://github.com/josh/ruby-coffee-script]
                      und eine {Möglichkeit JavaScript auszuführen}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
Dateierweiterungs::   <tt>.coffee</tt>
Beispiel::            <tt>coffee :index</tt>

=== Eingebettete Templates

  get '/' do
    haml '%div.title Hallo Welt'
  end

Rendert den eingebetteten Template-String.

=== Auf Variablen in Templates zugreifen

Templates werden in demselben Kontext ausgeführt wie Routen. Instanzvariablen 
in Routen sind auch direkt im Template verfügbar:

  get '/:id' do
    @foo = Foo.find(params[:id])
    haml '%h1= @foo.name'
  end

Oder durch einen expliziten Hash von lokalen Variablen:

  get '/:id' do
    foo = Foo.find(params[:id])
    haml '%h1= bar.name', :locals => { :bar => foo }
  end

Dies wird typischerweise bei Verwendung von Subtemplates (partials) in anderen
Templates eingesetzt.

=== Inline-Templates

Templates können auch am Ende der Datei definiert werden:

  require 'sinatra'

  get '/' do
    haml :index
  end

  __END__

  @@ layout
  %html
    = yield

  @@ index
  %div.title Hallo Welt!!!!!

Anmerkung: Inline-Templates, die in der Datei definiert sind, die <tt>require
'sinatra'</tt> aufruft, werden automatisch geladen. Um andere Inline-Templates
in anderen Dateien aufzurufen, muss explizit <tt>enable :inline_templates</tt> 
verwendet werden.

=== Benannte Templates

Templates können auch mit der Top-Level <tt>template</tt>-Methode definiert
werden:

  template :layout do
    "%html\n  =yield\n"
  end

  template :index do
    '%div.title Hallo Welt!'
  end

  get '/' do
    haml :index
  end

Wenn ein Template mit dem Namen "layout" existiert, wird es bei jedem Aufruf
verwendet. Durch <tt>:layout => false</tt> kann das Ausführen verhindert 
werden:

  get '/' do
    haml :index, :layout => request.xhr?
  end

=== Dateiendungen zuordnen

Um eine Dateiendung einer Template-Engine zuzuordnen, kann 
<tt>Tilt.register</tt> genutzt werden. Wenn etwa die Dateiendung +tt+ für 
Textile-Templates genutzt werden soll, lässt sich dies wie folgt 
bewerkstelligen:

  Tilt.register :tt, Tilt[:textile]

=== Eine eigene Template-Engine hinzufügen

Zu allererst muss die Engine bei Tilt registriert und danach eine 
Rendering-Methode erstellt werden:

  Tilt.register :mtt, MeineTolleTemplateEngine

  helpers do
    def mtt(*args) render(:mtt, *args) end
  end

  get '/' do
    mtt :index
  end

Dieser Code rendert <tt>./views/application.mtt</tt>. Siehe
github.com/rtomayko/tilt[https://github.com/rtomayko/tilt], um mehr über Tilt 
zu lernen.

== Filter

Before-Filter werden vor jedem Request in demselben Kontext, wie danach die 
Routen, ausgeführt. So können etwa Request und Antwort geändert werden.
Gesetzte Instanzvariablen in Filtern können in Routen und Templates verwendet
werden:

  before do
    @note = 'Hi!'
    request.path_info = '/foo/bar/baz'
  end

  get '/foo/*' do
    @note #=> 'Hi!'
    params[:splat] #=> 'bar/baz'
  end

After-Filter werden nach jedem Request in demselben Kontext ausgeführt und
können ebenfalls Request und Antwort ändern. In Before-Filtern gesetzte
Instanzvariablen können in After-Filtern verwendet werden:

  after do
    puts response.status
  end

Filter können optional auch mit einem Muster ausgestattet werden, welches auf
den Request-Pfad passen muss, damit der Filter ausgeführt wird:

  before '/protected/*' do
    authenticate!
  end

  after '/create/:slug' do |slug|
    session[:last_slug] = slug
  end

Ähnlich wie Routen können Filter auch mit weiteren Bedingungen eingeschränkt
werden:

  before :agent => /Songbird/ do
    # ...
  end
  
  after '/blog/*', :host_name => 'example.com' do
    # ...
  end

== Helfer

Durch die Top-Level <tt>helpers</tt>-Methode werden sogenannte Helfer-Methoden
definiert, die in Routen und Templates verwendet werden können:

  helpers do
    def bar(name)
      "#{name}bar"
    end
  end

  get '/:name' do
    bar(params[:name])
  end

=== Sessions verwenden
Sessions werden verwendet, um Zustände zwischen den Requests zu speichern.
Sind sie aktiviert, kann ein Session-Hash je Benutzer-Session verwendet werden.

  enable :sessions

  get '/' do
    "value = " << session[:value].inspect
  end

  get '/:value' do
    session[:value] = params[:value]
  end

Beachte, dass <tt>enable :sessions</tt> alle Daten in einem Cookie speichert.
Unter Umständen kann dies negative Effekte haben, z.B. verursachen viele Daten
höheren, teilweise überflüssigen Traffic. Um das zu vermeiden, kann eine Rack-
Session-Middleware verwendet werden. Dabei wird auf <tt>enable :sessions</tt>
verzichtet und die Middleware wie üblich im Programm eingebunden:

  use Rack::Session::Pool, :expire_after => 2592000

  get '/' do
    "value = " << session[:value].inspect
  end

  get '/:value' do
    session[:value] = params[:value]
  end

Um die Sicherheit zu erhöhen, werden Cookies, die Session-Daten führen, mit
einem sogenannten Session-Secret signiert. Da sich dieses Geheimwort bei jedem
Neustart der Applikation automatisch ändert, ist es sinnvoll, ein eigenes zu
wählen, damit sich alle Instanzen der Applikation dasselbe Session-Secret
teilen:

  set :session_secret, 'super secret'

Zur weiteren Konfiguration kann man einen Hash mit Optionen in den +sessions+
Einstellungen ablegen.

  set :sessions, :domain => 'foo.com'

== Anhalten

Zum sofortigen Stoppen eines Request in einem Filter oder einer Route:

  halt

Der Status kann beim Stoppen auch angegeben werden:

  halt 410

Oder auch den Response-Body:

  halt 'Hier steht der Body'

Oder beides:

  halt 401, 'verschwinde!'

Sogar mit Headern:

  halt 402, {'Content-Type' => 'text/plain'}, 'Rache'

Natürlich ist es auch möglich, ein Template mit +halt+ zu verwenden:

  halt erb(:error)

== Weiterspringen

Eine Route kann mittels <tt>pass</tt> zu der nächsten passenden Route springen:

  get '/raten/:wer' do
    pass unless params[:wer] == 'Frank'
    'Du hast mich!'
  end

  get '/raten/*' do
    'Du hast mich nicht!'
  end

Der Block wird sofort verlassen und es wird nach der nächsten treffenden Route
gesucht. Ein 404-Fehler wird zurückgegeben, wenn kein treffendes Routen-Muster
gefunden wird.

=== Eine andere Route ansteuern

Manchmal entspricht +pass+ nicht den Anforderungen, wenn das Ergebnis einer
anderen Route gefordert wird. Um das zu erreichen, lässt sich +call+ nutzen:

  get '/foo' do
    status, headers, body = call env.merge("PATH_INFO" => '/bar')
    [status, headers, body.map(&:upcase)]
  end

  get '/bar' do
    "bar"
  end

Beachte, dass in dem oben angegeben Beispiel die Performance erheblich erhöht
werden kann, wenn <tt>"bar"</tt> in eine Helfer-Methode umgewandelt wird, auf 
die <tt>/foo</tt> und <tt>/bar</tt> zugreifen können.

Wenn der Request innerhalb derselben Applikations-Instanz aufgerufen und keine
Kopie der Instanz erzeugt werden soll, kann <tt>call!</tt> anstelle von
+call+ verwendet werden.

Die Rack-Spezifikationen enthalten weitere Informationen zu +call+.

=== Body, Status-Code und Header setzen

Es ist möglich und empfohlen, den Status-Code sowie den Response-Body mit einem
Returnwert in der Route zu setzen. In manchen Situationen kann es jedoch sein,
dass der Body an irgendeiner anderen Stelle während der Ausführung gesetzt
wird. Das lässt sich mit der Helfer-Methode +body+ bewerkstelligen. Wird +body+
verwendet, lässt sich der Body jederzeit über diese Methode aufrufen:

  get '/foo' do
    body "bar"
  end
  
  after do
    puts body
  end

Ebenso ist es möglich, einen Block an +body+ weiterzureichen, der dann vom
Rack-Handler ausgeführt wird (lässt sich z.B. zur Umsetzung von Streaming 
einsetzen, siehe auch "Rückgabewerte").

Vergleichbar mit +body+ lassen sich auch Status-Code und Header setzen:

  get '/foo' do
    status 418
    headers \
      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN",
      "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
    halt "Ich bin ein Teekesselchen"
  end

Genau wie bei +body+ liest ein Aufrufen von +headers+ oder +status+ ohne
Argumente den aktuellen Wert aus.

=== Response-Streams

In manchen Situationen sollen Daten bereits an den Client zurückgeschickt 
werden, bevor ein vollständiger Response bereit steht. Manchmal will man die 
Verbindung auch erst dann beenden und Daten so lange an den Client 
zurückschicken, bis er die Verbindung abbricht. Für diese Fälle gibt es die 
+stream+-Helfer-Methode, die es einem erspart eigene Lösungen zu schreiben:

  get '/' do
    stream do |out|
      out << "Das ist ja mal wieder fanta -\n"
      sleep 0.5
      out << " (bitte warten…) \n"
      sleep 1
      out << "- stisch!\n"
    end
  end

Damit lassen sich Streaming-APIs realisieren, sog. {Server Sent Events}[http://dev.w3.org/html5/eventsource/]
die als Basis für {WebSockets}[http://en.wikipedia.org/wiki/WebSocket] dienen.
Ebenso können sie verwendet werden, um den Durchsatz zu erhöhen, wenn ein Teil
der Daten von langsamen Ressourcen abhängig ist.

Es ist zu beachten, dass das Verhalten beim Streaming, insbesondere die Anzahl
nebenläufiger Anfragen, stark davon abhängt, welcher Webserver für die 
Applikation verwendet wird. Einige Server, z.B. WEBRick, unterstützen Streaming
nicht oder nur teilweise. Sollte der Server Streaming nicht unterstützen, wird
ein vollständiger Response-Body zurückgeschickt, sobald der an +stream+ 
weitergegebene Block abgearbeitet ist.

Ist der optionale Parameter +keep_open+ aktiviert, wird beim gestreamten Objekt
+close+ nicht aufgerufen und es ist einem überlassen dies an einem beliebigen 
späteren Zeitpunkt nachholen. Die Funktion ist jedoch nur bei Event-gesteuerten 
Serven wie Thin oder Rainbows möglich, andere Server werden trotzdem den Stream 
beenden:

  set :server, :thin
  connections = []

  get '/' do
    # Den Stream offen halten
    stream(:keep_open) { |out| connections << out }
  end

  post '/' do
    # In alle offenen Streams schreiben
    connections.each { |out| out << params[:message] << "\n" }
    "Nachricht verschickt"
  end
  
=== Logger

Im Geltungsbereich eines Request stellt die +logger+ Helfer-Methode eine
+Logger+ Instanz zur Verfügung:

  get '/' do
    logger.info "es passiert gerade etwas"
    # ...
  end

Der Logger übernimmt dabei automatisch alle im Rack-Handler eingestellten Log-
Vorgaben. Ist Loggen ausgeschaltet, gibt die Methode ein Leerobjekt zurück.
In den Routen und Filtern muss man sich also nicht weiter darum kümmern.

Beachte, dass das Loggen standardmäßig nur für <tt>Sinatra::Application</tt>
voreingestellt ist. Wird über <tt>Sinatra::Base</tt> vererbt, muss es erst
aktiviert werden:

  class MyApp < Sinatra::Base
    configure(:production, :development) do
      enable :logging
    end
  end

== Mime-Types

Wenn <tt>send_file</tt> oder statische Dateien verwendet werden, kann es
vorkommen, dass Sinatra den Mime-Typ nicht kennt. Registriert wird dieser mit
+mime_type+ per Dateiendung:

  configure do
    mime_type :foo, 'text/foo'
  end
  
Es kann aber auch der +content_type+-Helfer verwendet werden:

  get '/' do
    content_type :foo
    "foo foo foo"
  end

=== URLs generieren

Zum Generieren von URLs sollte die +url+-Helfer-Methode genutzen werden, so 
z.B. beim Einsatz von Haml:

  %a{:href => url('/foo')} foo

Soweit vorhanden, wird Rücksicht auf Proxys und Rack-Router genommen.

Diese Methode ist ebenso über das Alias +to+ zu erreichen (siehe Beispiel
unten).

=== Browser-Umleitung

Eine Browser-Umleitung kann mithilfe der +redirect+-Helfer-Methode erreicht
werden:

  get '/foo' do
    redirect to('/bar')
  end

Weitere Parameter werden wie Argumente der +halt+-Methode behandelt:

  redirect to('/bar'), 303
  redirect 'http://google.com', 'Hier bist du falsch'

Ebenso leicht lässt sich ein Schritt zurück mit dem Alias 
<tt>redirect back</tt> erreichen:

  get '/foo' do
    "<a href='/bar'>mach was</a>"
  end

  get '/bar' do
    mach_was
    redirect back
  end

Um Argumente an ein Redirect weiterzugeben, können sie entweder dem Query
übergeben:

  redirect to('/bar?summe=42')

oder eine Session verwendet werden:

  enable :sessions
  
  get '/foo' do
    session[:secret] = 'foo'
    redirect to('/bar')
  end
  
  get '/bar' do
    session[:secret]
  end


=== Cache einsetzen

Ein sinnvolles Einstellen von Header-Daten ist die Grundlage für ein
ordentliches HTTP-Caching.

Der Cache-Control-Header lässt sich ganz einfach einstellen:

  get '/' do
    cache_control :public
    "schon gecached!"
  end

Profitipp: Caching im before-Filter aktivieren

  before do
    cache_control :public, :must_revalidate, :max_age => 60
  end

Bei Verwendung der +expires+-Helfermethode zum Setzen des gleichnamigen
Headers, wird <tt>Cache-Control</tt> automatisch eigestellt:

  before do
    expires 500, :public, :must_revalidate
  end

Um alles richtig zu machen, sollten auch +etag+ oder +last_modified+ verwendet
werden. Es wird empfohlen, dass diese Helfer aufgerufen werden *bevor* die
eigentliche Arbeit anfängt, da sie sofort eine Antwort senden, wenn der
Client eine aktuelle Version im Cache vorhält:

  get '/article/:id' do
    @article = Article.find params[:id]
    last_modified @article.updated_at
    etag @article.sha1
    erb :article
  end

ebenso ist es möglich einen
{schwachen ETag}[http://de.wikipedia.org/wiki/HTTP_ETag] zu verwenden:

  etag @article.sha1, :weak

Diese Helfer führen nicht das eigentliche Caching aus, sondern geben die dafür
notwendigen Informationen an den Cache weiter. Für schnelle Reverse-Proxy 
Cache-Lösungen bietet sich z.B. 
{rack-cache}[http://rtomayko.github.com/rack-cache/] an:

  require "rack/cache"
  require "sinatra"
  
  use Rack::Cache
  
  get '/' do
    cache_control :public, :max_age => 36000
    sleep 5
    "hello"
  end
  
Um den <tt>Cache-Control</tt>-Header mit Informationen zu versorgen, verwendet
man die <tt>:static_cache_control</tt>-Einstellung (s.u.).  

=== Dateien versenden

Zum Versenden von Dateien kann die <tt>send_file</tt>-Helfer-Methode verwendet
werden:

  get '/' do
    send_file 'foo.png'
  end

Für <tt>send_file</tt> stehen einige Hash-Optionen zur Verfügung:

  send_file 'foo.png', :type => :jpg

[filename]
  Dateiname als Response. Standardwert ist der eigentliche Dateiname.

[last_modified]
  Wert für den Last-Modified-Header, Standardwert ist +mtime+ der Datei.

[type]
  Content-Type, der verwendet werden soll. Wird, wenn nicht angegeben, von der
  Dateiendung abgeleitet.
  
[disposition]
  Verwendet für Content-Disposition. Mögliche Werte sind: +nil+ (Standard),
  <tt>:attachment</tt> und <tt>:inline</tt>.

[length]
  Content-Length-Header. Standardwert ist die Dateigröße.

Soweit vom Rack-Handler unterstützt, werden neben der Übertragung über den
Ruby-Prozess auch andere Möglichkeiten genutzt. Bei Verwendung der
<tt>send_file</tt>-Helfer-Methode kümmert sich Sinatra selbstständig um die
Range-Requests.

== Das Request-Objekt

Auf das +request+-Objekt der eigehenden Anfrage kann vom Anfrage-Scope aus
zugegriffen werden:

  # App läuft unter http://example.com/example
  get '/foo' do
    t = %w[text/css text/html application/javascript]
    request.accept              # ['text/html', '*/*']
    request.accept? 'text/xml'  # true
    request.preferred_type(t)   # 'text/html'
    request.body                # Request-Body des Client (siehe unten)
    request.scheme              # "http"
    request.script_name         # "/example"
    request.path_info           # "/foo"
    request.port                # 80
    request.request_method      # "GET"
    request.query_string        # ""
    request.content_length      # Länge des request.body
    request.media_type          # Medientypus von request.body
    request.host                # "example.com"
    request.get?                # true (ähnliche Methoden für andere Verben)
    request.form_data?          # false
    request["IRGENDEIN_HEADER"] # Wert von IRGENDEIN_HEADER header
    request.referrer            # Der Referrer des Clients oder '/'
    request.user_agent          # User-Agent (verwendet in der :agent Bedingung)
    request.cookies             # Hash des Browser-Cookies
    request.xhr?                # Ist das hier ein Ajax-Request?
    request.url                 # "http://example.com/example/foo"
    request.path                # "/example/foo"
    request.ip                  # IP-Adresse des Clients
    request.secure?             # false (true wenn SSL)
    request.forwarded?          # true (Wenn es hinter einem Reverse-Proxy verwendet wird)
    request.env                 # vollständiger env-Hash von Rack übergeben
  end

Manche Optionen, wie etwa <tt>script_name</tt> oder <tt>path_info</tt>, sind
auch schreibbar:

  before { request.path_info = "/" }
  
  get "/" do
    "Alle Anfragen kommen hier an!"
  end

Der <tt>request.body</tt> ist ein IO- oder StringIO-Objekt:

  post "/api" do
    request.body.rewind # falls schon jemand davon gelesen hat
    daten = JSON.parse request.body.read
    "Hallo #{daten['name']}!"
  end

=== Anhänge

Damit der Browser erkennt, dass ein Response gespeichert und nicht im Browser
angezeigt werden soll, kann der +attachment+-Helfer verwendet werden:

  get '/' do
    attachment
    "Speichern!"
  end

Ebenso kann eine Dateiname als Parameter hinzugefügt werden:

  get '/' do
    attachment "info.txt"
    "Speichern!"
  end

=== Umgang mit Datum und Zeit

Sinatra bietet eine <tt>time_for</tt>-Helfer-Methode, die aus einem gegebenen 
Wert ein Time-Objekt generiert. Ebenso kann sie nach +DateTime+, +Date+ und 
ähnliche Klassen konvertieren:

  get '/' do
    pass if Time.now > time_for('Dec 23, 2012')
    "noch Zeit"
  end

Diese Methode wird intern für +expires, +last_modiefied+ und Freunde verwendet.
Mit ein paar Handgriffen lässt sich diese Methode also in ihrem Verhalten 
erweitern, indem man +time_for+ in der eigenen Applikation überschreibt:

  helpers do
    def time_for(value)
      case value
      when :yesterday then Time.now - 24*60*60
      when :tomorrow  then Time.now + 24*60*60
      else super
      end
    end
  end

  get '/' do
    last_modified :yesterday
    expires :tomorrow
    "Hallo"
  end
  
=== Nachschlagen von Template-Dateien

Die <tt>find_template</tt>-Helfer-Methode wird genutzt, um Template-Dateien zum
Rendern aufzufinden:

  find_template settings.views, 'foo', Tilt[:haml] do |file|
    puts "könnte diese hier sein: #{file}"
  end

Das ist zwar nicht wirklich brauchbar, aber wenn man sie überschreibt, kann sie
nützlich werden, um eigene Nachschlage-Mechanismen einzubauen. Zum Beispiel
dann, wenn mehr als nur ein view-Verzeichnis verwendet werden soll:

  set :views, ['views', 'templates']

  helpers do
    def find_template(views, name, engine, &block)
      Array(views).each { |v| super(v, name, engine, &block) }
    end
  end

Ein anderes Beispiel wäre, verschiedene Vereichnisse für verschiedene Engines
zu verwenden:

  set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views'

  helpers do
    def find_template(views, name, engine, &block)
      _, folder = views.detect { |k,v| engine == Tilt[k] }
      folder ||= views[:default]
      super(folder, name, engine, &block)
    end
  end

Ebensogut könnte eine Extension aber auch geschrieben und mit anderen geteilt
werden!

Beachte, dass <tt>find_template</tt> nicht prüft, ob eine Datei tatsächlich
existiert. Es wird lediglich der angegebene Block aufgerufen und nach allen
möglichen Pfaden gesucht. Das ergibt kein Performance-Problem, da +render+
+block+ verwendet, sobald eine Datei gefunden wurde. Ebenso werden
Template-Pfade samt Inhalt gecached, solange nicht im Entwicklungsmodus
gearbeitet wird. Das sollte im Hinterkopf behalten werden, wenn irgendwelche
verrückten Methoden zusammenbastelt werden.

== Konfiguration

Wird einmal beim Starten in jedweder Umgebung ausgeführt:

  configure do
    # setze eine Option
    set :option, 'wert'
    
    # setze mehrere Optionen
    set :a => 1, :b => 2
    
    # das gleiche wie `set :option, true`
    enable :option
    
    # das gleiche wie `set :option, false`
    disable :option
    
    # dynamische Einstellungen mit Blöcken
    set(:css_dir) { File.join(views, 'css') }
  end

Läuft nur, wenn die Umgebung (RACK_ENV-Umgebungsvariable) auf
<tt>:production</tt> gesetzt ist:

  configure :production do
    ...
  end

Läuft nur, wenn die Umgebung auf <tt>:production</tt> oder auf <tt>:test</tt>
gesetzt ist:

  configure :production, :test do
    ...
  end

Diese Einstellungen sind über +settings+ erreichbar:

  configure do
    set :foo, 'bar'
  end

  get '/' do
    settings.foo? # => true
    settings.foo  # => 'bar'
    ...
  end

=== Einstellung des Angriffsschutzes

Sinatra verwendet 
{Rack::Protection}[https://github.com/rkh/rack-protection#readme], um die
Anwendung vor häufig vorkommenden Angriffen zu schützen. Diese Voreinstellung
lässt sich selbstverständlich auch deaktivieren, z.B. um 
Geschwindigkeitsvorteile zu gewinnen:

  disable :protection

Um einen bestimmten Schutzmechanismus zu deaktivieren, fügt man +protection+
einen Hash mit Optionen hinzu:

  set :protection, :except => :path_traversal

Neben Strings akzeptiert <tt>:except</tt> auch Arrays, um gleich mehrere 
Schutzmechanismen zu deaktivieren:

  set :protections, :except => [:path_traversal, :session_hijacking]
  
=== Mögliche Einstellungen

[absolute_redirects]    Wenn ausgeschaltet, wird Sinatra relative Redirects
                        zulassen. Jedoch ist Sinatra dann nicht mehr mit RFC 
                        2616 (HTTP 1.1) konform, das nur absolute Redirects 
                        zulässt.

                        Sollte eingeschaltet werden, wenn die Applikation
                        hinter einem Reverse-Proxy liegt, der nicht ordentlich 
                        eingerichtet ist. Beachte, dass die 
                        +url+-Helfer-Methode nach wie vor absolute URLs 
                        erstellen wird, es sei denn, es wird als zweiter 
                        Parameter +false+ angegeben.

                        Standardmäßig nicht aktiviert.
                      

[add_charsets]          Mime-Types werden hier automatisch der Helfer-Methode
                        <tt>content_type</tt> zugeordnet.
                      
                        Es empfielt sich, Werte hinzuzufügen statt sie zu
                        überschreiben:
                      
                          settings.add_charsets << "application/foobar"

[app_file]              Hauptdatei der Applikation. Wird verwendet, um das
                        Wurzel-, Inline-, View- und öffentliche Verzeichnis des
                        Projekts festzustellen.
                      
[bind]                  IP-Address, an die gebunden wird 
                        (Standardwert: 0.0.0.0). Wird nur für den eingebauten 
                        Server verwendet.

[default_encoding]      Das Encoding, falls keines angegeben wurde.
                        Standardwert ist <tt>"utf-8"</tt>.

[dump_errors]           Fehler im Log anzeigen.

[environment]           Momentane Umgebung. Standardmäßig auf
                        <tt>content_type</tt> oder <tt>"development"</tt>
                        eingestellt, soweit ersteres nicht vorhanden.

[logging]               Den Logger verwenden.

[lock]                  Jeder Request wird gelocked. Es kann nur ein Request
                        pro Ruby-Prozess gleichzeitig verarbeitet werden.
                      
                        Eingeschaltet, wenn die Applikation threadsicher ist.
                        Standardmäßig nicht aktiviert.

[method_override]       Verwende <tt>_method</tt>, um put/delete-Formulardaten
                        in Browsern zu verwenden, die dies normalerweise nicht
                        unterstützen.

[port]                  Port für die Applikation. Wird nur im internen Server
                        verwendet.

[prefixed_redirects]    Entscheidet, ob <tt>request.script_name</tt> in 
                        Redirects eingefügt wird oder nicht, wenn kein 
                        absoluter Pfad angegeben ist. Auf diese Weise verhält 
                        sich <tt>redirect '/foo'</tt> so, als wäre es ein
                        <tt>redirect to('/foo')</tt>. Standardmäßig nicht 
                        aktiviert.
                        
[protection]            Legt fest, ob der Schutzmechanismus für häufig 
                        Vorkommende Webangriffe auf Webapplikationen aktiviert 
                        wird oder nicht. Weitere Informationen im vorhergehenden 
                        Abschnitt.

[public_folder]         Das öffentliche Verzeichnis, aus dem Daten zur
                        Verfügung gestellt werden können.

[reload_templates]      Im development-Modus aktiviert.

[root]                  Wurzelverzeichnis des Projekts.

[raise_errors]          Einen Ausnahmezustand aufrufen. Beendet die 
                        Applikation.

[run]                   Wenn aktiviert, wird Sinatra versuchen, den Webserver
                        zu starten. Nicht verwenden, wenn Rackup oder anderes
                        verwendet werden soll.

[running]               Läuft der eingebaute Server? Diese Einstellung nicht
                        ändern!

[server]                Server oder Liste von Servern, die als eingebaute 
                        Server zur Verfügung stehen.
                        Standardmäßig auf ['thin', 'mongrel', 'webrick']
                        voreingestellt. Die Anordnung gibt die Priorität vor.

[sessions]              Sessions auf Cookiebasis aktivieren.

[show_exceptions]       Stacktrace im Browser bei Fehlern anzeigen.

[static]                Entscheidet, ob Sinatra statische Dateien zur Verfügung
                        stellen soll oder nicht.
                        Sollte nicht aktiviert werden, wenn ein Server 
                        verwendet wird, der dies auch selbstständig erledigen 
                        kann. Deaktivieren wird die Performance erhöhen.
                        Standardmäßig aktiviert.

[static_cache_control]  Wenn Sinatra statische Daten zur Verfügung stellt, 
                        können mit dieser Einstellung die +Cache-Control+ 
                        Header zu den Responses hinzugefügt werden. Die 
                        Einstellung verwendet dazu die +cache_control+
                        Helfer-Methode. Standardmäßig deaktiviert.
                        Ein Array wird verwendet, um mehrere Werte gleichzeitig
                        zu übergeben:
                        <tt>set :static_cache_control, [:public, :max_age => 300]</tt>                       

[views]                 Verzeichnis der Views.

== Fehlerbehandlung

Error-Handler laufen in demselben Kontext wie Routen und Filter, was bedeutet,
dass alle Goodies wie <tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt>, etc.
verwendet werden können.

=== Nicht gefunden

Wenn eine <tt>Sinatra::NotFound</tt>-Exception geworfen wird oder der
Statuscode 404 ist, wird der <tt>not_found</tt>-Handler ausgeführt:

  not_found do
    'Seite kann nirgendwo gefunden werden.'
  end

=== Fehler

Der +error+-Handler wird immer ausgeführt, wenn eine Exception in einem
Routen-Block oder in einem Filter geworfen wurde. Die Exception kann über die
<tt>sinatra.error</tt>-Rack-Variable angesprochen werden:

  error do
    'Entschuldige, es gab einen hässlichen Fehler - ' + env['sinatra.error'].name
  end

Benutzerdefinierte Fehler:

  error MeinFehler do
    'Au weia, ' + env['sinatra.error'].message
  end

Dann, wenn das passiert:

  get '/' do
    raise MeinFehler, 'etwas Schlimmes ist passiert'
  end

bekommt man dieses:

  Au weia, etwas Schlimmes ist passiert

Alternativ kann ein Error-Handler auch für einen Status-Code definiert werden:

  error 403 do
    'Zugriff verboten'
  end

  get '/geheim' do
    403
  end

Oder ein Status-Code-Bereich:

  error 400..510 do
    'Hallo?'
  end

Sinatra setzt verschiedene <tt>not_found</tt>- und <tt>error</tt>-Handler in
der Development-Umgebung.

== Rack-Middleware

Sinatra baut auf Rack[http://rack.rubyforge.org/], einem minimalistischen
Standard-Interface für Ruby-Webframeworks. Eines der interessantesten
Features für Entwickler ist der Support von Middlewares, die zwischen den 
Server und die Anwendung geschaltet werden und so HTTP-Request und/oder Antwort
überwachen und/oder manipulieren können.

Sinatra macht das Erstellen von Middleware-Verkettungen mit der 
Top-Level-Methode +use+ zu einem Kinderspiel:

  require 'sinatra'
  require 'meine_middleware'

  use Rack::Lint
  use MeineMiddleware

  get '/hallo' do
    'Hallo Welt'
  end

Die Semantik von +use+ entspricht der gleichnamigen Methode der
Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html]-DSL
(meist verwendet in Rackup-Dateien). Ein Beispiel dafür ist, dass die
+use+-Methode mehrere/verschiedene Argumente und auch Blöcke entgegennimmt:

  use Rack::Auth::Basic do |username, password|
    username == 'admin' && password == 'geheim'
  end

Rack bietet eine Vielzahl von Standard-Middlewares für Logging, Debugging,
URL-Routing, Authentifizierung und Session-Verarbeitung. Sinatra verwendet 
viele von diesen Komponenten automatisch, abhängig von der Konfiguration. So 
muss +use+ häufig nicht explizit verwendet werden.

Hilfreiche Middleware gibt es z.B. hier:
{rack}[https://github.com/rack/rack/tree/master/lib/rack],
{rack-contrib}[https://github.com/rack/rack-contrib#readme],
mit {CodeRack}[http://coderack.org/] oder im
{Rack wiki}[https://github.com/rack/rack/wiki/List-of-Middleware].

== Testen

Sinatra-Tests können mit jedem auf Rack aufbauendem Test-Framework geschrieben
werden. {Rack::Test}[http://rdoc.info/github/brynary/rack-test/master/frames]
wird empfohlen:

  require 'my_sinatra_app'
  require 'test/unit'
  require 'rack/test'

  class MyAppTest < Test::Unit::TestCase
    include Rack::Test::Methods

    def app
      Sinatra::Application
    end

    def test_my_default
      get '/'
      assert_equal 'Hallo Welt!', last_response.body
    end

    def test_with_params
      get '/meet', :name => 'Frank'
      assert_equal 'Hallo Frank!', last_response.body
    end

    def test_with_rack_env
      get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
      assert_equal "Du verwendest Songbird!", last_response.body
    end
  end

== Sinatra::Base - Middleware, Bibliotheken und modulare Anwendungen

Das Definieren einer Top-Level-Anwendung funktioniert gut für 
Mikro-Anwendungen, hat aber Nachteile, wenn wiederverwendbare Komponenten wie 
Middleware, Rails Metal, einfache Bibliotheken mit Server-Komponenten oder auch
Sinatra-Erweiterungen geschrieben werden sollen.

Die Top-Level-DSL belastet den Objekt-Namespace und setzt einen
Mikro-Anwendungsstil voraus (eine einzelne Anwendungsdatei, <tt>./public</tt>
und <tt>./views</tt> Ordner, Logging, Exception-Detail-Seite, usw.). Genau 
hier kommt <tt>Sinatra::Base</tt> ins Spiel:

  require 'sinatra/base'

  class MyApp < Sinatra::Base
    set :sessions, true
    set :foo, 'bar'

    get '/' do
      'Hallo Welt!'
    end
  end

Die MyApp-Klasse ist eine unabhängige Rack-Komponente, die als Middleware,
Endpunkt oder via Rails Metal verwendet werden kann. Verwendet wird sie durch
+use+ oder +run+ von einer Rackup-<tt>config.ru</tt>-Datei oder als 
Server-Komponente einer Bibliothek:

   MyApp.run! :host => 'localhost', :port => 9090

Die Methoden der <tt>Sinatra::Base</tt>-Subklasse sind genau dieselben wie die
der Top-Level-DSL. Die meisten Top-Level-Anwendungen können mit nur zwei 
Veränderungen zu <tt>Sinatra::Base</tt> konvertiert werden:

* Die Datei sollte <tt>require 'sinatra/base'</tt> anstelle von
  <tt>require 'sinatra/base'</tt> aufrufen, ansonsten werden alle von
  Sinatras DSL-Methoden in den Top-Level-Namespace importiert.
* Alle Routen, Error-Handler, Filter und Optionen der Applikation müssen in
  einer Subklasse von <tt>Sinatra::Base</tt> definiert werden.

<tt>Sinatra::Base</tt> ist ein unbeschriebenes Blatt. Die meisten Optionen sind
per Standard deaktiviert. Das betrifft auch den eingebauten Server. Siehe
{Optionen und Konfiguration}[http://sinatra.github.com/configuration.html] für
Details über mögliche Optionen.

=== Modularer vs. klassischer Stil

Entgegen häufiger Meinungen gibt es nichts gegen den klassischen Stil
einzuwenden. Solange es die Applikation nicht beeinträchtigt, besteht kein
Grund, eine modulare Applikation zu erstellen.

Lediglich zwei Nachteile gegenüber dem modularen Stil sollten beachtet werden:

* Es kann nur eine Sinatra Applikation pro Ruby-Prozess laufen. Sollten mehrere
  zum Einsatz kommen, muss auf den modularen Stil umgestiegen werden.
  
* Der klassische Stil füllt Object mit Delegations-Methoden. Sollte die
  Applikation als Gem/Bibliothek zum Einsatz kommen, sollte auf den modularen
  Stil umgestiegen werden.
  
Es gibt keinen Grund, warum modulare und klassische Elemente nicht
vermischt werden sollten.

Will man jedoch von einem Stil auf den anderen umsteigen, sollten einige
Unterschiede beachtet werden:

  Szenario            Classic                 Modular

  app_file            sinatra ladende Datei   Sinatra::Base subklassierende Datei
  run                 $0 == app_file          false
  logging             true                    false
  method_override     true                    false
  inline_templates    true                    false

=== Eine modulare Applikation bereitstellen

Es gibt zwei übliche Wege, eine modulare Anwendung zu starten. Zum einen über
<tt>run!</tt>:

  # mein_app.rb
  require 'sinatra/base'
  
  class MeinApp < Sinatra::Base
    # ... Anwendungscode hierhin ...
    
    # starte den Server, wenn die Ruby-Datei direkt ausgeführt wird
    run! if app_file == $0
  end

Starte mit:

  ruby mein_app.rb

Oder über eine <tt>config.ru</tt>-Datei, die es erlaubt, einen beliebigen
Rack-Handler zu verwenden:

  # config.ru
  require './mein_app'
  run MeineApp

Starte:

  rackup -p 4567

=== Eine klassische Anwendung mit einer config.ru verwenden

Schreibe eine Anwendungsdatei:

  # app.rb
  require 'sinatra'
  
  get '/' do
    'Hallo Welt!'
  end

sowie eine dazugehörige <tt>config.ru</tt>-Datei:

  require './app'
  run Sinatra::Application

=== Wann sollte eine config.ru-Datei verwendet werden?

Anzeichen dafür, dass eine <tt>config.ru</tt>-Datei gebraucht wird:

* Es soll ein anderer Rack-Handler verwendet werden (Passenger, Unicorn,
  Heroku, ...).
* Es gibt mehr als nur eine Subklasse von <tt>Sinatra::Base</tt>.
* Sinatra soll als Middleware verwendet werden, nicht als Endpunkt.

<b>Es gibt keinen Grund, eine <tt>config.ru</tt>-Datei zu verwenden, nur weil
eine Anwendung im modularen Stil betrieben werden soll. Ebenso wird keine 
Anwendung mit modularem Stil benötigt, um eine <tt>config.ru</tt>-Datei zu 
verwenden.</b>

=== Sinatra als Middleware nutzen

Es ist nicht nur möglich, andere Rack-Middleware mit Sinatra zu nutzen, es kann
außerdem jede Sinatra-Anwendung selbst als Middleware vor jeden beliebigen 
Rack-Endpunkt gehangen werden. Bei diesem Endpunkt muss es sich nicht um eine 
andere Sinatra-Anwendung handeln, es kann jede andere Rack-Anwendung sein
(Rails/Ramaze/Camping/...):

  require 'sinatra/base'

  class LoginScreen < Sinatra::Base
    enable :sessions
  
    get('/login') { haml :login }
  
    post('/login') do
      if params[:name] == 'admin' && params[:password] == 'admin'
        session['user_name'] = params[:name]
      else
        redirect '/login'
      end
    end
  end

  class MyApp < Sinatra::Base
    # Middleware wird vor Filtern ausgeführt
    use LoginScreen
  
    before do
      unless session['user_name']
        halt "Zugriff verweigert, bitte <a href='/login'>einloggen</a>."
      end
    end
  
    get('/') { "Hallo #{session['user_name']}." }
  end

=== Dynamische Applikationserstellung

Manche Situationen erfordern die Erstellung neuer Applikationen zur Laufzeit,
ohne dass sie einer Konstanten zugeordnet werden. Dies lässt sich mit 
<tt>Sinatra.new</tt> erreichen:

  require 'sinatra/base'
  my_app = Sinatra.new { get('/') { "hallo" } }
  my_app.run!

Die Applikation kann mit Hilfe eines optionalen Parameters erstellt werden:

  # config.ru
  require 'sinatra/base'

  controller = Sinatra.new do
    enable :logging
    helpers MyHelpers
  end

  map('/a') do
    run Sinatra.new(controller) { get('/') { 'a' } }
  end

  map('/b') do
    run Sinatra.new(controller) { get('/') { 'b' } }
  end

Das ist besonders dann interessant, wenn Sinatra-Erweiterungen getestet werden
oder Sinatra in einer Bibliothek Verwendung findet.

Ebenso lassen sich damit hervorragend Sinatra-Middlewares erstellen:

  require 'sinatra/base'

  use Sinatra do
    get('/') { ... }
  end

  run RailsProject::Application

== Geltungsbereich und Bindung

Der Geltungsbereich (Scope) legt fest, welche Methoden und Variablen zur
Verfügung stehen.

=== Anwendungs- oder Klassen-Scope

Jede Sinatra-Anwendung entspricht einer <tt>Sinatra::Base</tt>-Subklasse. Falls
die Top- Level-DSL verwendet wird (<tt>require 'sinatra'</tt>), handelt es sich
um <tt>Sinatra::Application</tt>, andernfalls ist es jene Subklasse, die 
explizit angelegt wurde. Auf Klassenebene stehen Methoden wie +get+ oder 
+before+ zur Verfügung, es gibt aber keinen Zugriff auf das +request+-Object 
oder die +session+, da nur eine einzige Klasse für alle eingehenden Anfragen 
genutzt wird.

Optionen, die via +set+ gesetzt werden, sind Methoden auf Klassenebene:

  class MyApp < Sinatra::Base
    # Hey, ich bin im Anwendungsscope!
    set :foo, 42
    foo # => 42
    
    get '/foo' do
      # Hey, ich bin nicht mehr im Anwendungs-Scope!
    end
  end

Im Anwendungs-Scope befindet man sich:

* In der Anwendungs-Klasse.
* In Methoden, die von Erweiterungen definiert werden.
* Im Block, der an +helpers+ übergeben wird.
* In Procs und Blöcken, die an +set+ übergeben werden.
* Der an <tt>Sinatra.new</tt> übergebene Block

Auf das Scope-Objekt (die Klasse) kann wie folgt zugegriffen werden:

* Über das Objekt, das an den +configure+-Block übergeben wird (<tt>configure
  { |c| ... }</tt>).
* +settings+ aus den anderen Scopes heraus.

=== Anfrage- oder Instanz-Scope

Für jede eingehende Anfrage wird eine neue Instanz der Anwendungs-Klasse
erstellt und alle Handler in diesem Scope ausgeführt. Aus diesem Scope
heraus kann auf +request+ oder +session+ zugegriffen und Methoden wie +erb+
oder +haml+ aufgerufen werden. Außerdem kann mit der +settings+-Method auf den
Anwendungs-Scope zugegriffen werden:

  class MyApp < Sinatra::Base
    # Hey, ich bin im Anwendungs-Scope!
    get '/neue_route/:name' do
      # Anfrage-Scope für '/neue_route/:name'
      @value = 42
      
      settings.get "/#{params[:name]}" do
        # Anfrage-Scope für "/#{params[:name]}"
        @value # => nil (nicht dieselbe Anfrage)
      end
      
      "Route definiert!"
    end
  end

Im Anfrage-Scope befindet man sich:

* In get/head/post/put/delete-Blöcken
* In before/after-Filtern
* In Helfer-Methoden
* In Templates

=== Delegation-Scope

Vom Delegation-Scope aus werden Methoden einfach an den Klassen-Scope
weitergeleitet. Dieser verhält sich jedoch nicht 100%ig wie der Klassen-Scope,
da man nicht die Bindung der Klasse besitzt: Nur Methoden, die explizit als
delegierbar markiert wurden, stehen hier zur Verfügung und es kann nicht auf
die Variablen des Klassenscopes zugegriffen werden (mit anderen Worten: es gibt
ein anderes +self+). Weitere Delegationen können mit 
<tt>Sinatra::Delegator.delegate :methoden_name</tt> hinzugefügt werden.

Im Delegation-Scop befindet man sich:

* Im Top-Level, wenn <tt>require 'sinatra'</tt> aufgerufen wurde.
* In einem Objekt, das mit dem <tt>Sinatra::Delegator</tt>-Mixin erweitert 
  wurde.

Schau am besten im Code nach: Hier ist
{Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L1064]
definiert und wird in den
{globalen Namespace eingebunden}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb#L25].

== Kommandozeile

Sinatra-Anwendungen können direkt von der Kommandozeile aus gestartet werden:

  ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-h HOST] [-s HANDLER]

Die Optionen sind:

  -h # Hilfe
  -p # Port setzen (Standard ist 4567)
  -h # Host setzen (Standard ist 0.0.0.0)
  -e # Umgebung setzen (Standard ist development)
  -s # Rack-Server/Handler setzen (Standard ist thin)
  -x # Mutex-Lock einschalten (Standard ist off)

== Systemanforderungen

Die folgenden Versionen werden offiziell unterstützt:

[ Ruby 1.8.7 ]
  1.8.7 wird vollständig unterstützt, aber solange nichts dagegen spricht, 
  wird ein Update auf 1.9.2 oder ein Umstieg auf JRuby/Rubinius empfohlen.
  Unterstützung für 1.8.7 wird es mindestens bis Sinatra 2.0 und Ruby 2.0 geben,
  es sei denn, dass der unwahrscheinliche Fall eintritt und 1.8.8 rauskommt. Doch
  selbst dann ist es eher wahrscheinlich, dass 1.8.7 weiterhin unterstützt wird.
  <b>Ruby 1.8.6 wird nicht mehr unterstützt.</b> Soll Sinatra unter 1.8.6 
  eingesetzt werden, muss Sinatra 1.2 verwendet werden, dass noch bis zum 
  Release von Sinatra 1.4.0 fortgeführt wird.

[ Ruby 1.9.2 ]
  1.9.2 wird voll unterstützt und empfohlen. Beachte, dass Markaby und Radius
  momentan noch nicht kompatibel mit 1.9 sind. Version 1.9.2p0 sollte nicht
  verwendet werden, da unter Sinatra immer wieder Segfaults auftreten.
  Unterstützung wird es mindestens bis zum Release von Ruby 1.9.4/2.0 geben und
  das letzte Sinatra Release für 1.9 wird so lange unterstützt, wie das Ruby 
  Core-Team 1.9 pflegt.

[ Ruby 1.9.3 ]
  Obwohl Tests bereits auf 1.9.3 laufen, sind bisher keine Applikationen auf 
  1.9.3 in Produktion bekannt. Ebenso wie bei 1.9.2 besteht die gleiche Warnung 
  zum Patchlevel 0.
  
[ Rubinius ]
  Rubinius (rbx >= 1.2.4) wird offiziell unter Einbezug aller Templates 
  unterstützt.

[ JRuby ]
  JRuby wird offiziell unterstützt (JRuby >= 1.6.3). Probleme mit Template-
  Bibliotheken Dritter sind nicht bekannt. Falls JRuby zum Einsatz kommt, 
  sollte aber darauf geachtet werden, dass ein JRuby-Rack-Handler zum Einsatz 
  kommt – der Thin-Web-Server wird bisher nicht unterstütz. JRubys 
  Unterstützung für C-Erweiterungen sind zur Zeit noch experimenteller Natur,
  betrifft im Moment aber nur RDiscount und Redcarpet.


Weiterhin werden wir auf kommende Ruby-Versionen ein Auge haben.

Die nachfolgend aufgeführten Ruby-Implementierungen werden offiziell nicht von 
Sinatra unterstützt, funktionieren aber normalerweise:

* Ruby Enterprise Edition
* Ältere Versionen von JRuby und Rubinius
* MacRuby, Maglev, IronRuby
* Ruby 1.9.0 und 1.9.1 (wird jedoch nicht empfohlen, s.o.)

Nicht offiziell unterstützt bedeutet, dass wenn Sachen nicht funktionieren, 
wir davon ausgehen, dass es nicht an Sinatra sondern an der jeweiligen 
Implentierung liegt.

Im Rahmen unserer CI (Kontinuierlichen Integration) wird bereits ruby-head
(das kommende Ruby 1.9.4) mit eingebunden. Da noch alles im Fluss ist, kann zur
Zeit für nichts garantiert werden. Es kann aber erwartet werden, dass Ruby 
1.9.4p0 von Sinatra unterstützt werden wird.

Sinatra sollte auf jedem Betriebssystem laufen, dass den gewählten Ruby-
Interpreter unterstützt.

Sinatra wird aktuell nicht unter Cardinal, SmallRuby, BleuRuby oder irgendeiner 
Version von Ruby vor 1.8.7 laufen.

== Der neuste Stand (The Bleeding Edge)

Um auf dem neusten Stand zu bleiben, kann der Master-Branch verwendet werden.
Er sollte recht stabil sein. Ebenso gibt es von Zeit zu Zeit prerelease Gems, 
die so installiert werden:
  
  gem install sinatra --pre

=== Mit Bundler

Wenn die Applikation mit der neuesten Version von Sinatra und
{Bundler}[http://gembundler.com/] genutzt werden soll, empfehlen wir den
nachfolgenden Weg.

Soweit Bundler noch nicht installiert ist:

  gem install bundler

Anschließend wird eine +Gemfile+-Datei im Projektverzeichnis mit folgendem
Inhalt erstellt:

  source :rubygems
  gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
  
  # evtl. andere Abhängigkeiten
  gem 'haml'                    # z.B. wenn du Haml verwendest...
  gem 'activerecord', '~> 3.0'  # ...oder ActiveRecord 3.x

Beachte: Hier sollten alle Abhängigkeiten eingetragen werden. Sinatras eigene,
direkte Abhängigkeiten (Tilt und Rack) werden von Bundler automatisch aus dem
Gemfile von Sinatra hinzugefügt.

Jetzt kannst du deine Applikation starten:

  bundle exec ruby myapp.rb

  
=== Eigenes Repository
Um auf dem neuesten Stand von Sinatras Code zu sein, kann eine lokale Kopie
angelegt werden. Gestartet wird in der Anwendung mit dem <tt>sinatra/lib</tt>-
Ordner im <tt>LOAD_PATH</tt>:

  cd myapp
  git clone git://github.com/sinatra/sinatra.git
  ruby -Isinatra/lib myapp.rb

Alternativ kann der <tt>sinatra/lib</tt>-Ordner zum <tt>LOAD_PATH</tt> in
der Anwendung hinzugefügt werden:

  $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib'
  require 'rubygems'
  require 'sinatra'

  get '/ueber' do
    "Ich laufe auf Version " + Sinatra::VERSION
  end

Um Sinatra-Code von Zeit zu Zeit zu aktualisieren:

  cd myproject/sinatra
  git pull

=== Gem erstellen

Aus der eigenen lokalen Kopie kann nun auch ein globales Gem gebaut werden:

  git clone git://github.com/sinatra/sinatra.git
  cd sinatra
  rake sinatra.gemspec
  rake install

Falls Gems als Root installiert werden sollen, sollte die letzte Zeile 
folgendermaßen lauten:

  sudo rake install

== Versions-Verfahren

Sinatra folgt dem sogenannten {Semantic Versioning}[http://semver.org/], d.h. 
SemVer und SemVerTag.

== Mehr

* {Projekt-Website}[http://sinatra.github.com/] - Ergänzende Dokumentation,
  News und Links zu anderen Ressourcen.
* {Mitmachen}[http://sinatra.github.com/contributing.html] - Einen
  Fehler gefunden? Brauchst du Hilfe? Hast du einen Patch?
* {Issue-Tracker}[http://github.com/sinatra/sinatra/issues]
* {Twitter}[http://twitter.com/sinatra]
* {Mailing-Liste}[http://groups.google.com/group/sinatrarb]
* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] auf http://freenode.net
* {Sinatra Book}[http://sinatra-book.gittr.com] Kochbuch Tutorial
* {Sinatra Recipes}[http://recipes.sinatrarb.com/] Sinatra-Rezepte aus
  der Community
* API Dokumentation für die {aktuelle Version}[http://rubydoc.info/gems/sinatra]
  oder für {HEAD}[http://rubydoc.info/github/sinatra/sinatra] auf
  http://rubydoc.info
* {CI Server}[http://ci.rkh.im/view/Sinatra/]