2013-01-26 21:30:47 -05:00
|
|
|
|
# Sinatra
|
|
|
|
|
|
|
|
|
|
*Wichtig: Dieses Dokument ist eine Übersetzung aus dem Englischen und unter
|
2014-09-13 17:17:49 -04:00
|
|
|
|
Umständen nicht auf dem aktuellen Stand (aktuell Sinatra 1.4.5).*
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
Sinatra ist eine
|
2016-01-21 11:34:30 -05:00
|
|
|
|
[DSL](https://de.wikipedia.org/wiki/Domänenspezifische_Sprache), die das
|
2013-01-26 21:30:47 -05:00
|
|
|
|
schnelle Erstellen von Webanwendungen in Ruby mit minimalem Aufwand
|
|
|
|
|
ermöglicht:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Sinatra via `rubygems` installieren:
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
gem install sinatra
|
|
|
|
|
```
|
|
|
|
|
|
2013-05-27 09:13:55 -04:00
|
|
|
|
Eine Datei mit dem Namen `myapp.rb` erstellen:
|
2013-03-24 16:42:36 -04:00
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require 'sinatra'
|
|
|
|
|
get '/' do
|
|
|
|
|
'Hallo Welt!'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
und im gleichen Verzeichnis ausführen:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
ruby myapp.rb
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Die Seite kann nun unter [http://localhost:4567](http://localhost:4567)
|
|
|
|
|
aufgerufen werden.
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-15 05:25:20 -04:00
|
|
|
|
## Inhalt
|
|
|
|
|
|
|
|
|
|
* [Sinatra](#sinatra)
|
|
|
|
|
* [Routen](#routen)
|
|
|
|
|
* [Bedingungen](#bedingungen)
|
|
|
|
|
* [Rückgabewerte](#rckgabewerte)
|
|
|
|
|
* [Eigene Routen-Muster](#eigene-routen-muster)
|
|
|
|
|
* [Statische Dateien](#statische-dateien)
|
|
|
|
|
* [Views/Templates](#viewstemplates)
|
|
|
|
|
* [Direkte Templates](#direkte-templates)
|
|
|
|
|
* [Verfügbare Templatesprachen](#verfgbare-templatesprachen)
|
|
|
|
|
* [Haml Templates](#haml-templates)
|
|
|
|
|
* [Erb Templates](#erb-templates)
|
|
|
|
|
* [Builder Templates](#builder-templates)
|
|
|
|
|
* [Nokogiri Templates](#nokogiri-templates)
|
|
|
|
|
* [Sass Templates](#sass-templates)
|
|
|
|
|
* [SCSS Templates](#scss-templates)
|
|
|
|
|
* [Less Templates](#less-templates)
|
|
|
|
|
* [Liquid Templates](#liquid-templates)
|
|
|
|
|
* [Markdown Templates](#markdown-templates)
|
|
|
|
|
* [Textile Templates](#textile-templates)
|
|
|
|
|
* [RDoc Templates](#rdoc-templates)
|
2014-09-13 18:02:31 -04:00
|
|
|
|
* [AsciiDoc Templates](#asciidoc-templates)
|
2013-03-15 05:25:20 -04:00
|
|
|
|
* [Radius Templates](#radius-templates)
|
|
|
|
|
* [Markaby Templates](#markaby-templates)
|
|
|
|
|
* [RABL Templates](#rabl-templates)
|
|
|
|
|
* [Slim Templates](#slim-templates)
|
|
|
|
|
* [Creole Templates](#creole-templates)
|
2014-09-13 18:02:31 -04:00
|
|
|
|
* [MediaWiki Templates](#mediawiki-templates)
|
2013-03-15 05:25:20 -04:00
|
|
|
|
* [CoffeeScript Templates](#coffeescript-templates)
|
|
|
|
|
* [Stylus Templates](#stylus-templates)
|
|
|
|
|
* [Yajl Templates](#yajl-templates)
|
|
|
|
|
* [WLang Templates](#wlang-templates)
|
|
|
|
|
* [Auf Variablen in Templates zugreifen](#auf-variablen-in-templates-zugreifen)
|
|
|
|
|
* [Templates mit `yield` und verschachtelte Layouts](#templates-mit-yield-und-verschachtelte-layouts)
|
|
|
|
|
* [Inline-Templates](#inline-templates)
|
|
|
|
|
* [Benannte Templates](#benannte-templates)
|
|
|
|
|
* [Dateiendungen zuordnen](#dateiendungen-zuordnen)
|
|
|
|
|
* [Eine eigene Template-Engine hinzufügen](#eine-eigene-template-engine-hinzufgen)
|
2014-09-13 18:02:31 -04:00
|
|
|
|
* [Eigene Methoden zum Aufsuchen von Templates verwenden](#eigene-methoden-zum-aufsuchen-von-templates-verwenden)
|
2013-03-15 05:25:20 -04:00
|
|
|
|
* [Filter](#filter)
|
|
|
|
|
* [Helfer](#helfer)
|
|
|
|
|
* [Sessions verwenden](#sessions-verwenden)
|
|
|
|
|
* [Anhalten](#anhalten)
|
|
|
|
|
* [Weiterspringen](#weiterspringen)
|
|
|
|
|
* [Eine andere Route ansteuern](#eine-andere-route-ansteuern)
|
|
|
|
|
* [Body, Status-Code und Header setzen](#body-status-code-und-header-setzen)
|
|
|
|
|
* [Response-Streams](#response-streams)
|
|
|
|
|
* [Logger](#logger)
|
|
|
|
|
* [Mime-Types](#mime-types)
|
|
|
|
|
* [URLs generieren](#urls-generieren)
|
|
|
|
|
* [Browser-Umleitung](#browser-umleitung)
|
|
|
|
|
* [Cache einsetzen](#cache-einsetzen)
|
|
|
|
|
* [Dateien versenden](#dateien-versenden)
|
|
|
|
|
* [Das Request-Objekt](#das-request-objekt)
|
|
|
|
|
* [Anhänge](#anhnge)
|
|
|
|
|
* [Umgang mit Datum und Zeit](#umgang-mit-datum-und-zeit)
|
|
|
|
|
* [Nachschlagen von Template-Dateien](#nachschlagen-von-template-dateien)
|
|
|
|
|
* [Konfiguration](#konfiguration)
|
|
|
|
|
* [Einstellung des Angriffsschutzes](#einstellung-des-angriffsschutzes)
|
|
|
|
|
* [Mögliche Einstellungen](#mgliche-einstellungen)
|
|
|
|
|
* [Umgebungen](#umgebungen)
|
|
|
|
|
* [Fehlerbehandlung](#fehlerbehandlung)
|
|
|
|
|
* [Nicht gefunden](#nicht-gefunden)
|
|
|
|
|
* [Fehler](#fehler)
|
|
|
|
|
* [Rack-Middleware](#rack-middleware)
|
|
|
|
|
* [Testen](#testen)
|
|
|
|
|
* [Sinatra::Base - Middleware, Bibliotheken und modulare Anwendungen](#sinatrabase---middleware-bibliotheken-und-modulare-anwendungen)
|
|
|
|
|
* [Modularer vs. klassischer Stil](#modularer-vs-klassischer-stil)
|
|
|
|
|
* [Eine modulare Applikation bereitstellen](#eine-modulare-applikation-bereitstellen)
|
|
|
|
|
* [Eine klassische Anwendung mit einer config.ru verwenden](#eine-klassische-anwendung-mit-einer-configru-verwenden)
|
|
|
|
|
* [Wann sollte eine config.ru-Datei verwendet werden?](#wann-sollte-eine-configru-datei-verwendet-werden)
|
|
|
|
|
* [Sinatra als Middleware nutzen](#sinatra-als-middleware-nutzen)
|
|
|
|
|
* [Dynamische Applikationserstellung](#dynamische-applikationserstellung)
|
|
|
|
|
* [Geltungsbereich und Bindung](#geltungsbereich-und-bindung)
|
|
|
|
|
* [Anwendungs- oder Klassen-Scope](#anwendungs--oder-klassen-scope)
|
|
|
|
|
* [Anfrage- oder Instanz-Scope](#anfrage--oder-instanz-scope)
|
|
|
|
|
* [Delegation-Scope](#delegation-scope)
|
|
|
|
|
* [Kommandozeile](#kommandozeile)
|
2015-12-02 11:16:27 -05:00
|
|
|
|
* [Multi-Threading](#multi-threading)
|
2013-03-15 05:25:20 -04:00
|
|
|
|
* [Systemanforderungen](#systemanforderungen)
|
|
|
|
|
* [Der neuste Stand (The Bleeding Edge)](#der-neuste-stand-the-bleeding-edge)
|
|
|
|
|
* [Mit Bundler](#mit-bundler)
|
|
|
|
|
* [Eigenes Repository](#eigenes-repository)
|
|
|
|
|
* [Gem erstellen](#gem-erstellen)
|
|
|
|
|
* [Versions-Verfahren](#versions-verfahren)
|
|
|
|
|
* [Mehr](#mehr)
|
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
## Routen
|
|
|
|
|
|
|
|
|
|
In Sinatra wird eine Route durch eine HTTP-Methode und ein URL-Muster definiert.
|
|
|
|
|
Jeder dieser Routen wird ein Ruby-Block zugeordnet:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
.. zeige etwas ..
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
post '/' do
|
|
|
|
|
.. erstelle etwas ..
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
put '/' do
|
|
|
|
|
.. update etwas ..
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
delete '/' do
|
|
|
|
|
.. entferne etwas ..
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
options '/' do
|
|
|
|
|
.. zeige, was wir können ..
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
link '/' do
|
|
|
|
|
.. verbinde etwas ..
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
unlink '/' do
|
|
|
|
|
.. trenne etwas ..
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/hallo/:name' do
|
|
|
|
|
# passt auf "GET /hallo/foo" und "GET /hallo/bar"
|
2014-08-04 18:51:59 -04:00
|
|
|
|
# params['name'] ist dann 'foo' oder 'bar'
|
|
|
|
|
"Hallo #{params['name']}!"
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Man kann auf diese auch mit Block-Parametern zugreifen:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/hallo/:name' do |n|
|
2014-08-04 18:51:59 -04:00
|
|
|
|
# n entspricht hier params['name']
|
2013-03-09 17:46:38 -05:00
|
|
|
|
"Hallo #{n}!"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Routen-Muster können auch mit sog. Splat- oder Wildcard-Parametern über das
|
2014-08-04 18:51:59 -04:00
|
|
|
|
`params['splat']`-Array angesprochen werden:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/sag/*/zu/*' do
|
2013-03-24 16:42:36 -04:00
|
|
|
|
# passt z.B. auf /sag/hallo/zu/welt
|
2014-08-04 18:51:59 -04:00
|
|
|
|
params['splat'] # => ["hallo", "welt"]
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/download/*.*' do
|
|
|
|
|
# passt auf /download/pfad/zu/datei.xml
|
2014-08-04 18:51:59 -04:00
|
|
|
|
params['splat'] # => ["pfad/zu/datei", "xml"]
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Oder mit Block-Parametern:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/download/*.*' do |pfad, endung|
|
|
|
|
|
[pfad, endung] # => ["Pfad/zu/Datei", "xml"]
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Routen mit regulären Ausdrücken sind auch möglich:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2015-02-05 16:15:53 -05:00
|
|
|
|
get /\A\/hallo\/([\w]+)\z/ do
|
2014-08-04 18:51:59 -04:00
|
|
|
|
"Hallo, #{params['captures'].first}!"
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
2013-02-26 10:09:24 -05:00
|
|
|
|
```
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
Und auch hier können Block-Parameter genutzt werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get %r{/hallo/([\w]+)} do |c|
|
|
|
|
|
"Hallo, #{c}!"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Routen-Muster können auch mit optionalen Parametern ausgestattet werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/posts.?:format?' do
|
|
|
|
|
# passt auf "GET /posts" sowie jegliche Erweiterung
|
|
|
|
|
# wie "GET /posts.json", "GET /posts.xml" etc.
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2014-09-13 17:17:49 -04:00
|
|
|
|
Routen können auch den query-Parameter verwenden:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2014-09-13 17:17:49 -04:00
|
|
|
|
get '/posts' do
|
|
|
|
|
# matches "GET /posts?title=foo&author=bar"
|
|
|
|
|
title = params['title']
|
|
|
|
|
author = params['author']
|
|
|
|
|
# uses title and author variables; query is optional to the /posts route
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
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
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
An Routen können eine Vielzahl von Bedingungen geknüpft werden, die erfüllt
|
2013-01-26 21:30:47 -05:00
|
|
|
|
sein müssen, damit der Block ausgeführt wird. Möglich wäre etwa eine
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Einschränkung des User-Agents über die interne Bedingung `:agent`:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
|
2014-08-04 18:51:59 -04:00
|
|
|
|
"Du verwendest Songbird Version #{params['agent'][0]}"
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Wird Songbird als Browser nicht verwendet, springt Sinatra zur nächsten Route:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
# passt auf andere Browser
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Andere mitgelieferte Bedingungen sind `:host_name` und `:provides`:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/', :host_name => /^admin\./ do
|
|
|
|
|
"Adminbereich, Zugriff verweigert!"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/', :provides => 'html' do
|
|
|
|
|
haml :index
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/', :provides => ['rss', 'atom', 'xml'] do
|
|
|
|
|
builder :feed
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
2014-09-13 17:17:49 -04:00
|
|
|
|
`provides` durchsucht den Accept-Header der Anfrage
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Eigene Bedingungen können relativ einfach hinzugefügt werden:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-24 16:42:36 -04:00
|
|
|
|
set(:wahrscheinlichkeit) { |value| condition { rand <= value } }
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
get '/auto_gewinnen', :wahrscheinlichkeit => 0.1 do
|
2013-03-09 17:46:38 -05:00
|
|
|
|
"Du hast gewonnen!"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/auto_gewinnen' do
|
|
|
|
|
"Tut mir leid, verloren."
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Bei Bedingungen, die mehrere Werte annehmen können, sollte ein Splat verwendet
|
|
|
|
|
werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
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
|
2013-01-26 21:30:47 -05:00
|
|
|
|
end
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get "/mein/account/", :auth => [:user, :admin] do
|
|
|
|
|
"Mein Account"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get "/nur/admin/", :auth => :admin do
|
|
|
|
|
"Nur Admins dürfen hier rein!"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
class Stream
|
|
|
|
|
def each
|
|
|
|
|
100.times { |i| yield "#{i}\n" }
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get('/') { Stream.new }
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
class AllButPattern
|
|
|
|
|
Match = Struct.new(:captures)
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
def initialize(except)
|
|
|
|
|
@except = except
|
|
|
|
|
@captures = Match.new([])
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
def match(str)
|
|
|
|
|
@captures unless @except === str
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
def all_but(pattern)
|
|
|
|
|
AllButPattern.new(pattern)
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get all_but("/index") do
|
|
|
|
|
# ...
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Beachte, dass das obige Beispiel etwas übertrieben wirkt. Es geht auch einfacher:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get // do
|
|
|
|
|
pass if request.path_info == "/index"
|
|
|
|
|
# ...
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Oder unter Verwendung eines negativen look ahead:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get %r{^(?!/index$)} do
|
|
|
|
|
# ...
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
2013-03-24 16:42:36 -04:00
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
## Statische Dateien
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Statische Dateien werden im `./public`-Ordner erwartet. Es ist möglich,
|
2013-01-26 21:30:47 -05:00
|
|
|
|
einen anderen Ort zu definieren, indem man die `:public_folder`-Option setzt:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
set :public_folder, File.dirname(__FILE__) + '/static'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Zu beachten ist, dass der Ordnername `public` nicht Teil der URL ist. Die Datei
|
2013-01-26 21:30:47 -05:00
|
|
|
|
`./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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
erb :index
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Dieses Beispiel rendert `views/index.erb`.
|
|
|
|
|
|
|
|
|
|
Anstelle eines Templatenamens kann man auch direkt die Templatesprache verwenden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
code = "<%= Time.now %>"
|
|
|
|
|
erb code
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Templates nehmen ein zweite Argument an, den Options-Hash:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
erb :index, :layout => :post
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
haml :index, :format => :html5
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Für alle Templates können auch Einstellungen, die für alle Routen gelten,
|
|
|
|
|
festgelegt werden:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
set :haml, :format => :html5
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
haml :index
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Optionen, die an die Rendermethode weitergegeben werden, überschreiben die
|
|
|
|
|
Einstellungen, die mit `set` festgelegt wurden.
|
|
|
|
|
|
|
|
|
|
Einstellungen:
|
|
|
|
|
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>locals</dt>
|
2013-03-24 16:42:36 -04:00
|
|
|
|
<dd>Liste von lokalen Variablen, die an das Dokument weitergegeben werden.
|
2013-03-09 17:46:38 -05:00
|
|
|
|
Praktisch für Partials:
|
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt></dd>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<dt>default_encoding</dt>
|
|
|
|
|
<dd>Gibt die Stringkodierung an, die verwendet werden soll. Voreingestellt
|
|
|
|
|
auf <tt>settings.default_encoding</tt>.</dd>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<dt>views</dt>
|
2013-03-24 16:42:36 -04:00
|
|
|
|
<dd>Ordner, aus dem die Templates geladen werden. Voreingestellt auf
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<tt>settings.views</tt>.</dd>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<dt>layout</dt>
|
|
|
|
|
<dd>Legt fest, ob ein Layouttemplate verwendet werden soll oder nicht
|
2013-03-09 17:46:38 -05:00
|
|
|
|
(<tt>true</tt> oder<tt>false</tt>). Ist es ein Symbol, dann legt es fest,
|
|
|
|
|
welches Template als Layout verwendet wird:
|
|
|
|
|
|
|
|
|
|
<tt>erb :index, :layout => !request.xhr?</tt></dd>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<dt>content_type</dt>
|
2013-03-24 16:42:36 -04:00
|
|
|
|
<dd>Content-Typ den das Template ausgibt. Voreinstellung hängt von der
|
2013-01-26 21:30:47 -05:00
|
|
|
|
Templatesprache ab.</dd>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<dt>scope</dt>
|
|
|
|
|
<dd>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.</dd>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<dt>layout_engine</dt>
|
|
|
|
|
<dd>Legt fest, welcher Renderer für das Layout verantwortlich ist. Hilfreich
|
|
|
|
|
für Sprachen, die sonst keine Templates unterstützen. Voreingestellt auf
|
2013-03-09 17:46:38 -05:00
|
|
|
|
den Renderer, der für das Template verwendet wird:
|
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<tt>set :rdoc, :layout_engine => :erb</tt></dd>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
<dt>layout_options</dt>
|
|
|
|
|
<dd>Besondere Einstellungen, die nur für das Rendering verwendet werden:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
<tt>set :rdoc, :layout_options => { :views => 'views/layouts' }</tt></dd>
|
|
|
|
|
</dl>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
Sinatra geht davon aus, dass die Templates sich im `./views` Verzeichnis
|
|
|
|
|
befinden. Es kann jedoch ein anderer Ordner festgelegt werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
set :views, settings.root + '/templates'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Es ist zu beachten, dass immer mit Symbolen auf Templates verwiesen werden muss,
|
|
|
|
|
auch dann, wenn sie sich in einem Unterordner befinden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
haml :'unterverzeichnis/template'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Rendering-Methoden rendern jeden String direkt.
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
### Direkte Templates
|
2013-03-09 17:46:38 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
haml '%div.title Hallo Welt'
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Hier wird der String direkt gerendert.
|
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
### Verfügbare Templatesprachen
|
|
|
|
|
|
|
|
|
|
Einige Sprachen haben mehrere Implementierungen. Um festzulegen, welche
|
|
|
|
|
verwendet wird (und dann auch Thread-sicher ist), verwendet man am besten zu
|
2013-03-09 17:46:38 -05:00
|
|
|
|
Beginn ein `'require'`:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require 'rdiscount' # oder require 'bluecloth'
|
|
|
|
|
get('/') { markdown :index }
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Haml Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
|
|
|
|
<td><a href="http://haml.info/">haml</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.haml</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>haml :index, :format => :html5</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Erb Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
<td><a href="http://www.kuwata-lab.com/erubis/">erubis</a> oder erb
|
2013-01-26 21:30:47 -05:00
|
|
|
|
(Standardbibliothek von Ruby)</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterungen</td>
|
|
|
|
|
<td><tt>.erb</tt>, <tt>.rhtml</tt> oder <tt>.erubis</tt> (nur Erubis)</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>erb :index</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Builder Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
2014-09-19 10:24:03 -04:00
|
|
|
|
<td><a href="https://github.com/jimweirich/builder">builder</a></td>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.builder</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>builder { |xml| xml.em "Hallo" }</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Nokogiri Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
2016-01-21 11:34:30 -05:00
|
|
|
|
<td><a href="http://www.nokogiri.org/">nokogiri</a></td>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.nokogiri</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>nokogiri { |xml| xml.em "Hallo" }</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Sass Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
|
|
|
|
<td><a href="http://sass-lang.com/">sass</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.sass</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>sass :stylesheet, :style => :expanded</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### SCSS Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
|
|
|
|
<td><a href="http://sass-lang.com/">sass</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.scss</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>scss :stylesheet, :style => :expanded</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Less Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
2016-01-21 11:34:30 -05:00
|
|
|
|
<td><a href="http://lesscss.org/">less</a></td>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.less</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>less :stylesheet</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Liquid Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
2016-01-21 11:34:30 -05:00
|
|
|
|
<td><a href="http://liquidmarkup.org/">liquid</a></td>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.liquid</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>liquid :index, :locals => { :key => 'Wert' }</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Markdown Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
<td>Eine der folgenden Bibliotheken:
|
2016-01-21 11:34:30 -05:00
|
|
|
|
<a href="https://github.com/davidfstr/rdiscount" title="RDiscount">RDiscount</a>,
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<a href="https://github.com/vmg/redcarpet" title="RedCarpet">RedCarpet</a>,
|
|
|
|
|
<a href="http://deveiate.org/projects/BlueCloth" title="BlueCloth">BlueCloth</a>,
|
2014-09-19 10:24:03 -04:00
|
|
|
|
<a href="http://kramdown.gettalong.org/" title="kramdown">kramdown</a> oder
|
|
|
|
|
<a href="https://github.com/bhollis/maruku" title="maruku">maruku</a>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterungen</td>
|
|
|
|
|
<td><tt>.markdown</tt>, <tt>.mkd</tt> und <tt>.md</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>markdown :index, :layout_engine => :erb</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
erb :overview, :locals => { :text => markdown(:einfuehrung) }
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Beachte, dass man die `markdown`-Methode auch aus anderen Templates heraus
|
|
|
|
|
aufrufen kann:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
%h1 Gruß von Haml!
|
|
|
|
|
%p= markdown(:Grüße)
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Textile Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
|
|
|
|
<td><a href="http://redcloth.org/">RedCloth</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.textile</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>textile :index, :layout_engine => :erb</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
erb :overview, :locals => { :text => textile(:einfuehrung) }
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Beachte, dass man die `textile`-Methode auch aus anderen Templates heraus
|
|
|
|
|
aufrufen kann:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
%h1 Gruß von Haml!
|
|
|
|
|
%p= textile(:Grüße)
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### RDoc Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
2014-09-19 10:24:03 -04:00
|
|
|
|
<td><a href="http://rdoc.sourceforge.net/">rdoc</a></td>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.rdoc</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>textile :README, :layout_engine => :erb</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
erb :overview, :locals => { :text => rdoc(:einfuehrung) }
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Beachte, dass man die `rdoc`-Methode auch aus anderen Templates heraus
|
|
|
|
|
aufrufen kann:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
%h1 Gruß von Haml!
|
|
|
|
|
%p= rdoc(:Grüße)
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2014-09-13 17:17:49 -04:00
|
|
|
|
#### AsciiDoc Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
|
|
|
|
<td><a href="http://asciidoctor.org/" title="Asciidoctor">Asciidoctor</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterungen</td>
|
|
|
|
|
<td><tt>.asciidoc</tt>, <tt>.adoc</tt> und <tt>.ad</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>asciidoc :README, :layout_engine => :erb</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
Da man aus dem AsciiDoc-Template heraus keine Ruby-Methoden aufrufen kann
|
|
|
|
|
(ausgenommen `yield`), wird man üblicherweise locals verwenden wollen, mit
|
|
|
|
|
denen man Variablen weitergibt.
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Radius Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
2014-09-19 10:24:03 -04:00
|
|
|
|
<td><a href="https://github.com/jlong/radius">radius</a></td>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.radius</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>radius :index, :locals => { :key => 'Wert' }</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
Da man aus dem Radius-Template heraus keine Ruby-Methoden aufrufen kann, wird
|
|
|
|
|
man üblicherweise locals verwenden wollen, mit denen man Variablen weitergibt.
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Markaby Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
2016-01-21 11:34:30 -05:00
|
|
|
|
<td><a href="http://markaby.github.io/">markaby</a></td>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.mab</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>markaby { h1 "Willkommen!" }</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### RABL Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
|
|
|
|
<td><a href="https://github.com/nesquena/rabl">rabl</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.rabl</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>rabl :index</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Slim Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
|
|
|
|
<td><a href="http://slim-lang.com/">slim</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.slim</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>slim :index</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Creole Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
|
|
|
|
<td><a href="https://github.com/minad/creole">creole</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.creole</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>creole :wiki, :layout_engine => :erb</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
erb :overview, :locals => { :text => creole(:einfuehrung) }
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Beachte, dass man die `creole`-Methode auch aus anderen Templates heraus
|
|
|
|
|
aufrufen kann:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
%h1 Gruß von Haml!
|
|
|
|
|
%p= creole(:Grüße)
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2014-09-13 17:17:49 -04:00
|
|
|
|
#### MediaWiki Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
|
|
|
|
<td><a href="https://github.com/nricciar/wikicloth" title="WikiCloth">WikiCloth</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterungen</td>
|
|
|
|
|
<td><tt>.mediawiki</tt> und <tt>.mw</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>mediawiki :wiki, :layout_engine => :erb</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
Da man aus dem Mediawiki-Template heraus keine Ruby-Methoden aufrufen und auch
|
|
|
|
|
keine locals verwenden kann, wird man Mediawiki üblicherweise in Kombination mit
|
|
|
|
|
anderen Renderern verwenden wollen:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2014-09-13 17:17:49 -04:00
|
|
|
|
erb :overview, :locals => { :text => mediawiki(:introduction) }
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Beachte: Man kann die `mediawiki`-Methode auch aus anderen Templates
|
|
|
|
|
heraus aufrufen:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2014-09-13 17:17:49 -04:00
|
|
|
|
%h1 Grüße von Haml!
|
|
|
|
|
%p= mediawiki(:greetings)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Da man Ruby nicht von MediaWiki heraus aufrufen kann, können auch Layouts nicht
|
|
|
|
|
in MediaWiki 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.
|
|
|
|
|
|
2013-03-12 12:19:39 -04:00
|
|
|
|
#### CoffeeScript Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
<td><a href="https://github.com/josh/ruby-coffee-script">coffee-script</a>
|
2013-03-12 12:19:39 -04:00
|
|
|
|
und eine <a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme">Möglichkeit JavaScript auszuführen</a>.
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.coffee</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>coffee :index</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
#### Stylus Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
|
|
|
|
<td>
|
2016-01-21 11:34:30 -05:00
|
|
|
|
<a href="https://github.com/forgecrafted/ruby-stylus" title="Ruby Stylus">
|
2013-03-09 17:46:38 -05:00
|
|
|
|
Stylus
|
2013-09-16 09:02:43 -04:00
|
|
|
|
</a> und eine Möglichkeit
|
2013-03-09 17:46:38 -05:00
|
|
|
|
<a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme" title="ExecJS">
|
|
|
|
|
JavaScript auszuführen
|
|
|
|
|
</a>.
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.styl</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>stylus :index</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
Um Stylus-Templates ausführen zu können, müssen `stylus` und `stylus/tilt`
|
|
|
|
|
zuerst geladen werden:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require 'sinatra'
|
|
|
|
|
require 'stylus'
|
|
|
|
|
require 'stylus/tilt'
|
|
|
|
|
|
|
|
|
|
get '/' do
|
|
|
|
|
stylus :example
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Yajl Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
|
|
|
|
<td><a href="https://github.com/brianmario/yajl-ruby" title="yajl-ruby">yajl-ruby</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.yajl</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td>
|
|
|
|
|
<tt>
|
|
|
|
|
yajl :index,
|
|
|
|
|
:locals => { :key => 'qux' },
|
|
|
|
|
:callback => 'present',
|
|
|
|
|
:variable => 'resource'
|
|
|
|
|
</tt>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
Die Template-Quelle wird als Ruby-String evaluiert. Die daraus resultierende
|
|
|
|
|
json Variable wird mit Hilfe von `#to_json` umgewandelt:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
json = { :foo => 'bar' }
|
|
|
|
|
json[:baz] = key
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Die `:callback` und `:variable` Optionen können mit dem gerenderten Objekt
|
|
|
|
|
verwendet werden:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```javascript
|
2014-09-13 17:17:49 -04:00
|
|
|
|
var resource = {"foo":"bar","baz":"qux"};
|
|
|
|
|
present(resource);
|
2013-03-09 17:46:38 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-12 12:19:39 -04:00
|
|
|
|
#### WLang Templates
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Abhängigkeit</td>
|
|
|
|
|
<td><a href="https://github.com/blambeau/wlang/">wlang</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dateierweiterung</td>
|
|
|
|
|
<td><tt>.wlang</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Beispiel</td>
|
|
|
|
|
<td><tt>wlang :index, :locals => { :key => 'value' }</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2014-09-13 17:17:49 -04:00
|
|
|
|
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.
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/:id' do
|
2014-08-04 18:51:59 -04:00
|
|
|
|
@foo = Foo.find(params['id'])
|
2013-03-09 17:46:38 -05:00
|
|
|
|
haml '%h1= @foo.name'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Oder durch einen expliziten Hash von lokalen Variablen:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/:id' do
|
2014-08-04 18:51:59 -04:00
|
|
|
|
foo = Foo.find(params['id'])
|
2013-03-09 17:46:38 -05:00
|
|
|
|
haml '%h1= bar.name', :locals => { :bar => foo }
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Dies wird typischerweise bei Verwendung von Subtemplates (partials) in anderen
|
|
|
|
|
Templates eingesetzt.
|
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
### Templates mit `yield` und verschachtelte Layouts
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Ein Layout ist üblicherweise ein Template, das ein `yield` aufruft. Ein solches
|
|
|
|
|
Template kann entweder wie oben beschrieben über die `:template` Option
|
2013-03-09 17:46:38 -05:00
|
|
|
|
verwendet werden oder mit einem Block gerendert werden:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
erb :post, :layout => false do
|
|
|
|
|
erb :index
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Dieser Code entspricht weitestgehend `erb :index, :layout => :post`.
|
|
|
|
|
|
|
|
|
|
Blöcke an Render-Methoden weiterzugeben ist besonders bei verschachtelten
|
|
|
|
|
Layouts hilfreich:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
erb :main_layout, :layout => false do
|
|
|
|
|
erb :admin_layout do
|
|
|
|
|
erb :user
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Der gleiche Effekt kann auch mit weniger Code erreicht werden:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
erb :admin_layout, :layout => :main_layout do
|
|
|
|
|
erb :user
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Zur Zeit nehmen folgende Renderer Blöcke an: `erb`, `haml`, `liquid`, `slim `
|
|
|
|
|
und `wlang`.
|
|
|
|
|
|
|
|
|
|
Das gleich gilt auch für die allgemeine `render` Methode.
|
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
### Inline-Templates
|
|
|
|
|
|
|
|
|
|
Templates können auch am Ende der Datei definiert werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require 'sinatra'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
haml :index
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
__END__
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
@@ layout
|
|
|
|
|
%html
|
|
|
|
|
= yield
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
@@ index
|
|
|
|
|
%div.title Hallo Welt!!!!!
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
template :layout do
|
|
|
|
|
"%html\n =yield\n"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
template :index do
|
|
|
|
|
'%div.title Hallo Welt!'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
haml :index
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Wenn ein Template mit dem Namen "layout" existiert, wird es bei jedem Aufruf
|
|
|
|
|
verwendet. Durch `:layout => false` kann das Ausführen verhindert werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
2013-03-24 16:42:36 -04:00
|
|
|
|
haml :index, :layout => !request.xhr?
|
|
|
|
|
# !request.xhr? prüft, ob es sich um einen asynchronen Request handelt.
|
|
|
|
|
# wenn nicht, dann verwende ein Layout (negiert durch !)
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
Tilt.register :tt, Tilt[:textile]
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Eine eigene Template-Engine hinzufügen
|
|
|
|
|
|
|
|
|
|
Zu allererst muss die Engine bei Tilt registriert und danach eine
|
|
|
|
|
Rendering-Methode erstellt werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
Tilt.register :mtt, MeineTolleTemplateEngine
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
helpers do
|
|
|
|
|
def mtt(*args) render(:mtt, *args) end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
mtt :index
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Dieser Code rendert `./views/application.mtt`. Siehe
|
|
|
|
|
[github.com/rtomayko/tilt](https://github.com/rtomayko/tilt), um mehr über
|
|
|
|
|
Tilt zu erfahren.
|
|
|
|
|
|
2014-09-13 17:17:49 -04:00
|
|
|
|
### Eigene Methoden zum Aufsuchen von Templates verwenden
|
|
|
|
|
|
|
|
|
|
Um einen eigenen Mechanismus zum Aufsuchen von Templates zu
|
|
|
|
|
implementieren, muss `#find_template` definiert werden:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2014-09-13 17:17:49 -04:00
|
|
|
|
configure do
|
|
|
|
|
set :views [ './views/a', './views/b' ]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def find_template(views, name, engine, &block)
|
|
|
|
|
Array(views).each do |v|
|
|
|
|
|
super(v, name, engine, &block)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
## 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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
before do
|
|
|
|
|
@note = 'Hi!'
|
|
|
|
|
request.path_info = '/foo/bar/baz'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/foo/*' do
|
|
|
|
|
@note #=> 'Hi!'
|
2014-08-04 18:51:59 -04:00
|
|
|
|
params['splat'] #=> 'bar/baz'
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
after do
|
|
|
|
|
puts response.status
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Filter können optional auch mit einem Muster ausgestattet werden, das auf den
|
|
|
|
|
Request-Pfad passen muss, damit der Filter ausgeführt wird:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
before '/protected/*' do
|
|
|
|
|
authenticate!
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
after '/create/:slug' do |slug|
|
|
|
|
|
session[:last_slug] = slug
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Ähnlich wie Routen können Filter auch mit weiteren Bedingungen eingeschränkt
|
|
|
|
|
werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
before :agent => /Songbird/ do
|
|
|
|
|
# ...
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
after '/blog/*', :host_name => 'example.com' do
|
|
|
|
|
# ...
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Helfer
|
|
|
|
|
|
|
|
|
|
Durch die Top-Level `helpers`-Methode werden sogenannte Helfer-Methoden
|
|
|
|
|
definiert, die in Routen und Templates verwendet werden können:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
helpers do
|
|
|
|
|
def bar(name)
|
|
|
|
|
"#{name}bar"
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/:name' do
|
2014-08-04 18:51:59 -04:00
|
|
|
|
bar(params['name'])
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
enable :sessions
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
"value = " << session[:value].inspect
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/:value' do
|
2014-08-04 18:51:59 -04:00
|
|
|
|
session[:value] = params['value']
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
use Rack::Session::Pool, :expire_after => 2592000
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
"value = " << session[:value].inspect
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/:value' do
|
2014-08-04 18:51:59 -04:00
|
|
|
|
session[:value] = params['value']
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-24 16:42:36 -04:00
|
|
|
|
set :session_secret, 'super_geheimes_Gegeimnis'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Zur weiteren Konfiguration kann man einen Hash mit Optionen in den `sessions`
|
|
|
|
|
Einstellungen ablegen.
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
set :sessions, :domain => 'foo.com'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2014-09-13 17:17:49 -04:00
|
|
|
|
Um eine Session mit anderen Apps und zwischen verschiedenen Subdomains
|
|
|
|
|
von foo.com zu teilen, wird ein *.* der Domain vorangestellt:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2014-09-13 17:17:49 -04:00
|
|
|
|
set :sessions, :domain => '.foo,com'
|
|
|
|
|
```
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
### Anhalten
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
Zum sofortigen Stoppen eines Request in einem Filter oder einer Route:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
halt
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Der Status kann beim Stoppen mit angegeben werden:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
halt 410
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Oder auch den Response-Body:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
halt 'Hier steht der Body'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Oder beides:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
halt 401, 'verschwinde!'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Sogar mit Headern:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
halt 402, {'Content-Type' => 'text/plain'}, 'Rache'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Natürlich ist es auch möglich, ein Template mit `halt` zu verwenden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
halt erb(:error)
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
### Weiterspringen
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
Eine Route kann mittels `pass` zu der nächsten passenden Route springen:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/raten/:wer' do
|
2014-08-04 18:51:59 -04:00
|
|
|
|
pass unless params['wer'] == 'Frank'
|
2013-03-09 17:46:38 -05:00
|
|
|
|
'Du hast mich!'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/raten/*' do
|
|
|
|
|
'Du hast mich nicht!'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Wenn nicht zu einer anderen Route gesprungen werden soll, sondern nur das
|
|
|
|
|
Ergebnis einer anderen Route gefordert wird, kann `call` für einen internen
|
|
|
|
|
Request verwendet werden:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
status, headers, body = call env.merge("PATH_INFO" => '/bar')
|
|
|
|
|
[status, headers, body.map(&:upcase)]
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/bar' do
|
|
|
|
|
"bar"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
### Body, Status-Code und Header setzen
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
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 anderer Stelle während der Ausführung gesetzt werden soll.
|
|
|
|
|
Dafür kann man die Helfer-Methode `body` einsetzen. Ist sie gesetzt, kann sie zu
|
|
|
|
|
einem späteren Zeitpunkt aufgerufen werden:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
body "bar"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
after do
|
|
|
|
|
puts body
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
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
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
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
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-09-16 09:02:43 -04:00
|
|
|
|
Damit lassen sich Streaming-APIs realisieren, sog.
|
2016-01-21 11:34:30 -05:00
|
|
|
|
[Server Sent Events](https://w3c.github.io/eventsource/), die als Basis für
|
|
|
|
|
[WebSockets](https://en.wikipedia.org/wiki/WebSocket) dienen. Ebenso können sie
|
2013-01-26 21:30:47 -05:00
|
|
|
|
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
|
2015-03-19 18:29:50 -04:00
|
|
|
|
Applikation verwendet wird. Einige Server unterstützen
|
2013-01-26 21:30:47 -05:00
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
# Durchgehende Anfrage (long polling)
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
set :server, :thin
|
|
|
|
|
connections = []
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/subscribe' do
|
|
|
|
|
# Client-Registrierung beim Server, damit Events mitgeteilt werden können
|
2014-11-15 12:42:37 -05:00
|
|
|
|
stream(:keep_open) do |out|
|
|
|
|
|
connections << out
|
|
|
|
|
# tote Verbindungen entfernen
|
|
|
|
|
connections.reject!(&:closed?)
|
|
|
|
|
end
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
|
|
|
|
|
2014-09-13 17:17:49 -04:00
|
|
|
|
post '/:message' do
|
2013-03-09 17:46:38 -05:00
|
|
|
|
connections.each do |out|
|
|
|
|
|
# Den Client über eine neue Nachricht in Kenntnis setzen
|
|
|
|
|
# notify client that a new message has arrived
|
2014-08-04 18:51:59 -04:00
|
|
|
|
out << params['message'] << "\n"
|
2013-03-09 17:46:38 -05:00
|
|
|
|
|
|
|
|
|
# Den Client zur erneuten Verbindung auffordern
|
|
|
|
|
out.close
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Rückmeldung
|
|
|
|
|
"Mitteiling erhalten"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Logger
|
|
|
|
|
|
|
|
|
|
Im Geltungsbereich eines Request stellt die `logger` Helfer-Methode eine `Logger`
|
|
|
|
|
Instanz zur Verfügung:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
logger.info "es passiert gerade etwas"
|
|
|
|
|
# ...
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-09-16 09:02:43 -04:00
|
|
|
|
Der Logger übernimmt dabei automatisch alle im Rack-Handler eingestellten
|
2013-01-26 21:30:47 -05:00
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
class MyApp < Sinatra::Base
|
|
|
|
|
configure :production, :development do
|
|
|
|
|
enable :logging
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
2013-09-16 09:02:43 -04:00
|
|
|
|
eigener Logger eingerichtet werden soll. Sinatra wird dann verwenden, was in
|
2013-01-26 21:30:47 -05:00
|
|
|
|
`env['rack.logger']` eingetragen ist.
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
### Mime-Types
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
configure do
|
|
|
|
|
mime_type :foo, 'text/foo'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Es kann aber auch der `content_type`-Helfer verwendet werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
content_type :foo
|
|
|
|
|
"foo foo foo"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### URLs generieren
|
|
|
|
|
|
|
|
|
|
Zum Generieren von URLs sollte die `url`-Helfer-Methode genutzen werden, so z.B.
|
|
|
|
|
beim Einsatz von Haml:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
%a{:href => url('/foo')} foo
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
redirect to('/bar')
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Weitere Parameter werden wie Argumente der `halt`-Methode behandelt:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
redirect to('/bar'), 303
|
2016-01-21 11:34:30 -05:00
|
|
|
|
redirect 'http://www.google.com/', 'Hier bist du falsch'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Ebenso leicht lässt sich ein Schritt zurück mit dem Alias `redirect back`
|
|
|
|
|
erreichen:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
"<a href='/bar'>mach was</a>"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/bar' do
|
|
|
|
|
mach_was
|
|
|
|
|
redirect back
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Um Argumente an ein Redirect weiterzugeben, können sie entweder dem Query
|
|
|
|
|
übergeben:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
redirect to('/bar?summe=42')
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
oder eine Session verwendet werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
enable :sessions
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
session[:secret] = 'foo'
|
|
|
|
|
redirect to('/bar')
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/bar' do
|
|
|
|
|
session[:secret]
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
cache_control :public
|
|
|
|
|
"schon gecached!"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Profitipp: Caching im before-Filter aktivieren
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
before do
|
|
|
|
|
cache_control :public, :must_revalidate, :max_age => 60
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Bei Verwendung der `expires`-Helfermethode zum Setzen des gleichnamigen Headers,
|
|
|
|
|
wird `Cache-Control` automatisch eigestellt:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
before do
|
|
|
|
|
expires 500, :public, :must_revalidate
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/article/:id' do
|
2014-08-04 18:51:59 -04:00
|
|
|
|
@article = Article.find params['id']
|
2013-03-09 17:46:38 -05:00
|
|
|
|
last_modified @article.updated_at
|
|
|
|
|
etag @article.sha1
|
|
|
|
|
erb :article
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-09-16 09:02:43 -04:00
|
|
|
|
ebenso ist es möglich einen
|
2016-01-21 11:34:30 -05:00
|
|
|
|
[schwachen ETag](https://de.wikipedia.org/wiki/HTTP_ETag) zu verwenden:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
etag @article.sha1, :weak
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require "rack/cache"
|
|
|
|
|
require "sinatra"
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
use Rack::Cache
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
cache_control :public, :max_age => 36000
|
|
|
|
|
sleep 5
|
|
|
|
|
"hello"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/create' do
|
|
|
|
|
etag '', :new_resource => true
|
|
|
|
|
Article.create
|
|
|
|
|
erb :new_article
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Soll das schwache ETag trotzdem verwendet werden, verwendet man die `:kind`
|
|
|
|
|
Option:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
etag '', :new_resource => true, :kind => :weak
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Dateien versenden
|
|
|
|
|
|
2014-09-13 17:17:49 -04:00
|
|
|
|
Um den Inhalt einer Datei als Response zurückzugeben, kann die
|
|
|
|
|
`send_file`-Helfer-Methode verwendet werden:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
send_file 'foo.png'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Für `send_file` stehen einige Hash-Optionen zur Verfügung:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
send_file 'foo.png', :type => :jpg
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>filename</dt>
|
|
|
|
|
<dd>Dateiname als Response. Standardwert ist der eigentliche Dateiname.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>last_modified</dt>
|
|
|
|
|
<dd>Wert für den Last-Modified-Header, Standardwert ist <tt>mtime</tt> der
|
|
|
|
|
Datei.</dd>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<dt>type</dt>
|
|
|
|
|
<dd>Content-Type, der verwendet werden soll. Wird, wenn nicht angegeben, von
|
|
|
|
|
der Dateiendung abgeleitet.</dd>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<dt>disposition</dt>
|
|
|
|
|
<dd>Verwendet für Content-Disposition. Mögliche Werte sind: <tt>nil</tt>
|
|
|
|
|
(Standard), <tt>:attachment</tt> und <tt>:inline</tt>.</dd>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<dt>length</dt>
|
|
|
|
|
<dd>Content-Length-Header. Standardwert ist die Dateigröße.</dd>
|
|
|
|
|
</dl>
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
### Das Request-Objekt
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
Auf das `request`-Objekt der eigehenden Anfrage kann vom Anfrage-Scope aus
|
|
|
|
|
zugegriffen werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
# 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_param"] # Wert von einem Parameter; [] ist die Kurzform für den params Hash
|
|
|
|
|
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
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Manche Optionen, wie etwa `script_name` oder `path_info`, sind auch
|
|
|
|
|
schreibbar:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
before { request.path_info = "/" }
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get "/" do
|
|
|
|
|
"Alle Anfragen kommen hier an!"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Der `request.body` ist ein IO- oder StringIO-Objekt:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
post "/api" do
|
|
|
|
|
request.body.rewind # falls schon jemand davon gelesen hat
|
|
|
|
|
daten = JSON.parse request.body.read
|
|
|
|
|
"Hallo #{daten['name']}!"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Anhänge
|
|
|
|
|
|
|
|
|
|
Damit der Browser erkennt, dass ein Response gespeichert und nicht im Browser
|
|
|
|
|
angezeigt werden soll, kann der `attachment`-Helfer verwendet werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
attachment
|
|
|
|
|
"Speichern!"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Ebenso kann eine Dateiname als Parameter hinzugefügt werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
attachment "info.txt"
|
|
|
|
|
"Speichern!"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
pass if Time.now > time_for('Dec 23, 2012')
|
|
|
|
|
"noch Zeit"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Diese Methode wird intern für +expires, `last_modiefied` und ihresgleichen
|
|
|
|
|
verwendet. Mit ein paar Handgriffen lässt sich diese Methode also in ihrem
|
2013-09-16 09:02:43 -04:00
|
|
|
|
Verhalten erweitern, indem man `time_for` in der eigenen Applikation
|
2013-01-26 21:30:47 -05:00
|
|
|
|
überschreibt:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
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
|
2013-01-26 21:30:47 -05:00
|
|
|
|
end
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
last_modified :yesterday
|
|
|
|
|
expires :tomorrow
|
|
|
|
|
"Hallo"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Nachschlagen von Template-Dateien
|
|
|
|
|
|
|
|
|
|
Die `find_template`-Helfer-Methode wird genutzt, um Template-Dateien zum Rendern
|
|
|
|
|
aufzufinden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
find_template settings.views, 'foo', Tilt[:haml] do |file|
|
|
|
|
|
puts "könnte diese hier sein: #{file}"
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
set :views, ['views', 'templates']
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
helpers do
|
|
|
|
|
def find_template(views, name, engine, &block)
|
|
|
|
|
Array(views).each { |v| super(v, name, engine, &block) }
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Ein anderes Beispiel wäre, verschiedene Vereichnisse für verschiedene Engines
|
|
|
|
|
zu verwenden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
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
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
2013-09-16 09:02:43 -04:00
|
|
|
|
Pfaden gesucht. Das ergibt kein Performance-Problem, da `render` `block`
|
2013-01-26 21:30:47 -05:00
|
|
|
|
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.
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
### Konfiguration
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
Wird einmal beim Starten in jedweder Umgebung ausgeführt:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
configure do
|
|
|
|
|
# setze eine Option
|
|
|
|
|
set :option, 'wert'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
# setze mehrere Optionen
|
|
|
|
|
set :a => 1, :b => 2
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
# das gleiche wie `set :option, true`
|
|
|
|
|
enable :option
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
# das gleiche wie `set :option, false`
|
|
|
|
|
disable :option
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
# dynamische Einstellungen mit Blöcken
|
|
|
|
|
set(:css_dir) { File.join(views, 'css') }
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Läuft nur, wenn die Umgebung (RACK_ENV-Umgebungsvariable) auf `:production`
|
|
|
|
|
gesetzt ist:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
configure :production do
|
|
|
|
|
...
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Läuft nur, wenn die Umgebung auf `:production` oder auf `:test` gesetzt ist:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
configure :production, :test do
|
|
|
|
|
...
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Diese Einstellungen sind über `settings` erreichbar:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
configure do
|
|
|
|
|
set :foo, 'bar'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
settings.foo? # => true
|
|
|
|
|
settings.foo # => 'bar'
|
|
|
|
|
...
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Einstellung des Angriffsschutzes
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
Sinatra verwendet
|
2016-01-21 11:34:30 -05:00
|
|
|
|
[Rack::Protection](https://github.com/sinatra/rack-protection#readme), um die
|
2013-01-26 21:30:47 -05:00
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
disable :protection
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Um einen bestimmten Schutzmechanismus zu deaktivieren, fügt man `protection`
|
|
|
|
|
einen Hash mit Optionen hinzu:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
set :protection, :except => :path_traversal
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Neben Strings akzeptiert `:except` auch Arrays, um gleich mehrere
|
|
|
|
|
Schutzmechanismen zu deaktivieren:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
set :protection, :except => [:path_traversal, :session_hijacking]
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
2013-03-24 16:42:36 -04:00
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
#### Mögliche Einstellungen
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>absolute_redirects</dt>
|
|
|
|
|
<dd>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 <tt>url</tt>-Helfer-Methode nach wie vor absolute URLs erstellen
|
|
|
|
|
wird, es sei denn, es wird als zweiter Parameter <tt>false</tt> angegeben.
|
|
|
|
|
Standardmäßig nicht aktiviert.</dd>
|
|
|
|
|
|
2014-09-05 12:33:35 -04:00
|
|
|
|
<dt>add_charset</dt>
|
2013-03-09 17:46:38 -05:00
|
|
|
|
<dd>Mime-Types werden hier automatisch der Helfer-Methode
|
|
|
|
|
<tt>content_type</tt> zugeordnet. Es empfielt sich, Werte hinzuzufügen statt
|
2014-09-05 12:33:35 -04:00
|
|
|
|
sie zu überschreiben: <tt>settings.add_charset << "application/foobar"</tt>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
|
|
<dt>app_file</dt>
|
|
|
|
|
<dd>Pfad zur Hauptdatei der Applikation. Wird verwendet, um das Wurzel-,
|
|
|
|
|
Inline-, View- und öffentliche Verzeichnis des Projekts festzustellen.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>bind</dt>
|
2013-03-24 16:42:36 -04:00
|
|
|
|
<dd>IP-Address, an die gebunden wird (Standardwert: <tt>0.0.0.0</tt>
|
|
|
|
|
<em>oder</em> <tt>localhost</tt>). Wird nur für den eingebauten Server
|
|
|
|
|
verwendet.</dd>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<dt>default_encoding</dt>
|
|
|
|
|
<dd>Das Encoding, falls keines angegeben wurde. Standardwert ist
|
|
|
|
|
<tt>"utf-8"</tt>.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>dump_errors</dt>
|
|
|
|
|
<dd>Fehler im Log anzeigen.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>environment</dt>
|
|
|
|
|
<dd>Momentane Umgebung. Standardmäßig auf <tt>content_type</tt> oder
|
|
|
|
|
<tt>"development"</tt> eingestellt, soweit ersteres nicht vorhanden.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>logging</dt>
|
|
|
|
|
<dd>Den Logger verwenden.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>lock</dt>
|
|
|
|
|
<dd>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.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>method_override</dt>
|
|
|
|
|
<dd>Verwende <tt>_method</tt>, um put/delete-Formulardaten in Browsern zu
|
|
|
|
|
verwenden, die dies normalerweise nicht unterstützen.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>port</dt>
|
|
|
|
|
<dd>Port für die Applikation. Wird nur im internen Server verwendet.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>prefixed_redirects</dt>
|
|
|
|
|
<dd>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.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>protection</dt>
|
|
|
|
|
<dd>Legt fest, ob der Schutzmechanismus für häufig Vorkommende Webangriffe
|
|
|
|
|
auf Webapplikationen aktiviert wird oder nicht. Weitere Informationen im
|
|
|
|
|
vorhergehenden Abschnitt.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>public_folder</dt>
|
|
|
|
|
<dd>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. <tt>static</tt> Option). Leitet sich von der
|
|
|
|
|
<tt>app_file</tt> Einstellung ab, wenn nicht gesetzt.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>public_dir</dt>
|
|
|
|
|
<dd>Alias für <tt>public_folder</tt>, s.o.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>reload_templates</dt>
|
|
|
|
|
<dd>Im development-Modus aktiviert.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>root</dt>
|
|
|
|
|
<dd>Wurzelverzeichnis des Projekts. Leitet sich von der <tt>app_file</tt>
|
|
|
|
|
Einstellung ab, wenn nicht gesetzt.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>raise_errors</dt>
|
|
|
|
|
<dd>Einen Ausnahmezustand aufrufen. Beendet die Applikation. Ist automatisch
|
|
|
|
|
aktiviert, wenn die Umgebung auf <tt>"test"</tt> eingestellt ist. Ansonsten
|
|
|
|
|
ist diese Option deaktiviert.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>run</dt>
|
|
|
|
|
<dd>Wenn aktiviert, wird Sinatra versuchen, den Webserver zu starten. Nicht
|
|
|
|
|
verwenden, wenn Rackup oder anderes verwendet werden soll.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>running</dt>
|
|
|
|
|
<dd>Läuft der eingebaute Server? Diese Einstellung nicht ändern!</dd>
|
|
|
|
|
|
|
|
|
|
<dt>server</dt>
|
|
|
|
|
<dd>Server oder Liste von Servern, die als eingebaute Server zur Verfügung
|
2013-09-16 09:02:43 -04:00
|
|
|
|
stehen. Die Reihenfolge gibt die Priorität vor, die Voreinstellung hängt von
|
2013-03-24 16:42:36 -04:00
|
|
|
|
der verwendenten Ruby Implementierung ab.</dd>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
<dt>sessions</dt>
|
|
|
|
|
<dd>Sessions auf Cookiebasis mittels
|
|
|
|
|
<tt>Rack::Session::Cookie</tt>aktivieren. Für weitere Infos bitte in der
|
|
|
|
|
Sektion ‘Sessions verwenden’ nachschauen.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>show_exceptions</dt>
|
|
|
|
|
<dd>Bei Fehlern einen Stacktrace im Browseranzeigen. Ist automatisch
|
|
|
|
|
aktiviert, wenn die Umgebung auf <tt>"development"</tt> eingestellt ist.
|
|
|
|
|
Ansonsten ist diese Option deaktiviert. Kann auch auf <tt>:after_handler</tt>
|
|
|
|
|
gestellt werden, um eine anwendungsspezifische Fehlerbehandlung auszulösen,
|
|
|
|
|
bevor der Fehlerverlauf im Browser angezeigt wird.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>static</dt>
|
|
|
|
|
<dd>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.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>static_cache_control</dt>
|
|
|
|
|
<dd>Wenn Sinatra statische Daten zur Verfügung stellt, können mit dieser
|
|
|
|
|
Einstellung die <tt>Cache-Control</tt> Header zu den Responses hinzugefügt
|
|
|
|
|
werden. Die Einstellung verwendet dazu die <tt>cache_control</tt>
|
|
|
|
|
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></dd>
|
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
<dt>threaded</dt>
|
|
|
|
|
<dd>Wird es auf <tt>true</tt> gesetzt, wird Thin aufgefordert
|
|
|
|
|
<tt>EventMachine.defer</tt> zur Verarbeitung des Requests einzusetzen.</dd>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
|
2014-09-13 17:17:49 -04:00
|
|
|
|
<dt>traps</dt>
|
|
|
|
|
<dd>Einstellung, Sinatra System signalen umgehen soll.</dd>
|
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
<dt>views</dt>
|
|
|
|
|
<dd>Verzeichnis der Views. Leitet sich von der <tt>app_file</tt> Einstellung
|
|
|
|
|
ab, wenn nicht gesetzt.</dd>
|
2013-03-09 17:46:38 -05:00
|
|
|
|
|
|
|
|
|
<dt>x_cascade</dt>
|
|
|
|
|
<dd>Einstellung, ob der X-Cascade Header bei fehlender Route gesetzt wird oder
|
|
|
|
|
nicht. Standardeinstellung ist <tt>true</tt>.</dd>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
</dl>
|
|
|
|
|
|
|
|
|
|
## 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:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
ruby my_app.rb -e [ENVIRONMENT]
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
not_found do
|
|
|
|
|
'Seite kann nirgendwo gefunden werden.'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Fehler
|
|
|
|
|
|
|
|
|
|
Der `error`-Handler wird immer ausgeführt, wenn eine Exception in einem
|
2014-09-13 17:17:49 -04:00
|
|
|
|
Routen-Block oder in einem Filter geworfen wurde. In der
|
|
|
|
|
`development`-Umgebung wird es nur dann funktionieren, wenn die
|
|
|
|
|
`:show_exceptions`-Option auf `:after_handler` eingestellt wurde:
|
|
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
|
set :show_exceptions, :after_handler
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Die Exception kann über die `sinatra.error`-Rack-Variable angesprochen werden:
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
error do
|
2014-08-14 04:48:02 -04:00
|
|
|
|
'Entschuldige, es gab einen hässlichen Fehler - ' + env['sinatra.error'].message
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Benutzerdefinierte Fehler:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
error MeinFehler do
|
|
|
|
|
'Au weia, ' + env['sinatra.error'].message
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Dann, wenn das passiert:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
raise MeinFehler, 'etwas Schlimmes ist passiert'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
bekommt man dieses:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
Au weia, etwas Schlimmes ist passiert
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Alternativ kann ein Error-Handler auch für einen Status-Code definiert werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
error 403 do
|
|
|
|
|
'Zugriff verboten'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/geheim' do
|
|
|
|
|
403
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Oder ein Status-Code-Bereich:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
error 400..510 do
|
|
|
|
|
'Hallo?'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Sinatra setzt verschiedene `not_found`- und `error`-Handler in der
|
2013-03-09 17:46:38 -05:00
|
|
|
|
Development-Umgebung ein, um hilfreiche Debugging Informationen und Stack Traces
|
|
|
|
|
anzuzeigen.
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
## Rack-Middleware
|
|
|
|
|
|
2014-09-19 10:24:03 -04:00
|
|
|
|
Sinatra baut auf [Rack](http://rack.github.io/), einem minimalistischen
|
2013-01-26 21:30:47 -05:00
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require 'sinatra'
|
|
|
|
|
require 'meine_middleware'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
use Rack::Lint
|
|
|
|
|
use MeineMiddleware
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/hallo' do
|
|
|
|
|
'Hallo Welt'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Die Semantik von `use` entspricht der gleichnamigen Methode der
|
2016-01-21 11:34:30 -05:00
|
|
|
|
[Rack::Builder](http://www.rubydoc.info/github/rack/rack/master/Rack/Builder)-DSL
|
2013-01-26 21:30:47 -05:00
|
|
|
|
(meist verwendet in Rackup-Dateien). Ein Beispiel dafür ist, dass die
|
|
|
|
|
`use`-Methode mehrere/verschiedene Argumente und auch Blöcke entgegennimmt:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
use Rack::Auth::Basic do |username, password|
|
|
|
|
|
username == 'admin' && password == 'geheim'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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),
|
2014-09-19 10:24:03 -04:00
|
|
|
|
[rack-contrib](https://github.com/rack/rack-contrib#readme),
|
2014-02-24 09:57:22 -05:00
|
|
|
|
oder im [Rack wiki](https://github.com/rack/rack/wiki/List-of-Middleware).
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
## Testen
|
|
|
|
|
|
|
|
|
|
Sinatra-Tests können mit jedem auf Rack aufbauendem Test-Framework geschrieben
|
2016-01-21 11:34:30 -05:00
|
|
|
|
werden. [Rack::Test](http://www.rubydoc.info/github/brynary/rack-test/master/frames)
|
2013-01-26 21:30:47 -05:00
|
|
|
|
wird empfohlen:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require 'my_sinatra_app'
|
2015-01-10 14:30:47 -05:00
|
|
|
|
require 'minitest/autorun'
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require 'rack/test'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2015-01-10 14:30:47 -05:00
|
|
|
|
class MyAppTest < Minitest::Test
|
2013-03-09 17:46:38 -05:00
|
|
|
|
include Rack::Test::Methods
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
def app
|
|
|
|
|
Sinatra::Application
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
def test_my_default
|
|
|
|
|
get '/'
|
|
|
|
|
assert_equal 'Hallo Welt!', last_response.body
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
def test_with_params
|
|
|
|
|
get '/meet', :name => 'Frank'
|
|
|
|
|
assert_equal 'Hallo Frank!', last_response.body
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
def test_with_rack_env
|
|
|
|
|
get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
|
|
|
|
|
assert_equal "Du verwendest Songbird!", last_response.body
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
Hinweis: Wird Sinatra modular verwendet, muss <tt>Sinatra::Application</tt> mit
|
|
|
|
|
dem Namen der Applikations-Klasse ersetzt werden.
|
|
|
|
|
|
2013-03-15 05:24:33 -04:00
|
|
|
|
## Sinatra::Base - Middleware, Bibliotheken und modulare Anwendungen
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require 'sinatra/base'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
class MyApp < Sinatra::Base
|
|
|
|
|
set :sessions, true
|
|
|
|
|
set :foo, 'bar'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
'Hallo Welt!'
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
MyApp.run! :host => 'localhost', :port => 9090
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
2016-01-21 11:34:30 -05:00
|
|
|
|
[Optionen und Konfiguration](http://www.sinatrarb.com/configuration.html) für
|
2013-01-26 21:30:47 -05:00
|
|
|
|
Details über mögliche Optionen.
|
|
|
|
|
|
2014-09-13 17:17:49 -04:00
|
|
|
|
Damit eine App sich ähnlich wie eine klassische App verhält, kann man
|
|
|
|
|
auch eine Subclass von `Sinatra::Application` erstellen:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2014-09-13 17:17:49 -04:00
|
|
|
|
require 'sinatra/base'
|
|
|
|
|
|
|
|
|
|
class MyApp < Sinatra::Application
|
|
|
|
|
get '/' do
|
|
|
|
|
'Hello world!'
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
### 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:
|
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<th>Szenario</th>
|
|
|
|
|
<th>Classic</th>
|
|
|
|
|
<th>Modular</th>
|
2014-09-13 17:17:49 -04:00
|
|
|
|
<th>Modular</th>
|
2013-03-09 17:46:38 -05:00
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>app_file</td>
|
|
|
|
|
<td>Sinatra ladende Datei</td>
|
|
|
|
|
<td>Sinatra::Base subklassierende Datei</td>
|
2014-09-13 17:17:49 -04:00
|
|
|
|
<td>Sinatra::Application subklassierende Datei</td>
|
2013-03-09 17:46:38 -05:00
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>run</td>
|
|
|
|
|
<td>$0 == app_file</td>
|
|
|
|
|
<td>false</td>
|
2014-09-13 17:17:49 -04:00
|
|
|
|
<td>false</td>
|
2013-03-09 17:46:38 -05:00
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>logging</td>
|
|
|
|
|
<td>true</td>
|
|
|
|
|
<td>false</td>
|
2014-09-13 17:17:49 -04:00
|
|
|
|
<td>true</td>
|
2013-03-09 17:46:38 -05:00
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>method_override</td>
|
|
|
|
|
<td>true</td>
|
|
|
|
|
<td>false</td>
|
2014-09-13 17:17:49 -04:00
|
|
|
|
<td>true</td>
|
2013-03-09 17:46:38 -05:00
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>inline_templates</td>
|
|
|
|
|
<td>true</td>
|
|
|
|
|
<td>false</td>
|
2014-09-13 17:17:49 -04:00
|
|
|
|
<td>true</td>
|
2013-03-09 17:46:38 -05:00
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>static</td>
|
|
|
|
|
<td>true</td>
|
|
|
|
|
<td>false</td>
|
2014-09-13 17:17:49 -04:00
|
|
|
|
<td>true</td>
|
2013-03-09 17:46:38 -05:00
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
### Eine modulare Applikation bereitstellen
|
|
|
|
|
|
|
|
|
|
Es gibt zwei übliche Wege, eine modulare Anwendung zu starten. Zum einen über
|
|
|
|
|
`run!`:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
# mein_app.rb
|
|
|
|
|
require 'sinatra/base'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
class MeinApp < Sinatra::Base
|
|
|
|
|
# ... Anwendungscode hierhin ...
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
# starte den Server, wenn die Ruby-Datei direkt ausgeführt wird
|
|
|
|
|
run! if app_file == $0
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Starte mit:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
ruby mein_app.rb
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Oder über eine `config.ru`-Datei, die es erlaubt, einen beliebigen
|
|
|
|
|
Rack-Handler zu verwenden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
# config.ru (mit rackup starten)
|
|
|
|
|
require './mein_app'
|
|
|
|
|
run MeineApp
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Starte:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
rackup -p 4567
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Eine klassische Anwendung mit einer config.ru verwenden
|
|
|
|
|
|
|
|
|
|
Schreibe eine Anwendungsdatei:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
# app.rb
|
|
|
|
|
require 'sinatra'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
'Hallo Welt!'
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
sowie eine dazugehörige `config.ru`-Datei:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require './app'
|
|
|
|
|
run Sinatra::Application
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 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/...):
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require 'sinatra/base'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
class LoginScreen < Sinatra::Base
|
|
|
|
|
enable :sessions
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get('/login') { haml :login }
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
post('/login') do
|
2014-08-04 18:51:59 -04:00
|
|
|
|
if params['name'] == 'admin' && params['password'] == 'admin'
|
|
|
|
|
session['user_name'] = params['name']
|
2013-03-09 17:46:38 -05:00
|
|
|
|
else
|
|
|
|
|
redirect '/login'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
end
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
class MyApp < Sinatra::Base
|
|
|
|
|
# Middleware wird vor Filtern ausgeführt
|
|
|
|
|
use LoginScreen
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
before do
|
|
|
|
|
unless session['user_name']
|
|
|
|
|
halt "Zugriff verweigert, bitte <a href='/login'>einloggen</a>."
|
2013-01-26 21:30:47 -05:00
|
|
|
|
end
|
2013-03-09 17:46:38 -05:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
get('/') { "Hallo #{session['user_name']}." }
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require 'sinatra/base'
|
|
|
|
|
my_app = Sinatra.new { get('/') { "hallo" } }
|
|
|
|
|
my_app.run!
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Die Applikation kann mit Hilfe eines optionalen Parameters erstellt werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
# config.ru
|
|
|
|
|
require 'sinatra/base'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
controller = Sinatra.new do
|
|
|
|
|
enable :logging
|
|
|
|
|
helpers MyHelpers
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
map('/a') do
|
|
|
|
|
run Sinatra.new(controller) { get('/') { 'a' } }
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
map('/b') do
|
|
|
|
|
run Sinatra.new(controller) { get('/') { 'b' } }
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
require 'sinatra/base'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
use Sinatra do
|
|
|
|
|
get('/') { ... }
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
run RailsProject::Application
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
class MyApp < Sinatra::Base
|
|
|
|
|
# Hey, ich bin im Anwendungsscope!
|
|
|
|
|
set :foo, 42
|
|
|
|
|
foo # => 42
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
# Hey, ich bin nicht mehr im Anwendungs-Scope!
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
class MyApp < Sinatra::Base
|
|
|
|
|
# Hey, ich bin im Anwendungs-Scope!
|
|
|
|
|
get '/neue_route/:name' do
|
|
|
|
|
# Anfrage-Scope für '/neue_route/:name'
|
|
|
|
|
@value = 42
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2014-08-04 18:51:59 -04:00
|
|
|
|
settings.get "/#{params['name']}" do
|
|
|
|
|
# Anfrage-Scope für "/#{params['name']}"
|
2013-03-09 17:46:38 -05:00
|
|
|
|
@value # => nil (nicht dieselbe Anfrage)
|
2013-01-26 21:30:47 -05:00
|
|
|
|
end
|
2013-03-09 17:46:38 -05:00
|
|
|
|
|
|
|
|
|
"Route definiert!"
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Im Anfrage-Scope befindet man sich:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
* In get, head, post, put, delete, options, patch, link und unlink Blöcken
|
|
|
|
|
* In before und after Filtern
|
2013-01-26 21:30:47 -05:00
|
|
|
|
* 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
|
|
|
|
|
|
|
|
|
|
## Kommandozeile
|
|
|
|
|
|
|
|
|
|
Sinatra-Anwendungen können direkt von der Kommandozeile aus gestartet werden:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-h HOST] [-s HANDLER]
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Die Optionen sind:
|
|
|
|
|
|
|
|
|
|
```
|
2013-03-09 17:46:38 -05:00
|
|
|
|
-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)
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
2015-12-02 11:16:27 -05:00
|
|
|
|
### Multi-threading
|
|
|
|
|
|
|
|
|
|
_Paraphrasiert von [dieser Antwort auf StackOverflow][so-answer] von Konstantin_
|
|
|
|
|
|
|
|
|
|
Sinatra erlegt kein Nebenläufigkeitsmodell auf, sondern überlässt dies dem
|
|
|
|
|
selbst gewählten Rack-Proxy (Server), so wie Thin, Puma oder WEBrick.
|
|
|
|
|
Sinatra selbst ist Thread-sicher, somit ist es kein Problem wenn der
|
|
|
|
|
Rack-Proxy ein anderes Threading-Modell für Nebenläufigkeit benutzt.
|
|
|
|
|
Das heißt, dass wenn der Server gestartet wird, dass man die korrekte
|
|
|
|
|
Aufrufsmethode benutzen sollte für den jeweiligen Rack-Proxy.
|
|
|
|
|
Das folgende Beispiel ist eine Veranschaulichung eines mehrprozessigen
|
|
|
|
|
Thin Servers:
|
|
|
|
|
|
|
|
|
|
``` ruby
|
|
|
|
|
# app.rb
|
|
|
|
|
|
|
|
|
|
require 'sinatra/base'
|
|
|
|
|
|
|
|
|
|
class App < Sinatra::Base
|
|
|
|
|
get '/' do
|
|
|
|
|
"Hello, World"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
App.run!
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Um den Server zu starten, führt man das folgende Kommando aus:
|
|
|
|
|
|
|
|
|
|
``` shell
|
|
|
|
|
thin --threaded start
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
[so-answer]: http://stackoverflow.com/questions/6278817/is-sinatra-multi-threaded/6282999#6282999)
|
|
|
|
|
|
2013-01-26 21:30:47 -05:00
|
|
|
|
## Systemanforderungen
|
|
|
|
|
|
|
|
|
|
Die folgenden Versionen werden offiziell unterstützt:
|
|
|
|
|
|
|
|
|
|
<dl>
|
2013-03-24 16:42:36 -04:00
|
|
|
|
<dt>Ruby 1.8.7</dt>
|
|
|
|
|
<dd>1.8.7 wird vollständig unterstützt, ein Wechsel zu JRuby oder Rubinius wird
|
|
|
|
|
aber empfohlen. Ruby 1.8.7 wird noch bis Sinatra 2.0 unterstützt werden. Frühere
|
|
|
|
|
Versionen von Ruby sind nicht kompatibel mit Sinatra.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>Ruby 1.9.2</dt>
|
|
|
|
|
<dd>1.9.2 wird mindestens bis Sinatra 1.5 voll unterstützt. Version 1.9.2p0
|
|
|
|
|
sollte nicht verwendet werden, da unter Sinatra immer wieder Segfaults
|
|
|
|
|
auftreten.</dd>
|
|
|
|
|
|
|
|
|
|
<dt>Ruby 1.9.3</dt>
|
|
|
|
|
<dd>1.9.3 wird vollständig unterstützt und empfohlen. Achtung, bei einem
|
|
|
|
|
Upgrade von einer früheren Version von Ruby zu Ruby 1.9.3 werden alle Sessions
|
|
|
|
|
ungültig. Ruby 1.9.3 wird bis Sinatra 2.0 unterstützt werden.</dd>
|
|
|
|
|
|
2014-09-13 17:17:49 -04:00
|
|
|
|
<dt>Ruby 2.x</dt>
|
|
|
|
|
<dd>2.x wird vollständig unterstützt.</dd>
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
<dt>Rubinius</dt>
|
|
|
|
|
<dd>Rubinius (Version >= 2.x) wird offiziell unterstützt. Es wird empfohlen, den
|
|
|
|
|
<a href="http://puma.io">Puma Server</a> zu installieren (<tt>gem install puma
|
|
|
|
|
</tt>)</dd>
|
|
|
|
|
|
|
|
|
|
<dt>JRuby</dt>
|
|
|
|
|
<dd>Aktuelle JRuby Versionen werden offiziell unterstützt. Es wird empfohlen,
|
2013-09-16 09:02:43 -04:00
|
|
|
|
keine C-Erweiterungen zu verwenden und als Server Trinidad zu verwenden
|
2013-03-24 16:42:36 -04:00
|
|
|
|
(<tt>gem install trinidad</tt>).</dd>
|
2013-01-26 21:30:47 -05:00
|
|
|
|
</dl>
|
|
|
|
|
|
|
|
|
|
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
|
2013-03-24 16:42:36 -04:00
|
|
|
|
* MacRuby (<tt>gem install control_tower</tt> wird empfohlen), Maglev, IronRuby
|
|
|
|
|
* Ruby 1.9.0 und 1.9.1
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
Nicht offiziell unterstützt bedeutet, dass wenn Sachen nicht funktionieren,
|
|
|
|
|
wir davon ausgehen, dass es nicht an Sinatra sondern an der jeweiligen
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Implementierung liegt.
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
Im Rahmen unserer CI (Kontinuierlichen Integration) wird bereits ruby-head
|
2014-09-13 17:17:49 -04:00
|
|
|
|
(zukünftige Versionen von MRI) mit eingebunden. Es kann davon ausgegangen
|
|
|
|
|
werden, dass Sinatra MRI auch weiterhin vollständig unterstützen wird.
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Sinatra sollte auf jedem Betriebssystem laufen, dass einen funktionierenden
|
|
|
|
|
Ruby-Interpreter aufweist.
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
Sinatra läuft aktuell nicht unter Cardinal, SmallRuby, BlueRuby oder Ruby <= 1.8.7.
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
|
|
|
|
## 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:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
gem install sinatra --pre
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Mit Bundler
|
|
|
|
|
|
|
|
|
|
Wenn die Applikation mit der neuesten Version von Sinatra und
|
2016-01-21 11:34:30 -05:00
|
|
|
|
[Bundler](http://bundler.io) genutzt werden soll, empfehlen wir den
|
2013-01-26 21:30:47 -05:00
|
|
|
|
nachfolgenden Weg.
|
|
|
|
|
|
|
|
|
|
Soweit Bundler noch nicht installiert ist:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
gem install bundler
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Anschließend wird eine `Gemfile`-Datei im Projektverzeichnis mit folgendem
|
|
|
|
|
Inhalt erstellt:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
source :rubygems
|
|
|
|
|
gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
# evtl. andere Abhängigkeiten
|
|
|
|
|
gem 'haml' # z.B. wenn du Haml verwendest...
|
|
|
|
|
gem 'activerecord', '~> 3.0' # ...oder ActiveRecord 3.x
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
bundle exec ruby myapp.rb
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Eigenes Repository
|
|
|
|
|
Um auf dem neuesten Stand von Sinatras Code zu sein, kann eine lokale Kopie
|
2013-03-24 16:42:36 -04:00
|
|
|
|
angelegt werden. Gestartet wird in der Anwendung mit dem `sinatra/lib`-Ordner
|
2013-01-26 21:30:47 -05:00
|
|
|
|
im `LOAD_PATH`:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
cd myapp
|
|
|
|
|
git clone git://github.com/sinatra/sinatra.git
|
|
|
|
|
ruby -Isinatra/lib myapp.rb
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Alternativ kann der `sinatra/lib`-Ordner zum `LOAD_PATH` in der Anwendung
|
|
|
|
|
hinzugefügt werden:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-03-09 17:46:38 -05:00
|
|
|
|
$LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib'
|
|
|
|
|
require 'rubygems'
|
|
|
|
|
require 'sinatra'
|
2013-01-26 21:30:47 -05:00
|
|
|
|
|
2013-03-09 17:46:38 -05:00
|
|
|
|
get '/ueber' do
|
|
|
|
|
"Ich laufe auf Version " + Sinatra::VERSION
|
|
|
|
|
end
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Um Sinatra-Code von Zeit zu Zeit zu aktualisieren:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
cd myproject/sinatra
|
|
|
|
|
git pull
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Gem erstellen
|
|
|
|
|
|
|
|
|
|
Aus der eigenen lokalen Kopie kann nun auch ein globales Gem gebaut werden:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
git clone git://github.com/sinatra/sinatra.git
|
|
|
|
|
cd sinatra
|
|
|
|
|
rake sinatra.gemspec
|
|
|
|
|
rake install
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Falls Gems als Root installiert werden sollen, sollte die letzte Zeile
|
|
|
|
|
folgendermaßen lauten:
|
|
|
|
|
|
2013-03-24 16:42:36 -04:00
|
|
|
|
```shell
|
2013-03-09 17:46:38 -05:00
|
|
|
|
sudo rake install
|
2013-01-26 21:30:47 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Versions-Verfahren
|
|
|
|
|
|
|
|
|
|
Sinatra folgt dem sogenannten [Semantic Versioning](http://semver.org/), d.h.
|
|
|
|
|
SemVer und SemVerTag.
|
|
|
|
|
|
|
|
|
|
## Mehr
|
|
|
|
|
|
2016-01-21 11:34:30 -05:00
|
|
|
|
* [Projekt-Website](http://www.sinatrarb.com/) - Ergänzende Dokumentation,
|
2013-01-26 21:30:47 -05:00
|
|
|
|
News und Links zu anderen Ressourcen.
|
2016-01-21 11:34:30 -05:00
|
|
|
|
* [Mitmachen](http://www.sinatrarb.com/contributing.html) - Einen Fehler
|
2013-01-26 21:30:47 -05:00
|
|
|
|
gefunden? Brauchst du Hilfe? Hast du einen Patch?
|
2016-01-21 11:34:30 -05:00
|
|
|
|
* [Issue-Tracker](https://github.com/sinatra/sinatra/issues)
|
|
|
|
|
* [Twitter](https://twitter.com/sinatra)
|
2013-01-26 21:30:47 -05:00
|
|
|
|
* [Mailing-Liste](http://groups.google.com/group/sinatrarb)
|
2013-09-16 09:02:43 -04:00
|
|
|
|
* [#sinatra](irc://chat.freenode.net/#sinatra) auf http://freenode.net Es
|
2013-03-24 16:42:36 -04:00
|
|
|
|
gibt dort auch immer wieder deutschsprachige Entwickler, die gerne weiterhelfen.
|
2014-09-12 21:16:06 -04:00
|
|
|
|
* [Sinatra Book](https://github.com/sinatra/sinatra-book/) Kochbuch Tutorial
|
2013-01-26 21:30:47 -05:00
|
|
|
|
* [Sinatra Recipes](http://recipes.sinatrarb.com/) Sinatra-Rezepte aus der
|
|
|
|
|
Community
|
|
|
|
|
* API Dokumentation für die [aktuelle
|
2016-01-21 11:34:30 -05:00
|
|
|
|
Version](http://www.rubydoc.info//gems/sinatra) oder für
|
|
|
|
|
[HEAD](http://www.rubydoc.info/github/sinatra/sinatra) auf http://rubydoc.info
|
|
|
|
|
* [CI Server](https://travis-ci.org/sinatra/sinatra)
|