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("

Sinatra

", "") - File.open(f.name, 'wb') { |io| io.write(html) } -end -CLEAN.include '_includes/README.html' - -# Build _includes/README.jp.html from RDoc -file '_includes/README.jp.html' => ['_sinatra/README.jp.rdoc', 'Rakefile'] do |f| - html = RDoc::Markup::ToHtml.new.convert(File.read("_sinatra/README.jp.rdoc")). - sub("

Sinatra

", "") - File.open(f.name, 'wb') { |io| io.write(html) } +readme do |fn| + file "_includes/#{fn}.html" => ["_sinatra/#{fn}.rdoc", "Rakefile"] do |f| + html = + RDoc::Markup::ToHtml.new. + convert(File.read("_sinatra/#{fn}.rdoc")). + sub("

Sinatra

", "") + File.open(f.name, 'wb') { |io| io.write(html) } + end + CLEAN.include "_includes/#{fn}.html" end -CLEAN.include '_includes/README.jp.html' desc 'Rebuild site under _site with Jekyll' task :jekyll do diff --git a/_includes/README.de.html b/_includes/README.de.html new file mode 100644 index 00000000..9813d047 --- /dev/null +++ b/_includes/README.de.html @@ -0,0 +1,1092 @@ + +

+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. +

+

Routen

+

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

+
+  get '/' do
+    .. zeige etwas ..
+  end
+
+  post '/' do
+    .. erstelle etwas ..
+  end
+
+  put '/' do
+    .. update etwas ..
+  end
+
+  delete '/' do
+    .. entferne etwas ..
+  end
+
+

+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
+
+

Bedingungen

+

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

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

+Andere mitgelieferte Bedingungen sind host_name und +provides: +

+
+  get '/', :host_name => /^admin\./ do
+    "Adminbereich, Zugriff verweigert!"
+  end
+
+  get '/', :provides => 'html' do
+    haml :index
+  end
+
+  get '/', :provides => ['rss', 'atom', 'xml'] do
+    builder :feed
+  end
+
+

+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
+
+

Rückgabewerte

+

+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: +

+ +

+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

+

+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. +

+

Views / Templates

+

+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. +

+

Haml-Templates

+

+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-Templates

+
+  ## erb muss eingebunden werden
+  require 'erb'
+
+  get '/' do
+    erb :index
+  end
+
+

+Dieser Code rendert ./views/index.erb. +

+

Erubis

+

+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. +

+

Builder-Templates

+

+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. +

+

Nokogiri-Templates

+

+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. +

+

Sass-Templates

+

+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
+
+

Scss-Templates

+

+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
+
+

Less-Templates

+

+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. +

+

Liquid-Templates

+

+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' }
+
+

Markdown-Templates

+

+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)
+
+

Textile-Templates

+

+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)
+
+

RDoc-Templates

+

+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)
+
+

Radius-Templates

+

+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' }
+
+

Markaby-Templates

+

+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. +

+

CoffeScript-Templates

+

+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. +

+

Inline-Templates

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

+Rendert den Inline-Template-String. +

+

Auf Variablen in Templates zugreifen

+

+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. +

+

Inline-Templates

+

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

+
+  require 'sinatra'
+
+  get '/' do
+    haml :index
+  end
+
+  __END__
+
+  @@ layout
+  %html
+    = yield
+
+  @@ index
+  %div.title Hallo Welt!!!!!
+
+

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

+

Benannte Templates

+

+Templates können auch mit der Top-Level template-Methode +definiert werden: +

+
+  template :layout do
+    "%html\n  =yield\n"
+  end
+
+  template :index do
+    '%div.title Hallo Welt!'
+  end
+
+  get '/' do
+    haml :index
+  end
+
+

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

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

Helfer

+

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

+
+  helpers do
+    def bar(name)
+      "#{name}bar"
+    end
+  end
+
+  get '/:name' do
+    bar(params[:name])
+  end
+
+

Filter

+

+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
+
+

Anhalten

+

+Zum sofortigen stoppen eines Request in einem Filter oder einer Route: +

+
+  halt
+
+

+Der Status kann beim stoppen auch angegeben werden: +

+
+  halt 410
+
+

+Oder auch den Response-Body: +

+
+  halt 'Hier steht der Body'
+
+

+Oder beides: +

+
+  halt 401, 'verschwinde!'
+
+

+Sogar mit Headers: +

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

Weiterspringen

+

+Eine Route kann mittels pass zu der nächsten passenden Route +springen: +

+
+  get '/raten/:wer' do
+    pass unless params[:wer] == 'Frank'
+    'Du hast mich!'
+  end
+
+  get '/raten/*' do
+    'Du hast mich 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. +

+

Konfiguration

+

+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
+
+

Fehlerbehandlung

+

+Error Handler laufen im selben Kontext wie Routen und Filter, was bedeutet, +dass alle Goodies wie haml, erb, halt, etc. +verwendet werden können. +

+

Nicht gefunden

+

+Wenn eine Sinatra::NotFound Exception geworfen wird oder der +Statuscode 404 ist, wird der not_found Handler ausgeführt: +

+
+  not_found do
+    'Seite kann nirgendwo gefunden werden.'
+  end
+
+

Fehler

+

+Der error Handler wird immer ausgeführt, wenn eine Exception in +einem 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. +

+

Mime-Types

+

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

+
+  mime_type :foo, 'text/foo'
+
+

+Es kann aber auch der content_type Helfer verwendet werden: +

+
+  content_type :foo
+
+

Rack Middleware

+

+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. +

+

Testen

+

+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. +

+

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

+

+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: +

+ +

+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. +

+

Kommandozeile

+

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

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

+Die Optionen sind: +

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

Der neueste Stand (The Bleeding Edge)

+

+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
+
+

Mehr

+ diff --git a/_includes/README.es.html b/_includes/README.es.html new file mode 100644 index 00000000..8f33c53c --- /dev/null +++ b/_includes/README.es.html @@ -0,0 +1,1325 @@ + +

+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 +

+

Rutas

+

+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
+
+

Condiciones

+

+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
+
+

Valores de retorno

+

+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: +

+ +

+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 }
+
+

Archivos estáticos

+

+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. +

+

Vistas / Plantillas

+

+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. +

+

Plantillas Haml

+

+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
+
+

Plantillas Erb

+
+  ## Vas a necesitar requerir erb en tu app
+  require 'erb'
+
+  get '/' do
+    erb :index
+  end
+
+

+Renderiza ./views/index.erb +

+

Erubis

+

+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 +

+

Plantillas Builder

+

+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. +

+

Plantillas Nokogiri

+

+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. +

+

Plantillas Sass

+

+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
+
+

Plantillas Scss

+

+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
+
+

Plantillas Less

+

+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. +

+

Plantillas Liquid

+

+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' }
+
+

Plantillas Markdown

+

+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)
+
+

Plantilla Textile

+

+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)
+
+

Plantillas RDoc

+

+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)
+
+

Plantillas Radius

+

+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' }
+
+

Plantillas Markaby

+

+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. +

+

Plantillas CoffeeScript

+

+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. +

+

Plantillas Inline

+
+  get '/' do
+    haml '%div.titulo Hola Mundo'
+  end
+
+

+Renderiza el template contenido en el string. +

+

Accediendo a Variables en Plantillas

+

+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. +

+

Plantillas Inline

+

+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. +

+

Plantillas Nombradas

+

+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
+
+

Ayudantes

+

+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
+
+

Filtros

+

+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
+
+

Interrupción

+

+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'
+
+

Paso

+

+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. +

+

Accediendo al objeto de la petición

+

+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
+
+

Configuración

+

+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
+
+

Manejo de errores

+

+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. +

+

No encontrado (Not Found)

+

+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
+
+

Error

+

+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”. +

+

Tipos Mime

+

+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
+
+

Rack Middleware

+

+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. +

+

Pruebas

+

+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. +

+

Sinatra::Base - Middleware, Librerías, y Aplicaciones Modulares

+

+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: +

+ +

+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. +

+

Utilizando Sinatra como Middleware

+

+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
+
+

Ámbitos y Ligaduras

+

+El ámbito en el que te encontrás determina que métodos y variables +están disponibles. +

+

Ámbito de Aplicación/Clase

+

+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: +

+ +

+Este ámbito puede alcanzarse de las siguientes maneras: +

+ +

Ámbito de Petición/Instancia

+

+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: +

+ +

Ámbito de Delegación

+

+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: +

+ +

+Pegale una mirada al código: acá está el Sinatra::Delegator +mixin que es incluido +en el espacio de nombres principal. +

+

Línea de comandos

+

+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)
+
+

A la vanguardia

+

+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
+
+

Más

+ diff --git a/_includes/README.fr.html b/_includes/README.fr.html new file mode 100644 index 00000000..0c3b312c --- /dev/null +++ b/_includes/README.fr.html @@ -0,0 +1,1292 @@ + +

+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 +

+

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 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
+
+

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: +

+ +

+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 ./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. +

+

Vues / Templates

+

+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. +

+

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: ./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
+
+

Templates Erb

+
+  ## Chargez la bibliothèque erb dans votre application
+  require 'erb'
+
+  get '/' do
+    erb :index
+  end
+
+

+Utilisera le template: ./views/index.erb +

+

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: ./views/index.erubis +

+

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: ./views/index.builder. +

+

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: ./views/index.nokogiri. +

+

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: ./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
+
+

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 ./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
+
+

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: ./views/stylesheet.less. +

+

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 ./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' }
+
+

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 ./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)
+
+

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 ./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)
+
+

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 ./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)
+
+

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 ./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' }
+
+

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 ./views/index.mab. +

+

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 ./views/application.coffee. +

+

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 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. +

+

Templates nommés

+

+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
+
+

Helpers

+

+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
+
+

Filtres

+

+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
+
+

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 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é. +

+

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 :production: +

+
+  configure :production do
+    ...
+  end
+
+

+Lancé si l’environnement est :production ou :test: +

+
+  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 haml, erb, halt, etc. +

+

Pas Trouvé

+

+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
+
+

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 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. +

+

Types Mime

+

+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
+
+

Les Middlewares Rack

+

+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. +

+

Tester

+

+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 . +

+

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: +

+ +

+ de noms. +

+ +

+ 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. +

+

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 (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: +

+ +

+Vous pouvez atteindre ce contexte (donc la classe) de la façon suivante: +

+ +

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: +

+ +

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 Sinatra::Delegator.delegate :method_name. +

+

+Vous avez le binding du contexte de délégation dans: +

+ +

+Jetez un oeil pour vous faire une idée: voici le mixin Sinatra::Delegator +qui est inclus +dans l'espace de noms principal +

+

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 +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
+
+

Mais encore

+ diff --git a/_includes/README.html b/_includes/README.html index 36f3793f..d341d98b 100644 --- a/_includes/README.html +++ b/_includes/README.html @@ -278,13 +278,27 @@

Builder Templates

require 'builder' get '/' do - content_type 'application/xml', :charset => 'utf-8' builder :index end

Renders ./views/index.builder.

+

Nokogiri Templates

+

+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. +

Sass Templates

The sass gem/library is required to render Sass templates: @@ -294,7 +308,6 @@

Sass Templates

require 'sass' get '/stylesheet.css' do - content_type 'text/css', :charset => 'utf-8' sass :stylesheet end @@ -312,10 +325,38 @@

Sass Templates

set :sass, :style => :compact # default Sass style is :nested get '/stylesheet.css' do - content_type 'text/css', :charset => 'utf-8' sass :stylesheet, :style => :expanded # overridden end +

Scss Templates

+

+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
+

Less Templates

The less gem/library is required to render Less templates: @@ -325,13 +366,178 @@

Less Templates

require 'less' get '/stylesheet.css' do - content_type 'text/css', :charset => 'utf-8' less :stylesheet end

Renders ./views/stylesheet.less.

+

Liquid Templates

+

+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' }
+
+

Markdown Templates

+

+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)
+
+

Textile Templates

+

+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)
+
+

RDoc Templates

+

+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)
+
+

Radius Templates

+

+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' }
+
+

Markaby Templates

+

+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. +

+

CoffeeScript Templates

+

+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. +

Inline Templates

   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
+

Halting

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 @@

Builder テンプレート

require 'builder' get '/' do - content_type 'application/xml', :charset => 'utf-8' builder :index end

./views/index.builderを表示します。

+

鋸 テンプレート

+

+鋸を使うには鋸ライブラリが必要です: +

+
+  ## 鋸を読み込みます
+  require 'nokogiri'
+
+  get '/' do
+    nokogiri :index
+  end
+
+

+./views/index.nokogiriを表示します。 +

Sass テンプレート

Sassテンプレートを使うにはsassライブラリが必要です: @@ -202,7 +220,6 @@

Sass テンプレート

require 'sass' get '/stylesheet.css' do - content_type 'text/css', :charset => 'utf-8' sass :stylesheet end @@ -222,7 +239,6 @@

Sass テンプレート

set :sass, {:style => :compact } # デフォルトのSass styleは :nested get '/stylesheet.css' do - content_type 'text/css', :charset => 'utf-8' sass :stylesheet, :sass_options => {:style => :expanded } # 上書き end diff --git a/intro-de.html b/intro-de.html new file mode 100644 index 00000000..c770c0ee --- /dev/null +++ b/intro-de.html @@ -0,0 +1,14 @@ +--- +title: "Sinatra: README (Japanese)" +layout: default +--- + +

This page is also available in + English, + French, + Spanish and + Japanese.

+ +

Einführung

+ +{% include README.de.html %} diff --git a/intro-es.html b/intro-es.html new file mode 100644 index 00000000..6c704f20 --- /dev/null +++ b/intro-es.html @@ -0,0 +1,14 @@ +--- +title: "Sinatra: README (Japanese)" +layout: default +--- + +

This page is also available in + English, + German, + French and + Japanese.

+ +

Introducción

+ +{% include README.es.html %} diff --git a/intro-fr.html b/intro-fr.html new file mode 100644 index 00000000..ba981647 --- /dev/null +++ b/intro-fr.html @@ -0,0 +1,14 @@ +--- +title: "Sinatra: README (Japanese)" +layout: default +--- + +

This page is also available in + English, + German, + Spanish and + Japanese.

+ +

Introduction

+ +{% include README.fr.html %} diff --git a/intro-jp.html b/intro-jp.html index a9266cd5..a805b8fd 100644 --- a/intro-jp.html +++ b/intro-jp.html @@ -4,7 +4,10 @@ ---

This page is also available in -English.

+ English, + German, + French and + Spanish.

始めよう

diff --git a/intro.html b/intro.html index 0d105fbd..87a9ff30 100644 --- a/intro.html +++ b/intro.html @@ -4,6 +4,9 @@ ---

This page is also available in + German, + French, + Spanish and Japanese.

Getting Started