diff --git a/Rakefile b/Rakefile index ff560bcf..f9c4affc 100644 --- a/Rakefile +++ b/Rakefile @@ -1,16 +1,20 @@ require 'rake/clean' require 'rdoc/markup/to_html' +def readme(pattern = "%s", &block) + return readme(pattern).each(&block) if block_given? + %w[en de es fr jp].map do |lang| + pattern % "README#{lang == "en" ? "" : ".#{lang}"}" + end +end + task :default => ['_sinatra', :build] desc "Build outdated static files and API docs" task :build => ['build:static'] desc "Build outdated static files" -task 'build:static' => [ - '_includes/README.html', - '_includes/README.jp.html' -] +task 'build:static' => readme("_includes/%s.html") desc "Build anything that's outdated and stage changes for next commit" task :regen => [:build] do @@ -36,25 +40,19 @@ end file('_sinatra') { Rake::Task['pull:sinatra'].invoke } CLOBBER.include '_sinatra' -%w[README.rdoc README.jp.rdoc AUTHORS].each do |fn| - file "_sinatra/#{fn}" => ['_sinatra'] -end +readme("_sinatra/%s.rdoc") { |fn| file fn => '_sinatra' } +file 'AUTHORS' => '_sinatra' -# Build _includes/README.html from RDoc -file '_includes/README.html' => ['_sinatra/README.rdoc', 'Rakefile'] do |f| - html = RDoc::Markup::ToHtml.new.convert(File.read("_sinatra/README.rdoc")). - sub("
+Wichtig: Dieses Dokument ist eine Übersetzung aus dem Englischen und +unter Umständen nicht auf dem aktuellsten Stand. +
++Sinatra ist eine DSL, die das schnelle Erstellen von Webanwendungen in Ruby +mit minimalen Aufwand ermöglicht: +
++ # myapp.rb + require 'sinatra' + get '/' do + 'Hallo Welt!' + end ++
+Einfach via rubygems installieren und starten: +
++ gem install sinatra + ruby -rubygems myapp.rb ++
+Die Seite kann nun unter localhost:4567 +betrachtet werden. +
++In Sinatra wird eine Route durch eine HTTP-Methode und ein URL-Muster +definiert. Jeder dieser Routen wird ein Ruby-Block zugeordnet. +
++ get '/' do + .. zeige etwas .. + end + + post '/' do + .. erstelle etwas .. + end + + put '/' do + .. update etwas .. + end + + delete '/' do + .. entferne etwas .. + end ++
+Die Routen werden in der Reihenfolge durchlaufen, in der sie definiert +wurden. Das erste Routemuster, das mit dem Request übereinstimmt, wird +ausgeführt. +
++Die Muster der Routen können benannte Parameter beinhalten, die über den +params-Hash zugänglich gemacht werden: +
++ get '/hallo/:name' do + # passt auf "GET /hallo/foo" und "GET /hallo/bar" + # params[:name] ist 'foo' oder 'bar' + "Hallo #{params[:name]}!" + end ++
+Man kann auf diese auch mit Blockparametern zugreifen: +
++ get '/hallo/:name' do |n| + "Hallo #{n}!" + end ++
+Routenmuster können auch mit Splat- oder Wildcardparametern über das +params[:splat] Array angesprochen werden. +
++ get '/sag/*/zu/*' do + # passt auf /sag/hallo/zu/welt + params[:splat] # => ["hallo", "welt"] + end + + get '/download/*.*' do + # passt auf /download/pfad/zu/datei.xml + params[:splat] # => ["pfad/zu/datei", "xml"] + end ++
+Routen mit regulären Ausdrücken sind auch möglich: +
++ get %r{/hallo/([\w]+)} do + "Hallo, #{params[:captures].first}!" + end ++
+Und auch hier kann man Blockparameter nutzen: +
++ get %r{/hallo/([\w]+)} do |c| + "Hallo, #{c}!" + end ++
+An Routen können eine Vielzahl von Bedingungen angehängt werden, die +erfüllt sein müssen, damit der Block ausgeführt wird. Möglich wäre +etwa eine Einschränkung des User Agents: +
++ get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do + "Du verwendest Songbird Version #{params[:agent][0]}" + end + + get '/foo' do + # passt auf andere Browser + end ++
+Andere mitgelieferte Bedingungen sind host_name und +provides: +
++ get '/', :host_name => /^admin\./ do + "Adminbereich, Zugriff verweigert!" + end + + get '/', :provides => 'html' do + haml :index + end + + get '/', :provides => ['rss', 'atom', 'xml'] do + builder :feed + end ++
+Man kann auch relativ einfach eigene Bedingungen hinzufügen: +
++ set(:probability) { |value| condition { rand <= value } } + + get '/auto_gewinnen', :probability => 0.1 do + "Du hast gewonnen!" + end + + get '/auto_gewinnen' do + "Tut mir leid, verloren." + end ++
+Durch den Rückgabewert eines Routenblocks 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. +
++Man kann jedes Objekt zurückgeben, bei dem es sich entweder um einenen +validen Rack-Rückgabewert, einen validen Rack-Body oder einen HTTP Status +Code handelt: +
++Ein Array mit drei Elementen: [Status (Fixnum), Headers (Hash), +Response Body (hört auf #each)] +
++Ein Array mit zwei Elementen: [Status (Fixnum), Response Body (hört +auf #each)] +
++Ein Objekt, das auf #each hört und den an diese Methode +übergebenen Block nur mit Strings als Übergabewerte aufruft. +
++Ein Fixnum, das den Status Code festlegt. +
++Damit lässt sich relativ einfach Streaming implementieren: +
++ class Stream + def each + 100.times { |i| yield "#{i}\n" } + end + end + + get('/') { Stream.new } ++
+Statische Dateien werden aus dem ./public Ordner ausgeliefert. Es +ist möglich einen anderen Ort zu definieren, indem man die +:public Option setzt: +
++ set :public, File.dirname(__FILE__) + '/static' ++
+Zu beachten ist, dass der Ordnername public nicht Teil der URL ist. Die +Datei ./public/css/style.css ist unter example.com/css/style.css +zu finden. +
++Standardmäßig wird davon ausgegangen, dass sich Templates im +./views Ordner befinden. Man kann jedoch einen anderen Ordner +festlegen: +
++ set :views, File.dirname(__FILE__) + '/templates' ++
+Eine wichtige Sache, die man sich hierbei merken sollte, ist das man immer +mit Symbols auf Templates verweisen sollte, auch wenn sich ein Template in +einem Unterordner befindet (in diesen Fall :'subdir/template'). +Renderingmethoden rendern jeden String direkt. +
++Das haml gem wird benötigt, um Haml-Templates rendern zu können: +
++ ## haml muss eingebunden werden + require 'haml' + + get '/' do + haml :index + end ++
+Dieser Code rendert ./views/index.haml. +
++Hamls +Optionen können global durch die Sinatrakonfiguration gesetzt werden, +siehe Optionen und +Konfiguration, und individuell überschrieben werden. +
++ set :haml, :format => :html5 # Standard Haml-Format ist :xhtml + + get '/' do + haml :index, :format => :html4 # überschrieben + end ++
+ ## erb muss eingebunden werden + require 'erb' + + get '/' do + erb :index + end ++
+Dieser Code rendert ./views/index.erb. +
++Das erubis gem wird benötigt, um Erubis-Templates rendern zu können: +
++ ## erbubis muss eingebunden werden + require 'erubis' + + get '/' do + erubis :index + end ++
+Dieser Code rendert ./views/index.erubis. +
++Das buidler gem wird benötigt, um Builder-Templates rendern zu können: +
++ ## builder muss eingebunden werden + require 'builder' + + get '/' do + builder :index + end ++
+Dieser Code rendert ./views/index.builder. +
++Das nokogiri gem wird benötigt, um Nokogiri-Templates rendern zu können: +
++ ## nokogiri muss eingebunden werden + require 'nokogiri' + + get '/' do + nokogiri :index + end ++
+Dieser Code rendert ./views/index.nokogiri. +
++Das haml gem wird benötigt, um SASS-Templates rendern zu können: +
++ ## sass muss eingebunden werden + require 'sass' + + get '/stylesheet.css' do + sass :stylesheet + end ++
+Dieser Code rendert ./views/stylesheet.sass. +
++Sass +Optionen können global durch die Sinatra-Konfiguration gesetzt werden, +siehe Optionen und +Konfiguration, und individuell überschrieben werden. +
++ set :sass, :style => :compact # Standard Sass-Style ist :nested + + get '/stylesheet.css' do + sass :stylesheet, :style => :expanded # überschrieben + end ++
+Das haml gem wird benötigt, um SCSS-Templates rendern zu können: +
++ ## sass muss eingebunden werden + require 'sass' + + get '/stylesheet.css' do + scss :stylesheet + end ++
+Dieser Code rendert ./views/stylesheet.scss. +
++Scss +Optionen können global durch die Sinatra-Konfiguration gesetzt werden, +siehe Optionen und +Konfiguration, und individuell überschrieben werden. +
++ set :scss, :style => :compact # Standard Scss-Style ist :nested + + get '/stylesheet.css' do + scss :stylesheet, :style => :expanded # überschrieben + end ++
+Das less gem wird benötigt, um Less-Templates rendern zu können: +
++ ## less muss eingebunden werden + require 'less' + + get '/stylesheet.css' do + less :stylesheet + end ++
+Dieser Code rendert ./views/stylesheet.less. +
++Das liquid gem wird benötigt, um Liquid-Templates rendern zu können: +
++ ## liquid muss eingebunden werden + require 'liquid' + + get '/' do + liquid :index + end ++
+Dieser Code rendert ./views/index.liquid. +
++Da man aus Liquid-Templates heraus keine Methoden (abgesehen von +yield) aufrufen kann, will man nahezu in allen Fällen +locals übergeben: +
++ liquid :index, :locals => { :key => 'value' } ++
+Das rdiscount gem wird benötigt, um Markdown-Templates rendern zu können: +
++ ## rdiscount muss eingebunden werden + require "rdiscount" + + get '/' do + markdown :index + end ++
+Dieser Code rendert ./views/index.markdown (md und +mkd sind ebenfalls zulässige Dateiendungen). +
++Da es weder möglich ist Methoden aufzurufen, noch locals zu +übergeben, ist es am sinnvollsten Markdown in Kombination mit einer +anderen Template-Engine zu nutzen: +
++ erb :overview, :locals => { :text => markdown(:introduction) } ++
+Es ist auch möglich die markdown Methode aus anderen Templates +heraus aufzurufen: +
++ %h1 Hallo von Haml! + %p= markdown(:greetings) ++
+Das RedCloth gem wird benötigt, um Textile-Templates rendern zu können: +
++ ## redcloth muss eingebunden werden + require "redcloth" + + get '/' do + textile :index + end ++
+Dieser Code rendert ./views/index.textile. +
++Da es weder möglich ist Methoden aufzurufen, noch locals zu +übergeben, ist es am sinnvollsten Textile in Kombination mit einer anderen +Template-Engine zu nutzen: +
++ erb :overview, :locals => { :text => textile(:introduction) } ++
+Es ist auch möglich die textile Methode aus anderen Templates +heraus aufzurufen: +
++ %h1 Hallo von Haml! + %p= textile(:greetings) ++
+Das rdoc gem wird benötigt, um RDoc-Templates rendern zu können: +
++ ## RDoc muss eingebunden werden + require "rdoc" + + get '/' do + rdoc :index + end ++
+Dieser Code rendert ./views/index.rdoc. +
++Da es weder möglich ist Methoden aufzurufen, noch locals zu +übergeben, ist es am sinnvollsten RDoc in Kombination mit einer anderen +Template-Engine zu nutzen: +
++ erb :overview, :locals => { :text => rdoc(:introduction) } ++
+Es ist auch möglich die rdoc Methode aus anderen Templates heraus +aufzurufen: +
++ %h1 Hallo von Haml! + %p= rdoc(:greetings) ++
+Das radius gem wird benötigt, um Radius-Templates rendern zu können: +
++ ## radius muss eingebunden werden + require 'radius' + + get '/' do + radius :index + end ++
+Dieser Code rendert ./views/index.radius. +
++Da man aus Radius-Templates heraus keine Methoden (abgesehen von +yield) aufrufen kann, will man nahezu in allen Fällen +locals übergeben: +
++ radius :index, :locals => { :key => 'value' } ++
+Das markaby gem wird benötigt, um Markaby-Templates rendern zu können: +
++ ## markaby muss eingebunden werden + require 'markaby' + + get '/' do + markaby :index + end ++
+Dieser Code rendert ./views/index.mab. +
++Das coffee-script gem und das `coffee`-Programm werden benötigt, um +CoffeScript-Templates rendern zu können: +
++ ## coffee-script muss eingebunden werden + require 'coffee-script' + + get '/application.js' do + coffee :application + end ++
+Dieser Code rendert ./views/application.coffee. +
++ get '/' do + haml '%div.title Hallo Welt' + end ++
+Rendert den Inline-Template-String. +
++Templates werden im selben Kontext ausgeführt wie Routen. Instanzvariablen +in Routen sind auch direkt im Template verfügbar: +
++ get '/:id' do + @foo = Foo.find(params[:id]) + haml '%h1= @foo.name' + end ++
+Oder durch einen expliziten Hash von lokalen Variablen: +
++ get '/:id' do + foo = Foo.find(params[:id]) + haml '%h1= foo.name', :locals => { :foo => foo } + end ++
+Dies wird typischerweise bei Verwendung von Subtemplates (partials) in +anderen Templates eingesetzt. +
++Templates können auch am Ende der Datei definiert werden: +
++ require 'sinatra' + + get '/' do + haml :index + end + + __END__ + + @@ layout + %html + = yield + + @@ index + %div.title Hallo Welt!!!!! ++
+Anmerkung: Inline-Templates die in der Datei definiert sind, die +require 'sinatra' aufruft, werden automatisch geladen. Um andere +Inline-Templates in anderen Dateien aufzurufen, muss enable +:inline_templates explizit verwendet werden. +
++Templates können auch mit der Top-Level template-Methode +definiert werden: +
++ template :layout do + "%html\n =yield\n" + end + + template :index do + '%div.title Hallo Welt!' + end + + get '/' do + haml :index + end ++
+Wenn ein Template mit dem Namen “layout” existiert, wird es bei +jedem Aufruf verwendet. Durch :layout => false kann das +Ausführen verhindert werden. +
++ get '/' do + haml :index, :layout => !request.xhr? + end ++
+Durch die Top-Level helpers-Methode, werden sogenannte +Helfer-Methoden definiert, die in Routen und Templates verwendet werden +können: +
++ helpers do + def bar(name) + "#{name}bar" + end + end + + get '/:name' do + bar(params[:name]) + end ++
+Before-Filter werden immer vor jedem Request in dem selben Kontext wie +danach die Routen ausgeführt. So kann man etwa Request und Antwort +ändern. Gesetzte Instanzvariablen in Filtern können in Routen und +Templates verwendet werden: +
++ before do + @note = 'Hi!' + request.path_info = '/foo/bar/baz' + end + + get '/foo/*' do + @note #=> 'Hi!' + params[:splat] #=> 'bar/baz' + end ++
+After-Filter werden nach jedem Request im selben Kontext ausgeführt, und +können ebenfalls Request und Antwort ändern. In Before-Filtern gesetzte +Instanzvariablen können in After-Filterm verwendet werden: +
++ after do + puts response.status + end ++
+Filter können optional auch mit einem Pattern ausgestattet werden, welche +auf den Request-Pfad passen müssen, damit der Filter ausgeführt wird: +
++ before '/protected/*' do + authenticate! + end + + after '/create/:slug' do |slug| + session[:last_slug] = slug + end ++
+Zum sofortigen stoppen eines Request in einem Filter oder einer Route: +
++ halt ++
+Der Status kann beim stoppen auch angegeben werden: +
++ halt 410 ++
+Oder auch den Response-Body: +
++ halt 'Hier steht der Body' ++
+Oder beides: +
++ halt 401, 'verschwinde!' ++
+Sogar mit Headers: +
++ halt 402, {'Content-Type' => 'text/plain'}, 'Rache' ++
+Eine Route kann mittels pass zu der nächsten passenden Route +springen: +
++ get '/raten/:wer' do + pass unless params[:wer] == 'Frank' + 'Du hast mich!' + end + + get '/raten/*' do + 'Du hast mich verfehlt!' + end ++
+Der Block wird sofort verlassen und es wird nach der nächsten treffenden +Route gesucht. Ein 404 Fehler wird zurückgegeben, wenn kein treffendes +Routen-Muster gefunden wird. +
++Wird einmal beim Starten in jedweder Umgebung ausgeführt: +
++ configure do + ... + end ++
+Läuft nur, wenn die Umgebung (RACK_ENV Umgebungsvariable) auf +:production gesetzt ist: +
++ configure :production do + ... + end ++
+Läuft nur, wenn die Umgebung auf :production oder auf +:test gesetzt ist: +
++ configure :production, :test do + ... + end ++
+Error Handler laufen im selben Kontext wie Routen und Filter, was bedeutet, +dass alle Goodies wie haml, erb, halt, etc. +verwendet werden können. +
++Wenn eine Sinatra::NotFound Exception geworfen wird oder der +Statuscode 404 ist, wird der not_found Handler ausgeführt: +
++ not_found do + 'Seite kann nirgendwo gefunden werden.' + end ++
+Der error Handler wird immer ausgeführt, wenn eine Exception in +einem Routenblock oder in einen Filter geworfen wurde. Die Exception kann +über die sinatra.error Rack-Variable angesprochen werden: +
++ error do + 'Entschuldige es gab einen hässlichen Fehler - ' + env['sinatra.error'].name + end ++
+Benutzerdefinierte Fehler: +
++ error MeinFehler do + 'Was passiert ist...' + request.env['sinatra.error'].message + end ++
+Dann, wenn das passiert: +
++ get '/' do + raise MeinFehler, 'etwas schlechtes' + end ++
+Bekommt man dieses: +
++ Was passiert ist... etwas schlechtes ++
+Alternativ kann ein Error Handler auch für Statuscode definiert werden: +
++ error 403 do + 'Zugriff verboten' + end + + get '/geheim' do + 403 + end ++
+Oder ein Statuscode-Bereich: +
++ error 400..510 do + 'Boom' + end ++
+Sinatra setzt verschiedene not_found und error Handler in +der Development Umgebung. +
++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: +
++ mime_type :foo, 'text/foo' ++
+Es kann aber auch der content_type Helfer verwendet werden: +
++ content_type :foo ++
+Sinatra baut auf Rack, einem +minimalen Standardinterface für Ruby Webframeworks. Eines der +interessantesten Features für Entwickler ist der Support von Middleware, +die zwischen den Server und die Anwendung geschaltet wird und so HTTP +Request und/oder Antwort überwachen und/oder manipulieren kann. +
++Sinatra macht das erstellen von Middleware-Verkettungen mit der Top-Level +Methode use zu einem Kinderspiel: +
++ require 'sinatra' + require 'meine_middleware' + + use Rack::Lint + use MeineMiddleware + + get '/hallo' do + 'Hallo Welt' + end ++
+Die Semantik von use entspricht der gleichnamigen Methode der Rack::Builder +DSL (meist verwendet in Rackup-Dateien). Ein Beispiel dafür ist, dass die +use-Methode mehrere/verschiedene Argumente und auch Blöcke +entgegen nimmt: +
++ use Rack::Auth::Basic do |username, password| + username == 'admin' && password == 'geheim' + end ++
+Rack bietet eine Vielzahl von standard Middleware für Logging, Debugging, +URL-Routing, Authentifizierung und Session-Verarbeitung. Sinatra verwendet +viele von diesen Komponenten automatisch, abhängig von der Konfiguration. +So muss man häufig use nicht explizit verwenden. +
++Sinatra Tests können mit jedem auf Rack aufbauendem Test Framework +geschrieben werden. Rack::Test wird empfohlen: +
++ require 'my_sinatra_app' + require 'rack/test' + + class MyAppTest < Test::Unit::TestCase + include Rack::Test::Methods + + def app + Sinatra::Application + end + + def test_my_default + get '/' + assert_equal 'Hallo Welt!', last_response.body + end + + def test_with_params + get '/meet', :name => 'Frank' + assert_equal 'Hallo Frank!', last_response.body + end + + def test_with_rack_env + get '/', {}, 'HTTP_USER_AGENT' => 'Songbird' + assert_equal "Du verwendest Songbird!", last_response.body + end + end ++
+Anmerkung: Das eingebaute Sinatra::Test Modul und die Sinatra::TestHarness +Klasse werden seit Version 0.9.2 nicht mehr unterstützt. +
++Das Definitieren einer Top-Level Anwendung funktioniert gut für +Microanwendungen, hat aber Nachteile, wenn man wiederverwendbare +Komponenten wie Middleware, Rails Metal, einfache Bibliotheken mit Server +Komponenten oder auch Sinatra Erweiterungen bauen will. Die Top-Level DSL +belastet den Objekt-Namespace und setzt einen Microanwendungsstil voraus +(eine einzelne Anwendungsdatei, ./public und ./views Ordner, Logging, +Exception-Detail-Seite, usw.). Genau hier kommt Sinatra::Base ins Spiel: +
++ require 'sinatra/base' + + class MyApp < Sinatra::Base + set :sessions, true + set :foo, 'bar' + + get '/' do + 'Hallo Welt!' + end + end ++
+Die MyApp-Klasse ist eine unabhängige Rack-Komponente, die als Middleware, +Endpunkt oder via Rails Metal verwendet werden kann. Verwendet wird sie +durch use oder run von einer Rackup config.ru +Datei oder als Serverkomponente einer Bibliothek: +
++ MyApp.run! :host => 'localhost', :port => 9090 ++
+Die Methoden der Sinatra::Base-Subklasse sind genau die selben wie die der +Top-Level DSL. Die meisten Top-Level Anwendungen können mit nur zwei +Veränderungen zu Sinatra::Base-Komponenten 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 unbeschriebense Blatt. Die meisten Optionen +sind per default deaktiviert. Das betrifft auch den eingebauten Server. +Siehe Optionen und +Konfiguration für Details über möglichen Optionen. +
++SIDEBAR: Sinatras Top-Level DSL-Methoden sind als einfache Delegationen +implementiert. Die Sinatra::Application-Klasse — eine spezielle +Subklasse von Sinatra::Base — erhält alle :get, :put, :post, +:delete, :before, :error, :not_found, :configure und :set Meldungen, die +vom Top-Level aus gesendet werden. Schau am besten im Code nach: Hier ist +Sinatra::Delegator +mixin definiert und wird in den globalen +Namespace eingebunden. +
++Sinatra Anwendungen können direkt von der Kommandozeile aus gestartet +werden: +
++ ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-h HOST] [-s HANDLER] ++
+Die Optionen sind: +
++ -h # Hilfe + -p # Port setzen (Standard ist 4567) + -h # Host setzen (Standard ist 0.0.0.0) + -e # Umgebung setzen (Standard ist development) + -s # Rack Server/Handler setzen (Standard ist thin) + -x # Mutex lock einschalten (Standard ist off) ++
+Um auf den neuesten Stand von Sinatras Code zu sein, kann eine lokale Kopie +angelegt werden. Gestartet wird in der Anwendung mit dem +sinatra/lib Ordner im LOAD_PATH: +
++ cd myapp + git clone git://github.com/sinatra/sinatra.git + ruby -Isinatra/lib myapp.rb ++
+Alternativ kann der sinatra/lib Ordner zum LOAD_PATH in +der Anwendung hinzugefügt werden: +
++ $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib' + require 'rubygems' + require 'sinatra' + + get '/ueber' do + "Ich laufe auf Version " + Sinatra::VERSION + end ++
+Um Sinatra Code von Zeit zu Zeit zu aktualisieren: +
++ cd myproject/sinatra + git pull ++
+Projekt Website - Ergänzende +Dokumentation, News und Links zu anderen Ressourcen. +
++Hilfe beisteuern +- Einen Fehler gefunden? Brauchst du Hilfe? Hast du einen Patch? +
++Twitter +
++Mailingliste +
++IRC: #sinatra auf freenode.net +
++Atención: Este documento es una traducción de la versión en inglés +y puede estar desactualizado. +
++Sinatra es un DSL para crear aplicaciones web rápidamente en Ruby con un +mínimo esfuerzo: +
++ # miapp.rb + require 'sinatra' + + get '/' do + 'Hola mundo!' + end ++
+Instalá la gem y ejecutá la aplicación con: +
++ gem install sinatra + ruby -rubygems miapp.rb ++
+Podés verla en: localhost:4567 +
++En Sinatra, una ruta está compuesta por un método HTTP y un patrón de +una URL. Cada ruta se asocia con un bloque: +
++ get '/' do + .. mostrar algo .. + end + + post '/' do + .. crear algo .. + end + + put '/' do + .. actualizar algo .. + end + + delete '/' do + .. aniquilar algo .. + end ++
+Las rutas son comparadas en el orden en el que son definidas. La primer +ruta que coincide con la petición es invocada. +
++Los patrones de las rutas pueden incluir parámetros nombrados, accesibles +a través de el hash params: +
++ get '/hola/:nombre' do + # coincide con "GET /hola/foo" y "GET /hola/bar" + # params[:nombre] es 'foo' o 'bar' + "Hola #{params[:nombre]}!" + end ++
+También podés acceder a los parámetros nombrados usando parámetros de +bloque: +
++ get '/hola/:nombre' do |n| + "Hola #{n}!" + end ++
+Los patrones de ruta también pueden incluir parámetros splat (o +wildcard), accesibles a través del arreglo params[:splat]. +
++ get '/decir/*/al/*' do + # coincide con /decir/hola/al/mundo + params[:splat] # => ["hola", "mundo"] + end + + get '/descargar/*.*' do + # coincide con /descargar/path/al/archivo.xml + params[:splat] # => ["path/al/archivo", "xml"] + end ++
+Rutas con Expresiones Regulares: +
++ get %r{/hola/([\w]+)} do + "Hola, #{params[:captures].first}!" + end ++
+O con un parámetro de bloque: +
++ get %r{/hola/([\w]+)} do |c| + "Hola, #{c}!" + end ++
+Las rutas pueden incluir una variedad de condiciones de selección, como +por ejemplo el user agent: +
++ get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do + "Estás usando la versión de Songbird #{params[:agent][0]}" + end + + get '/foo' do + # Coincide con browsers que no sean songbird + end ++
+Otras condiciones disponibles son host_name y provides: +
++ get '/', :host_name => /^admin\./ do + "Área de Administración, Acceso denegado!" + end + + get '/', :provides => 'html' do + haml :index + end + + get '/', :provides => ['rss', 'atom', 'xml'] do + builder :feed + end ++
+Podés definir tus propias condiciones fácilmente: +
++ set(:probabilidad) { |valor| condition { rand <= valor } } + + get '/gana_un_auto', :probabilidad => 0.1 do + "Ganaste!" + end + + get '/gana_un_auto' do + "Lo siento, perdiste." + end ++
+El valor de retorno de un bloque de ruta determina al menos el cuerpo de la +respuesta que se le pasa al cliente HTTP o al siguiente middleware en la +pila de Rack. Lo más común es que sea un string, como en los ejemplos +anteriores. Sin embargo, otros valor también son aceptados. +
++Podés devolver cualquier objeto que sea una respuesta Rack válida, un +objeto que represente el cuerpo de una respuesta Rack o un código de +estado HTTP: +
++Un arreglo con tres elementos: [estado (Fixnum), cabeceras (Hash), +cuerpo de la respuesta (responde a #each)] +
++Un arreglo con dos elementos: [estado (Fixnum), cuerpo de la respuesta +(responde a #each)] +
++Un objeto que responde a #each y que le pasa únicamente strings +al bloque dado +
++Un Fixnum representando el código de estado +
++De esa manera podemos, por ejemplo, implementar fácilmente un streaming: +
++ class Stream + def each + 100.times { |i| yield "#{i}\n" } + end + end + + get('/') { Stream.new } ++
+Los archivos estáticos son servidos desde el directorio público +./public. Podés especificar una ubicación diferente ajustando la +opción :public: +
++ set :public, File.dirname(__FILE__) + '/estaticos' ++
+Notá que el nombre del directorio público no está incluido en la URL. +Por ejemplo, el archivo ./public/css/style.css se accede a través +de ejemplo.com/css/style.css. +
++Se asume que las plantillas están ubicadas directamente bajo el directorio +./views. Para usar un directorio de vistas diferente: +
++ set :views, File.dirname(__FILE__) + '/plantillas' ++
+Es importante acordarse que siempre tenés que referenciar a las plantillas +con símbolos, incluso cuando se encuentran en un subdirectorio (en este +caso tenés que usar :'subdir/plantilla'). Tenés que usar un +símbolo porque los métodos de renderización van a renderizar +directamente cualquier string que se les pase como argumento. +
++La gem/librería haml es necesaria para para renderizar plantillas HAML: +
++ ## Vas a necesitar requerir haml en tu app + require 'haml' + + get '/' do + haml :index + end ++
+Renderiza ./views/index.haml. +
++Las opciones +de Haml pueden ser ajustadas globalmente a través de las +configuraciones de Sinatra, ver Opciones y +Configuraciones, y reemplazadas individualmente. +
++ set :haml, :format => :html5 # el formato por defecto de Haml es :xhtml + + get '/' do + haml :index, :format => :html4 # reemplazado + end ++
+ ## Vas a necesitar requerir erb en tu app + require 'erb' + + get '/' do + erb :index + end ++
+Renderiza ./views/index.erb +
++La gem/librería erubis es necesaria para renderizar plantillas erubis: +
++ ## Vas a necesitar requerir erubis en tu app + require 'erubis' + + get '/' do + erubis :index + end ++
+Renderiza ./views/index.erubis +
++La gem/librería builder es necesaria para renderizar plantillas builder: +
++ ## Vas a necesitar requerir builder en tu app + require 'builder' + + get '/' do + builder :index + end ++
+Renderiza ./views/index.builder. +
++La gem/librería nokogiri es necesaria para renderizar plantillas nokogiri: +
++ ## Vas a necesitar requerir nokogiri en tu app + require 'nokogiri' + + get '/' do + nokogiri :index + end ++
+Renderiza ./views/index.nokogiri. +
++La gem/librería sass es necesaria para renderizar plantillas Sass: +
++ ## Vas a necesitar requerir haml o sass en tu app + require 'sass' + + get '/stylesheet.css' do + sass :stylesheet + end ++
+Renderiza ./views/stylesheet.sass. +
++Las opciones +de Sass pueden ser ajustadas globalmente a través de las +configuraciones de Sinatra, ver Opciones y +Configuraciones, y reemplazadas individualmente. +
++ set :sass, :style => :compact # el estilo por defecto de Sass es :nested + + get '/stylesheet.css' do + sass :stylesheet, :style => :expanded # reemplazado + end ++
+La gem/librería sass es necesaria para renderizar plantillas Scss: +
++ ## Vas a necesitar requerir haml o sass en tu app + require 'sass' + + get '/stylesheet.css' do + scss :stylesheet + end ++
+Renderiza ./views/stylesheet.scss. +
++Las opciones +de Scss pueden ser ajustadas globalmente a través de las +configuraciones de Sinatra, ver Opciones y +Configuraciones, y reemplazadas individualmente. +
++ set :scss, :style => :compact # el estilo por defecto de Sass es :nested + + get '/stylesheet.css' do + scss :stylesheet, :style => :expanded # reemplazado + end ++
+La gem/librería less es necesaria para renderizar plantillas Less: +
++ ## Vas a necesitar requerir less en tu app + require 'less' + + get '/stylesheet.css' do + less :stylesheet + end ++
+Renderiza ./views/stylesheet.less. +
++La gem/librería liquid es necesaria para renderizar plantillas Liquid: +
++ ## Vas a necesitar requerir liquid en tu app + require 'liquid' + + get '/' do + liquid :index + end ++
+Renderiza ./views/index.liquid. +
++Como no vas a poder llamar a métodos de Ruby (excepto a yield) +desde una plantilla Liquid, casi siempre vas a querer pasarle locales: +
++ liquid :index, :locals => { :clave => 'valor' } ++
+La gem/librería rdiscount es necesaria para renderizar plantillas +Markdown: +
++ ## Vas a necesitar requerir rdiscount en tu app + require "rdiscount" + + get '/' do + markdown :index + end ++
+Renderiza ./views/index.markdown (md y mkd +también son extensiones de archivo válidas). +
++No es posible llamar métodos desde markdown, ni pasarle locales. Por lo +tanto, generalmente vas a usarlo en combinación con otro motor de +renderizado: +
++ erb :resumen, :locals => { :texto => markdown(:introduccion) } ++
+Tené en cuenta que también podés llamar al método markdown desde otras +plantillas: +
++ %h1 Hola Desde Haml! + %p= markdown(:saludos) ++
+La gem/librería RedCloth es necesaria para renderizar plantillas Textile: +
++ ## Vas a necesitar requerir redcloth en tu app + require "redcloth" + + get '/' do + textile :index + end ++
+Renderiza ./views/index.textile. +
++No es posible llamar métodos desde textile, ni pasarle locales. Por lo +tanto, generalmente vas a usarlo en combinación con otro motor de +renderizado: +
++ erb :resumen, :locals => { :texto => textile(:introduccion) } ++
+Tené en cuenta que también podés llamar al método textile desde otras +plantillas: +
++ %h1 Hola Desde Haml! + %p= textile(:saludos) ++
+La gem/librería RDoc es necesaria para renderizar plantillas RDoc: +
++ ## Vas a necesitar requerir rdoc en tu app + require "rdoc" + + get '/' do + rdoc :index + end ++
+Renderiza ./views/index.rdoc. +
++No es posible llamar métodos desde rdoc, ni pasarle locales. Por lo tanto, +generalmente vas a usarlo en combinación con otro motor de renderizado: +
++ erb :resumen, :locals => { :texto => rdoc(:introduccion) } ++
+Tené en cuenta que también podés llamar al método rdoc desde otras +plantillas: +
++ %h1 Hola Desde Haml! + %p= rdoc(:saludos) ++
+La gem/librería radius es necesaria para renderizar plantillas Radius: +
++ ## Vas a necesitar requerir radius en tu app + require 'radius' + + get '/' do + radius :index + end ++
+Renderiza ./views/index.radius. +
++Como no vas a poder llamar a métodos de Ruby (excepto a yield) +desde una plantilla Radius, casi siempre vas a querer pasarle locales: +
++ radius :index, :locals => { :clave => 'valor' } ++
+La gem/librería markaby es necesaria para renderizar plantillas Markaby: +
++ ## Vas a necesitar requerir markaby en tu app + require 'markaby' + + get '/' do + markaby :index + end ++
+Renderiza ./views/index.mab. +
++La gem/librería coffee-script y el binario `coffee` son necesarios para +renderizar plantillas CoffeeScript: +
++ ## Vas a necesitar requerir coffee-script en tu app + require 'coffee-script' + + get '/application.js' do + coffee :application + end ++
+Renderiza ./views/application.coffee. +
++ get '/' do + haml '%div.titulo Hola Mundo' + end ++
+Renderiza el template contenido en el string. +
++Las plantillas son evaluadas dentro del mismo contexto que los manejadores +de ruta. Las variables de instancia asignadas en los manejadores de ruta +son accesibles directamente por las plantillas: +
++ get '/:id' do + @foo = Foo.find(params[:id]) + haml '%h1= @foo.nombre' + end ++
+O es posible especificar un Hash de variables locales explícitamente: +
++ get '/:id' do + foo = Foo.find(params[:id]) + haml '%h1= foo.nombre', :locals => { :foo => foo } + end ++
+Esto es usado típicamente cuando se renderizan plantillas como parciales +desde adentro de otras plantillas. +
++Las plantillas pueden ser definidas al final del archivo fuente: +
++ require 'rubygems' + require 'sinatra' + + get '/' do + haml :index + end + + __END__ + + @@ layout + %html + = yield + + @@ index + %div.titulo Hola mundo!!!!! ++
+NOTA: únicamente las plantillas inline definidas en el archivo fuente que +requiere sinatra son cargadas automáticamente. Llamá enable +:inline_templates explícitamente si tenés plantillas inline en otros +archivos fuente. +
++Las plantillas también pueden ser definidas usando el método top-level +template: +
++ template :layout do + "%html\n =yield\n" + end + + template :index do + '%div.titulo Hola Mundo!' + end + + get '/' do + haml :index + end ++
+Si existe una plantilla con el nombre “layout”, va a ser usada +cada vez que una plantilla es renderizada. Podés desactivar los layouts +pasando :layout => false. +
++ get '/' do + haml :index, :layout => !request.xhr? + end ++
+Usá el método top-level helpers para definir métodos ayudantes +que pueden ser utilizados dentro de los manejadores de rutas y las +plantillas: +
++ helpers do + def bar(nombre) + "#{nombre}bar" + end + end + + get '/:nombre' do + bar(params[:nombre]) + end ++
+Los filtros before son evaluados antes de cada petición dentro del mismo +contexto que las rutas serán evaluadas y pueden modificar la petición y +la respuesta. Las variables de instancia asignadas en los filtros son +accesibles por las rutas y las plantillas: +
++ before do + @nota = 'Hey!' + request.path_info = '/foo/bar/baz' + end + + get '/foo/*' do + @nota #=> 'Hey!' + params[:splat] #=> 'bar/baz' + end ++
+Los filtros after son evaluados después de cada petición dentro del mismo +contexto y también pueden modificar la petición y la respuesta. Las +variables de instancia asignadas en los filtros before y rutas son +accesibles por los filtros after: +
++ after do + puts response.status + end ++
+Los filtros aceptan un patrón opcional, que cuando está presente causa +que los mismos sean evaluados únicamente si el path de la petición +coincide con ese patrón: +
++ before '/protegido/*' do + autenticar! + end + + after '/crear/:slug' do |slug| + session[:ultimo_slug] = slug + end ++
+Para detener inmediatamente una petición dentro de un filtro o una ruta +usá: +
++ halt ++
+También podés especificar el estado: +
++ halt 410 ++
+O el cuerpo: +
++ halt 'esto va a ser el cuerpo' ++
+O los dos: +
++ halt 401, 'salí de acá!' ++
+Con cabeceras: +
++ halt 402, { 'Content-Type' => 'text/plain' }, 'venganza' ++
+Una ruta puede pasarle el procesamiento a la siguiente ruta que coincida +con la petición usando pass: +
++ get '/adivina/:quien' do + pass unless params[:quien] == 'Franco' + 'Adivinaste!' + end + + get '/adivina/*' do + 'Erraste!' + end ++
+Se sale inmediatamente del bloque de la ruta y se le pasa el control a la +siguiente ruta que coincida. Si no coincide ninguna ruta, se devuelve un +404. +
++El objeto de la petición entrante puede ser accedido desde el nivel de la +petición (filtros, rutas y manejadores de errores) a través del método +`request`: +
++ # app corriendo en http://ejemplo.com/ejemplo + get '/foo' do + request.body # cuerpo de la petición enviado por el cliente (ver más abajo) + request.scheme # "http" + request.script_name # "/ejemplo" + request.path_info # "/foo" + request.port # 80 + request.request_method # "GET" + request.query_string # "" + request.content_length # longitud de request.body + request.media_type # tipo de medio de request.body + request.host # "ejemplo.com" + request.get? # verdadero (hay métodos análogos para los otros verbos) + request.form_data? # falso + request["UNA_CABECERA"] # valor de la cabecera UNA_CABECERA + request.referer # la referencia del cliente o '/' + request.user_agent # user agent (usado por la condición :agent) + request.cookies # hash de las cookies del browser + request.xhr? # es una petición ajax? + request.url # "http://ejemplo.com/ejemplo/foo" + request.path # "/ejemplo/foo" + request.ip # dirección IP del cliente + request.secure? # falso + requuest.env # hash de entorno directamente entregado por Rack + end ++
+Algunas opciones, como script_name o path_info pueden +también ser escritas: +
++ before { request.path_info = "/" } + + get "/" do + "todas las peticiones llegan acá" + end ++
+El objeto request.body es una instancia de IO o StringIO: +
++ post "/api" do + request.body.rewind # en caso de que alguien ya lo haya leído + datos = JSON.parse request.body.read + "Hola #{datos['nombre']}!" + end ++
+Ejecutar una vez, en el inicio, en cualquier entorno: +
++ configure do + ... + end ++
+Ejecutar únicamente cuando el entorno (la variable de entorno RACK_ENV) es +:production: +
++ configure :production do + ... + end ++
+Ejecutar cuando el entorno es :production o :test: +
++ configure :production, :test do + ... + end ++
+Los manejadores de errores se ejecutan dentro del mismo contexto que las +rutas y los filtros before, lo que significa que podés usar, por ejemplo, +haml, erb, halt, etc. +
++Cuando se eleva una excepción Sinatra::NotFound, o el código de +estado de la respuesta es 404, el manejador not_found es invocado: +
++ not_found do + 'No existo' + end ++
+El manejador error es invocado cada vez que una excepción es +elevada desde un bloque de ruta o un filtro. El objeto de la excepción se +puede obtener de la variable Rack sinatra.error: +
++ error do + 'Disculpá, ocurrió un error horrible - ' + env['sinatra.error'].name + end ++
+Errores personalizados: +
++ error MiErrorPersonalizado do + 'Lo que pasó fue...' request.env['sinatra.error'].message + end ++
+Entonces, si pasa esto: +
++ get '/' do + raise MiErrorPersonalizado, 'algo malo' + end ++
+Obtenés esto: +
++ Lo que pasó fue... algo malo ++
+También, podés instalar un manejador de errores para un código de +estado: +
++ error 403 do + 'Acceso prohibido' + end + + get '/secreto' do + 403 + end ++
+O un rango: +
++ error 400..510 do + 'Boom' + end ++
+Sinatra instala manejadores not_found y error especiales +cuando se ejecuta dentro del entorno de desarrollo +“development”. +
++Cuando usás send_file o archivos estáticos tal vez tengas tipos +mime que Sinatra no entiende. Usá mime_type para registrarlos a +través de la extensión de archivo: +
++ mime_type :foo, 'text/foo' ++
+También lo podés usar con el ayudante content_type: +
++ content_type :foo ++
+Sinatra corre sobre Rack, una +interfaz minimalista que es un estándar para frameworks webs escritos en +Ruby. Una de las capacidades más interesantes de Rack para los +desarrolladores de aplicaciones es el soporte de “middleware” +— componentes que se ubican entre el servidor y tu aplicación, +supervisando y/o manipulando la petición/respuesta HTTP para proporcionar +varios tipos de funcionalidades comunes. +
++Sinatra hace muy sencillo construir tuberías de Rack middleware a través +del método top-level use: +
++ require 'sinatra' + require 'mi_middleware_personalizado' + + use Rack::Lint + use MiMiddlewarePersonalizado + + get '/hola' do + 'Hola Mundo' + end ++
+Las semánticas de use son idénticas a las definidas para el DSL +Rack::Builder +(más frecuentemente usado desde archivos rackup). Por ejemplo, el método +use acepta argumentos múltiples/variables así como bloques: +
++ use Rack::Auth::Basic do |nombre_de_usuario, password| + nombre_de_usuario == 'admin' && password == 'secreto' + end ++
+Rack es distribuido con una variedad de middleware estándar para logging, +debugging, enrutamiento URL, autenticación, y manejo de sesiones. Sinatra +usa muchos de estos componentes automáticamente de acuerdo a su +configuración para que típicamente no tengas que usarlas (con +use) explícitamente. +
++Las pruebas para las aplicaciones Sinatra pueden ser escritas utilizando +cualquier framework o librería de pruebas basada en Rack. Se recomienda +usar Rack::Test: +
++ require 'mi_app_sinatra' + require 'test/unit' + require 'rack/test' + + class MiAppTest < Test::Unit::TestCase + include Rack::Test::Methods + + def app + Sinatra::Application + end + + def test_mi_defecto + get '/' + assert_equal 'Hola Mundo!', last_response.body + end + + def test_con_parametros + get '/saludar', :name => 'Franco' + assert_equal 'Hola Frank!', last_response.body + end + + def test_con_entorno_rack + get '/', {}, 'HTTP_USER_AGENT' => 'Songbird' + assert_equal "Estás usando Songbird!", last_response.body + end + end ++
+NOTA: El módulo Sinatra::Test y la clase Sinatra::TestHarness están +deprecados a partir de la versión 0.9.2. +
++Definir tu aplicación en el top-level funciona bien para +micro-aplicaciones pero trae inconvenientes considerables a la hora de +construir componentes reutilizables como Rack middleware, Rails metal, +simple librerías con un componente de servidor, o incluso extensiones de +Sinatra. El DSL de top-level contamina el espacio de nombres de Object y +asume una configuración apropiada para micro-aplicaciones (por ejemplo, un +único archivo de aplicación, los directorios ./public y ./views, logging, +página con detalles de excepción, etc.). Ahí es donde Sinatra::Base +entra en el juego: +
++ require 'sinatra/base' + + class MiApp < Sinatra::Base + set :sessions, true + set :foo, 'bar' + + get '/' do + 'Hola Mundo!' + end + end ++
+La clase MiApp es un componente Rack independiente que puede actuar como +Rack middleware, una aplicación Rack, o Rails metal. Podés usar (con +use) o ejecutar (con run) esta clase desde un archivo +rackup config.ru; o, controlar un componente de servidor provisto +como una librería: +
++ MiApp.run! :host => 'localhost', :port => 9090 ++
+Las subclases de Sinatra::Base tienen disponibles exactamente los mismos +métodos que los provistos por el DSL de top-level. La mayoría de las +aplicaciones top-level se pueden convertir en componentes Sinatra::Base con +dos modificaciones: +
++Tu archivo debe requerir sinatra/base en lugar de +sinatra; de otra manera, todos los métodos del DSL de sinatra son +importados dentro del espacio de nombres principal. +
++Poné las rutas, manejadores de errores, filtros y opciones de tu +aplicación en una subclase de Sinatra::Base. +
++Sinatra::Base es una pizarra en blanco. La mayoría de las +opciones están desactivadas por defecto, incluyendo el servidor +incorporado. Mirá Opciones y +Configuraciones para detalles sobre las opciones disponibles y su +comportamiento. +
++Sinatra no solo es capaz de usar otro Rack middleware, sino que a su vez, +cualquier aplicación Sinatra puede ser agregada delante de un endpoint +Rack como middleware. Este endpoint puede ser otra aplicación Sinatra, o +cualquier aplicación basada en Rack (Rails/Ramaze/Camping/…). +
++ require 'sinatra/base' + + class PantallaDeLogin< Sinatra::Base + enable :session + + get('/login') { haml :login } + + post('/login') do + if params[:nombre] = 'admin' and params[:password] = 'admin' + session['nombre_de_usuario'] = params[:nombre] + else + redirect '/login' + end + end + end + + class MiApp < Sinatra::Base + # el middleware se ejecutará antes que los filtros + use PantallaDeLogin + + before do + unless session['nombre_de_usuario'] + halt "Acceso denegado, por favor <a href='/login'>iniciá sesión</a>." + end + end + + get('/') { "Hola #{session['nombre_de_usuario']}." } + end ++
+El ámbito en el que te encontrás determina que métodos y variables +están disponibles. +
++Cada aplicación Sinatra es una subclase de Sinatra::Base. Si estás usando +el DSL de top-level (require 'sinatra'), entonces esta clase es +Sinatra::Application, de otra manera es la subclase que creaste +explícitamente. Al nivel de la clase tenés métodos como `get` o +`before`, pero no podés acceder a los objetos `request` o `session`, ya +que hay una única clase de la aplicación para todas las peticiones. +
++Las opciones creadas utilizando `set` son métodos al nivel de la clase: +
++ class MiApp << Sinatra::Base + # Ey, estoy en el ámbito de la aplicación! + set :foo, 42 + foo # => 42 + + get '/foo' do + # Hey, ya no estoy en el ámbito de la aplicación! + end + end ++
+Tenés la ligadura al ámbito de la aplicación dentro de: +
++El cuerpo de la clase de tu aplicación +
++Métodos definidos por extensiones +
++El bloque pasado a `helpers` +
++Procs/bloques usados como el valor para `set` +
++Este ámbito puede alcanzarse de las siguientes maneras: +
++A través del objeto pasado a los bloques de configuración (configure +{ |c| ...}) +
++Llamando a `settings` desde dentro del ámbito de la petición +
++Para cada petición entrante, una nueva instancia de la clase de tu +aplicación es creada y todos los bloques de rutas son ejecutados en ese +ámbito. Desde este ámbito podés acceder a los objetos `request` y +`session` o llamar a los métodos de renderización como `erb` o `haml`. +Podés acceder al ámbito de la aplicación desde el ámbito de la +petición utilizando `settings`: +
++ class MiApp << Sinatra::Base + # Ey, estoy en el ámbito de la aplicación! + get '/definir_ruta/:nombre' do + # Ámbito de petición para '/definir_ruta/:nombre' + @valor = 42 + + settings.get("/#{params[:nombre]}") do + # Ámbito de petición para "/#{params[:nombre]}" + @valor # => nil (no es la misma petición) + end + + "Ruta definida!" + end + end ++
+Tenés la ligadura al ámbito de la petición dentro de: +
++bloques pasados a get/head/post/put/delete +
++filtros before/after +
++métodos ayudantes +
++plantillas/vistas +
++El ámbito de delegación solo reenvía métodos al ámbito de clase. De +cualquier manera, no se comporta 100% como el ámbito de clase porque no +tenés la ligadura de la clase: únicamente métodos marcados +explícitamente para delegación están disponibles y no compartís +variables/estado con el ámbito de clase (léase: tenés un `self` +diferente). Podés agregar delegaciones de método llamando a +Sinatra::Delegator.delegate :nombre_del_metodo. +
++Tenés la ligadura al ámbito de delegación dentro de: +
++La ligadura del top-level, si hiciste require "sinatra" +
++Un objeto extendido con el mixin `Sinatra::Delegator` +
++Pegale una mirada al código: acá está el Sinatra::Delegator +mixin que es incluido +en el espacio de nombres principal. +
++Las aplicaciones Sinatra pueden ser ejecutadas directamente: +
++ ruby miapp.rb [-h] [-x] [-e ENTORNO] [-p PUERTO] [-o HOST] [-s MANEJADOR] ++
+Las opciones son: +
++ -h # ayuda + -p # asigna el puerto (4567 es usado por defecto) + -o # asigna el host (0.0.0.0 es usado por defecto) + -e # asigna el entorno (development es usado por defecto) + -s # especifica el servidor/manejador rack (thin es usado por defecto) + -x # activa el mutex lock (está desactivado por defecto) ++
+Si querés usar el código de Sinatra más reciente, cloná el repositorio +localmente y ejecutá tu aplicación, asegurándote que el directorio +sinatra/lib esté en el LOAD_PATH: +
++ cd miapp + git clone git://github.com/sinatra/sinatra.git + ruby -Isinatra/lib miapp.rb ++
+Otra opción consiste en agregar el directorio sinatra/lib al +LOAD_PATH dentro de tu aplicación: +
++ $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib' + require 'rubygems' + require 'sinatra' + + get '/acerca-de' do + "Estoy usando la versión " + Sinatra::VERSION + end ++
+Para actualizar el código fuente de Sinatra en el futuro: +
++ cd miproyecto/sinatra + git pull ++
+Sito web del proyecto - +Documentación adicional, noticias, y enlaces a otros recursos. +
++Contribuyendo - +¿Encontraste un error?. ¿Necesitás ayuda?. ¿Tenés un parche?. +
++Twitter +
++IRC: #sinatra en freenode.net +
++Attention: Ce document correspond à la traduction de la version +anglaise et il n’est peut être plus à jour. +
++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: localhost:4567 +
++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 params: +
++ 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 params[:splat]. +
++ 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 ++
+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 ++
+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: [code retour (Fixnum), entêtes (Hash), +corps de réponse (répondant à #each)] +
++Un tableau de 2 élements: [code retour (Fixnum), corps de réponse +(répondant à #each)] +
++Un objet qui répond à #each 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 } ++
+Par défaut, le dossier ./public est utilisé pour servir les +fichiers statiques. Vous pouvez changer ce dossier pour un autre nom grâce +à l’option :public: +
++ set :public, File.dirname(__FILE__) + '/statique' ++
+Notez que le nom du dossier public n’est pas inclus dans l’URL. +Un fichier sous ./public/css/style.css est appelé avec +l’URL: exemple.com/css/style.css. +
++Par défaut, les templates sont cherchés dans le dossier ./views. +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 +:'sous_repertoire/template'). 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. +
++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: ./views/index.haml. +
++Les +options de Haml peuvent se manipuler directement avec la configuration +de Sinatra, voir Options et +Configuration, 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 ++
+ ## Chargez la bibliothèque erb dans votre application + require 'erb' + + get '/' do + erb :index + end ++
+Utilisera le template: ./views/index.erb +
++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: ./views/index.erubis +
++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: ./views/index.builder. +
++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: ./views/index.nokogiri. +
++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: ./views/stylesheet.sass. +
++Les +options de Sass peuvent se manipuler directement avec la configuration +de Sinatra, voir Options et +Configuration, 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 ++
+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 ./views/stylesheet.scss. +
++Les +options de Scss peuvent se manipuler directement avec la configuration +de Sinatra, voir Options et +Configuration, 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 ++
+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: ./views/stylesheet.less. +
++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 ./views/index.liquid. +
++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' } ++
+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 ./views/index.markdown (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) ++
+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 ./views/index.textile. +
++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) ++
+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 ./views/index.rdoc. +
++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) ++
+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 ./views/index.radius. +
++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' } ++
+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 ./views/index.mab. +
++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 ./views/application.coffee. +
++ 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. +
++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. +
++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 require +'sinatra' sont automatiquement chargés. Si vous avez des templates +dans d’autres fichiers source, il faut explicitement les déclarer +via: enable :inline_templates. +
++Les templates peuvent aussi être définis grâce à la méthode de haut +niveau template: +
++ 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 :layout => false. +
++ get '/' do + haml :index, :layout => !request.xhr? + end ++
+Utilisez la méthode de haut niveau helpers 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 ++
+Un filtre before 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 after 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 +before et dans le gestionnaire de route sont accessibles dans le +filtre after: +
++ 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 ++
+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' ++
+Une route peut passer le relais aux autres routes qui correspondent +également avec pass: +
++ 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é. +
++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 :production: +
++ configure :production do + ... + end ++
+Lancé si l’environnement est :production ou :test: +
++ configure :production, :test do + ... + end ++
+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 haml, erb, halt, etc. +
++Quand une exception Sinatra::NotFound est soulevée, ou que le +code retour est 404, le gestionnaire not_found est invoqué: +
++ not_found do + 'Pas moyen de trouver ce que vous cherchez' + end ++
+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 sinatra.error: +
++ 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 not_found et +error génériques lorsque vous êtes en environnement +development. +
++Quand vous utilisez send_file 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 ++
+Sinatra tourne avec Rack, 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 +(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. +
++Les tests pour Sinatra peuvent être écrit avec n’importe quelle +bibliothèque basée sur Rack. 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 . +
++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. +
++Sinatra::Base est plutôt épuré. La plupart des options sont +désactivées par défaut, ceci inclus le serveur. Voir Options et +Configuration pour plus de détails sur les options et leur +comportement. +
++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 ++
+Le contexte dans lequel vous êtes détermine les méthodes et variables +disponibles. +
++Toute application Sinatra correspond à une sous-classe de Sinatra::Base. +Si vous utilisez le DSL haut niveau (require 'sinatra'), 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` (configure { |c| +... }) +
++En utilisant `settings` dans le contexte de la requête +
++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 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 Sinatra::Delegator.delegate :method_name. +
++Vous avez le binding du contexte de délégation dans: +
++Le binding de haut niveau, si vous avez utilisé require +"sinatra" +
++Un objet qui inclut le module `Sinatra::Delegator` +
++Jetez un oeil pour vous faire une idée: voici le mixin Sinatra::Delegator +qui est inclus +dans l'espace de noms principal +
++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) ++
+Si vous voulez utiliser la toute dernière version de Sinatra, créez un +clone local et lancez votre application avec le dossier +sinatra/lib dans votre LOAD_PATH: +
++ cd mon_application + git clone git://github.com/sinatra/sinatra.git + ruby -Isinatra/lib mon_application.rb ++
+Alternativement, vous pouvez ajoutez le dossier sinatra/lib à +votre LOAD_PATH à 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 ++
+Site internet - Plus de +documentation, de news, et des liens vers d’autres ressources. +
++Contribuer - Vous avez +trouvé un bug? Besoin d’aide? Vous avez un patch? +
++Twitter +
++Mailing List +
++IRC: #sinatra sur freenode.net +
+Renders ./views/index.builder.
++The nokogiri gem/library is required to render nokogiri templates: +
++ ## You'll need to require nokogiri in your app + require 'nokogiri' + + get '/' do + nokogiri :index + end ++
+Renders ./views/index.nokogiri. +
The sass gem/library is required to render Sass templates: @@ -294,7 +308,6 @@
+The sass gem/library is required to render Scss templates: +
++ ## You'll need to require haml or sass in your app + require 'sass' + + get '/stylesheet.css' do + scss :stylesheet + end ++
+Renders ./views/stylesheet.scss. +
++Scss' +options can be set globally through Sinatra’s configurations, see +Options and +Configurations, and overridden on an individual basis. +
++ set :scss, :style => :compact # default Scss style is :nested + + get '/stylesheet.css' do + scss :stylesheet, :style => :expanded # overridden + end +
The less gem/library is required to render Less templates: @@ -325,13 +366,178 @@
Renders ./views/stylesheet.less.
++The liquid gem/library is required to render Liquid templates: +
++ ## You'll need to require liquid in your app + require 'liquid' + + get '/' do + liquid :index + end ++
+Renders ./views/index.liquid. +
++Since you cannot call Ruby methods (except for yield) from a +Liquid template, you almost always want to pass locals to it: +
++ liquid :index, :locals => { :key => 'value' } ++
+The rdiscount gem/library is required to render Markdown templates: +
++ ## You'll need to require rdiscount in your app + require "rdiscount" + + get '/' do + markdown :index + end ++
+Renders ./views/index.markdown (md and mkd are +also valid file extensions). +
++It is not possible to call methods from markdown, nor to pass locals to it. +You therefore will usually use it in combination with another rendering +engine: +
++ erb :overview, :locals => { :text => markdown(:introduction) } ++
+Note that you may also call the markdown method from within other +templates: +
++ %h1 Hello From Haml! + %p= markdown(:greetings) ++
+The RedCloth gem/library is required to render Textile templates: +
++ ## You'll need to require redcloth in your app + require "redcloth" + + get '/' do + textile :index + end ++
+Renders ./views/index.textile. +
++It is not possible to call methods from textile, nor to pass locals to it. +You therefore will usually use it in combination with another rendering +engine: +
++ erb :overview, :locals => { :text => textile(:introduction) } ++
+Note that you may also call the textile method from within other templates: +
++ %h1 Hello From Haml! + %p= textile(:greetings) ++
+The RDoc gem/library is required to render RDoc templates: +
++ ## You'll need to require rdoc in your app + require "rdoc" + + get '/' do + rdoc :index + end ++
+Renders ./views/index.rdoc. +
++It is not possible to call methods from rdoc, nor to pass locals to it. You +therefore will usually use it in combination with another rendering engine: +
++ erb :overview, :locals => { :text => rdoc(:introduction) } ++
+Note that you may also call the rdoc method from within other templates: +
++ %h1 Hello From Haml! + %p= rdoc(:greetings) ++
+The radius gem/library is required to render Radius templates: +
++ ## You'll need to require radius in your app + require 'radius' + + get '/' do + radius :index + end ++
+Renders ./views/index.radius. +
++Since you cannot call Ruby methods (except for yield) from a +Radius template, you almost always want to pass locals to it: +
++ radius :index, :locals => { :key => 'value' } ++
+The markaby gem/library is required to render Markaby templates: +
++ ## You'll need to require markaby in your app + require 'markaby' + + get '/' do + markaby :index + end ++
+Renders ./views/index.mab. +
++The coffee-script gem/library and the `coffee` binary are required to +render CoffeeScript templates: +
++ ## You'll need to require coffee-script in your app + require 'coffee-script' + + get '/application.js' do + coffee :application + end ++
+Renders ./views/application.coffee. +
get '/' do @@ -460,6 +666,19 @@+Filters
puts response.status end
+Filters optionally taking a pattern, causing them to be evaluated only if +the request path matches that pattern: +
++ before '/protected/*' do + authenticate! + end + + after '/create/:slug' do |slug| + session[:last_slug] = slug + end +
To immediately stop a request within a filter or route use: diff --git a/_includes/README.jp.html b/_includes/README.jp.html index 4a27ea03..2b762771 100644 --- a/_includes/README.jp.html +++ b/_includes/README.jp.html @@ -1,4 +1,8 @@ +
+注) +本文書は英語から翻訳したものであり、その内容が最新でない場合もあります。最新の情報はオリジナルの英語版を参照して下さい。 +
SinatraはRubyで下記のような最小労力で手早くウェブアプリケーションを作成するためのDSLです。
@@ -186,13 +190,27 @@./views/index.builderを表示します。
++鋸を使うには鋸ライブラリが必要です: +
++ ## 鋸を読み込みます + require 'nokogiri' + + get '/' do + nokogiri :index + end ++
+./views/index.nokogiriを表示します。 +
Sassテンプレートを使うにはsassライブラリが必要です: @@ -202,7 +220,6 @@
This page is also available in + English, + French, + Spanish and + Japanese.
+ +This page is also available in + English, + German, + French and + Japanese.
+ +This page is also available in + English, + German, + Spanish and + Japanese.
+ +This page is also available in -English.
+ English, + German, + French and + Spanish.This page is also available in + German, + French, + Spanish and Japanese.