diff --git a/README.de.rdoc b/README.de.rdoc
deleted file mode 100644
index b8166875..00000000
--- a/README.de.rdoc
+++ /dev/null
@@ -1,2116 +0,0 @@
-= Sinatra
-
-Wichtig: Dieses Dokument ist eine Übersetzung aus dem Englischen und unter
-Umständen nicht auf dem aktuellen Stand.
-
-Sinatra ist eine
-{DSL}[http://de.wikipedia.org/wiki/Domänenspezifische_Sprache], 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 myapp.rb
-
-Die Seite kann nun unter http://localhost:4567 betrachtet werden.
-
-Es wird empfohlen, den Thin-Server via gem install thin 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
-params-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
-params[:splat]-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: [Status (Fixnum), Headers (Hash),
- Response-Body (antwortet auf #each)].
-* Ein Array mit zwei Elementen: [Status (Fixnum), Response-Body (antwortet
- auf #each)].
-* Ein Objekt, das auf #each 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 ./public-Ordner ausgeliefert. Es ist
-möglich, einen anderen Ort zu definieren, indem man die
-:public_folder-Option setzt:
-
- set :public_folder, File.dirname(__FILE__) + '/static'
-
-Zu beachten ist, dass der Ordnername public nicht Teil der URL ist. Die Datei
-./public/css/style.css ist unter
-http://example.com/css/style.css zu finden.
-
-Um den Cache-Control-Header mit Informationen zu versorgen, verwendet
-man die :static_cache_control-Einstellung (s.u.).
-
-== Views/Templates
-
-Alle Templatesprachen verwenden ihre eigene Renderingmethode, die jeweils
-einen String zurückgibt:
-
- get '/' do
- erb :index
- end
-
-Dieses Beispiel rendert views/index.erb.
-
-Anstelle eines Templatenamens kann man auch direkt die Templatesprache
-verwenden:
-
- get '/' do
- code = "<%= Time.now %>"
- erb code
- end
-
-Templates nehmen ein zweite Argument an, den Options-Hash:
-
- get '/' do
- erb :index, :layout => :post
- end
-
-Dieses Beispiel rendert views/index.erb eingebettet in
-views/post.erb (Voreinstellung ist views/layout.erb, sofern
-es vorhanden ist.)
-
-Optionen, die Sinatra nicht versteht, werden an das Template weitergereicht:
-
- get '/' do
- haml :index, :format => :html5
- end
-
-Für alle Templates können auch generelle Einstellungen festgelegt werden:
-
- set :haml, :format => :html5
-
- get '/' do
- haml :index
- end
-
-Optionen, die an die Rendermethode weitergegeben werden, überschreiben die
-Einstellungen, die mit +set+ festgelegt wurden.
-
-Mögliche Einstellungen:
-
-[locals]
- Liste von lokalen Variablen, die and das Dokument weitergegeben werden.
- Praktisch für Partials.
- Beispiel: erb "<%= foo %>", :locals => {:foo => "bar"}
-
-[default_encoding]
- Gibt die Stringkodierung an, die verwendet werden soll. Voreingestellt auf
- settings.default_encoding.
-
-[views]
- Ordner, aus dem die Templates heraus geladen werden. Voreingestellt auf
- settings.views.
-
-[layout]
- Legt fest, ob ein Layouttemplate verwendet werden soll oder nicht (+true+
- oder +false+). Ist es ein Symbol, dass legt es fest, welches Template als
- Layout verwendet wird. Beispiel:
- erb :index, :layout => !request.xhr?
-
-[content_type]
- Content-Type den das Template ausgibt. Voreinstellung hängt von der
- Templatesprache ab.
-
-[scope]
- Scope, in dem das Template gerendert wird. Liegt standardmäßig innerhalb der
- App-Instanz. Wird Scope geändert, sind Instanzvariablen und Helfermethoden
- nicht verfügbar.
-
-[layout_engine]
- Legt fest, welcher Renderer für das Layout verantwortlich ist.
- Hilfreich für Sprachen, die sonst keine Templates unterstützen.
- Voreingestellt auf den Renderer, der für das Template verwendet wird.
- Beispiel: set :rdoc, :layout_engine => :erb
-
-Sinatra geht davon aus, dass die Templates sich im ./views Verzeichnis
-befinden. Es kann jedoch ein anderer Ordner festgelegt werden:
-
- set :views, settings.root + '/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.info/]
-Dateierweiterung:: .haml
-Beispiel:: haml :index, :format => :html5
-
-=== Erb Templates
-
-Abhängigkeit:: {erubis}[http://www.kuwata-lab.com/erubis/] oder
- erb (included in Ruby)
-Dateierweiterungen:: .erb, .rhtml oder .erubis
- (nur Erubis)
-Beispiel:: erb :index
-
-=== Builder Templates
-
-Abhängigkeit:: {builder}[http://builder.rubyforge.org/]
-Dateierweiterung:: .builder
-Beispiel:: builder { |xml| xml.em "Hallo" }
-
-Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
-
-=== Nokogiri Templates
-
-Abhängigkeit:: {nokogiri}[http://nokogiri.org/]
-Dateierweiterung:: .nokogiri
-Beispiel:: nokogiri { |xml| xml.em "Hallo" }
-
-Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
-
-=== Sass Templates
-
-Abhängigkeit:: {sass}[http://sass-lang.com/]
-Dateierweiterung:: .sass
-Beispiel:: sass :stylesheet, :style => :expanded
-
-=== SCSS Templates
-
-Abhängigkeit:: {sass}[http://sass-lang.com/]
-Dateierweiterung:: .scss
-Beispiel:: scss :stylesheet, :style => :expanded
-
-=== Less Templates
-
-Abhängigkeit:: {less}[http://www.lesscss.org/]
-Dateierweiterung:: .less
-Beispiel:: less :stylesheet
-
-=== Liquid Templates
-
-Abhängigkeit:: {liquid}[http://www.liquidmarkup.org/]
-Dateierweiterung:: .liquid
-Beispiel:: liquid :index, :locals => { :key => 'Wert' }
-
-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/vmg/redcarpet],
- {bluecloth}[http://deveiate.org/projects/BlueCloth],
- {kramdown}[http://kramdown.rubyforge.org/] *oder*
- {maruku}[http://maruku.rubyforge.org/]
-Dateierweiterungen:: .markdown, .mkd und .md
-Beispiel:: markdown :index, :layout_engine => :erb
-
-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üße)
-
-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
-:layout_engine-Option verwendet wird.
-
-=== Textile Templates
-
-Abhängigkeit:: {RedCloth}[http://redcloth.org/]
-Dateierweiterung:: .textile
-Beispiel:: textile :index, :layout_engine => :erb
-
-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üße)
-
-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
-:layout_engine-Option verwendet wird.
-
-=== RDoc Templates
-
-Abhängigkeit:: {rdoc}[http://rdoc.rubyforge.org/]
-Dateierweiterung:: .rdoc
-Beispiel:: textile :README, :layout_engine => :erb
-
-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
-:layout_engine-Option verwendet wird.
-
-=== Radius Templates
-
-Abhängigkeit:: {radius}[http://radius.rubyforge.org/]
-Dateierweiterung:: .radius
-Beispiel:: radius :index, :locals => { :key => 'Wert' }
-
-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/]
-Dateierweiterung:: .mab
-Beispiel:: markaby { h1 "Willkommen!" }
-
-Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
-
-=== RABL Templates
-
-Abhängigkeit:: {rabl}[https://github.com/nesquena/rabl]
-Dateierweiterung:: .rabl
-Beispiel:: rabl :index
-
-=== Slim Templates
-
-Abhängigkeit:: {slim}[http://slim-lang.com/]
-Dateierweiterung:: .slim
-Beispiel:: slim :index
-
-=== Creole Templates
-
-Abhängigkeit:: {creole}[https://github.com/minad/creole]
-Dateierweiterung:: .creole
-Beispiel:: creole :wiki, :layout_engine => :erb
-
-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
-:layout_engine-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]
-Dateierweiterung:: .coffee
-Beispiel:: coffee :index
-
-=== WLang Templates
-
-Abhängigkeit:: {wlang}[https://github.com/blambeau/wlang/]
-Dateierweiterung:: .wlang
-Beispiel:: wlang :index, :locals => { :key => 'value' }
-
-Ruby-Methoden in wlang aufzurufen entspricht nicht den idiomatischen Vorgaben
-von wlang, es bietet sich deshalb an, :locals zu verwenden.
-Layouts, die wlang und +yield+ verwenden, werden aber trotzdem unterstützt.
-
-=== 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 require
-'sinatra' aufruft, werden automatisch geladen. Um andere Inline-Templates
-in anderen Dateien aufzurufen, muss explizit enable :inline_templates
-verwendet werden.
-
-=== Benannte Templates
-
-Templates können auch mit der Top-Level template-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 :layout => false kann das Ausführen verhindert
-werden:
-
- get '/' do
- haml :index, :layout => request.xhr?
- end
-
-=== Dateiendungen zuordnen
-
-Um eine Dateiendung einer Template-Engine zuzuordnen, kann
-Tilt.register 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 ./views/application.mtt. 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 helpers-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 enable :sessions 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 enable :sessions
-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 pass 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 "bar" in eine Helfer-Methode umgewandelt wird, auf
-die /foo und /bar zugreifen können.
-
-Wenn der Request innerhalb derselben Applikations-Instanz aufgerufen und keine
-Kopie der Instanz erzeugt werden soll, kann call! 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. Mit Shotgun funktioniert Streaming z.B.
-überhaupt nicht.
-
-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 Sinatra::Application
-voreingestellt ist. Wird über Sinatra::Base vererbt, muss es erst
-aktiviert werden:
-
- class MyApp < Sinatra::Base
- configure :production, :development do
- enable :logging
- end
- end
-
-Damit auch keine Middleware das Logging aktivieren kann, muss die +logging+
-Einstellung auf +nil+ gesetzt werden. Das heißt aber auch, dass +logger+ in
-diesem Fall +nil+ zurückgeben wird. Üblicherweise wird das eingesetzt, wenn ein
-eigener Logger eingerichtet werden soll. Sinatra wird dann verwenden, was in
-env['rack.logger'] eingetragen ist.
-
-== Mime-Types
-
-Wenn send_file 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
-redirect back erreichen:
-
- get '/foo' do
- "mach was"
- 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 Cache-Control 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}[https://github.com/rtomayko/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 Cache-Control-Header mit Informationen zu versorgen, verwendet
-man die :static_cache_control-Einstellung (s.u.).
-
-Nach RFC 2616 sollte sich die Anwendung anders verhalten, wenn ein
-If-Match oder ein If-None_match Header auf * gesetzt wird in
-Abhängigkeit davon, ob die Resource bereits existiert. Sinatra geht
-davon aus, dass Ressourcen bei sicheren Anfragen (z.B. bei get oder Idempotenten
-Anfragen wie put) bereits existieren, wobei anderen Ressourcen
-(besipielsweise bei post), als neue Ressourcen behandelt werden. Dieses
-Verhalten lässt sich mit der :new_resource Option ändern:
-
- get '/create' do
- etag '', :new_resource => true
- Article.create
- erb :new_article
- end
-
-Soll das schwache ETag trotzdem verwendet werden, verwendet man die
-:kind Option:
-
- etag '', :new_resource => true, :kind => :weak
-
-=== Dateien versenden
-
-Zum Versenden von Dateien kann die send_file-Helfer-Methode verwendet
-werden:
-
- get '/' do
- send_file 'foo.png'
- end
-
-Für send_file 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),
- :attachment und :inline.
-
-[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
-send_file-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 script_name oder path_info, sind
-auch schreibbar:
-
- before { request.path_info = "/" }
-
- get "/" do
- "Alle Anfragen kommen hier an!"
- end
-
-Der request.body 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 time_for-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 find_template-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 find_template 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
-:production gesetzt ist:
-
- configure :production do
- ...
- end
-
-Läuft nur, wenn die Umgebung auf :production oder auf :test
-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 deaktivieren, der damit verbundene
-Geschwindigkeitszuwachs steht aber in keinem Verhätnis zu den möglichen
-Risiken.
-
- 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 :except auch Arrays, um gleich mehrere
-Schutzmechanismen zu deaktivieren:
-
- set :protection, :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
- content_type zugeordnet.
-
- Es empfielt sich, Werte hinzuzufügen statt sie zu
- überschreiben:
-
- settings.add_charsets << "application/foobar"
-
-[app_file] Pfad zur 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 "utf-8".
-
-[dump_errors] Fehler im Log anzeigen.
-
-[environment] Momentane Umgebung. Standardmäßig auf
- content_type oder "development"
- 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 _method, 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 request.script_name in
- Redirects eingefügt wird oder nicht, wenn kein
- absoluter Pfad angegeben ist. Auf diese Weise verhält
- sich redirect '/foo' so, als wäre es ein
- redirect to('/foo'). 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. Wird nur dann
- verwendet, wenn statische Daten zur Verfügung
- gestellt werden können (s.u. static
- Option). Leitet sich von der app_file
- Einstellung ab, wenn nicht gesetzt.
-
-[public_dir] Alias für public_folder, s.o.
-
-[reload_templates] Im development-Modus aktiviert.
-
-[root] Wurzelverzeichnis des Projekts. Leitet sich von der
- app_file Einstellung ab, wenn nicht gesetzt.
-
-[raise_errors] Einen Ausnahmezustand aufrufen. Beendet die
- Applikation. Ist automatisch aktiviert, wenn die
- Umgebung auf "test" eingestellt ist.
- Ansonsten ist diese Option deaktiviert.
-
-[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 mittels
- Rack::Session::Cookieaktivieren. Für
- weitere Infos bitte in der Sektion 'Sessions
- verwenden' nachschauen.
-
-[show_exceptions] Bei Fehlern einen Stacktrace im Browseranzeigen. Ist
- automatisch aktiviert, wenn die Umgebung auf
- "development" eingestellt ist. Ansonsten ist
- diese Option deaktiviert.
- Kann auch auf :after_handler gestellt werden,
- um eine anwendungsspezifische Fehlerbehandlung
- auszulösen, bevor der Fehlerverlauf im Browser
- angezeigt wird.
-
-[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:
- set :static_cache_control, [:public, :max_age => 300]
-
-[views] Verzeichnis der Views. Leitet sich von der
- app_file Einstellung ab, wenn nicht gesetzt.
-
-== Umgebungen
-
-Es gibt drei voreingestellte Umgebungen in Sinatra: "development",
-"production" und "test". Umgebungen können über die
-+RACK_ENV+ Umgebungsvariable gesetzt werden. Die Standardeinstellung ist
-"development". In diesem Modus werden alle Templates zwischen
-Requests neu geladen. Dazu gibt es besondere Fehlerseiten für 404 Stati
-und Fehlermeldungen. In "production" und "test" werden
-Templates automatisch gecached.
-
-Um die Anwendung in einer anderen Umgebung auszuführen kann man die
--e Option verwenden:
-
- ruby my_app.rb -e [ENVIRONMENT]
-
-In der Anwendung kann man die die Methoden +development?+, +test?+ und
-+production?+ verwenden, um die aktuelle Umgebung zu erfahren.
-
-== Fehlerbehandlung
-
-Error-Handler laufen in demselben Kontext wie Routen und Filter, was bedeutet,
-dass alle Goodies wie haml, erb, halt, etc.
-verwendet werden können.
-
-=== Nicht gefunden
-
-Wenn eine Sinatra::NotFound-Exception geworfen wird oder der
-Statuscode 404 ist, wird der not_found-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
-sinatra.error-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 not_found- und error-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.
-
-Das Top-Level geht von einer Konfiguration für eine Mikro-Anwendung aus
-(wie sie z.B. bei einer einzelnen Anwendungsdatei, ./public
-und ./views Ordner, Logging, Exception-Detail-Seite, usw.). Genau
-hier kommt Sinatra::Base 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-config.ru-Datei oder als
-Server-Komponente einer Bibliothek:
-
- MyApp.run! :host => 'localhost', :port => 9090
-
-Die Methoden der Sinatra::Base-Subklasse sind genau dieselben wie die
-der Top-Level-DSL. Die meisten Top-Level-Anwendungen können mit nur zwei
-Veränderungen zu Sinatra::Base konvertiert werden:
-
-* Die Datei sollte require 'sinatra/base' anstelle von
- require 'sinatra/base' 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 Sinatra::Base definiert werden.
-
-Sinatra::Base 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.
-
-Der größte Nachteil der klassischen Sinatra Anwendung gegenüber einer modularen
-ist die Einschränkung auf eine Sinatra Anwendung pro Ruby-Prozess. Sollen
-mehrere zum Einsatz kommen, muss auf den modularen Stil umgestiegen werden.
-Dabei ist es kein Problem klassische und modulare Anwendungen miteinander zu
-vermischen.
-
-Bei einem Umstieg, sollten einige Unterschiede in den Einstellungen 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
-run!:
-
- # 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 config.ru-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 config.ru-Datei:
-
- require './app'
- run Sinatra::Application
-
-=== Wann sollte eine config.ru-Datei verwendet werden?
-
-Anzeichen dafür, dass eine config.ru-Datei gebraucht wird:
-
-* Es soll ein anderer Rack-Handler verwendet werden (Passenger, Unicorn,
- Heroku, ...).
-* Es gibt mehr als nur eine Subklasse von Sinatra::Base.
-* Sinatra soll als Middleware verwendet werden, nicht als Endpunkt.
-
-Es gibt keinen Grund, eine config.ru-Datei zu verwenden, nur weil
-eine Anwendung im modularen Stil betrieben werden soll. Ebenso wird keine
-Anwendung mit modularem Stil benötigt, um eine config.ru-Datei zu
-verwenden.
-
-=== 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 einloggen."
- 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
-Sinatra.new 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 Sinatra::Base-Subklasse. Falls
-die Top- Level-DSL verwendet wird (require 'sinatra'), handelt es sich
-um Sinatra::Application, 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 Sinatra.new übergebene Block
-
-Auf das Scope-Objekt (die Klasse) kann wie folgt zugegriffen werden:
-
-* Über das Objekt, das an den +configure+-Block übergeben wird (configure
- { |c| ... }).
-* +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
-Sinatra::Delegator.delegate :methoden_name hinzugefügt werden.
-
-Im Delegation-Scop befindet man sich:
-
-* Im Top-Level, wenn require 'sinatra' aufgerufen wurde.
-* In einem Objekt, das mit dem Sinatra::Delegator-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. Ruby 1.8.6 wird nicht mehr unterstützt. 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. 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 ]
- 1.9.3 wird vollständig unterstützt und empfohlen. Achtung, bei einem Wechsel
- zu 1.9.3 werden alle Sessions ungültig.
-
-[ Rubinius ]
- Rubinius (rbx >= 1.2.4) wird offiziell unter Einbezug aller Templates
- unterstützt. Die kommende 2.0 Version wird ebenfalls unterstützt, samt 1.9
- Modus.
-
-[ JRuby ]
- JRuby wird offiziell unterstützt (JRuby >= 1.6.7). 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 – die Thin und Mongrel Web-Server werden bisher nicht unterstütz. JRubys
- Unterstützung für C-Erweiterungen sind zur Zeit ebenfalls experimenteller
- Natur, betrifft im Moment aber nur die RDiscount, Redcarpet, RedCloth und
- Yajl Templates.
-
-
-Weiterhin werden wir die kommende Ruby-Versionen im Auge behalten.
-
-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 2.0.0) und 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 2.0.0p0 und 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 sinatra/lib-
-Ordner im LOAD_PATH:
-
- cd myapp
- git clone git://github.com/sinatra/sinatra.git
- ruby -Isinatra/lib myapp.rb
-
-Alternativ kann der sinatra/lib-Ordner zum LOAD_PATH 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://travis-ci.org/sinatra/sinatra]
-