mirror of
https://github.com/sinatra/sinatra
synced 2023-03-27 23:18:01 -04:00
3feef2d055
Signed-off-by: Konstantin Haase <konstantin.mailinglists@googlemail.com>
1038 lines
30 KiB
Text
1038 lines
30 KiB
Text
= Sinatra
|
|
<i>Attention: Ce document correspond à la traduction de la version anglaise et il n'est peut être plus à jour.</i>
|
|
|
|
Sinatra est un DSL pour créer rapidement des applications web en Ruby et sans
|
|
effort:
|
|
|
|
# mon_application.rb
|
|
require 'sinatra'
|
|
get '/' do
|
|
'Bonjour Monde!'
|
|
end
|
|
|
|
Installez le gem et lancez avec:
|
|
|
|
gem install sinatra
|
|
ruby -rubygems mon_application.rb
|
|
|
|
Le résultat est visible sur: http://localhost:4567
|
|
|
|
== Routes
|
|
|
|
Dans Sinatra, une route est une méthode HTTP couplée à un masque (pattern) URL.
|
|
Chaque route est associée à un bloc:
|
|
|
|
get '/' do
|
|
.. montrer quelque chose ..
|
|
end
|
|
|
|
post '/' do
|
|
.. créer quelque chose ..
|
|
end
|
|
|
|
put '/' do
|
|
.. changer quelque chose ..
|
|
end
|
|
|
|
delete '/' do
|
|
.. effacer quelque chose ..
|
|
end
|
|
|
|
Les routes sont comparées dans l'ordre où elles ont été définies. La première route qui
|
|
correspond à la requête est invoquée.
|
|
|
|
Les masques peuvent inclure des paramètres, accessibles par l'intermédiaire du
|
|
hash <tt>params</tt>:
|
|
|
|
get '/bonjour/:nom' do
|
|
# répond aux requêtes "GET /bonjour/foo" et "GET /bonjour/bar"
|
|
# params[:nom] est 'foo' ou 'bar'
|
|
"Bonjour #{params[:nom]}!"
|
|
end
|
|
|
|
Vous pouvez aussi les nommer directement dans les paramètres du bloc comme ceci:
|
|
|
|
get '/bonjour/:nom' do |n|
|
|
"Bonjour #{n}!"
|
|
end
|
|
|
|
Une route peut contenir un splat (caractère joker), accessible par l'intermédiaire de
|
|
la liste <tt>params[:splat]</tt>.
|
|
|
|
get '/dire/*/a/*' do
|
|
# répondrait à /dire/bonjour/a/monde
|
|
params[:splat] # => ["bonjour", "monde"]
|
|
end
|
|
|
|
get '/telecharger/*.*' do
|
|
# répondrait à /telecharger/chemin/vers/fichier.xml
|
|
params[:splat] # => ["chemin/vers/fichier", "xml"]
|
|
end
|
|
|
|
Une route peut s'exprimer avec une Expression Régulière:
|
|
|
|
get %r{/bonjour/([\w]+)} do
|
|
"Bonjour, #{params[:captures].first}!"
|
|
end
|
|
|
|
Là aussi on peut utiliser les paramètres de bloc:
|
|
|
|
get %r{/bonjour/([\w]+)} do |c|
|
|
"Bonjour, #{c}!"
|
|
end
|
|
|
|
=== Conditions
|
|
|
|
Les routes peuvent définir toutes sortes de conditions, comme par exemple le "user agent":
|
|
|
|
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
|
|
"Vous utilisez Songbird version #{params[:agent][0]}"
|
|
end
|
|
|
|
get '/foo' do
|
|
# Correspond à tous les autres navigateurs
|
|
end
|
|
|
|
Les autres conditions disponibles sont +host_name+ et +provides+:
|
|
|
|
get '/', :host_name => /^admin\./ do
|
|
"Zone Administrateur, Accès refusé!"
|
|
end
|
|
|
|
get '/', :provides => 'html' do
|
|
haml :index
|
|
end
|
|
|
|
get '/', :provides => ['rss', 'atom', 'xml'] do
|
|
builder :feed
|
|
end
|
|
|
|
Vous pouvez facilement définir vos propres conditions:
|
|
|
|
set(:probability) { |value| condition { rand <= value } }
|
|
|
|
get '/gagner_une_voiture', :probability => 0.1 do
|
|
"Vous avez gagné!"
|
|
end
|
|
|
|
get '/gagner_une_voiture' do
|
|
"Désolé, vous avez perdu."
|
|
end
|
|
|
|
=== Valeurs de retour
|
|
|
|
La valeur de retour d'un bloc définissant une route détermine le corps de la réponse
|
|
qui sera transmise au client HTTP ou du moins au prochain middleware dans la pile Rack.
|
|
Le plus généralement, il s'agit d'une chaîne de caractères, comme dans les exemples
|
|
précédents. Cependant, d'autres valeurs sont acceptées.
|
|
|
|
Vous pouvez renvoyer n'importe quel objet qui soit une réponse Rack valide, un corps de
|
|
réponse Rack ou un code retour HTTP:
|
|
|
|
* Un tableau de 3 éléments: <tt>[code retour (Fixnum), entêtes (Hash), corps de réponse (répondant à #each)]</tt>
|
|
* Un tableau de 2 élements: <tt>[code retour (Fixnum), corps de réponse (répondant à #each)]</tt>
|
|
* Un objet qui répond à <tt>#each</tt> et qui ne transmet que des chaînes de caractères au bloc fourni
|
|
* Un Fixnum représentant le code retour
|
|
|
|
Ainsi, on peut facilement implémenter un streaming par exemple:
|
|
|
|
class Stream
|
|
def each
|
|
100.times { |i| yield "#{i}\n" }
|
|
end
|
|
end
|
|
|
|
get('/') { Stream.new }
|
|
|
|
== Fichiers statiques
|
|
|
|
Par défaut, le dossier <tt>./public</tt> est utilisé pour servir les fichiers statiques.
|
|
Vous pouvez changer ce dossier pour un autre nom grâce à l'option <tt>:public</tt>:
|
|
|
|
set :public, File.dirname(__FILE__) + '/statique'
|
|
|
|
Notez que le nom du dossier public n'est pas inclus dans l'URL. Un fichier sous
|
|
<tt>./public/css/style.css</tt> est appelé avec l'URL: <tt>http://exemple.com/css/style.css</tt>.
|
|
|
|
== Vues / Templates
|
|
|
|
Par défaut, les templates sont cherchés dans le dossier <tt>./views</tt>.
|
|
Pour utiliser un autre dossier, il faut le déclarer:
|
|
|
|
set :views, File.dirname(__FILE__) + '/templates'
|
|
|
|
Il est important de noter que les templates sont toujours référencés
|
|
sous forme de symboles, même s'il s'agit d'un sous-répertoire (dans ce
|
|
cas, utilisez <tt>:'sous_repertoire/template'</tt>). Vous devez utiliser un symbole
|
|
car les méthodes de rendu évalueront le contenu des chaînes de caractères
|
|
au lieu de les considérer comme un chemin vers un fichier.
|
|
|
|
=== Templates Haml
|
|
|
|
Le gem haml est nécessaire pour utiliser la fonction de rendu Haml:
|
|
|
|
## Chargez la bibliothèque haml dans votre application
|
|
require 'haml'
|
|
|
|
get '/' do
|
|
haml :index
|
|
end
|
|
|
|
Utilisera le template: <tt>./views/index.haml</tt>.
|
|
|
|
{Les options de Haml}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options]
|
|
peuvent se manipuler directement avec la configuration de Sinatra,
|
|
voir {Options et Configuration}[http://www.sinatrarb.com/configuration.html],
|
|
et supportent aussi la réécriture (surcharge) comme dans cet exemple.
|
|
|
|
set :haml, :format => :html5 # le format par défaut dans Haml est :xhtml
|
|
|
|
get '/' do
|
|
haml :index, :format => :html4 # surcharge
|
|
end
|
|
|
|
|
|
=== Templates Erb
|
|
|
|
## Chargez la bibliothèque erb dans votre application
|
|
require 'erb'
|
|
|
|
get '/' do
|
|
erb :index
|
|
end
|
|
|
|
Utilisera le template: <tt>./views/index.erb</tt>
|
|
|
|
=== Erubis
|
|
|
|
Le gem erubis est nécessaire pour utiliser la fonction de rendu erubis:
|
|
|
|
## Chargez la bibliothèque erubis dans votre application
|
|
require 'erubis'
|
|
|
|
get '/' do
|
|
erubis :index
|
|
end
|
|
|
|
Utilisera le template: <tt>./views/index.erubis</tt>
|
|
|
|
=== Templates Builder
|
|
|
|
Le gem builder est nécessaire pour utiliser la fonction de rendu builder:
|
|
|
|
## Chargez la bibliothèque builder dans votre application
|
|
require 'builder'
|
|
|
|
get '/' do
|
|
builder :index
|
|
end
|
|
|
|
Utilisera le template: <tt>./views/index.builder</tt>.
|
|
|
|
=== Templates Nokogiri
|
|
|
|
Le gem nokogiri est nécessaire pour utiliser la fonction de rendu nokogiri:
|
|
|
|
## Chargez la bibliothèque nokogiri dans votre application
|
|
require 'nokogiri'
|
|
|
|
get '/' do
|
|
nokogiri :index
|
|
end
|
|
|
|
Utilisera le template: <tt>./views/index.nokogiri</tt>.
|
|
|
|
=== Templates Sass
|
|
|
|
Le gem sass est nécessaire pour utiliser la fonction de rendu Sass:
|
|
|
|
## Chargez la bibliothèque haml ou sass dans votre application
|
|
require 'sass'
|
|
|
|
get '/stylesheet.css' do
|
|
sass :stylesheet
|
|
end
|
|
|
|
Utilisera le template: <tt>./views/stylesheet.sass</tt>.
|
|
|
|
{Les options de Sass}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
|
|
peuvent se manipuler directement avec la configuration de Sinatra,
|
|
voir {Options et Configuration}[http://www.sinatrarb.com/configuration.html],
|
|
et supportent aussi la réécriture (surcharge) comme dans cet exemple.
|
|
|
|
set :sass, :style => :compact # le style par défaut dans Sass est :nested
|
|
|
|
get '/stylesheet.css' do
|
|
sass :stylesheet, :style => :expanded # surcharge
|
|
end
|
|
|
|
=== Scss Templates
|
|
|
|
Le gem sass est nécessaire pour utiliser la fonction de rendu Scss:
|
|
|
|
## Chargez la bibliothèque haml ou sass dans votre application
|
|
require 'sass'
|
|
|
|
get '/stylesheet.css' do
|
|
scss :stylesheet
|
|
end
|
|
|
|
Utilisera le template <tt>./views/stylesheet.scss</tt>.
|
|
|
|
{Les options de Scss}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
|
|
peuvent se manipuler directement avec la configuration de Sinatra,
|
|
voir {Options et Configuration}[http://www.sinatrarb.com/configuration.html],
|
|
et supportent aussi la réécriture (surcharge) comme dans cet exemple.
|
|
|
|
set :scss, :style => :compact # le style par défaut de Scss est :nested
|
|
|
|
get '/stylesheet.css' do
|
|
scss :stylesheet, :style => :expanded # surcharge
|
|
end
|
|
|
|
=== Templates Less
|
|
|
|
Le gem less est nécessaire pour utiliser la fonction de rendu Less:
|
|
|
|
## Chargez la bibliothèque less dans votre application
|
|
require 'less'
|
|
|
|
get '/stylesheet.css' do
|
|
less :stylesheet
|
|
end
|
|
|
|
Utilisera le template: <tt>./views/stylesheet.less</tt>.
|
|
|
|
=== Templates Liquid
|
|
|
|
Le gem liquid est nécessaire pour utiliser la fonction de rendu Liquid:
|
|
|
|
## Chargez la bibliothèque liquid dans votre application
|
|
require 'liquid'
|
|
|
|
get '/' do
|
|
liquid :index
|
|
end
|
|
|
|
Utilisera <tt>./views/index.liquid</tt>.
|
|
|
|
Comme vous ne pouvez pas appeler des méthodes Ruby (excepté +yield+) dans un
|
|
template Liquid, il sera toujours nécessaire de lui passer des variables locales:
|
|
|
|
liquid :index, :locals => { :key => 'value' }
|
|
|
|
=== Templates Markdown
|
|
|
|
Le gem rdiscount est nécessaire pour utiliser la fonction de rendu Markdown:
|
|
|
|
## Chargez la bibliothèque rdiscount dans votre application
|
|
require "rdiscount"
|
|
|
|
get '/' do
|
|
markdown :index
|
|
end
|
|
|
|
Utilisera <tt>./views/index.markdown</tt> (les extensions de fichier +md+ et +mkd+
|
|
sont également acceptées).
|
|
|
|
Il n'est pas possible d'appeler des méthodes depuis markdown, ni même de lui passer des variables
|
|
locales. Par conséquent, il sera le plus souvent utilisé en combinaison avec un autre moteur
|
|
de rendu:
|
|
|
|
erb :vuedensemble, :locals => { :texte => markdown(:introduction) }
|
|
|
|
Notez que vous pouvez également appeler la méthode markdown au sein d'autres templates:
|
|
|
|
%h1 Bonjour Depuis Haml!
|
|
%p= markdown(:salutations)
|
|
|
|
=== Templates Textile
|
|
|
|
Le gem RedCloth est nécessaire pour utiliser la fonction de rendu Textile:
|
|
|
|
## Chargez la bibliothèqye redcloth dans votre application
|
|
require "redcloth"
|
|
|
|
get '/' do
|
|
textile :index
|
|
end
|
|
|
|
Utilisera <tt>./views/index.textile</tt>.
|
|
|
|
Il n'est pas possible d'appeler des méthodes depuis textile, ni même de lui passer des variables
|
|
locales. Par conséquent, il sera le plus souvent utilisé en combinaison avec un autre moteur
|
|
de rendu:
|
|
|
|
erb :vuedensemble, :locals => { :texte => textile(:introduction) }
|
|
|
|
Notez que vous pouvez également appeler la méthode textile au sein d'autres templates:
|
|
|
|
%h1 Bonjour Depuis Haml!
|
|
%p= textile(:salutations)
|
|
|
|
=== Templates RDoc
|
|
|
|
Le gem RDoc est nécessaire pour utiliser la fonction de rendu RDoc:
|
|
|
|
## Chargez la bibliothèque rdoc dans votre application
|
|
require "rdoc"
|
|
|
|
get '/' do
|
|
rdoc :index
|
|
end
|
|
|
|
Utilisera <tt>./views/index.rdoc</tt>.
|
|
|
|
Il n'est pas possible d'appeler des méthodes depuis rdoc, ni même de lui passer des variables
|
|
locales. Par conséquent, il sera le plus souvent utilisé en combinaison avec un autre moteur
|
|
de rendu:
|
|
|
|
erb :vuedensemble, :locals => { :texte => rdoc(:introduction) }
|
|
|
|
Notez que vous pouvez également appeler la méthode rdoc au sein d'autres templates:
|
|
|
|
%h1 Bonjour Depuis Haml!
|
|
%p= rdoc(:salutations)
|
|
|
|
=== Templates Radius
|
|
|
|
Le gem radius est nécessaire pour utiliser la fonction de rendu Radius:
|
|
|
|
## Chargez la bibliotèque radius dans votre application
|
|
require 'radius'
|
|
|
|
get '/' do
|
|
radius :index
|
|
end
|
|
|
|
Utilisera <tt>./views/index.radius</tt>.
|
|
|
|
Comme vous ne pouvez pas appeler des méthodes Ruby (excepté +yield+) dans un
|
|
template Radius, il sera toujours nécessaire de lui passer des variables locales:
|
|
|
|
radius :index, :locals => { :key => 'value' }
|
|
|
|
=== Templates Markaby
|
|
|
|
Le gem markaby est nécessaire pour utiliser la fonction de rendu Markaby:
|
|
|
|
## Chargez la bibliothèque markaby dans votre application
|
|
require 'markaby'
|
|
|
|
get '/' do
|
|
markaby :index
|
|
end
|
|
|
|
Utilisera <tt>./views/index.mab</tt>.
|
|
|
|
=== Templates CoffeeScript
|
|
|
|
Le gem coffee-script et l'exécutable `coffee` sont nécessaires pour utiliser la
|
|
fonction de rendu CoffeeScript:
|
|
|
|
## Chargez la bibliothèque coffee-script dans votre application
|
|
require 'coffee-script'
|
|
|
|
get '/application.js' do
|
|
coffee :application
|
|
end
|
|
|
|
Utilisera <tt>./views/application.coffee</tt>.
|
|
|
|
=== Templates en ligne
|
|
|
|
get '/' do
|
|
haml '%div.title Bonjour Monde'
|
|
end
|
|
|
|
Utilisera le texte passé en argument pour générer la page, au lieu d'aller
|
|
chercher le texte dans un fichier.
|
|
|
|
=== Accéder aux variables dans un Template
|
|
|
|
Un template est évalué dans le même contexte que l'endroit d'où il a été
|
|
appelé (gestionnaire de route). Les variables d'instance déclarées dans le
|
|
gestionnaire de route sont directement accessibles dans le template:
|
|
|
|
get '/:id' do
|
|
@foo = Foo.find(params[:id])
|
|
haml '%h1= @foo.nom'
|
|
end
|
|
|
|
Alternativement, on peut passer un hash contenant des variables locales:
|
|
|
|
get '/:id' do
|
|
foo = Foo.find(params[:id])
|
|
haml '%h1= foo.nom', :locals => { :foo => foo }
|
|
end
|
|
|
|
Ceci est généralement utilisé lorsque l'on veut utiliser un template comme partiel
|
|
(depuis un autre template) et qu'il est donc nécessaire d'adapter les noms de variables.
|
|
|
|
=== Templates dans le fichier source
|
|
|
|
Des templates peuvent être définis dans le fichier source comme ceci:
|
|
|
|
require 'rubygems'
|
|
require 'sinatra'
|
|
|
|
get '/' do
|
|
haml :index
|
|
end
|
|
|
|
__END__
|
|
|
|
@@ layout
|
|
%html
|
|
= yield
|
|
|
|
@@ index
|
|
%div.title Bonjour Monde!!!!!
|
|
|
|
NOTE: Les templates du fichier source qui contient <tt>require 'sinatra'</tt>
|
|
sont automatiquement chargés. Si vous avez des templates dans d'autres fichiers source,
|
|
il faut explicitement les déclarer via: <tt>enable :inline_templates</tt>.
|
|
|
|
=== Templates nommés
|
|
|
|
Les templates peuvent aussi être définis grâce à la méthode de haut niveau <tt>template</tt>:
|
|
|
|
template :layout do
|
|
"%html\n =yield\n"
|
|
end
|
|
|
|
template :index do
|
|
'%div.title Bonjour Monde!'
|
|
end
|
|
|
|
get '/' do
|
|
haml :index
|
|
end
|
|
|
|
Si un template nommé "layout" existe, il sera utilisé à chaque fois qu'un template
|
|
sera affiché. Vous pouvez désactivez le layout à tout moment en passant
|
|
<tt>:layout => false</tt>.
|
|
|
|
get '/' do
|
|
haml :index, :layout => !request.xhr?
|
|
end
|
|
|
|
== Helpers
|
|
|
|
Utilisez la méthode de haut niveau <tt>helpers</tt> pour définir des routines qui
|
|
seront accessibles dans vos gestionnaires de route et dans vos templates:
|
|
|
|
helpers do
|
|
def bar(nom)
|
|
"#{nom}bar"
|
|
end
|
|
end
|
|
|
|
get '/:nom' do
|
|
bar(params[:nom])
|
|
end
|
|
|
|
== Filtres
|
|
|
|
Un filtre <tt>before</tt> est évalué avant n'importe quelle requête, dans le
|
|
contexte de celle-ci, et peut modifier la requête ou la réponse. Les variables
|
|
d'instance déclarées dans le filtre sont accessibles au gestionnaire de route
|
|
et au template:
|
|
|
|
before do
|
|
@note = 'Coucou!'
|
|
request.path_info = '/foo/bar/baz'
|
|
end
|
|
|
|
get '/foo/*' do
|
|
@note #=> 'Coucou!'
|
|
params[:splat] #=> 'bar/baz'
|
|
end
|
|
|
|
Un filtre <tt>after</tt> est évalué après chaque requête, dans le contexte
|
|
de celle-ci et peut également modifier la requête et/ou la réponse. Toutes les
|
|
variables d'instance déclarées dans un filtre <tt>before</tt> et dans le gestionnaire
|
|
de route sont accessibles dans le filtre <tt>after</tt>:
|
|
|
|
after do
|
|
puts response.status
|
|
end
|
|
|
|
En option, on peut passer un masque au filtre, ce qui le rend actif uniquement
|
|
si la requête correspond au masque en question:
|
|
|
|
before '/secret/*' do
|
|
authentification!
|
|
end
|
|
|
|
after '/faire/:travail' do |travail|
|
|
session[:dernier_travail] = travail
|
|
end
|
|
|
|
== Halt
|
|
|
|
Pour arrêter immédiatement la requête dans un filtre ou un gestionnaire de route:
|
|
|
|
halt
|
|
|
|
Vous pouvez aussi passer le code retour ...
|
|
|
|
halt 410
|
|
|
|
Ou le texte ...
|
|
|
|
halt 'Ceci est le texte'
|
|
|
|
Ou les deux ...
|
|
|
|
halt 401, 'Partez!'
|
|
|
|
Ainsi que les entêtes ...
|
|
|
|
halt 402, {'Content-Type' => 'text/plain'}, 'revanche'
|
|
|
|
== Passer
|
|
|
|
Une route peut passer le relais aux autres routes qui correspondent également
|
|
avec <tt>pass</tt>:
|
|
|
|
get '/devine/:qui' do
|
|
pass unless params[:qui] == 'Frank'
|
|
"Tu m'as eu!"
|
|
end
|
|
|
|
get '/devine/*' do
|
|
'Manqué!'
|
|
end
|
|
|
|
On sort donc immédiatement de ce gestionnaire et on continue à chercher,
|
|
dans les masques suivants, le prochain qui correspond à la requête.
|
|
Si aucun des masques suivants ne correspond, un code 404 est retourné.
|
|
|
|
== Accéder à l'objet requête
|
|
|
|
L'objet correspondant à la requête envoyée peut être récupéré dans le contexte de la requête (filtres, routes, gestionnaires d'erreur) au moyen de la méthode `request`:
|
|
|
|
# application tournant à l'adresse http://exemple.com/exemple
|
|
get '/foo' do
|
|
request.body # corps de la requête envoyée par le client (voir ci-dessous)
|
|
request.scheme # "http"
|
|
request.script_name # "/exemple"
|
|
request.path_info # "/foo"
|
|
request.port # 80
|
|
request.request_method # "GET"
|
|
request.query_string # ""
|
|
request.content_length # taille de request.body
|
|
request.media_type # type de média pour request.body
|
|
request.host # "exemple.com"
|
|
request.get? # true (méthodes similaires pour les autres verbes HTTP)
|
|
request.form_data? # false
|
|
request["UN_ENTETE"] # valeur de l'entête UN_ENTETE
|
|
request.referer # référant du client ou '/'
|
|
request.user_agent # user agent (utilisé par la condition :agent)
|
|
request.cookies # tableau contenant les cookies du navigateur
|
|
request.xhr? # requête AJAX ?
|
|
request.url # "http://exemple.com/exemple/foo"
|
|
request.path # "/exemple/foo"
|
|
request.ip # adresse IP du client
|
|
request.secure? # false
|
|
request.env # tableau brut de l'environnement fourni par Rack
|
|
end
|
|
|
|
Certaines options, telles que <tt>script_name</tt> ou <tt>path_info</tt> peuvent
|
|
également être modifiées:
|
|
|
|
before { request.path_info = "/" }
|
|
|
|
get "/" do
|
|
"toutes les requêtes arrivent ici"
|
|
end
|
|
|
|
<tt>request.body</tt> est un objet IO ou StringIO:
|
|
|
|
post "/api" do
|
|
request.body.rewind # au cas où il a déjà été lu
|
|
donnees = JSON.parse request.body.read
|
|
"Bonjour #{donnees['nom']}!"
|
|
end
|
|
|
|
== Configuration
|
|
|
|
Lancé une seule fois au démarrage de tous les environnements:
|
|
|
|
configure do
|
|
...
|
|
end
|
|
|
|
Lancé si l'environnement (variable d'environnement RACK_ENV) est défini comme
|
|
<tt>:production</tt>:
|
|
|
|
configure :production do
|
|
...
|
|
end
|
|
|
|
Lancé si l'environnement est <tt>:production</tt> ou
|
|
<tt>:test</tt>:
|
|
|
|
configure :production, :test do
|
|
...
|
|
end
|
|
|
|
== Gérer les erreurs
|
|
|
|
Les gestionnaires d'erreur s'exécutent dans le même contexte que les routes ou les
|
|
filtres, ce qui veut dire que vous avez accès (entre autres) aux bons vieux
|
|
<tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt>, etc.
|
|
|
|
=== Pas Trouvé
|
|
|
|
Quand une exception <tt>Sinatra::NotFound</tt> est soulevée, ou que le code retour
|
|
est 404, le gestionnaire <tt>not_found</tt> est invoqué:
|
|
|
|
not_found do
|
|
'Pas moyen de trouver ce que vous cherchez'
|
|
end
|
|
|
|
=== Erreur
|
|
|
|
Le gestionnaire +error+ est invoqué à chaque fois qu'une exception est soulevée dans une route
|
|
ou un filtre. L'objet exception est accessible via la variable Rack <tt>sinatra.error</tt>:
|
|
|
|
error do
|
|
'Désolé mais une méchante erreur est survenue - ' + env['sinatra.error'].name
|
|
end
|
|
|
|
Erreur sur mesure:
|
|
|
|
error MonErreurSurMesure do
|
|
'Donc il est arrivé ceci...' + request.env['sinatra.error'].message
|
|
end
|
|
|
|
Donc si ceci arrive:
|
|
|
|
get '/' do
|
|
raise MonErreurSurMesure, 'quelque chose de mal'
|
|
end
|
|
|
|
Vous obtenez ça:
|
|
|
|
Donc il est arrivé ceci... quelque chose de mal
|
|
|
|
Alternativement, vous pouvez avoir un gestionnaire d'erreur associé à un code particulier:
|
|
|
|
error 403 do
|
|
'Accès interdit'
|
|
end
|
|
|
|
get '/secret' do
|
|
403
|
|
end
|
|
|
|
Ou un intervalle:
|
|
|
|
error 400..510 do
|
|
'Boom'
|
|
end
|
|
|
|
Sinatra installe pour vous quelques gestionnaires <tt>not_found</tt> et <tt>error</tt>
|
|
génériques lorsque vous êtes en environnement <tt>development</tt>.
|
|
|
|
== Types Mime
|
|
|
|
Quand vous utilisez <tt>send_file</tt> et que le fichier possède une extension que Sinatra
|
|
ne reconnaît pas, utilisez +mime_type+ pour la déclarer:
|
|
|
|
mime_type :foo, 'text/foo'
|
|
|
|
Vous pouvez aussi l'utiliser avec +content_type+:
|
|
|
|
content_type :foo
|
|
|
|
== Les Middlewares Rack
|
|
|
|
Sinatra tourne avec Rack[http://rack.rubyforge.org/], une interface standard
|
|
et minimale pour les web frameworks Ruby. Un des points forts de Rack est le
|
|
support de ce que l'on appelle des "middlewares" -- composant qui vient se situer
|
|
entre le serveur et votre application, et dont le but est de visualiser/manipuler la
|
|
requête/réponse HTTP, et d'offrir diverses fonctionnalités classiques.
|
|
|
|
Sinatra permet de construire facilement des middlewares Rack via la méthode de
|
|
haut niveau +use+:
|
|
|
|
require 'sinatra'
|
|
require 'mon_middleware_perso'
|
|
|
|
use Rack::Lint
|
|
use MonMiddlewarePerso
|
|
|
|
get '/bonjour' do
|
|
'Bonjour Monde'
|
|
end
|
|
|
|
La sémantique de +use+ est identique à celle définie dans le DSL de
|
|
Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html]
|
|
(le plus souvent utilisé dans un fichier rackup). Par exemple, la méthode +use+
|
|
accepte divers arguments ainsi que des blocs:
|
|
|
|
use Rack::Auth::Basic do |login, password|
|
|
login == 'admin' && password == 'secret'
|
|
end
|
|
|
|
Rack est distribué avec une bonne variété de middlewares standards pour les logs,
|
|
débuguer, faire du routage URL, de l'authentification, gérer des sessions.
|
|
Sinatra utilise beaucoup de ces composants automatiquement via la configuration,
|
|
donc pour ceux-ci vous n'aurez pas à utiliser la méthode +use+.
|
|
|
|
== Tester
|
|
|
|
Les tests pour Sinatra peuvent être écrit avec n'importe quelle bibliothèque
|
|
basée sur Rack. {Rack::Test}[http://gitrdoc.com/brynary/rack-test] est
|
|
recommandé:
|
|
|
|
require 'mon_application_sinatra'
|
|
require 'rack/test'
|
|
|
|
class MonTest < Test::Unit::TestCase
|
|
include Rack::Test::Methods
|
|
|
|
def app
|
|
Sinatra::Application
|
|
end
|
|
|
|
def test_ma_racine
|
|
get '/'
|
|
assert_equal 'Bonjour Monde!', last_response.body
|
|
end
|
|
|
|
def test_avec_des_parametres
|
|
get '/rencontrer', :name => 'Frank'
|
|
assert_equal 'Salut Frank!', last_response.body
|
|
end
|
|
|
|
def test_avec_rack_env
|
|
get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
|
|
assert_equal "Vous utilisez Songbird!", last_response.body
|
|
end
|
|
end
|
|
|
|
NOTE: Le module intégré Sinatra::Test et la classe Sinatra::TestHarness
|
|
sont désapprouvés depuis la version 0.9.2 .
|
|
|
|
== Sinatra::Base - Les Middlewares, les Bibliothèques, et les Applications Modulaires
|
|
|
|
Définir votre application au niveau supérieur fonctionne bien pour les
|
|
micro-applications, mais peut s'avérer moins pratique lorsqu'il s'agit
|
|
de créer des composants réutilisables comme des middlewares Rack, faire
|
|
du Rails metal, ou de simples bibliothèques avec un composant serveur, ou
|
|
même une extension pour Sinatra. Le DSL de haut niveau pollue l'espace de noms
|
|
et est une configuration adaptée à une micro-application (un fichier unique
|
|
pour l'application, les dossiers ./public et ./views, les logs, pages d'erreur, etc.).
|
|
C'est là que Sinatra::Base entre en jeu:
|
|
|
|
require 'sinatra/base'
|
|
|
|
class MonApplication < Sinatra::Base
|
|
set :sessions, true
|
|
set :foo, 'bar'
|
|
|
|
get '/' do
|
|
'Bonjour Monde!'
|
|
end
|
|
end
|
|
|
|
La classe MonApplication est un composant Rack indépendant qui peut agir
|
|
comme un middleware Rack, une application Rack, ou Rails metal. vous pouvez
|
|
donc le lancer avec +run+ ou l'utiliser avec +use+ dans un fichier rackup;
|
|
ou contrôler un composant de serveur sous forme de bibliothèque:
|
|
|
|
MonApplication.run! :host => 'localhost', :port => 9090
|
|
|
|
Les méthodes disponibles dans Sinatra::Base sont exactement identiques à
|
|
celles disponibles dans le DSL de haut niveau. La plupart des applications
|
|
de haut niveau peuvent être converties en composant Sinatra::Base avec
|
|
deux modifications:
|
|
|
|
* Votre fichier doit charger +sinatra/base+ au lieu de +sinatra+;
|
|
autrement, toutes les méthodes de la DSL seront chargées dans l'espace
|
|
de noms.
|
|
* Mettre vos gestionnaires de route, vos gestionnaires d'erreur, vos filtres
|
|
et options dans une sous-classe de Sinatra::Base.
|
|
|
|
<tt>Sinatra::Base</tt> est plutôt épuré. La plupart des options sont désactivées par défaut,
|
|
ceci inclus le serveur. Voir {Options et Configuration}[http://sinatra.github.com/configuration.html]
|
|
pour plus de détails sur les options et leur comportement.
|
|
|
|
=== Utiliser Sinatra comme Middleware
|
|
|
|
Non seulement Sinatra peut utiliser d'autres middlewares Rack, il peut également être
|
|
à son tour utilisé au-dessus de n'importe quel +endpoint+ Rack en tant que middleware.
|
|
Ce +endpoint+ peut très bien être une autre application Sinatra, ou n'importe quelle
|
|
application basée sur Rack (Rails/Ramaze/Camping/...).
|
|
|
|
require 'sinatra/base'
|
|
|
|
class EcranDeConnexion < Sinatra::Base
|
|
enable :session
|
|
|
|
get('/connexion') { haml :connexion }
|
|
|
|
post('/connexion') do
|
|
if params[:nom] = 'admin' and params[:motdepasse] = 'admin'
|
|
session['nom_utilisateur'] = params[:nom]
|
|
else
|
|
redirect '/connexion'
|
|
end
|
|
end
|
|
end
|
|
|
|
class MonApp < Sinatra::Base
|
|
# le middleware sera appelé avant les filtres
|
|
use EcranDeConnexion
|
|
|
|
before do
|
|
unless session['nom_utilisateur']
|
|
halt "Accès refusé, merci de vous <a href='/connexion'>connecter</a>."
|
|
end
|
|
end
|
|
|
|
get('/') { "Bonjour #{session['nom_utilisateur']}." }
|
|
end
|
|
|
|
== Contextes et Binding
|
|
|
|
Le contexte dans lequel vous êtes détermine les méthodes et variables
|
|
disponibles.
|
|
|
|
=== Contexte de l'application/classe
|
|
|
|
Toute application Sinatra correspond à une sous-classe de Sinatra::Base. Si
|
|
vous utilisez le DSL haut niveau (<tt>require 'sinatra'</tt>), alors cette classe
|
|
est Sinatra::Application, sinon il s'agit de la sous-classe que vous avez définie.
|
|
Dans le contexte de la classe, vous avez accès aux méthodes telles que `get` ou
|
|
`before`, mais vous n'avez pas accès aux objets `request` ou `session` car c'est la
|
|
même classe d'application qui traitera toutes les requêtes.
|
|
|
|
Les options définies au moyen de `set` deviennent des méthodes de classe:
|
|
|
|
class MonApp << Sinatra::Base
|
|
# Eh, je suis dans le contexte de l'application!
|
|
set :foo, 42
|
|
foo # => 42
|
|
|
|
get '/foo' do
|
|
# Eh, je ne suis plus dans le contexte de l'application!
|
|
end
|
|
end
|
|
|
|
Vous avez le binding du contexte de l'application dans:
|
|
|
|
* Le corps de la classe d'application
|
|
* Les méthodes définies par les extensions
|
|
* Le bloc passé à `helpers`
|
|
* Les procs/blocs utilisés comme argument pour `set`
|
|
|
|
Vous pouvez atteindre ce contexte (donc la classe) de la façon suivante:
|
|
|
|
* Via l'objet passé dans les blocs `configure` (<tt>configure { |c| ... }</tt>)
|
|
* En utilisant `settings` dans le contexte de la requête
|
|
|
|
=== Contexte de la requête/instance
|
|
|
|
Pour tout traitement d'une requête, une nouvelle instance de votre classe d'application
|
|
est créée et tous vos gestionnaires sont exécutés dans ce contexte. Dans ce dernier,
|
|
vous pouvez accéder aux objets `request` et `session` et faire appel aux fonctions
|
|
de rendu telles que `erb` ou `haml`. Vous pouvez accéder au contexte de l'application
|
|
depuis le contexte de la requête au moyen de `settings`:
|
|
|
|
class MonApp << Sinatra::Base
|
|
# Eh, je suis dans le contexte de l'application!
|
|
get '/ajouter_route/:nom' do
|
|
# Contexte de la requête pour '/ajouter_route/:nom'
|
|
@value = 42
|
|
|
|
settings.get("/#{params[:nom]}") do
|
|
# Contexte de la requête pour "/#{params[:nom]}"
|
|
@value # => nil (on est pas au sein de la même requête)
|
|
end
|
|
|
|
"Route ajoutée!"
|
|
end
|
|
end
|
|
|
|
Vous avez le binding du contexte de la requête dans:
|
|
|
|
* les blocs get/head/post/put/delete
|
|
* les filtres before/after
|
|
* les méthodes utilitaires (définies au moyen de `helpers`)
|
|
* les vues/templates
|
|
|
|
=== Le contexte de délégation
|
|
|
|
Le contexte de délégation se contente de transmettre les appels de méthodes au
|
|
contexte de classe. Toutefois, il ne se comporte pas à 100% comme le contexte de
|
|
classe car vous n'avez pas le binding de la classe: seules les méthodes
|
|
spécifiquement déclarées pour délégation sont disponibles et il n'est pas possible
|
|
de partager des variables/états avec le contexte de classe (comprenez: `self` n'est
|
|
pas le même). Vous pouvez ajouter des délégation de méthodes en appelant
|
|
<tt>Sinatra::Delegator.delegate :method_name</tt>.
|
|
|
|
Vous avez le binding du contexte de délégation dans:
|
|
|
|
* Le binding de haut niveau, si vous avez utilisé <tt>require "sinatra"</tt>
|
|
* Un objet qui inclut le module `Sinatra::Delegator`
|
|
|
|
Jetez un oeil
|
|
pour vous faire une idée: voici le mixin {Sinatra::Delegator}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128]
|
|
qui est {inclus dans l'espace de noms principal}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28]
|
|
|
|
== Ligne de commande
|
|
|
|
Les applications en Sinatra peuvent être lancées directement:
|
|
|
|
ruby mon_application.rb [-h] [-x] [-e ENVIRONNEMENT] [-p PORT] [-o HOTE] [-s SERVEUR]
|
|
|
|
Les options sont:
|
|
|
|
-h # aide
|
|
-p # déclare le port (4567 par défaut)
|
|
-o # déclare l'hôte (0.0.0.0 par défaut)
|
|
-e # déclare l'environnement (+development+ par défaut)
|
|
-s # déclare le serveur/gestionnaire à utiliser (thin par défaut)
|
|
-x # active le mutex lock (off par défaut)
|
|
|
|
== Essuyer les plâtres
|
|
|
|
Si vous voulez utiliser la toute dernière version de Sinatra, créez un
|
|
clone local et lancez votre application avec le dossier <tt>sinatra/lib</tt>
|
|
dans votre <tt>LOAD_PATH</tt>:
|
|
|
|
cd mon_application
|
|
git clone git://github.com/sinatra/sinatra.git
|
|
ruby -Isinatra/lib mon_application.rb
|
|
|
|
Alternativement, vous pouvez ajoutez le dossier <tt>sinatra/lib</tt> à votre
|
|
<tt>LOAD_PATH</tt> à l'intérieur de votre application:
|
|
|
|
$LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib'
|
|
require 'rubygems'
|
|
require 'sinatra'
|
|
|
|
get '/a_propos' do
|
|
"J'utilise la version " + Sinatra::VERSION
|
|
end
|
|
|
|
Pour mettre à jour les sources de Sinatra par la suite:
|
|
|
|
cd mon_projet/sinatra
|
|
git pull
|
|
|
|
== Mais encore
|
|
|
|
* {Site internet}[http://www.sinatrarb.com/] - Plus de documentation,
|
|
de news, et des liens vers d'autres ressources.
|
|
* {Contribuer}[http://www.sinatrarb.com/contributing] - Vous avez trouvé un bug? Besoin
|
|
d'aide? Vous avez un patch?
|
|
* {Suivi des problèmes}[http://github.com/sinatra/sinatra/issues]
|
|
* {Twitter}[http://twitter.com/sinatra]
|
|
* {Mailing List}[http://groups.google.com/group/sinatrarb/topics]
|
|
* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] sur http://freenode.net
|