[DRE-commits] [SCM] ruby-sinatra.git branch, master, updated. debian/1.2.6-1-4-g01870af

Youhei SASAKI uwabami at gfd-dennou.org
Mon Apr 16 20:33:19 UTC 2012


The following commit has been merged in the master branch:
commit d6567a004d03ee8606db22e7dcabb2ea535ab71a
Author: Youhei SASAKI <uwabami at gfd-dennou.org>
Date:   Fri Jan 6 02:33:27 2012 +0900

    New upstream version:1.3.2
    
    Signed-off-by: Youhei SASAKI <uwabami at gfd-dennou.org>

diff --git a/.yardopts b/.yardopts
new file mode 100644
index 0000000..880f196
--- /dev/null
+++ b/.yardopts
@@ -0,0 +1,4 @@
+--readme README.rdoc
+--title 'Sinatra API Documentation'
+--charset utf-8
+'lib/**/*.rb' - '*.rdoc'
diff --git a/CHANGES b/CHANGES
index 1d78b8d..129a325 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,205 @@
+= 1.3.2 / 2011-12-30
+
+ * Don't automatically add `Rack::CommonLogger` if `Rack::Server` is adding it,
+   too. (Konstantin Haase)
+
+ * Setting `logging` to `nil` will avoid setting up `Rack::NullLogger`.
+   (Konstantin Haase)
+
+ * Route specific params are now available in the block passed to #stream.
+   (Konstantin Haase)
+
+ * Fix bug where rendering a second template in the same request, after the
+   first one raised an exception, skipped the default layout. (Nathan Baum)
+
+ * Fix bug where parameter escaping got enabled when disabling a different
+   protection. (Konstantin Haase)
+
+ * Fix regression: Filters without a pattern may now again manipulate the params
+   hash. (Konstantin Haase)
+
+ * Added examples directory. (Konstantin Haase)
+
+ * Improved documentation. (Gabriel Andretta, Markus Prinz, Erick Zetta, Just
+   Lest, Adam Vaughan, Aleksander Dąbrowski)
+
+ * Improved MagLev support. (Tim Felgentreff)
+
+= 1.3.1 / 2011-10-05
+
+ * Support adding more than one callback to the stream object. (Konstantin
+   Haase)
+
+ * Fix for infinite loop when streaming on 1.9.2 with Thin from a modular
+   application (Konstantin Haase)
+
+= 1.3.0 / 2011-09-30
+
+ * Added `stream` helper method for easily creating streaming APIs, Server
+   Sent Events or even WebSockets. See README for more on that topic.
+   (Konstantin Haase)
+
+ * If a HTTP 1.1 client is redirected from a different verb than GET, use 303
+   instead of 302 by default. You may still pass 302 explicitly. Fixes AJAX
+   redirects in Internet Explorer 9 (to be fair, everyone else is doing it
+   wrong and IE is behaving correct). (Konstantin Haase)
+
+ * Added support for HTTP PATCH requests. (Konstantin Haase)
+
+ * Use rack-protection to defend against common opportunistic attacks.
+   (Josh Lane, Jacob Burkhart, Konstantin Haase)
+
+ * Support for Creole templates, Creole is a standardized wiki markup,
+   supported by many wiki implementations. (Konstanin Haase)
+
+ * The `erubis` method has been deprecated. If Erubis is available, Sinatra
+   will automatically use it for rendering ERB templates. `require 'erb'`
+   explicitly to prevent that behavior. (Magnus Holm, Ryan Tomayko, Konstantin
+   Haase)
+
+ * Patterns now match against the escaped URLs rather than the unescaped
+   version. This makes Sinatra confirm with RFC 2396 section 2.2 and RFC 2616
+   section 3.2.3 (escaped reserved characters should not be treated like the
+   unescaped version), meaning that "/:name" will also match `/foo%2Fbar`, but
+   not `/foo/bar`. To avoid incompatibility, pattern matching has been
+   adjusted. Moreover, since we do no longer need to keep an unescaped version
+   of path_info around, we handle all changes to `env['PATH_INFO']` correctly.
+   (Konstantin Haase)
+
+ * `settings.app_file` now defaults to the file subclassing `Sinatra::Base` in
+   modular applications. (Konstantin Haase)
+
+ * Set up `Rack::Logger` or `Rack::NullLogger` depending on whether logging
+   was enabled or not. Also, expose that logger with the `logger` helper
+   method. (Konstantin Haase)
+
+ * The sessions setting may be an options hash now. (Konstantin Haase)
+
+ * Important: Ruby 1.8.6 support has been dropped. This version also depends
+   on at least Rack 1.3.0. This means that it is incompatible with Rails prior
+   to 3.1.0. Please use 1.2.x if you require an earlier version of Ruby or
+   Rack, which we will continue to supply with bug fixes. (Konstantin Haase)
+
+ * Renamed `:public` to `:public_folder` to avoid overriding Ruby's built-in
+   `public` method/keyword. `set(:public, ...)` is still possible but shows a
+   warning. (Konstantin Haase)
+
+ * It is now possible to use a different target class for the top level DSL
+   (aka classic style) than `Sinatra::Application` by setting
+   `Delegator.target`. This was mainly introduced to ease testing. (Konstantin
+   Haase)
+
+ * Error handlers defined for an error class will now also handle subclasses
+   of that class, unless more specific error handlers exist. (Konstantin
+   Haase)
+
+ * Error handling respects Exception#code, again. (Konstantin Haase)
+
+ * Changing a setting will merge hashes: `set(:x, :a => 1); set(:x :b => 2)`
+   will result in `{:a => 1, :b => 2}`. Use `set(:x, {:a => 1}, true)` to
+   avoid this behavior. (Konstantin Haase)
+
+ * Added `request.accept?` and `request.preferred_type` to ease dealing with
+   `Accept` headers. (Konstantin Haase)
+
+ * Added `:static_cache_control` setting to automatically set cache control
+   headers to static files. (Kenichi Nakamura)
+
+ * Added `informal?`, `success?`, `redirect?`, `client_error?`,
+   `server_error?` and `not_found?` helper methods to ease dealing with status
+   codes. (Konstantin Haase)
+
+ * Uses SecureRandom to generate default session secret. (Konstantin Haase)
+
+ * The `attachment` helper will set Content-Type (if it hasn't been set yet)
+   depending on the supplied file name. (Vasiliy Ermolovich)
+
+ * Conditional requests on `etag` helper now work properly for unsafe HTTP
+   methods. (Matthew Schinckel, Konstantin Haase)
+
+ * The `last_modified` helper does not stop execution and change the status code
+   if the status code is something different than 200. (Konstantin Haase)
+
+ * Added support for If-Unmodified-Since header. (Konstantin Haase)
+
+ * `Sinatra::Base.run!` now prints to stderr rather than stdout. (Andrew
+   Armenia)
+
+ * `Sinatra::Base.run!` takes a block allowing access to the Rack handler.
+   (David Waite)
+
+ * Automatic `app_file` detection now works in directories containing brackets
+   (Konstantin Haase)
+
+ * Exception objects are now passed to error handlers. (Konstantin Haase)
+
+ * Improved documentation. (Emanuele Vicentini, Peter Higgins, Takanori
+   Ishikawa, Konstantin Haase)
+
+ * Also specify charset in Content-Type header for JSON. (Konstantin Haase)
+
+ * Rack handler names will not be converted to lower case internally, this
+   allows you to run Sinatra with custom Rack handlers, like Kirk or Mongrel2.
+   Example: `ruby app.rb -s Mongrel2` (Konstantin Haase)
+
+ * Ignore `to_ary` on response bodies. Fixes compatibility to Rails 3.1.
+   (Konstantin Haase)
+
+ * Middleware setup is now distributed across multiple methods, allowing
+   Sinatra extensions to easily hook into the setup process. (Konstantin
+   Haase)
+
+ * Internal refactoring and minor performance improvements. (Konstantin Haase)
+
+ * Move Sinatra::VERSION to separate file, so it can be checked without
+   loading Sinatra. (Konstantin Haase)
+
+ * Command line options now complain if value passed to `-p` is not a valid
+   integer. (Konstantin Haase)
+
+ * Fix handling of broken query params when displaying exceptions. (Luke
+   Jahnke)
+
+= 1.2.8 (backports release) / 2011-12-30
+
+Backported from 1.3.2:
+
+* Fix bug where rendering a second template in the same request after the
+  first one raised an exception skipped the default layout (Nathan Baum)
+
+= 1.2.7 (backports release) / 2011-09-30
+
+Custom changes:
+
+ * Fix Ruby 1.8.6 issue with Accept header parsing. (Konstantin Haase)
+
+Backported from 1.3.0:
+
+ * Ignore `to_ary` on response bodies. Fixes compatibility to Rails 3.1.
+   (Konstantin Haase)
+
+ * `Sinatra.run!` now prints to stderr rather than stdout. (Andrew Armenia)
+
+ * Automatic `app_file` detection now works in directories containing brackets
+   (Konstantin Haase)
+
+ * Improved documentation. (Emanuele Vicentini, Peter Higgins, Takanori
+   Ishikawa, Konstantin Haase)
+
+ * Also specify charset in Content-Type header for JSON. (Konstantin Haase)
+
+ * Rack handler names will not be converted to lower case internally, this
+   allows you to run Sinatra with custom Rack handlers, like Kirk or Mongrel2.
+   Example: `ruby app.rb -s Mongrel2` (Konstantin Haase)
+
+ * Fix uninitialized instance variable warning. (David Kellum)
+
+ * Command line options now complain if value passed to `-p` is not a valid
+   integer. (Konstantin Haase)
+
+ * Fix handling of broken query params when displaying exceptions. (Luke
+   Jahnke)
+
 = 1.2.6 / 2011-05-01
 
  * Fix broken delegation, backport delegation tests from Sinatra 1.3.
@@ -130,7 +332,7 @@
  * Sinatra now ships with a Gemfile for development dependencies, since it eases
    supporting different platforms, like JRuby. (Konstantin Haase)
 
-= 1.1.4 / 2011-04-13
+= 1.1.4 (backports release) / 2011-04-13
 
  * Compatible with Tilt 1.3. (Konstantin Haase)
 
diff --git a/Gemfile b/Gemfile
index 383eba1..9cbb80a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,5 +1,5 @@
 # Why use bundler?
-# Well, not all development dependencies install on all rubies. Moreover, `gem 
+# Well, not all development dependencies install on all rubies. Moreover, `gem
 # install sinatra --development` doesn't work, as it will also try to install
 # development dependencies of our dependencies, and those are not conflict free.
 # So, here we are, `bundle install`.
@@ -7,52 +7,71 @@
 # If you have issues with a gem: `bundle install --without-coffee-script`.
 
 RUBY_ENGINE = 'ruby' unless defined? RUBY_ENGINE
-TILT_REPO = "git://github.com/rtomayko/tilt.git"
-
 source :rubygems unless ENV['QUICK']
 gemspec
 
 gem 'rake'
 gem 'rack-test', '>= 0.5.6'
+gem 'ci_reporter', :group => :ci
 
 # Allows stuff like `tilt=1.2.2 bundle install` or `tilt=master ...`.
 # Used by the CI.
-tilt = (ENV['tilt'] || 'stable').dup
-tilt.sub! 'tilt-', ''
-if tilt != 'stable'
-  tilt = {:git => TILT_REPO, :branch => tilt} unless tilt =~ /(\d+\.)+\d+/
-  gem 'tilt', tilt
+github = "git://github.com/%s.git"
+repos  = {'tilt' => github % "rtomayko/tilt", 'rack' => github % "rack/rack"}
+
+%w[tilt rack].each do |lib|
+  dep = case ENV[lib]
+        when 'stable', nil then nil
+        when /(\d+\.)+\d+/ then "~> " + ENV[lib].sub("#{lib}-", '')
+        else {:git => repos[lib], :branch => dep}
+        end
+  gem lib, dep
 end
 
-gem 'haml', '>= 3.0', :group => 'haml'
-gem 'builder', :group => 'builder'
-gem 'erubis', :group => 'erubis'
-gem 'less', :group => 'less'
-gem 'liquid', :group => 'liquid'
-gem 'nokogiri', :group => 'nokogiri'
-gem 'slim', :group => 'slim'
-gem 'RedCloth', :group => 'redcloth'
+gem 'haml', '>= 3.0'
+gem 'sass'
+gem 'builder'
+gem 'erubis'
+gem 'liquid'
+gem 'slim', '~> 1.0'
+gem 'temple', '!= 0.3.3'
+gem 'coffee-script', '>= 2.0'
+gem 'rdoc'
+gem 'kramdown'
+gem 'maruku'
+gem 'creole'
+gem 'markaby'
+gem 'radius'
 
+if RUBY_ENGINE == 'jruby'
+  gem 'nokogiri', '!= 1.5.0'
+  gem 'jruby-openssl'
+else
+  gem 'nokogiri'
+end
 
-if RUBY_VERSION > '1.8.6'
-  gem 'coffee-script', '>= 2.0', :group => 'coffee-script'
-  gem 'rdoc', :group => 'rdoc'
+if RUBY_ENGINE == "ruby"
+  gem 'less', '~> 2.0'
 else
-  gem 'rack', '~> 1.1.0'
+  gem 'less', '~> 1.0'
 end
 
-platforms :ruby do
-  gem 'rdiscount', :group => 'rdiscount'
+unless RUBY_ENGINE == 'jruby' && JRUBY_VERSION < "1.6.1" && !ENV['TRAVIS']
+  # C extensions
+  gem 'rdiscount'
+  platforms(:ruby_18) { gem 'redcarpet' }
+  gem 'RedCloth' unless RUBY_ENGINE == "macruby"
+
+  ## bluecloth is broken
+  #gem 'bluecloth'
 end
 
 platforms :ruby_18, :jruby do
-  gem 'json', :group => 'coffee-script'
-  gem 'markaby', :group => 'markaby'
-  gem 'radius', :group => 'radius'
+  gem 'json'
 end
 
 platforms :mri_18 do
   # bundler platforms are broken
-  next unless RUBY_ENGINE == 'ruby'
-  gem 'rcov', :group => 'rcov'
+  next if RUBY_ENGINE != 'ruby' or RUBY_VERSION > "1.8"
+  gem 'rcov'
 end
diff --git a/README.de.rdoc b/README.de.rdoc
index 3993687..a1a78a0 100644
--- a/README.de.rdoc
+++ b/README.de.rdoc
@@ -22,7 +22,6 @@ Die Seite kann nun unter http://localhost:4567 betrachtet werden.
 Es wird empfohlen, den Thin-Server via <tt>gem install thin</tt> zu 
 installieren, den Sinatra dann, soweit vorhanden, automatisch verwendet.
 
-
 == Routen
 
 In Sinatra wird eine Route durch eine HTTP-Methode und ein URL-Muster
@@ -45,7 +44,7 @@ definiert. Jeder dieser Routen wird ein Ruby-Block zugeordnet:
   end
   
   options '/' do
-    .. lege etwas fest ..
+    .. zeige, was wir können ..
   end
 
 Die Routen werden in der Reihenfolge durchlaufen, in der sie definiert wurden.
@@ -79,6 +78,12 @@ Routen-Muster können auch mit Splat- oder Wildcard-Parametern über das
     params[:splat] # => ["pfad/zu/datei", "xml"]
   end
 
+Oder mit Block-Parametern:
+
+  get '/download/*.*' do |pfad, endung|
+    [pfad, endung] # => ["Pfad/zu/Datei", "xml"]
+  end
+  
 Routen mit regulären Ausdrücken sind auch möglich:
 
   get %r{/hallo/([\w]+)} do
@@ -91,6 +96,16 @@ Und auch hier können Block-Parameter genutzt werden:
     "Hallo, #{c}!"
   end
 
+Routen-Muster können auch mit optionalen Parametern ausgestattet werden:
+
+  get '/posts.?:format?' do
+    # passt auf "GET /posts" sowie jegliche Erweiterung 
+    # wie "GET /posts.json", "GET /posts.xml" etc.
+  end
+
+Anmerkung: Solange man den sog. Path Traversal Attack-Schutz nicht deaktiviert
+(siehe weiter unten), kann es sein, dass der Request-Pfad noch vor dem Abgleich
+mit den Routen modifiziert wird.
 
 === Bedingungen
 
@@ -132,6 +147,24 @@ Es können auch andere Bedingungen relativ einfach hinzugefügt werden:
     "Tut mir leid, verloren."
   end
 
+Bei Bedingungen, die mehrere Werte annehmen können, sollte ein Splat verwendet 
+werden:
+
+  set(:auth) do |*roles|   # <- hier kommt der Splat ins Spiel
+    condition do
+      unless logged_in? && roles.any? {|role| current_user.in_role? role }
+        redirect "/login/", 303 
+      end
+    end
+  end
+
+  get "/mein/account/", :auth => [:user, :admin] do
+    "Mein Account"
+  end
+  
+  get "/nur/admin/", :auth => :admin do
+    "Nur Admins dürfen hier rein!"
+  end
 
 === Rückgabewerte
 
@@ -162,6 +195,8 @@ Damit lässt sich relativ einfach Streaming implementieren:
 
     get('/') { Stream.new }
 
+Ebenso kann die +stream+-Helfer-Methode (s.u.) verwendet werden, die Streaming
+direkt in die Route integriert.
 
 === Eigene Routen-Muster 
 
@@ -205,19 +240,20 @@ Oder unter Verwendung eines negativen look ahead:
     # ...
   end
 
-
 == Statische Dateien
 
 Statische Dateien werden aus dem <tt>./public</tt>-Ordner ausgeliefert. Es ist
-möglich, einen anderen Ort zu definieren, indem man die <tt>:public</tt>-Option
-setzt:
+möglich, einen anderen Ort zu definieren, indem man die
+<tt>:public_folder</tt>-Option setzt:
 
-  set :public, File.dirname(__FILE__) + '/static'
+  set :public_folder, File.dirname(__FILE__) + '/static'
 
 Zu beachten ist, dass der Ordnername public nicht Teil der URL ist. Die Datei
 <tt>./public/css/style.css</tt> ist unter
 <tt>http://example.com/css/style.css</tt> zu finden.
 
+Um den <tt>Cache-Control</tt>-Header mit Informationen zu versorgen, verwendet
+man die <tt>:static_cache_control</tt>-Einstellung (s.u.).
 
 == Views/Templates
 
@@ -227,425 +263,213 @@ werden:
 
   set :views, File.dirname(__FILE__) + '/templates'
 
-Eine wichtige Sache, die man sich hierbei merken sollte, ist, dass man immer 
-mit Symbols auf Templates verweisen sollte, auch wenn sich ein Template in 
-einem Unterordner befindet (in diesen Fall <tt>:'subdir/template'</tt>).
-Rendering-Methoden 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 <tt>./views/index.haml</tt>.
-
-{Haml-Optionen}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options]
-können global durch die Sinatra-Konfiguration gesetzt werden,
-siehe {Optionen und Konfiguration}[http://www.sinatrarb.com/configuration.html],
-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 <tt>./views/index.erb</tt>.
-
-
-=== 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 <tt>./views/index.erubis</tt>.
-
-Es ist auch möglich, Erb durch Erubis zu ersetzen:
-
-  require 'erubis'
-  Tilt.register :erb, Tilt[:erubis]
-  
-  get '/' do
-    erb :index
-  end
-
-Dieser Code rendert ebenfalls <tt>./views/index.erb</tt>.
-
-
-=== Builder-Templates
-
-Das +builder+-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 <tt>./views/index.builder</tt>.
-
-
-=== 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 <tt>./views/index.nokogiri</tt>.
-
-
-=== Sass-Templates
-
-Das +haml+- oder +sass+-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 <tt>./views/stylesheet.sass</tt>.
-
-{Sass-Optionen}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
-können global durch die Sinatra-Konfiguration gesetzt werden, siehe 
-{Optionen und Konfiguration}[http://www.sinatrarb.com/configuration.html],
-und individuell überschrieben werden.
+Es ist zu beachten, dass immer mit Symbolen auf Templates verwiesen werden 
+muss, auch dann, wenn sie sich in einem Unterordner befinden:
 
-  set :sass, :style => :compact # Standard Sass-Style ist :nested
+  haml :'unterverzeichnis/template'
 
-  get '/stylesheet.css' do
-    sass :stylesheet, :style => :expanded # überschrieben
-  end
-
-
-=== SCSS-Templates
-
-Das +haml+- oder +sass+-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 <tt>./views/stylesheet.scss</tt>.
-
-{SCSS-Optionen}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
-können global durch die Sinatra-Konfiguration gesetzt werden, siehe 
-{Optionen und Konfiguration}[http://www.sinatrarb.com/configuration.html], 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 <tt>./views/stylesheet.less</tt>.
-
-
-=== 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 <tt>./views/index.liquid</tt>.
-
-Da aus Liquid-Templates heraus keine Methoden (abgesehen von +yield+) 
-aufgerufen werden können, ist es möglich, +locals+ zu ü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 <tt>./views/index.markdown</tt> (+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)
-
-Da man Ruby aus Markdown heraus nicht aufrufen kann, ist es nicht möglich, 
-Layouts zu verwenden, die in Markdown geschrieben sind. Es ist aber möglich, 
-einen anderen Renderer für das Template zu verwenden als für das Layout, indem
-man die <tt>:layout_engine</tt>-Option angibt:
-
-  get '/' do
-    markdown :index, :layout_engine => :erb
-  end
-
-Das wird <tt>./views/index.md</tt> mit <tt>./views/layout.erb</tt> als Layout
-rendern.
-
-Denk daran, dass solche Einstellungen auch global gesetzt werden können:
-
-  set :markdown, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    markdown :index
-  end
-
-Das wird <tt>./views/index.md</tt> (und jedes andere Markdown-Template) mit
-<tt>./views/post.haml</tt> als Layout rendern.
-
-Ebenso ist es möglich, Markdown mit BlueCloth anstelle von RDiscount zu parsen:
-
-  require 'bluecloth'
-  
-  Tilt.register 'markdown', BlueClothTemplate
-  Tilt.register 'mkd',      BlueClothTemplate
-  Tilt.register 'md',       BlueClothTemplate
-  
-  get '/' do
-    markdown :index
-  end
+Rendering-Methoden rendern jeden String direkt.
 
-Das sollte <tt>./views/index.md</tt> mit BlueCloth rendern.
+=== Verfügbare Templatesprachen
 
+Einige Sprachen haben mehrere Implementierungen. Um festzulegen, welche
+verwendet wird (und dann auch Thread-sicher ist), verwendet man am besten zu
+Beginn ein 'require':
 
-=== Textile-Templates
+  require 'rdiscount' # oder require 'bluecloth'
+  get('/') { markdown :index }
 
-Das +redcloth+-Gem wird benötigt, um Textile-Templates rendern zu können:
+=== Haml Templates
 
-  # redcloth muss eingebunden werden
-  require "redcloth"
+Abhängigkeit::        {haml}[http://haml-lang.com/]
+Dateierweiterungs::   <tt>.haml</tt>
+Beispiel::           <tt>haml :index, :format => :html5</tt>
 
-  get '/' do
-    textile :index
-  end
+=== Erb Templates
 
-Dieser Code rendert <tt>./views/index.textile</tt>.
+Abhängigkeit::        {erubis}[http://www.kuwata-lab.com/erubis/] oder
+                    erb (included in Ruby)
+Dateierweiterungs::   <tt>.erb</tt>, <tt>.rhtml</tt> oder <tt>.erubis</tt> 
+                    (nur Erubis)
+Beispiel::           <tt>erb :index</tt>
 
-Da es weder möglich ist, Methoden aufzurufen, noch +locals+ zu übergeben, ist
-es sinnvoll, Textile in Kombination mit einer anderen Template-Engine zu 
-nutzen:
+=== Builder Templates
 
-  erb :overview, :locals => { :text => textile(:introduction) }
+Abhängigkeit::        {builder}[http://builder.rubyforge.org/]
+Dateierweiterungs::   <tt>.builder</tt>
+Beispiel::           <tt>builder { |xml| xml.em "Hallo" }</tt>
 
-Es ist auch möglich, die +textile+-Methode aus anderen Templates heraus
-aufzurufen:
+Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
 
-  %h1 Hallo von Haml!
-  %p= textile(:greetings)
+=== Nokogiri Templates
 
-Da man Ruby aus Textile heraus nicht aufrufen kann, ist es nicht möglich, 
-Layouts zu verwenden, die in Textile geschrieben sind. Es ist aber möglich, 
-einen anderen Renderer für das Template zu verwenden als für das Layout, indem
-man die <tt>:layout_engine</tt>-Option angibt:
+Abhängigkeit::        {nokogiri}[http://nokogiri.org/]
+Dateierweiterungs::   <tt>.nokogiri</tt>
+Beispiel::           <tt>nokogiri { |xml| xml.em "Hallo" }</tt>
 
-  get '/' do
-    textile :index, :layout_engine => :erb
-  end
+Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
 
-Das wird <tt>./views/index.textile</tt> mit <tt>./views/layout.erb</tt> als 
-Layout rendern.
+=== Sass Templates
 
-Denk daran, dass solche Einstellungen auch global gesetzt werden können:
+Abhängigkeit::        {sass}[http://sass-lang.com/]
+Dateierweiterungs::   <tt>.sass</tt>
+Beispiel::           <tt>sass :stylesheet, :style => :expanded</tt>
 
-  set :textile, :layout_engine => :haml, :layout => :post
+=== SCSS Templates
 
-  get '/' do
-    textile :index
-  end
+Abhängigkeit::        {sass}[http://sass-lang.com/]
+Dateierweiterungs::   <tt>.scss</tt>
+Beispiel::           <tt>scss :stylesheet, :style => :expanded</tt>
 
-Das wird <tt>./views/index.textile</tt> (und jedes andere Markdown-Template)
-mit <tt>./views/post.haml</tt> als Layout rendern.
+=== Less Templates
 
+Abhängigkeit::        {less}[http://www.lesscss.org/]
+Dateierweiterungs::   <tt>.less</tt>
+Beispiel::           <tt>less :stylesheet</tt>
 
-=== RDoc-Templates
+=== Liquid Templates
 
-Das +rdoc+-Gem wird benötigt, um RDoc-Templates rendern zu können:
+Abhängigkeit::        {liquid}[http://www.liquidmarkup.org/]
+Dateierweiterungs::   <tt>.liquid</tt>
+Beispiel::           <tt>liquid :index, :locals => { :key => 'Wert' }</tt>
 
-  # rdoc/markup/to_html muss eingebunden werden
-  require "rdoc/markup/to_html"
+Da man aus dem Liquid-Template heraus keine Ruby-Methoden aufrufen kann 
+(ausgenommen +yield+), wird man üblicherweise locals verwenden wollen, mit 
+denen man Variablen weitergibt.
 
-  get '/' do
-    rdoc :index
-  end
+=== Markdown Templates
 
-Dieser Code rendert <tt>./views/index.rdoc</tt>.
+Abhängigkeit::        {rdiscount}[https://github.com/rtomayko/rdiscount],
+                    {redcarpet}[https://github.com/tanoku/redcarpet],
+                    {bluecloth}[http://deveiate.org/projects/BlueCloth],
+                    {kramdown}[http://kramdown.rubyforge.org/] *oder*
+                    {maruku}[http://maruku.rubyforge.org/]
+Dateierweiterungs::   <tt>.markdown</tt>, <tt>.mkd</tt> und <tt>.md</tt>
+Beispiel::           <tt>markdown :index, :layout_engine => :erb</tt>
 
-Da es weder möglich ist, Methoden aufzurufen, noch +locals+ zu übergeben, ist
-es sinnvoll, RDoc in Kombination mit einer anderen Template-Engine zu nutzen:
+Da man aus den Markdown-Templates heraus keine Ruby-Methoden aufrufen und auch
+keine locals verwenden kann, wird man Markdown üblicherweise in Kombination mit
+anderen Renderern verwenden wollen:
 
-  erb :overview, :locals => { :text => rdoc(:introduction) }
+  erb :overview, :locals => { :text => markdown(:einfuehrung) }
 
-Es ist auch möglich, die +rdoc+-Methode aus anderen Templates heraus
-aufzurufen:
+Beachte, dass man die +markdown+-Methode auch aus anderen Templates heraus
+aufrufen kann:
 
-  %h1 Hallo von Haml!
-  %p= rdoc(:greetings)
+  %h1 Gruß von Haml!
+  %p= markdown(:Grüsse)
 
-Da man Ruby aus RDoc heraus nicht aufrufen kann, ist es nicht möglich, Layouts
-zu verwenden, die in RDoc geschrieben sind. Es ist aber möglich, einen anderen
-Renderer für das Template zu verwenden als für das Layout, indem man die 
-<tt>:layout_engine</tt> option angibt:
-
-  get '/' do
-    rdoc :index, :layout_engine => :erb
-  end
-
-Das wird <tt>./views/index.rdoc</tt> mit <tt>./views/layout.erb</tt> als Layout
-rendern.
-
-Denk daran, dass solche Einstellungen auch global gesetzt werden können:
-
-  set :rdoc, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    rdoc :index
-  end
+Da man Ruby nicht von Markdown heraus aufrufen kann, können auch Layouts nicht
+in Markdown geschrieben werden. Es ist aber möglich, einen Renderer für die 
+Templates zu verwenden und einen anderen für das Layout, indem die 
+<tt>:layout_engine</tt>-Option verwendet wird.
 
-Das wird <tt>./views/index.rdoc</tt> (und jedes andere Markdown-Template) mit
-<tt>./views/post.haml</tt> als Layout rendern.
+=== Textile Templates
 
+Abhängigkeit::        {RedCloth}[http://redcloth.org/]
+Dateierweiterungs::   <tt>.textile</tt>
+Beispiel::           <tt>textile :index, :layout_engine => :erb</tt>
 
-=== Radius-Templates
+Da man aus dem Textile-Template heraus keine Ruby-Methoden aufrufen und auch
+keine locals verwenden kann, wird man Textile üblicherweise in Kombination mit
+anderen Renderern verwenden wollen:
 
-Das +radius+-Gem wird benötigt, um Radius-Templates rendern zu können:
+  erb :overview, :locals => { :text => textile(:einfuehrung) }
 
-  # radius muss eingebunden werden
-  require 'radius'
+Beachte, dass man die +textile+-Methode auch aus anderen Templates heraus
+aufrufen kann:
 
-  get '/' do
-    radius :index
-  end
+  %h1 Gruß von Haml!
+  %p= textile(:Grüsse)
 
-Dieser Code rendert <tt>./views/index.radius</tt>.
+Da man Ruby nicht von Textile heraus aufrufen kann, können auch Layouts nicht
+in Textile geschrieben werden. Es ist aber möglich, einen Renderer für die 
+Templates zu verwenden und einen anderen für das Layout, indem die 
+<tt>:layout_engine</tt>-Option verwendet wird.
 
-Da aus Radius-Templates heraus keine Methoden (abgesehen von +yield+) 
-aufgerufen werden können, es es möglich, +locals+ zu übergeben:
+=== RDoc Templates
 
-  radius :index, :locals => { :key => 'value' }
+Abhängigkeit::        {rdoc}[http://rdoc.rubyforge.org/]
+Dateierweiterungs::   <tt>.rdoc</tt>
+Beispiel::           <tt>textile :README, :layout_engine => :erb</tt>
 
+Da man aus dem RDoc-Template heraus keine Ruby-Methoden aufrufen und auch
+keine locals verwenden kann, wird man RDoc üblicherweise in Kombination mit
+anderen Renderern verwenden wollen:
 
-=== Markaby-Templates
+  erb :overview, :locals => { :text => rdoc(:einfuehrung) }
 
-Das +markaby+-Gem wird benötigt, um Markaby-Templates rendern zu können:
+Beachte, dass man die +rdoc+-Methode auch aus anderen Templates heraus
+aufrufen kann:
 
-  # markaby muss eingebunden werden
-  require 'markaby'
+  %h1 Gruß von Haml!
+  %p= rdoc(:Grüße)
 
-  get '/' do
-    markaby :index
-  end
+Da man Ruby nicht von RDoc heraus aufrufen kann, können auch Layouts nicht
+in RDoc geschrieben werden. Es ist aber möglich, einen Renderer für die 
+Templates zu verwenden und einen anderen für das Layout, indem die 
+<tt>:layout_engine</tt>-Option verwendet wird.
 
-Dieser Code rendert <tt>./views/index.mab</tt>.
+=== Radius Templates
 
+Abhängigkeit::        {radius}[http://radius.rubyforge.org/]
+Dateierweiterungs::   <tt>.radius</tt>
+Beispiel::           <tt>radius :index, :locals => { :key => 'Wert' }</tt>
 
-=== Slim-Templates
+Da man aus dem Radius-Template heraus keine Ruby-Methoden aufrufen kann, wird
+man üblicherweise locals verwenden wollen, mit denen man Variablen weitergibt.
 
-Das +slim+-Gem wird benötigt, um Slim-Templates rendern zu können:
+=== Markaby Templates
 
-  # slim muss eingebunden werden
-  require 'slim'
+Abhängigkeit::        {markaby}[http://markaby.github.com/]
+Dateierweiterungs::   <tt>.mab</tt>
+Beispiel::           <tt>markaby { h1 "Willkommen!" }</tt>
 
-  get '/' do
-    slim :index
-  end
+Nimmt ebenso einen Block für Inline-Templates entgegen (siehe Beispiel).
 
-Dieser Code rendert <tt>./views/index.slim</tt>.
+=== Slim Templates
 
+Abhängigkeit::        {slim}[http://slim-lang.com/]
+Dateierweiterungs::   <tt>.slim</tt>
+Beispiel::           <tt>slim :index</tt>
 
-=== CoffeeScript-Templates
+=== Creole Templates
 
-Das <tt>coffee-script</tt>-Gem und mindestens eine der folgenden Optionen 
-werden benötigt, um JavaScript auf dem Server ausführen zu können:
+Abhängigkeit::        {creole}[https://github.com/minad/creole]
+Dateierweiterungs::   <tt>.creole</tt>
+Beispiel::           <tt>creole :wiki, :layout_engine => :erb</tt>
 
-* +node+ (von Node.js) befindet sich im Pfad
-* du bist unter OS X
-* +therubyracer+ gem/library
+Da man aus dem Creole-Template heraus keine Ruby-Methoden aufrufen und auch
+keine locals verwenden kann, wird man Creole üblicherweise in Kombination mit
+anderen Renderern verwenden wollen:
 
-Siehe auch http://github.com/josh/ruby-coffee-script für eine vollständige
-Liste aller Optionen.
+  erb :overview, :locals => { :text => creole(:einfuehrung) }
 
-Nun können CoffeeScript-Templates in der Applikation gerendert werden:
+Beachte, dass man die +creole+-Methode auch aus anderen Templates heraus
+aufrufen kann:
 
-  # coffee-script muss eingebunden werden
-  require 'coffee-script'
+  %h1 Gruß von Haml!
+  %p= creole(:Grüße)
 
-  get '/application.js' do
-    coffee :application
-  end
+Da man Ruby nicht von Creole heraus aufrufen kann, können auch Layouts nicht
+in Creole geschrieben werden. Es ist aber möglich, einen Renderer für die 
+Templates zu verwenden und einen anderen für das Layout, indem die 
+<tt>:layout_engine</tt>-Option verwendet wird.
 
-Dieser Code rendert <tt>./views/application.coffee</tt>.
+=== CoffeeScript Templates
 
+Abhängigkeit::        {coffee-script}[https://github.com/josh/ruby-coffee-script]
+                      und eine {Möglichkeit JavaScript auszuführen}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
+Dateierweiterungs::   <tt>.coffee</tt>
+Beispiel::            <tt>coffee :index</tt>
 
-=== Inline-Templates
+=== Eingebettete Templates
 
   get '/' do
     haml '%div.title Hallo Welt'
   end
 
-Rendert den Inline-Template-String.
-
+Rendert den eingebetteten Template-String.
 
 === Auf Variablen in Templates zugreifen
 
@@ -661,13 +485,12 @@ Oder durch einen expliziten Hash von lokalen Variablen:
 
   get '/:id' do
     foo = Foo.find(params[:id])
-    haml '%h1= foo.name', :locals => { :foo => foo }
+    haml '%h1= bar.name', :locals => { :bar => foo }
   end
 
 Dies wird typischerweise bei Verwendung von Subtemplates (partials) in anderen
 Templates eingesetzt.
 
-
 === Inline-Templates
 
 Templates können auch am Ende der Datei definiert werden:
@@ -692,7 +515,6 @@ Anmerkung: Inline-Templates, die in der Datei definiert sind, die <tt>require
 in anderen Dateien aufzurufen, muss explizit <tt>enable :inline_templates</tt> 
 verwendet werden.
 
-
 === Benannte Templates
 
 Templates können auch mit der Top-Level <tt>template</tt>-Methode definiert
@@ -715,10 +537,9 @@ verwendet. Durch <tt>:layout => false</tt> kann das Ausführen verhindert
 werden:
 
   get '/' do
-    haml :index, :layout => !request.xhr?
+    haml :index, :layout => request.xhr?
   end
 
-
 === Dateiendungen zuordnen
 
 Um eine Dateiendung einer Template-Engine zuzuordnen, kann 
@@ -728,7 +549,6 @@ bewerkstelligen:
 
   Tilt.register :tt, Tilt[:textile]
 
-
 === Eine eigene Template-Engine hinzufügen
 
 Zu allererst muss die Engine bei Tilt registriert und danach eine 
@@ -748,7 +568,6 @@ Dieser Code rendert <tt>./views/application.mtt</tt>. Siehe
 github.com/rtomayko/tilt[https://github.com/rtomayko/tilt], um mehr über Tilt 
 zu lernen.
 
-
 == Filter
 
 Before-Filter werden vor jedem Request in demselben Kontext, wie danach die 
@@ -796,7 +615,6 @@ werden:
     # ...
   end
 
-
 == Helfer
 
 Durch die Top-Level <tt>helpers</tt>-Methode werden sogenannte Helfer-Methoden
@@ -812,7 +630,6 @@ definiert, die in Routen und Templates verwendet werden können:
     bar(params[:name])
   end
 
-
 === Sessions verwenden
 Sessions werden verwendet, um Zustände zwischen den Requests zu speichern.
 Sind sie aktiviert, kann ein Session-Hash je Benutzer-Session verwendet werden.
@@ -851,6 +668,10 @@ teilen:
 
   set :session_secret, 'super secret'
 
+Zur weiteren Konfiguration kann man einen Hash mit Optionen in den +sessions+
+Einstellungen ablegen.
+
+  set :sessions, :domain => 'foo.com'
 
 == Anhalten
 
@@ -878,7 +699,6 @@ Natürlich ist es auch möglich, ein Template mit +halt+ zu verwenden:
 
   halt erb(:error)
 
-
 == Weiterspringen
 
 Eine Route kann mittels <tt>pass</tt> zu der nächsten passenden Route springen:
@@ -896,7 +716,6 @@ Der Block wird sofort verlassen und es wird nach der nächsten treffenden Route
 gesucht. Ein 404-Fehler wird zurückgegeben, wenn kein treffendes Routen-Muster
 gefunden wird.
 
-
 === Eine andere Route ansteuern
 
 Manchmal entspricht +pass+ nicht den Anforderungen, wenn das Ergebnis einer
@@ -921,7 +740,6 @@ Kopie der Instanz erzeugt werden soll, kann <tt>call!</tt> anstelle von
 
 Die Rack-Spezifikationen enthalten weitere Informationen zu +call+.
 
-
 === Body, Status-Code und Header setzen
 
 Es ist möglich und empfohlen, den Status-Code sowie den Response-Body mit einem
@@ -947,7 +765,7 @@ Vergleichbar mit +body+ lassen sich auch Status-Code und Header setzen:
   get '/foo' do
     status 418
     headers \
-      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN"
+      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN",
       "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
     halt "Ich bin ein Teekesselchen"
   end
@@ -955,6 +773,79 @@ Vergleichbar mit +body+ lassen sich auch Status-Code und Header setzen:
 Genau wie bei +body+ liest ein Aufrufen von +headers+ oder +status+ ohne
 Argumente den aktuellen Wert aus.
 
+=== Response-Streams
+
+In manchen Situationen sollen Daten bereits an den Client zurückgeschickt 
+werden, bevor ein vollständiger Response bereit steht. Manchmal will man die 
+Verbindung auch erst dann beenden und Daten so lange an den Client 
+zurückschicken, bis er die Verbindung abbricht. Für diese Fälle gibt es die 
++stream+-Helfer-Methode, die es einem erspart eigene Lösungen zu schreiben:
+
+  get '/' do
+    stream do |out|
+      out << "Das ist ja mal wieder fanta -\n"
+      sleep 0.5
+      out << " (bitte warten…) \n"
+      sleep 1
+      out << "- stisch!\n"
+    end
+  end
+
+Damit lassen sich Streaming-APIs realisieren, sog. {Server Sent Events}[http://dev.w3.org/html5/eventsource/]
+die als Basis für {WebSockets}[http://en.wikipedia.org/wiki/WebSocket] dienen.
+Ebenso können sie verwendet werden, um den Durchsatz zu erhöhen, wenn ein Teil
+der Daten von langsamen Ressourcen abhängig ist.
+
+Es ist zu beachten, dass das Verhalten beim Streaming, insbesondere die Anzahl
+nebenläufiger Anfragen, stark davon abhängt, welcher Webserver für die 
+Applikation verwendet wird. Einige Server, z.B. WEBRick, unterstützen Streaming
+nicht oder nur teilweise. Sollte der Server Streaming nicht unterstützen, wird
+ein vollständiger Response-Body zurückgeschickt, sobald der an +stream+ 
+weitergegebene Block abgearbeitet ist.
+
+Ist der optionale Parameter +keep_open+ aktiviert, wird beim gestreamten Objekt
++close+ nicht aufgerufen und es ist einem überlassen dies an einem beliebigen 
+späteren Zeitpunkt nachholen. Die Funktion ist jedoch nur bei Event-gesteuerten 
+Serven wie Thin oder Rainbows möglich, andere Server werden trotzdem den Stream 
+beenden:
+
+  set :server, :thin
+  connections = []
+
+  get '/' do
+    # Den Stream offen halten
+    stream(:keep_open) { |out| connections << out }
+  end
+
+  post '/' do
+    # In alle offenen Streams schreiben
+    connections.each { |out| out << params[:message] << "\n" }
+    "Nachricht verschickt"
+  end
+  
+=== Logger
+
+Im Geltungsbereich eines Request stellt die +logger+ Helfer-Methode eine
++Logger+ Instanz zur Verfügung:
+
+  get '/' do
+    logger.info "es passiert gerade etwas"
+    # ...
+  end
+
+Der Logger übernimmt dabei automatisch alle im Rack-Handler eingestellten Log-
+Vorgaben. Ist Loggen ausgeschaltet, gibt die Methode ein Leerobjekt zurück.
+In den Routen und Filtern muss man sich also nicht weiter darum kümmern.
+
+Beachte, dass das Loggen standardmäßig nur für <tt>Sinatra::Application</tt>
+voreingestellt ist. Wird über <tt>Sinatra::Base</tt> vererbt, muss es erst
+aktiviert werden:
+
+  class MyApp < Sinatra::Base
+    configure(:production, :development) do
+      enable :logging
+    end
+  end
 
 == Mime-Types
 
@@ -962,8 +853,10 @@ Wenn <tt>send_file</tt> 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'
-
+  configure do
+    mime_type :foo, 'text/foo'
+  end
+  
 Es kann aber auch der +content_type+-Helfer verwendet werden:
 
   get '/' do
@@ -971,7 +864,6 @@ Es kann aber auch der +content_type+-Helfer verwendet werden:
     "foo foo foo"
   end
 
-
 === URLs generieren
 
 Zum Generieren von URLs sollte die +url+-Helfer-Methode genutzen werden, so 
@@ -984,7 +876,6 @@ Soweit vorhanden, wird Rücksicht auf Proxys und Rack-Router genommen.
 Diese Methode ist ebenso über das Alias +to+ zu erreichen (siehe Beispiel
 unten).
 
-
 === Browser-Umleitung
 
 Eine Browser-Umleitung kann mithilfe der +redirect+-Helfer-Methode erreicht
@@ -1018,7 +909,7 @@ Um Argumente an ein Redirect weiterzugeben, können sie entweder dem Query
 
 oder eine Session verwendet werden:
 
-  enable :session
+  enable :sessions
   
   get '/foo' do
     session[:secret] = 'foo'
@@ -1030,6 +921,67 @@ oder eine Session verwendet werden:
   end
 
 
+=== Cache einsetzen
+
+Ein sinnvolles Einstellen von Header-Daten ist die Grundlage für ein
+ordentliches HTTP-Caching.
+
+Der Cache-Control-Header lässt sich ganz einfach einstellen:
+
+  get '/' do
+    cache_control :public
+    "schon gecached!"
+  end
+
+Profitipp: Caching im before-Filter aktivieren
+
+  before do
+    cache_control :public, :must_revalidate, :max_age => 60
+  end
+
+Bei Verwendung der +expires+-Helfermethode zum Setzen des gleichnamigen
+Headers, wird <tt>Cache-Control</tt> automatisch eigestellt:
+
+  before do
+    expires 500, :public, :must_revalidate
+  end
+
+Um alles richtig zu machen, sollten auch +etag+ oder +last_modified+ verwendet
+werden. Es wird empfohlen, dass diese Helfer aufgerufen werden *bevor* die
+eigentliche Arbeit anfängt, da sie sofort eine Antwort senden, wenn der
+Client eine aktuelle Version im Cache vorhält:
+
+  get '/article/:id' do
+    @article = Article.find params[:id]
+    last_modified @article.updated_at
+    etag @article.sha1
+    erb :article
+  end
+
+ebenso ist es möglich einen
+{schwachen ETag}[http://de.wikipedia.org/wiki/HTTP_ETag] zu verwenden:
+
+  etag @article.sha1, :weak
+
+Diese Helfer führen nicht das eigentliche Caching aus, sondern geben die dafür
+notwendigen Informationen an den Cache weiter. Für schnelle Reverse-Proxy 
+Cache-Lösungen bietet sich z.B. 
+{rack-cache}[http://rtomayko.github.com/rack-cache/] an:
+
+  require "rack/cache"
+  require "sinatra"
+  
+  use Rack::Cache
+  
+  get '/' do
+    cache_control :public, :max_age => 36000
+    sleep 5
+    "hello"
+  end
+  
+Um den <tt>Cache-Control</tt>-Header mit Informationen zu versorgen, verwendet
+man die <tt>:static_cache_control</tt>-Einstellung (s.u.).  
+
 === Dateien versenden
 
 Zum Versenden von Dateien kann die <tt>send_file</tt>-Helfer-Methode verwendet
@@ -1065,7 +1017,6 @@ Ruby-Prozess auch andere Möglichkeiten genutzt. Bei Verwendung der
 <tt>send_file</tt>-Helfer-Methode kümmert sich Sinatra selbstständig um die
 Range-Requests.
 
-
 == Das Request-Objekt
 
 Auf das +request+-Objekt der eigehenden Anfrage kann vom Anfrage-Scope aus
@@ -1073,29 +1024,33 @@ zugegriffen werden:
 
   # App läuft unter http://example.com/example
   get '/foo' do
-    request.body              # Request-Body des Clients (siehe unten)
-    request.scheme            # "http"
-    request.script_name       # "/example"
-    request.path_info         # "/foo"
-    request.port              # 80
-    request.request_method    # "GET"
-    request.query_string      # ""
-    request.content_length    # Länge von request.body
-    request.media_type        # Media-Type von request.body
-    request.host              # "example.com"
-    request.get?              # true (ähnliche Methoden für andere Verben)
-    request.form_data?        # false
-    request["SOME_HEADER"]    # Wert des SOME_HEADER-Headers
-    request.referrer          # der Referrer des Clients oder '/'
-    request.user_agent        # User-Agent (genutzt von :agent-Bedingung)
-    request.cookies           # Hash der Cookies
-    request.xhr?              # Ist dies eine Ajax-Anfrage?
-    request.url               # "http://example.com/example/foo"
-    request.path              # "/example/foo"
-    request.ip                # Client-IP-Addresse
-    request.secure?           # false (wäre true bei SSL)
-    request.forwarded?        # true (wenn hinter Reverse-Proxy)
-    requuest.env              # env-Hash, den Rack durchreicht
+    t = %w[text/css text/html application/javascript]
+    request.accept              # ['text/html', '*/*']
+    request.accept? 'text/xml'  # true
+    request.preferred_type(t)   # 'text/html'
+    request.body                # Request-Body des Client (siehe unten)
+    request.scheme              # "http"
+    request.script_name         # "/example"
+    request.path_info           # "/foo"
+    request.port                # 80
+    request.request_method      # "GET"
+    request.query_string        # ""
+    request.content_length      # Länge des request.body
+    request.media_type          # Medientypus von request.body
+    request.host                # "example.com"
+    request.get?                # true (ähnliche Methoden für andere Verben)
+    request.form_data?          # false
+    request["IRGENDEIN_HEADER"] # Wert von IRGENDEIN_HEADER header
+    request.referrer            # Der Referrer des Clients oder '/'
+    request.user_agent          # User-Agent (verwendet in der :agent Bedingung)
+    request.cookies             # Hash des Browser-Cookies
+    request.xhr?                # Ist das hier ein Ajax-Request?
+    request.url                 # "http://example.com/example/foo"
+    request.path                # "/example/foo"
+    request.ip                  # IP-Adresse des Clients
+    request.secure?             # false (true wenn SSL)
+    request.forwarded?          # true (Wenn es hinter einem Reverse-Proxy verwendet wird)
+    request.env                 # vollständiger env-Hash von Rack übergeben
   end
 
 Manche Optionen, wie etwa <tt>script_name</tt> oder <tt>path_info</tt>, sind
@@ -1115,7 +1070,6 @@ Der <tt>request.body</tt> ist ein IO- oder StringIO-Objekt:
     "Hallo #{daten['name']}!"
   end
 
-
 === Anhänge
 
 Damit der Browser erkennt, dass ein Response gespeichert und nicht im Browser
@@ -1133,7 +1087,37 @@ Ebenso kann eine Dateiname als Parameter hinzugefügt werden:
     "Speichern!"
   end
 
+=== Umgang mit Datum und Zeit
+
+Sinatra bietet eine <tt>time_for</tt>-Helfer-Methode, die aus einem gegebenen 
+Wert ein Time-Objekt generiert. Ebenso kann sie nach +DateTime+, +Date+ und 
+ähnliche Klassen konvertieren:
+
+  get '/' do
+    pass if Time.now > time_for('Dec 23, 2012')
+    "noch Zeit"
+  end
+
+Diese Methode wird intern für +expires, +last_modiefied+ und Freunde verwendet.
+Mit ein paar Handgriffen lässt sich diese Methode also in ihrem Verhalten 
+erweitern, indem man +time_for+ in der eigenen Applikation überschreibt:
 
+  helpers do
+    def time_for(value)
+      case value
+      when :yesterday then Time.now - 24*60*60
+      when :tomorrow  then Time.now + 24*60*60
+      else super
+      end
+    end
+  end
+
+  get '/' do
+    last_modified :yesterday
+    expires :tomorrow
+    "Hallo"
+  end
+  
 === Nachschlagen von Template-Dateien
 
 Die <tt>find_template</tt>-Helfer-Methode wird genutzt, um Template-Dateien zum
@@ -1179,7 +1163,6 @@ Template-Pfade samt Inhalt gecached, solange nicht im Entwicklungsmodus
 gearbeitet wird. Das sollte im Hinterkopf behalten werden, wenn irgendwelche
 verrückten Methoden zusammenbastelt werden.
 
-
 == Konfiguration
 
 Wird einmal beim Starten in jedweder Umgebung ausgeführt:
@@ -1227,105 +1210,138 @@ Diese Einstellungen sind über +settings+ erreichbar:
     ...
   end
 
+=== Einstellung des Angriffsschutzes
 
+Sinatra verwendet 
+{Rack::Protection}[https://github.com/rkh/rack-protection#readme], um die
+Anwendung vor häufig vorkommenden Angriffen zu schützen. Diese Voreinstellung
+lässt sich selbstverständlich auch deaktivieren, z.B. um 
+Geschwindigkeitsvorteile zu gewinnen:
+
+  disable :protection
+
+Um einen bestimmten Schutzmechanismus zu deaktivieren, fügt man +protection+
+einen Hash mit Optionen hinzu:
+
+  set :protection, :except => :path_traversal
+
+Neben Strings akzeptiert <tt>:except</tt> auch Arrays, um gleich mehrere 
+Schutzmechanismen zu deaktivieren:
+
+  set :protections, :except => [:path_traversal, :session_hijacking]
+  
 === Mögliche Einstellungen
 
-[absolute_redirects]  Wenn ausgeschaltet, wird Sinatra relative Redirects
-                      zulassen. Jedoch ist Sinatra dann nicht mehr mit RFC 2616
-                      (HTTP 1.1) konform, das nur absolute Redirects zulässt.
-                      
-                      Sollte eingeschaltet werden, wenn die Applikation hinter
-                      einem Reverse-Proxy liegt, der nicht ordentlich 
-                      eingerichtet ist. Beachte, dass die +url+-Helfer-Methode
-                      nach wie vor absolute URLs erstellen wird, es sei denn, 
-                      es wird als zweiter Parameter +false+ angegeben.
-                      
-                      Standardmäßig nicht aktiviert.
+[absolute_redirects]    Wenn ausgeschaltet, wird Sinatra relative Redirects
+                        zulassen. Jedoch ist Sinatra dann nicht mehr mit RFC 
+                        2616 (HTTP 1.1) konform, das nur absolute Redirects 
+                        zulässt.
+
+                        Sollte eingeschaltet werden, wenn die Applikation
+                        hinter einem Reverse-Proxy liegt, der nicht ordentlich 
+                        eingerichtet ist. Beachte, dass die 
+                        +url+-Helfer-Methode nach wie vor absolute URLs 
+                        erstellen wird, es sei denn, es wird als zweiter 
+                        Parameter +false+ angegeben.
+
+                        Standardmäßig nicht aktiviert.
                       
 
-[add_charsets]        Mime-Types werden hier automatisch der Helfer-Methode
-                      <tt>content_type</tt> zugeordnet.
+[add_charsets]          Mime-Types werden hier automatisch der Helfer-Methode
+                        <tt>content_type</tt> zugeordnet.
                       
-                      Es empfielt sich, Werte hinzuzufügen statt sie zu
-                      überschreiben:
+                        Es empfielt sich, Werte hinzuzufügen statt sie zu
+                        überschreiben:
                       
-                        settings.add_charsets << "application/foobar"
+                          settings.add_charsets << "application/foobar"
 
-[app_file]            Hauptdatei der Applikation. Wird verwendet, um das
-                      Wurzel-, Inline-, View- und öffentliche Verzeichnis des
-                      Projekts festzustellen.
+[app_file]              Hauptdatei der Applikation. Wird verwendet, um das
+                        Wurzel-, Inline-, View- und öffentliche Verzeichnis des
+                        Projekts festzustellen.
                       
-[bind]                IP-Address, an die gebunden wird (Standardwert: 0.0.0.0).
-                      Wird nur für den eingebauten Server verwendet.
+[bind]                  IP-Address, an die gebunden wird 
+                        (Standardwert: 0.0.0.0). Wird nur für den eingebauten 
+                        Server verwendet.
 
-[default_encoding]    Das Encoding, falls keines angegeben wurde.
-                      Standardwert ist <tt>"utf-8"</tt>.
+[default_encoding]      Das Encoding, falls keines angegeben wurde.
+                        Standardwert ist <tt>"utf-8"</tt>.
 
-[dump_errors]         Fehler im Log anzeigen.
+[dump_errors]           Fehler im Log anzeigen.
 
-[environment]         Momentane Umgebung. Standardmäßig auf
-                      <tt>content_type</tt> oder <tt>"development"</tt>
-                      eingestellt, soweit ersteres nicht vorhanden.
+[environment]           Momentane Umgebung. Standardmäßig auf
+                        <tt>content_type</tt> oder <tt>"development"</tt>
+                        eingestellt, soweit ersteres nicht vorhanden.
 
-[logging]             Den Logger verwenden.
+[logging]               Den Logger verwenden.
 
-[lock]                Jeder Request wird gelocked. Es kann nur ein Request pro
-                      Ruby-Prozess gleichzeitig verarbeitet werden.
+[lock]                  Jeder Request wird gelocked. Es kann nur ein Request
+                        pro Ruby-Prozess gleichzeitig verarbeitet werden.
                       
-                      Eingeschaltet, wenn die Applikation threadsicher ist.
-                      Standardmäßig nicht aktiviert.
+                        Eingeschaltet, wenn die Applikation threadsicher ist.
+                        Standardmäßig nicht aktiviert.
 
-[method_override]     Verwende <tt>_method</tt>, um put/delete-Formulardaten in
-                      Browsern zu verwenden, die dies normalerweise nicht
-                      unterstützen.
+[method_override]       Verwende <tt>_method</tt>, um put/delete-Formulardaten
+                        in Browsern zu verwenden, die dies normalerweise nicht
+                        unterstützen.
 
-[port]                Port für die Applikation. Wird nur im internen Server
-                      verwendet.
+[port]                  Port für die Applikation. Wird nur im internen Server
+                        verwendet.
 
-[prefixed_redirects]  Entscheidet, ob <tt>request.script_name</tt> in Redirects
-                      eingefügt wird oder nicht, wenn kein absoluter Pfad
-                      angegeben ist. Auf diese Weise verhält sich
-                      <tt>redirect '/foo'</tt> so, als wäre es ein
-                      <tt>redirect to('/foo')</tt>.
-                      Standardmäßig nicht aktiviert.
+[prefixed_redirects]    Entscheidet, ob <tt>request.script_name</tt> in 
+                        Redirects eingefügt wird oder nicht, wenn kein 
+                        absoluter Pfad angegeben ist. Auf diese Weise verhält 
+                        sich <tt>redirect '/foo'</tt> so, als wäre es ein
+                        <tt>redirect to('/foo')</tt>. Standardmäßig nicht 
+                        aktiviert.
+                        
+[protection]            Legt fest, ob der Schutzmechanismus für häufig 
+                        Vorkommende Webangriffe auf Webapplikationen aktiviert 
+                        wird oder nicht. Weitere Informationen im vorhergehenden 
+                        Abschnitt.
 
-[public]              Das öffentliche Verzeichnis, aus dem Daten zur Verfügung
-                      gestellt werden können.
+[public_folder]         Das öffentliche Verzeichnis, aus dem Daten zur
+                        Verfügung gestellt werden können.
 
-[reload_templates]    Entscheidet, ob Templates zwischen Anfragen neu geladen
-                      werden sollen oder nicht. Unter Ruby 1.8.6 ist es im
-                      Entwicklungsmodus eingeschaltet (um einen Fehler in Ruby
-                      auszugleichen, der ein Speicherleck verursacht).
+[reload_templates]      Im development-Modus aktiviert.
 
-[root]                Wurzelverzeichnis des Projekts.
+[root]                  Wurzelverzeichnis des Projekts.
 
-[raise_errors]        Einen Ausnahmezustand aufrufen. Beendet die Applikation.
+[raise_errors]          Einen Ausnahmezustand aufrufen. Beendet die 
+                        Applikation.
 
-[run]                 Wenn aktiviert, wird Sinatra versuchen, den Webserver zu
-                      starten. Nicht verwenden, wenn Rackup oder anderes
-                      verwendet werden soll.
+[run]                   Wenn aktiviert, wird Sinatra versuchen, den Webserver
+                        zu starten. Nicht verwenden, wenn Rackup oder anderes
+                        verwendet werden soll.
 
-[running]             Läuft der eingebaute Server? Diese Einstellung nicht
-                      ändern!
+[running]               Läuft der eingebaute Server? Diese Einstellung nicht
+                        ändern!
 
-[server]              Server oder Liste von Servern, die als eingebaute Server
-                      zur Verfügung stehen.
-                      Standardmäßig auf ['thin', 'mongrel', 'webrick']
-                      voreingestellt. Die Anordnung gibt die Priorität vor.
+[server]                Server oder Liste von Servern, die als eingebaute 
+                        Server zur Verfügung stehen.
+                        Standardmäßig auf ['thin', 'mongrel', 'webrick']
+                        voreingestellt. Die Anordnung gibt die Priorität vor.
 
-[sessions]            Sessions auf Cookiebasis aktivieren.
+[sessions]              Sessions auf Cookiebasis aktivieren.
 
-[show_exceptions]     Stacktrace im Browser bei Fehlern anzeigen.
+[show_exceptions]       Stacktrace im Browser bei Fehlern anzeigen.
 
-[static]              Entscheidet, ob Sinatra statische Dateien zur Verfügung
-                      stellen soll oder nicht.
-                      Sollte nicht aktiviert werden, wenn ein Server verwendet
-                      wird, der dies auch selbstständig erledigen kann.
-                      Deaktivieren wird die Performance erhöhen.
-                      Standardmäßig aktiviert.
+[static]                Entscheidet, ob Sinatra statische Dateien zur Verfügung
+                        stellen soll oder nicht.
+                        Sollte nicht aktiviert werden, wenn ein Server 
+                        verwendet wird, der dies auch selbstständig erledigen 
+                        kann. Deaktivieren wird die Performance erhöhen.
+                        Standardmäßig aktiviert.
 
-[views]               Verzeichnis der Views.
+[static_cache_control]  Wenn Sinatra statische Daten zur Verfügung stellt, 
+                        können mit dieser Einstellung die +Cache-Control+ 
+                        Header zu den Responses hinzugefügt werden. Die 
+                        Einstellung verwendet dazu die +cache_control+
+                        Helfer-Methode. Standardmäßig deaktiviert.
+                        Ein Array wird verwendet, um mehrere Werte gleichzeitig
+                        zu übergeben:
+                        <tt>set :static_cache_control, [:public, :max_age => 300]</tt>                       
 
+[views]                 Verzeichnis der Views.
 
 == Fehlerbehandlung
 
@@ -1333,7 +1349,6 @@ Error-Handler laufen in demselben Kontext wie Routen und Filter, was bedeutet,
 dass alle Goodies wie <tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt>, etc.
 verwendet werden können.
 
-
 === Nicht gefunden
 
 Wenn eine <tt>Sinatra::NotFound</tt>-Exception geworfen wird oder der
@@ -1343,7 +1358,6 @@ Statuscode 404 ist, wird der <tt>not_found</tt>-Handler ausgeführt:
     'Seite kann nirgendwo gefunden werden.'
   end
 
-
 === Fehler
 
 Der +error+-Handler wird immer ausgeführt, wenn eine Exception in einem
@@ -1389,7 +1403,6 @@ Oder ein Status-Code-Bereich:
 Sinatra setzt verschiedene <tt>not_found</tt>- und <tt>error</tt>-Handler in
 der Development-Umgebung.
 
-
 == Rack-Middleware
 
 Sinatra baut auf Rack[http://rack.rubyforge.org/], einem minimalistischen
@@ -1425,11 +1438,17 @@ URL-Routing, Authentifizierung und Session-Verarbeitung. Sinatra verwendet
 viele von diesen Komponenten automatisch, abhängig von der Konfiguration. So 
 muss +use+ häufig nicht explizit verwendet werden.
 
+Hilfreiche Middleware gibt es z.B. hier:
+{rack}[https://github.com/rack/rack/tree/master/lib/rack],
+{rack-contrib}[https://github.com/rack/rack-contrib#readme],
+mit {CodeRack}[http://coderack.org/] oder im
+{Rack wiki}[https://github.com/rack/rack/wiki/List-of-Middleware].
 
 == Testen
 
 Sinatra-Tests können mit jedem auf Rack aufbauendem Test-Framework geschrieben
-werden. {Rack::Test}[http://gitrdoc.com/brynary/rack-test] wird empfohlen:
+werden. {Rack::Test}[http://rdoc.info/github/brynary/rack-test/master/frames]
+wird empfohlen:
 
   require 'my_sinatra_app'
   require 'test/unit'
@@ -1458,10 +1477,6 @@ werden. {Rack::Test}[http://gitrdoc.com/brynary/rack-test] wird empfohlen:
     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 Definieren einer Top-Level-Anwendung funktioniert gut für 
@@ -1470,9 +1485,9 @@ Middleware, Rails Metal, einfache Bibliotheken mit Server-Komponenten oder auch
 Sinatra-Erweiterungen geschrieben werden sollen.
 
 Die Top-Level-DSL belastet den Objekt-Namespace und setzt einen
-Mikro-Anwendungsstil voraus (eine einzelne Anwendungsdatei, ./public und 
-./views Ordner, Logging, Exception-Detail-Seite, usw.). Genau hier kommt 
-Sinatra::Base ins Spiel:
+Mikro-Anwendungsstil voraus (eine einzelne Anwendungsdatei, <tt>./public</tt>
+und <tt>./views</tt> Ordner, Logging, Exception-Detail-Seite, usw.). Genau 
+hier kommt <tt>Sinatra::Base</tt> ins Spiel:
 
   require 'sinatra/base'
 
@@ -1492,22 +1507,21 @@ Server-Komponente einer Bibliothek:
 
    MyApp.run! :host => 'localhost', :port => 9090
 
-Die Methoden der Sinatra::Base-Subklasse sind genau dieselben wie die der 
-Top-Level-DSL. Die meisten Top-Level-Anwendungen können mit nur zwei 
-Veränderungen zu Sinatra::Base-Komponenten konvertiert werden:
+Die Methoden der <tt>Sinatra::Base</tt>-Subklasse sind genau dieselben wie die
+der Top-Level-DSL. Die meisten Top-Level-Anwendungen können mit nur zwei 
+Veränderungen zu <tt>Sinatra::Base</tt> konvertiert werden:
 
 * Die Datei sollte <tt>require 'sinatra/base'</tt> anstelle von
   <tt>require 'sinatra/base'</tt> 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.
+  einer Subklasse von <tt>Sinatra::Base</tt> definiert werden.
 
 <tt>Sinatra::Base</tt> ist ein unbeschriebenes Blatt. Die meisten Optionen sind
 per Standard deaktiviert. Das betrifft auch den eingebauten Server. Siehe
 {Optionen und Konfiguration}[http://sinatra.github.com/configuration.html] für
 Details über mögliche Optionen.
 
-
 === Modularer vs. klassischer Stil
 
 Entgegen häufiger Meinungen gibt es nichts gegen den klassischen Stil
@@ -1531,13 +1545,12 @@ Unterschiede beachtet werden:
 
   Szenario            Classic                 Modular
 
-  app_file            file loading sinatra    nil
+  app_file            sinatra ladende Datei   Sinatra::Base subklassierende Datei
   run                 $0 == app_file          false
   logging             true                    false
   method_override     true                    false
   inline_templates    true                    false
 
-
 === Eine modulare Applikation bereitstellen
 
 Es gibt zwei übliche Wege, eine modulare Anwendung zu starten. Zum einen über
@@ -1561,14 +1574,13 @@ Oder über eine <tt>config.ru</tt>-Datei, die es erlaubt, einen beliebigen
 Rack-Handler zu verwenden:
 
   # config.ru
-  require 'mein_app'
+  require './mein_app'
   run MeineApp
 
 Starte:
 
   rackup -p 4567
 
-
 === Eine klassische Anwendung mit einer config.ru verwenden
 
 Schreibe eine Anwendungsdatei:
@@ -1582,10 +1594,9 @@ Schreibe eine Anwendungsdatei:
 
 sowie eine dazugehörige <tt>config.ru</tt>-Datei:
 
-  require 'app'
+  require './app'
   run Sinatra::Application
 
-
 === Wann sollte eine config.ru-Datei verwendet werden?
 
 Anzeichen dafür, dass eine <tt>config.ru</tt>-Datei gebraucht wird:
@@ -1600,7 +1611,6 @@ eine Anwendung im modularen Stil betrieben werden soll. Ebenso wird keine
 Anwendung mit modularem Stil benötigt, um eine <tt>config.ru</tt>-Datei zu 
 verwenden.</b>
 
-
 === Sinatra als Middleware nutzen
 
 Es ist nicht nur möglich, andere Rack-Middleware mit Sinatra zu nutzen, es kann
@@ -1617,7 +1627,7 @@ andere Sinatra-Anwendung handeln, es kann jede andere Rack-Anwendung sein
     get('/login') { haml :login }
   
     post('/login') do
-      if params[:name] = 'admin' and params[:password] = 'admin'
+      if params[:name] == 'admin' && params[:password] == 'admin'
         session['user_name'] = params[:name]
       else
         redirect '/login'
@@ -1638,21 +1648,61 @@ andere Sinatra-Anwendung handeln, es kann jede andere Rack-Anwendung sein
     get('/') { "Hallo #{session['user_name']}." }
   end
 
+=== Dynamische Applikationserstellung
+
+Manche Situationen erfordern die Erstellung neuer Applikationen zur Laufzeit,
+ohne dass sie einer Konstanten zugeordnet werden. Dies lässt sich mit 
+<tt>Sinatra.new</tt> erreichen:
+
+  require 'sinatra/base'
+  my_app = Sinatra.new { get('/') { "hallo" } }
+  my_app.run!
+
+Die Applikation kann mit Hilfe eines optionalen Parameters erstellt werden:
+
+  # config.ru
+  require 'sinatra/base'
+
+  controller = Sinatra.new do
+    enable :logging
+    helpers MyHelpers
+  end
+
+  map('/a') do
+    run Sinatra.new(controller) { get('/') { 'a' } }
+  end
+
+  map('/b') do
+    run Sinatra.new(controller) { get('/') { 'b' } }
+  end
+
+Das ist besonders dann interessant, wenn Sinatra-Erweiterungen getestet werden
+oder Sinatra in einer Bibliothek Verwendung findet.
+
+Ebenso lassen sich damit hervorragend Sinatra-Middlewares erstellen:
+
+  require 'sinatra/base'
+
+  use Sinatra do
+    get('/') { ... }
+  end
+
+  run RailsProject::Application
 
 == Geltungsbereich und Bindung
 
 Der Geltungsbereich (Scope) legt fest, welche Methoden und Variablen zur
 Verfügung stehen.
 
-
 === Anwendungs- oder Klassen-Scope
 
-Jede Sinatra-Anwendung entspricht einer Sinatra::Base-Subklasse. Falls die Top-
-Level-DSL verwendet wird (<tt>require 'sinatra'</tt>), handelt es sich um 
-Sinatra::Application, andernfalls ist es jene Subklasse, die explizit angelegt 
-wurde. Auf Klassenebene stehen Methoden wie +get+ oder +before+ zur Verfügung,
-es gibt aber keinen Zugriff auf das +request+-Object oder die +session+, da nur
-eine einzige Klasse für alle eingehenden Anfragen genutzt wird.
+Jede Sinatra-Anwendung entspricht einer <tt>Sinatra::Base</tt>-Subklasse. Falls
+die Top- Level-DSL verwendet wird (<tt>require 'sinatra'</tt>), handelt es sich
+um <tt>Sinatra::Application</tt>, andernfalls ist es jene Subklasse, die 
+explizit angelegt wurde. Auf Klassenebene stehen Methoden wie +get+ oder 
++before+ zur Verfügung, es gibt aber keinen Zugriff auf das +request+-Object 
+oder die +session+, da nur eine einzige Klasse für alle eingehenden Anfragen 
+genutzt wird.
 
 Optionen, die via +set+ gesetzt werden, sind Methoden auf Klassenebene:
 
@@ -1672,6 +1722,7 @@ Im Anwendungs-Scope befindet man sich:
 * In Methoden, die von Erweiterungen definiert werden.
 * Im Block, der an +helpers+ übergeben wird.
 * In Procs und Blöcken, die an +set+ übergeben werden.
+* Der an <tt>Sinatra.new</tt> übergebene Block
 
 Auf das Scope-Objekt (die Klasse) kann wie folgt zugegriffen werden:
 
@@ -1679,7 +1730,6 @@ Auf das Scope-Objekt (die Klasse) kann wie folgt zugegriffen werden:
   { |c| ... }</tt>).
 * +settings+ aus den anderen Scopes heraus.
 
-
 === Anfrage- oder Instanz-Scope
 
 Für jede eingehende Anfrage wird eine neue Instanz der Anwendungs-Klasse
@@ -1710,7 +1760,6 @@ Im Anfrage-Scope befindet man sich:
 * In Helfer-Methoden
 * In Templates
 
-
 === Delegation-Scope
 
 Vom Delegation-Scope aus werden Methoden einfach an den Klassen-Scope
@@ -1732,7 +1781,6 @@ Schau am besten im Code nach: Hier ist
 definiert und wird in den
 {globalen Namespace eingebunden}[http://github.com/sinatra/sinatra/blob/master/lib/sinatra/main.rb#L25].
 
-
 == Kommandozeile
 
 Sinatra-Anwendungen können direkt von der Kommandozeile aus gestartet werden:
@@ -1748,74 +1796,79 @@ Die Optionen sind:
   -s # Rack-Server/Handler setzen (Standard ist thin)
   -x # Mutex-Lock einschalten (Standard ist off)
 
-
 == Systemanforderungen
 
-Es wird empfohlen, Sinatra unter Ruby 1.8.7, 1.9.2, JRuby oder Rubinius zu
-installieren.
-
 Die folgenden Versionen werden offiziell unterstützt:
 
-[ Ruby 1.8.6 ]
-  Es wird nicht empfohlen, 1.8.6 für Sinatra einzusetzen. Trotzdem wird es
-  offiziell bis Sinatra 1.3.0 unterstützt werden. RDoc- und CoffeeScript- 
-  Templates werden in dieser Version nicht unterstützt. 1.8.6 hat ein größeres
-  Speicherleck in seiner Hash-Implementation, das von Sinatra-Versionen vor 
-  1.1.1 ausgelöst wird. Die aktuelle Version verhindert das zwar explizit, aber 
-  unter Einbußen in der Performance. Ebenso muss Sinatra mit Rack 1.1.x laufen,
-  da Rack >= 1.2 Ruby 1.8.6 nicht mehr unterstützt.
-
 [ Ruby 1.8.7 ]
   1.8.7 wird vollständig unterstützt, aber solange nichts dagegen spricht, 
   wird ein Update auf 1.9.2 oder ein Umstieg auf JRuby/Rubinius empfohlen.
+  Unterstützung für 1.8.7 wird es mindestens bis Sinatra 2.0 und Ruby 2.0 geben,
+  es sei denn, dass der unwahrscheinliche Fall eintritt und 1.8.8 rauskommt. Doch
+  selbst dann ist es eher wahrscheinlich, dass 1.8.7 weiterhin unterstützt wird.
+  <b>Ruby 1.8.6 wird nicht mehr unterstützt.</b> Soll Sinatra unter 1.8.6 
+  eingesetzt werden, muss Sinatra 1.2 verwendet werden, dass noch bis zum 
+  Release von Sinatra 1.4.0 fortgeführt wird.
 
 [ Ruby 1.9.2 ]
-  1.9.2 wird unterstützt und empfohlen. Beachte, dass Markaby und Radius
-  momentan noch nicht kompatibel mit 1.9 sind. Version 1.9.0p0 sollte nicht
+  1.9.2 wird voll unterstützt und empfohlen. Beachte, dass Markaby und Radius
+  momentan noch nicht kompatibel mit 1.9 sind. Version 1.9.2p0 sollte nicht
   verwendet werden, da unter Sinatra immer wieder Segfaults auftreten.
-
+  Unterstützung wird es mindestens bis zum Release von Ruby 1.9.4/2.0 geben und
+  das letzte Sinatra Release für 1.9 wird so lange unterstützt, wie das Ruby 
+  Core-Team 1.9 pflegt.
+
+[ Ruby 1.9.3 ]
+  Obwohl Tests bereits auf 1.9.3 laufen, sind bisher keine Applikationen auf 
+  1.9.3 in Produktion bekannt. Ebenso wie bei 1.9.2 besteht die gleiche Warnung 
+  zum Patchlevel 0.
+  
 [ Rubinius ]
-  Rubinius (rbx >= 1.2.3) wird offiziell unter Einbezug aller Templates 
+  Rubinius (rbx >= 1.2.4) wird offiziell unter Einbezug aller Templates 
   unterstützt.
 
 [ JRuby ]
-  JRuby wird offiziell unterstützt (JRuby >= 1.6.0). Probleme mit Template- 
+  JRuby wird offiziell unterstützt (JRuby >= 1.6.3). Probleme mit Template-
   Bibliotheken Dritter sind nicht bekannt. Falls JRuby zum Einsatz kommt, 
   sollte aber darauf geachtet werden, dass ein JRuby-Rack-Handler zum Einsatz 
   kommt – der Thin-Web-Server wird bisher nicht unterstütz. JRubys 
   Unterstützung für C-Erweiterungen sind zur Zeit noch experimenteller Natur,
-  betrifft im Moment aber nur RDiscount.
+  betrifft im Moment aber nur RDiscount und Redcarpet.
+
 
 Weiterhin werden wir auf kommende Ruby-Versionen ein Auge haben.
 
-Die nachfolgend aufgeführten Ruby-Implementationen werden offiziell nicht von 
+Die nachfolgend aufgeführten Ruby-Implementierungen werden offiziell nicht von 
 Sinatra unterstützt, funktionieren aber normalerweise:
 
+* Ruby Enterprise Edition
 * Ältere Versionen von JRuby und Rubinius
 * MacRuby, Maglev, IronRuby
-* Ruby 1.9.0 und 1.9.1
+* Ruby 1.9.0 und 1.9.1 (wird jedoch nicht empfohlen, s.o.)
 
 Nicht offiziell unterstützt bedeutet, dass wenn Sachen nicht funktionieren, 
 wir davon ausgehen, dass es nicht an Sinatra sondern an der jeweiligen 
 Implentierung liegt.
 
 Im Rahmen unserer CI (Kontinuierlichen Integration) wird bereits ruby-head
-(das kommende Ruby 1.9.3) mit eingebunden. Da noch alles im Fluss ist, kann zur
+(das kommende Ruby 1.9.4) mit eingebunden. Da noch alles im Fluss ist, kann zur
 Zeit für nichts garantiert werden. Es kann aber erwartet werden, dass Ruby 
-1.9.3p0 von Sinatra unterstützt werden wird.
+1.9.4p0 von Sinatra unterstützt werden wird.
 
 Sinatra sollte auf jedem Betriebssystem laufen, dass den gewählten Ruby-
 Interpreter unterstützt.
 
+Sinatra wird aktuell nicht unter Cardinal, SmallRuby, BleuRuby oder irgendeiner 
+Version von Ruby vor 1.8.7 laufen.
+
+== Der neuste Stand (The Bleeding Edge)
 
-== Der neueste Stand (The Bleeding Edge)
 Um auf dem neusten Stand zu bleiben, kann der Master-Branch verwendet werden.
 Er sollte recht stabil sein. Ebenso gibt es von Zeit zu Zeit prerelease Gems, 
 die so installiert werden:
   
   gem install sinatra --pre
 
-
 === Mit Bundler
 
 Wenn die Applikation mit der neuesten Version von Sinatra und
@@ -1870,7 +1923,6 @@ Um Sinatra-Code von Zeit zu Zeit zu aktualisieren:
   cd myproject/sinatra
   git pull
 
-
 === Gem erstellen
 
 Aus der eigenen lokalen Kopie kann nun auch ein globales Gem gebaut werden:
@@ -1885,25 +1937,25 @@ folgendermaßen lauten:
 
   sudo rake install
 
-
 == Versions-Verfahren
 
 Sinatra folgt dem sogenannten {Semantic Versioning}[http://semver.org/], d.h. 
 SemVer und SemVerTag.
 
-
 == Mehr
 
 * {Projekt-Website}[http://sinatra.github.com/] - Ergänzende Dokumentation,
   News und Links zu anderen Ressourcen.
-* {Hilfe beisteuern}[http://sinatra.github.com/contributing.html] - Einen
+* {Mitmachen}[http://sinatra.github.com/contributing.html] - Einen
   Fehler gefunden? Brauchst du Hilfe? Hast du einen Patch?
 * {Issue-Tracker}[http://github.com/sinatra/sinatra/issues]
 * {Twitter}[http://twitter.com/sinatra]
 * {Mailing-Liste}[http://groups.google.com/group/sinatrarb]
 * {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] auf http://freenode.net
-
-* API Dokumentation für die {aktuelle Version}[http://rubydoc.info/gems/sinatra]	
-  oder für {HEAD}[http://rubydoc.info/github/sinatra/sinatra] auf 
+* {Sinatra Book}[http://sinatra-book.gittr.com] Kochbuch Tutorial
+* {Sinatra Recipes}[http://recipes.sinatrarb.com/] Sinatra-Rezepte aus
+  der Community
+* API Dokumentation für die {aktuelle Version}[http://rubydoc.info/gems/sinatra]
+  oder für {HEAD}[http://rubydoc.info/github/sinatra/sinatra] auf
   http://rubydoc.info
-
+* {CI Server}[http://ci.rkh.im/view/Sinatra/]
diff --git a/README.es.rdoc b/README.es.rdoc
index 8814306..b55054c 100644
--- a/README.es.rdoc
+++ b/README.es.rdoc
@@ -35,7 +35,11 @@ Cada ruta se asocia con un bloque:
   end
 
   put '/' do
-    .. actualizar algo ..
+    .. reemplazar algo ..
+  end
+
+  patch '/' do
+    .. modificar algo ..
   end
 
   delete '/' do
@@ -78,6 +82,12 @@ accesibles a través del arreglo <tt>params[:splat]</tt>:
     params[:splat] # => ["path/al/archivo", "xml"]
   end
 
+O, con parámetros de bloque:
+
+  get '/descargar/*.*' do |path, ext|
+    [path, ext] # => ["path/al/archivo", "xml"]
+  end
+
 Rutas con Expresiones Regulares:
 
   get %r{/hola/([\w]+)} do
@@ -90,6 +100,17 @@ O con un parámetro de bloque:
     "Hola, #{c}!"
   end
 
+Los patrones de ruta pueden contener parámetros opcionales:
+
+  get '/posts.?:formato?' do
+    # coincide con "GET /posts" y además admite cualquier extensión, por
+    # ejemplo, "GET /posts.json", "GET /posts.xml", etc.
+  end
+
+A propósito, a menos que desactivés la protección para el ataque <em>path
+traversal</em> (ver más abajo), el path de la petición puede ser modificado
+antes de que se compare con los de tus rutas.
+
 === Condiciones
 
 Las rutas pueden incluir una variedad de condiciones de selección, como por
@@ -129,6 +150,26 @@ Podés definir tus propias condiciones fácilmente:
     "Lo siento, perdiste."
   end
 
+Si tu condición acepta más de un argumento, podés pasarle un arreglo.  Al
+definir la condición puede resultarte conveniente utilizar el operador splat en
+la lista de parámetros:
+
+  set(:autorizar) do |*roles|   # <- mirá el splat
+    condition do
+      unless sesion_iniciada? && roles.any? {|rol| usuario_actual.tiene_rol? rol }
+        redirect "/iniciar_sesion/", 303
+      end
+    end
+  end
+
+  get "/mi/cuenta/", :autorizar => [:usuario, :administrador] do
+    "Detalles de mi cuenta"
+  end
+
+  get "/solo/administradores/", :autorizar => :administrador do
+    "Únicamente para administradores!"
+  end
+
 === Valores de Retorno
 
 El valor de retorno de un bloque de ruta determina al menos el cuerpo de la
@@ -199,209 +240,187 @@ O, usando un lookahead negativo:
 
 Los archivos estáticos son servidos desde el directorio público
 <tt>./public</tt>. Podés especificar una ubicación diferente ajustando la
-opción <tt>:public</tt>:
+opción <tt>:public_folder</tt>:
 
-  set :public, File.dirname(__FILE__) + '/estaticos'
+  set :public_folder, File.dirname(__FILE__) + '/estaticos'
 
 Notá que el nombre del directorio público no está incluido en la URL. Por
 ejemplo, el archivo <tt>./public/css/style.css</tt> se accede a través de
 <tt>http://ejemplo.com/css/style.css</tt>.
 
-== Vistas / Plantillas
-
-Se asume que las plantillas están ubicadas directamente bajo el directorio
-<tt>./views</tt>. 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 <tt>:'subdir/plantilla'</tt>). 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
+Usá la configuración <tt>:static_cache_control</tt> para agregar el encabezado
+<tt>Cache-Control</tt> (ver la sección de configuración para más detalles).
 
-La gem/librería <tt>haml</tt> es necesaria para para renderizar plantillas HAML:
+== Vistas / Plantillas
 
-  # Vas a necesitar requerir haml en tu app
-  require 'haml'
+Cada lenguaje de plantilla se expone a través de un método de renderizado que
+lleva su nombre.  Estos métodos simplemente devuelven un string:
 
   get '/' do
-    haml :index
+    erb :index
   end
 
-Renderiza <tt>./views/index.haml</tt>.
-
-Las {opciones de Haml}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options]
-pueden ser ajustadas globalmente a través de las configuraciones de Sinatra,
-ver {Opciones y Configuraciones}[http://www.sinatrarb.com/configuration.html],
-y reemplazadas individualmente.
+Renderiza <tt>views/index.erb</tt>.
 
-  set :haml, :format => :html5 # el formato por defecto de Haml es :xhtml
+En lugar del nombre de la plantilla podés proporcionar directamente el
+contenido de la misma:
 
   get '/' do
-    haml :index, :format => :html4 # reemplazado
+    codigo = "<%= Time.now %>"
+    erb codigo
   end
 
-=== Plantillas Erb
-
-  # Vas a necesitar requerir erb en tu app
-  require 'erb'
+Los métodos de renderizado, aceptan además un segundo argumento, el hash de
+opciones:
 
   get '/' do
-    erb :index
+    erb :index, :layout => :post
   end
 
-Renderiza <tt>./views/index.erb</tt>
-
-=== Plantillas Erubis
+Renderiza <tt>views/index.erb</tt> embebido en <tt>views/post.erb</tt> (por
+defecto, la plantilla :index es embebida en <tt>views/layout.erb</tt> siempre y
+cuando este último archivo exista).
 
-La gem/librería <tt>erubis</tt> es necesaria para renderizar plantillas Erubis:
-
-  # Vas a necesitar requerir erubis en tu app
-  require 'erubis'
+Cualquier opción que Sinatra no entienda le será pasada al motor de renderizado
+de la plantilla:
 
   get '/' do
-    erubis :index
+    haml :index, :format => :html5
   end
 
-Renderiza <tt>./views/index.erubis</tt>.
-
-También es posible reemplazar Erb con Erubis:
+Además podés definir las opciones para un lenguaje de plantillas de forma
+general:
 
-  require 'erubis'
-  Tilt.register :erb, Tilt[:erubis]
+  set :haml, :format => :html5
 
   get '/' do
-    erb :index
+    haml :index
   end
 
-Renderiza <tt>./views/index.erb</tt> con Erubis.
+Las opciones pasadas al método de renderizado tienen precedencia sobre las
+definidas mediante +set+.
 
-=== Plantillas Builder
+Opciones disponibles:
 
-La gem/librería <tt>builder</tt> es necesaria para renderizar plantillas
-builder:
+[locals]
+  Lista de variables locales pasadas al documento.  Resultan muy útiles cuando
+  se combinan con parciales.
+  Ejemplo: <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>
 
-  # Vas a necesitar requerir builder en tu app
-  require 'builder'
+[default_encoding]
+  Encoding utilizado cuando el de un string es dudoso.  Por defecto toma el
+  valor de <tt>settings.default_encoding</tt>.
 
-  get '/' do
-    builder :index
-  end
+[views]
+  Directorio desde donde se cargan las vistas.  Por defecto toma el valor de
+  <tt>settings.views</tt>.
 
-Renderiza <tt>./views/index.builder</tt>.
+[layout]
+  Si es +true+ o +false+ indica que se debe usar, o nó, un layout,
+  respectivamente.  También puede ser un símbolo que especifique qué plantilla
+  usar.  Ejemplo: <tt>erb :index, :layout => !request.xhr?</tt>
 
-=== Plantillas Nokogiri
-
-La gem/librería <tt>nokogiri</tt> es necesaria para renderizar plantillas
-nokogiri:
+[content_type]
+  Content-Type que produce la plantilla.  El valor por defecto depende de cada
+  lenguaje de plantillas.
 
-  # Vas a necesitar requerir nokogiri en tu app
-  require 'nokogiri'
+[scope]
+  Ámbito en el que se renderiza la plantilla.  Por defecto utiliza la instancia
+  de la aplicación.  Tené en cuenta que si cambiás esta opción las variables de
+  instancia y los helpers van a dejar de estar disponibles.
 
-  get '/' do
-    nokogiri :index
-  end
+[layout_engine]
+  Motor de renderizado de plantillas que usa para el layout.  Resulta
+  conveniente para lenguajes que no soportan layouts.  Por defecto toma el valor
+  del motor usado para renderizar la plantilla.
+  Ejemplo: <tt>set :rdoc, :layout_engine => :erb</tt>
 
-Renderiza <tt>./views/index.nokogiri</tt>.
+Se asume que las plantillas están ubicadas directamente bajo el directorio
+<tt>./views</tt>. Para usar un directorio de vistas diferente:
 
-=== Plantillas Sass
+  set :views, settings.root + '/plantillas'
 
-La gem/librería <tt>haml</tt> o <tt>sass</tt> es necesaria para renderizar
-plantillas Sass:
+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 <tt>:'subdir/plantilla'</tt>). 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.
 
-  # Vas a necesitar requerir haml o sass en tu app
-  require 'sass'
+=== Lenguajes de Plantillas Disponibles
 
-  get '/stylesheet.css' do
-    sass :stylesheet
-  end
+Algunos lenguajes tienen varias implementaciones.  Para especificar que
+implementación usar (y para ser thread-safe), deberías requerirla antes de
+usarla:
 
-Renderiza <tt>./views/stylesheet.sass</tt>.
+  require 'rdiscount' # o require 'bluecloth'
+  get('/') { markdown :index }
 
-Las {opciones de Sass}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
-pueden ser ajustadas globalmente a través de las configuraciones de Sinatra,
-ver {Opciones y Configuraciones}[http://www.sinatrarb.com/configuration.html],
-y reemplazadas individualmente.
+=== Plantillas Haml
 
-  set :sass, :style => :compact # el estilo por defecto de Sass es :nested
+Dependencias::             {haml}[http://haml-lang.com/]
+Extensiones de Archivo::   <tt>.haml</tt>
+Ejemplo::                  <tt>haml :index, :format => :html5</tt>
 
-  get '/stylesheet.css' do
-    sass :stylesheet, :style => :expanded # reemplazado
-  end
+=== Plantillas Erb
 
-=== Plantillas Scss
+Dependencias::             {erubis}[http://www.kuwata-lab.com/erubis/] o
+                           erb (incluida en Ruby)
+Extensiones de Archivo::   <tt>.erb</tt>, <tt>.rhtml</tt> o <tt>.erubis</tt>
+                           (solamente con Erubis)
+Ejemplo::                  <tt>erb :index</tt>
 
-La gem/librería <tt>haml</tt> o <tt>sass</tt>es necesaria para renderizar
-plantillas Scss:
+=== Plantillas Builder
 
-  # Vas a necesitar requerir haml o sass en tu app
-  require 'sass'
+Dependencias::             {builder}[http://builder.rubyforge.org/]
+Extensiones de Archivo::   <tt>.builder</tt>
+Ejemplo::                  <tt>builder { |xml| xml.em "hola" }</tt>
 
-  get '/stylesheet.css' do
-    scss :stylesheet
-  end
+Además, acepta un bloque con la definición de la plantilla (ver el ejemplo).
 
-Renderiza <tt>./views/stylesheet.scss</tt>.
+=== Plantillas Nokogiri
 
-Las {opciones de Scss}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
-pueden ser ajustadas globalmente a través de las configuraciones de Sinatra,
-ver {Opciones y Configuraciones}[http://www.sinatrarb.com/configuration.html],
-y reemplazadas individualmente.
+Dependencias::             {nokogiri}[http://nokogiri.org/]
+Extensiones de Archivo::   <tt>.nokogiri</tt>
+Ejemplo::                  <tt>nokogiri { |xml| xml.em "hola" }</tt>
 
-  set :scss, :style => :compact # el estilo por defecto de Sass es :nested
+Además, acepta un bloque con la definición de la plantilla (ver el ejemplo).
 
-  get '/stylesheet.css' do
-    scss :stylesheet, :style => :expanded # reemplazado
-  end
+=== Plantillas Sass
 
-=== Plantillas Less
+Dependencias::             {sass}[http://sass-lang.com/]
+Extensiones de Archivo::   <tt>.sass</tt>
+Ejemplo::                  <tt>sass :stylesheet, :style => :expanded</tt>
 
-La gem/librería <tt>less</tt> es necesaria para renderizar plantillas Less:
+=== Plantillas SCSS
 
-  # Vas a necesitar requerir less en tu app
-  require 'less'
+Dependencias::             {scss}[http://sass-lang.com/]
+Extensiones de Archivo::   <tt>.scss</tt>
+Ejemplo::                  <tt>scss :stylesheet, :style => :expanded</tt>
 
-  get '/stylesheet.css' do
-    less :stylesheet
-  end
+=== Plantillas Less
 
-Renderiza <tt>./views/stylesheet.less</tt>.
+Dependencias::             {less}[http://www.lesscss.org/]
+Extensiones de Archivo::   <tt>.less</tt>
+Ejemplo::                  <tt>less :stylesheet</tt>
 
 === Plantillas Liquid
 
-La gem/librería <tt>liquid</tt> es necesaria para renderizar plantillas Liquid:
+Dependencias::             {liquid}[http://www.liquidmarkup.org/]
+Extensiones de Archivo::   <tt>.liquid</tt>
+Ejemplo::                  <tt>liquid :index, :locals => { :clave => 'valor' }</tt>
 
-  # Vas a necesitar requerir liquid en tu app
-  require 'liquid'
-
-  get '/' do
-    liquid :index
-  end
-
-Renderiza <tt>./views/index.liquid</tt>.
-
-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' }
+Como no vas a poder llamar a métodos de Ruby (excepto por +yield+) desde una
+plantilla Liquid, casi siempre vas a querer pasarle locales.
 
 === Plantillas Markdown
 
-La gem/librería <tt>rdiscount</tt> es necesaria para renderizar plantillas
-Markdown:
-
-  # Vas a necesitar requerir rdiscount en tu app
-  require "rdiscount"
-
-  get '/' do
-    markdown :index
-  end
-
-Renderiza <tt>./views/index.markdown</tt> (+md+ y +mkd+ también son extensiones
-de archivo válidas).
+Dependencias::             {rdiscount}[https://github.com/rtomayko/rdiscount],
+                           {redcarpet}[https://github.com/tanoku/redcarpet],
+                           {bluecloth}[http://deveiate.org/projects/BlueCloth],
+                           {kramdown}[http://kramdown.rubyforge.org/] *o*
+                           {maruku}[http://maruku.rubyforge.org/]
+Extensiones de Archivo::   <tt>.markdown</tt>, <tt>.mkd</tt> y <tt>.md</tt>
+Ejemplo::                  <tt>markdown :index, :layout_engine => :erb</tt>
 
 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:
@@ -416,52 +435,13 @@ plantillas:
 
 Como no podés utilizar Ruby desde Markdown, no podés usar layouts escritos en
 Markdown. De todos modos, es posible usar un motor de renderizado para el
-layout distinto al de la plantilla pasando la opción <tt>:layout_engine</tt>:
-
-  get '/' do
-    markdown :index, :layout_engine => :erb
-  end
-
-Renderiza <tt>./views/index.md</tt> con el layout <tt>./views/layout.erb</tt>.
-
-Recordá que podés asignar las opciones de renderizado globalmente:
-
-  set :markdown, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    markdown :index
-  end
-
-Renderiza <tt>./views/index.md</tt> (o cualquier otra plantilla Markdown) con
-el layout <tt>./views/post.haml</tt>.
-
-También es posible parsear Markdown con BlueCloth en lugar de RDiscount:
-
-  require 'bluecloth'
-
-  Tilt.register 'markdown', BlueClothTemplate
-  Tilt.register 'mkd',      BlueClothTemplate
-  Tilt.register 'md',       BlueClothTemplate
-
-  get '/' do
-    markdown :index
-  end
-
-Renderiza <tt>./views/index.md</tt> con BlueCloth.
+layout distinto al de la plantilla pasando la opción <tt>:layout_engine</tt>.
 
 === Plantillas Textile
 
-La gem/librería <tt>RedCloth</tt> es necesaria para renderizar plantillas
-Textile:
-
-  # Vas a necesitar requerir redcloth en tu app
-  require "redcloth"
-
-  get '/' do
-    textile :index
-  end
-
-Renderiza <tt>./views/index.textile</tt>.
+Dependencias::             {RedCloth}[http://redcloth.org/]
+Extensiones de Archivo::   <tt>.textile</tt>
+Ejemplo::                  <tt>textile :index, :layout_engine => :erb</tt>
 
 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:
@@ -476,38 +456,13 @@ plantillas:
 
 Como no podés utilizar Ruby desde Textile, no podés usar layouts escritos en
 Textile. De todos modos, es posible usar un motor de renderizado para el
-layout distinto al de la plantilla pasando la opción <tt>:layout_engine</tt>:
-
-  get '/' do
-    textile :index, :layout_engine => :erb
-  end
-
-Renderiza <tt>./views/index.textile</tt> con el layout
-<tt>./views/layout.erb</tt>.
-
-Recordá que podés asignar las opciones de renderizado globalmente:
-
-  set :textile, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    textile :index
-  end
-
-Renderiza <tt>./views/index.textile</tt> (o cualquier otra plantilla Textile)
-con el layout <tt>./views/post.haml</tt>.
+layout distinto al de la plantilla pasando la opción <tt>:layout_engine</tt>.
 
 === Plantillas RDoc
 
-La gem/librería <tt>rdoc</tt> es necesaria para renderizar plantillas RDoc:
-
-  # Vas a necesitar requerir rdoc/markup/to_html en tu app
-  require "rdoc/markup/to_html"
-
-  get '/' do
-    rdoc :index
-  end
-
-Renderiza <tt>./views/index.rdoc</tt>.
+Dependencias::             {rdoc}[http://rdoc.rubyforge.org/]
+Extensiones de Archivo::   <tt>.rdoc</tt>
+Ejemplo::                  <tt>rdoc :LEEME, :layout_engine => :erb</tt>
 
 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:
@@ -522,96 +477,58 @@ plantillas:
 
 Como no podés utilizar Ruby desde RDoc, no podés usar layouts escritos en RDoc.
 De todos modos, es posible usar un motor de renderizado para el layout distinto
-al de la plantilla pasando la opción <tt>:layout_engine</tt>:
-
-  get '/' do
-    rdoc :index, :layout_engine => :erb
-  end
-
-Renderiza <tt>./views/index.rdoc</tt> con el layout <tt>./views/layout.erb</tt>.
-
-Recordá que podés asignar las opciones de renderizado globalmente:
-
-  set :rdoc, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    rdoc :index
-  end
-
-Renderiza <tt>./views/index.rdoc</tt> (o cualquier otra plantilla RDoc) con el
-layout <tt>./views/post.haml</tt>.
+al de la plantilla pasando la opción <tt>:layout_engine</tt>.
 
 === Plantillas Radius
 
-La gem/librería <tt>radius</tt> es necesaria para renderizar plantillas Radius:
-
-  # Vas a necesitar requerir radius en tu app
-  require 'radius'
-
-  get '/' do
-    radius :index
-  end
-
-Renderiza <tt>./views/index.radius</tt>.
+Dependencias::             {radius}[http://radius.rubyforge.org/]
+Extensiones de Archivo::   <tt>.radius</tt>
+Ejemplo::                  <tt>radius :index, :locals => { :clave => 'valor' }</tt>
 
-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' }
+Como no vas a poder llamar a métodos de Ruby (excepto por +yield+) desde una
+plantilla Radius, casi siempre vas a querer pasarle locales.
 
 === Plantillas Markaby
 
-La gem/librería <tt>markaby</tt> es necesaria para renderizar plantillas
-Markaby:
-
-  # Vas a necesitar requerir markaby en tu app
-  require 'markaby'
-
-  get '/' do
-    markaby :index
-  end
-
-Renderiza <tt>./views/index.mab</tt>.
-
-También podés usar Markaby inline:
-
-  get '/' do
-    markaby { h1 "Bienvenido!" }
-  end
+Dependencias::             {markaby}[http://markaby.github.com/]
+Extensiones de Archivo::   <tt>.mab</tt>
+Ejemplos::                 <tt>markaby { h1 "Bienvenido!" }</tt>
 
+Además, acepta un bloque con la definición de la plantilla (ver el ejemplo).
 
 === Plantillas Slim
 
-La gem/librería <tt>slim</tt> es necesaria para renderizar plantillas Slim:
+Dependencias::             {slim}[http://slim-lang.com/]
+Extensiones de Archivo::   <tt>.slim</tt>
+Ejemplo::                  <tt>slim :index</tt>
 
-  # Vas a necesitar requerir slim en tu app
-  require 'slim'
+=== Plantillas Creole
 
-  get '/' do
-    slim :index
-  end
-
-Renderiza <tt>./views/index.slim</tt>.
+Dependencias::             {creole}[https://github.com/minad/creole]
+Extensiones de Archivo::   <tt>.creole</tt>
+Ejemplo::                  <tt>creole :wiki, :layout_engine => :erb</tt>
 
-=== Plantillas CoffeeScript
+No es posible llamar métodos desde creole, ni pasarle locales. Por lo tanto,
+generalmente vas a usarlo en combinación con otro motor de renderizado:
 
-La gem/librería <tt>coffee-script</tt> y al menos <b>una</b> de las siguientes
-opciones para ejecutar JavaScript:
+  erb :resumen, :locals => { :texto => cerole(:introduccion) }
 
-* +node+ (de Node.js) en tu path
-* utilizar OSX
-* la gem/librería +therubyracer+
+Tené en cuenta que también podés llamar al método +creole+ desde otras
+plantillas:
 
-son necesarios para renderizar plantillas CoffeeScript:
+  %h1 Hola Desde Haml!
+  %p= creole(:saludos)
 
-  # Vas a necesitar requerir coffee-script en tu app
-  require 'coffee-script'
+Como no podés utilizar Ruby desde Creole, no podés usar layouts escritos en
+Creloe.  De todos modos, es posible usar un motor de renderizado para el layout
+distinto al de la plantilla pasando la opción <tt>:layout_engine</tt>.
 
-  get '/application.js' do
-    coffee :application
-  end
+=== Plantillas CoffeeScript
 
-Renderiza <tt>./views/application.coffee</tt>.
+Dependencias::             {coffee-script}[https://github.com/josh/ruby-coffee-script]
+                           y un {mecanismo para ejecutar javascript}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
+Extensiones de Archivo::   <tt>.coffee</tt>
+Ejemplo::                  <tt>coffee :index</tt>
 
 === Plantillas Embebidas
 
@@ -636,7 +553,7 @@ 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 }
+    haml '%h1= bar.nombre', :locals => { :bar => foo }
   end
 
 Esto es usado típicamente cuando se renderizan plantillas como parciales desde
@@ -825,6 +742,11 @@ compartan un único secreto, tenés que definirlo vos:
 
   set :session_secret, 'super secreto'
 
+Si necesitás una configuración más específica, +sessions+ acepta un
+Hash con opciones:
+
+  set :sessions, :domain => 'foo.com'
+
 === Interrupción
 
 Para detener inmediatamente una petición dentro de un filtro o una ruta usá:
@@ -916,7 +838,7 @@ De manera similar, también podés asignar el código de estado y encabezados:
   get '/foo' do
     status 418
     headers \
-      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN"
+      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN",
       "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
     body "I'm a tea pot!"
   end
@@ -924,13 +846,95 @@ De manera similar, también podés asignar el código de estado y encabezados:
 También, al igual que +body+, tanto +status+ como +headers+ pueden utilizarse
 para obtener sus valores cuando no se les pasa argumentos.
 
+=== Streaming De Respuestas
+
+A veces vas a querer empezar a enviar la respuesta a pesar de que todavía no
+terminaste de generar su cuerpo.  También es posible que, en algunos casos,
+quieras seguir enviando información hasta que el cliente cierre la conexión.
+Cuando esto ocurra, el +stream+ helper te va a ser de gran ayuda:
+
+  get '/' do
+    stream do |out|
+      out << "Esto va a ser legen -\n"
+      sleep 0.5
+      out << " (esperalo) \n"
+      sleep 1
+      out << "- dario!\n"
+    end
+  end
+
+Podés implementar APIs de streaming,
+{Server-Sent Events}[http://dev.w3.org/html5/eventsource/] y puede ser usado
+como base para {WebSockets}[http://es.wikipedia.org/wiki/WebSockets].  También
+puede ser usado para incrementar el throughput si solo una parte del contenido
+depende de un recurso lento.
+
+Hay que tener en cuenta que el comportamiento del streaming, especialmente el
+número de peticiones concurrentes, depende del servidor web utilizado para
+servir la aplicación.  Puede que algunos servidores, como es el caso de
+WEBRick, no soporten streaming directamente, así el cuerpo de la respuesta será
+enviado completamente de una vez cuando el bloque pasado a +stream+ finalice su
+ejecución.  Si estás usando Shotgun, el streaming no va a funcionar.
+
+Cuando se pasa +keep_open+ como parámetro, no se va a enviar el mensaje
++close+ al objeto de stream. Queda en vos cerrarlo en el punto de ejecución
+que quieras. Nuevamente, hay que tener en cuenta que este comportamiento es
+posible solo en servidores que soporten eventos, como Thin o Rainbows. El
+resto de los servidores van a cerrar el stream de todos modos:
+
+  set :server, :thin
+  conexiones = []
+
+  get '/' do
+    # mantenemos abierto el stream
+    stream(:keep_open) { |salida| conexiones << salida }
+  end
+
+  post '/' do
+    # escribimos a todos los streams abiertos
+    conexiones.each { |salida| salida << params[:mensaje] << "\n" }
+    "mensaje enviado"
+  end
+
+=== Log (Registro)
+
+En el ámbito de la petición, el helper +logger+ (registrador) expone
+una instancia de +Logger+:
+
+  get '/' do
+    logger.info "cargando datos"
+    # ...
+  end
+
+Este logger tiene en cuenta la configuración de logueo de tu Rack
+handler.  Si el logueo está desactivado, este método va a devolver un
+objeto que se comporta como un logger pero que en realidad no hace
+nada.  Así, no vas a tener que preocuparte por esta situación.
+
+Tené en cuenta que el logueo está habilitado por defecto únicamente
+para <tt>Sinatra::Application</tt>.  Si heredaste de
+<tt>Sinatra::Base</tt>, probablemente quieras habilitarlo manualmente:
+
+  class MiApp < Sinatra::Base
+    configure :production, :development do
+      enable :logging
+    end
+  end
+
+Para evitar que se inicialice cualquier middleware de logging, configurá
++logging+ a +nil+.  Tené en cuenta que, cuando hagas esto, +logger+ va a
+devolver +nil+.  Un caso común es cuando querés usar tu propio logger. Sinatra
+va a usar lo que encuentre en <tt>env['rack.logger']</tt>.
+
 === Tipos Mime
 
 Cuando usás <tt>send_file</tt> 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'
+  configure do
+    mime_type :foo, 'text/foo'
+  end
 
 También lo podés usar con el ayudante +content_type+:
 
@@ -983,7 +987,7 @@ búsqueda:
 
 O usar una sesión:
 
-  enable :session
+  enable :sessions
 
   get '/foo' do
     session[:secreto] = 'foo'
@@ -1019,7 +1023,7 @@ Si estás usando el helper +expires+ para definir el encabezado correspondiente,
     expires 500, :public, :must_revalidate
   end
 
-Para usar cachés adecuadamente, deberías considerar usar +etag+ y
+Para usar cachés adecuadamente, deberías considerar usar +etag+ o
 +last_modified+.  Es recomendable que llames a estos helpers *antes* de hacer
 cualquier trabajo pesado, ya que van a enviar la respuesta inmediatamente si
 el cliente ya tiene la versión actual en su caché:
@@ -1038,7 +1042,8 @@ También es posible usar una
 
 Estos helpers no van a cachear nada por vos, sino que van a facilitar la
 información necesaria para poder hacerlo.  Si estás buscando soluciones rápidas
-de cacheo, mirá {rack-cache}[http://rtomayko.github.com/rack-cache/]:
+de cacheo con proxys inversos, mirá
+{rack-cache}[http://rtomayko.github.com/rack-cache/]:
 
   require "rack/cache"
   require "sinatra"
@@ -1051,6 +1056,27 @@ de cacheo, mirá {rack-cache}[http://rtomayko.github.com/rack-cache/]:
     "hola"
   end
 
+Usá la configuración <tt>:static_cache_control</tt> para agregar el encabezado
+<tt>Cache-Control</tt> a archivos estáticos (ver la sección de configuración
+para más detalles).
+
+De acuerdo con la RFC 2616 tu aplicación debería comportarse diferente si a las
+cabeceras If-Match o If-None-Match se le asigna el valor <tt>*</tt> cuando el
+recurso solicitado ya existe.  Sinatra asume para peticiones seguras (como get)
+e idempotentes (como put) que el recurso existe, mientras que para el resto
+(como post), que no.  Podes cambiar este comportamiento con la opción
+<tt>:new_resource</tt>:
+
+  get '/crear' do
+    etag '', :new_resource => true
+    Articulo.create
+    erb :nuevo_articulo
+  end
+
+Si querés seguir usando una weak ETag, indicalo con la opción <tt>:kind</tt>:
+
+  etag '', :new_resource => true, :kind => :weak
+
 === Enviando Archivos
 
 Para enviar archivos, podés usar el método <tt>send_file</tt>:
@@ -1095,29 +1121,33 @@ petición (filtros, rutas y manejadores de errores) a través del método
 
   # 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.referrer          # 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 (sería verdadero sobre ssl)
-    request.forwarded?        # verdadero (si se está corriendo atrás de un proxy inverso)
-    requuest.env              # hash de entorno directamente entregado por Rack
+    t = %w[text/css text/html application/javascript]
+    request.accept              # ['text/html', '*/*']
+    request.accept? 'text/xml'  # true
+    request.preferred_type(t)   # 'text/html'
+    request.body                # 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?                # true (hay métodos análogos para los otros verbos)
+    request.form_data?          # false
+    request["UNA_CABECERA"]     # valor de la cabecera UNA_CABECERA
+    request.referrer            # 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?             # false (sería true sobre ssl)
+    request.forwarded?          # true (si se está corriendo atrás de un proxy inverso)
+    requuest.env                # hash de entorno directamente entregado por Rack
   end
 
 Algunas opciones, como <tt>script_name</tt> o <tt>path_info</tt> pueden
@@ -1154,6 +1184,38 @@ También podés pasarle un nombre de archivo:
     "guardalo!"
   end
 
+=== Fecha y Hora
+
+Sinatra pone a tu disposición el helper +time_for+, que genera un objeto +Time+
+a partir del valor que recibe como argumento.  Este valor puede ser un
++String+, pero también es capaz de convertir objetos +DateTime+, +Date+ y de
+otras clases similares:
+
+  get '/' do
+    pass if Time.now > time_for('Dec 23, 2012')
+    "todavía hay tiempo"
+  end
+
+Este método es usado internamente por métodos como +expires+ y +last_modified+,
+entre otros.  Por lo tanto, es posible extender el comportamiento de estos
+métodos sobreescribiendo +time_for+ en tu aplicación:
+
+  helpers do
+    def time_for(value)
+      case value
+      when :ayer then Time.now - 24*60*60
+      when :mañana then Time.now + 24*60*60
+      else super
+      end
+    end
+  end
+
+  get '/' do
+    last_modified :ayer
+    expires :mañana
+    "hola"
+  end
+
 === Buscando los Archivos de las Plantillas
 
 El helper <tt>find_template</tt> se utiliza para encontrar los archivos de las
@@ -1243,101 +1305,147 @@ Podés acceder a estas opciones utilizando el método <tt>settings</tt>:
     ...
   end
 
+=== Configurando la Protección de Ataques
+
+Sinatra usa {Rack::Protection}[https://github.com/rkh/rack-protection#readme]
+para defender a tu aplicación de los ataques más comunes.  Tenés que tener en
+cuenta que como consecuencia de esto puede venir asociada una disminución del
+rendimiento de tu aplicación.  Si por este, o algún otro motivo, querés
+desactivar está funcionalidad, podés hacerlo:
+
+  disable :protection
+
+También es posible desactivar una única capa de defensa:
+
+  set :protection, :except => :path_traversal
+
+O varias:
+
+  set :protection, :except => [:path_traversal, :session_hijacking]
+
 === Configuraciones Disponibles
 
-[absolute_redirects]  si está deshabilitada, Sinatra va a permitir redirecciones
-                      relativas, sin embargo, como consecuencia de esto, va a
-                      dejar de cumplir con el RFC 2616 (HTTP 1.1), que solamente
-                      permite redirecciones absolutas.
+[absolute_redirects]   si está deshabilitada, Sinatra va a permitir
+                       redirecciones relativas, sin embargo, como consecuencia
+                       de esto, va a dejar de cumplir con el RFC 2616 (HTTP
+                       1.1), que solamente permite redirecciones absolutas.
+
+                       Activalo si tu apliación está corriendo atrás de un proxy
+                       inverso que no se ha configurado adecuadamente.  Notá que
+                       el helper +url+ va a seguir produciendo URLs absolutas, a
+                       menos que le pasés +false+ como segundo parámetro.
+
+                       Deshabilitada por defecto.
+
+[add_charsets]         tipos mime a los que el helper <tt>content_type</tt> les
+                       añade automáticamente el charset.
 
-                      Activalo si tu apliación está corriendo atrás de un proxy
-                      inverso que no se ha configurado adecuadamente.  Notá que
-                      el helper +url+ va a seguir produciendo URLs absolutas, a
-                      menos que le pasés +false+ como segundo parámetro.
+                       En general, no deberías asignar directamente esta opción,
+                       sino añadirle los charsets que quieras:
 
-                      Deshabilitada por defecto.
+                         settings.add_charsets << "application/foobar"
 
-[add_charsets]        tipos mime a los que el helper <tt>content_type</tt> les
-                      añade automáticamente el charset.
+[app_file]             path del archivo principal de la aplicación, se utiliza
+                       para detectar la raíz del proyecto, el directorio de las
+                       vistas y el público, así como las plantillas inline.
 
-                      En general, no deberías asignar directamente esta opción,
-                      sino añadirle los charsets que quieras:
+[bind]                 dirección IP que utilizará el servidor integrado (por
+                       defecto: 0.0.0.0).
 
-                        settings.add_charsets << "application/foobar"
+[default_encoding]     encoding utilizado cuando el mismo se desconoce (por
+                       defecto <tt>"utf-8"</tt>).
 
-[app_file]            archivo principal de la aplicación, se utiliza para
-                      detectar la raíz del proyecto, el directorio de las vistas
-                      y el público así como las plantillas inline.
+[dump_errors]          mostrar errores en el log.
 
-[bind]                dirección IP que utilizará el servidor integrado (por
-                      defecto: 0.0.0.0).
+[environment]          entorno actual, por defecto toma el valor de
+                       <tt>ENV['RACK_ENV']</tt>, o <tt>"development"</tt> si no
+                       está disponible.
 
-[default_encoding]    encoding utilizado cuando el mismo se desconoce (por
-                      defecto <tt>"utf-8"</tt>).
+[logging]              define si se utiliza el logger.
 
-[dump_errors]         mostrar errores en el log.
+[lock]                 coloca un lock alrededor de cada petición, procesando
+                       solamente una por proceso.
 
-[environment]         entorno actual, por defecto toma el valor de
-                      <tt>ENV['RACK_ENV']</tt>, o <tt>"development"</tt> si no
-                      está disponible.
+                       Habilitá esta opción si tu aplicación no es thread-safe.
+                       Se encuentra deshabilitada por defecto.
 
-[logging]             define si se utiliza el logger.
+[method_override]      utiliza el parámetro <tt>_method</tt> para permtir
+                       formularios put/delete en navegadores que no los
+                       soportan.
 
-[lock]                coloca un lock alrededor de cada petición, procesando
-                      solamente una por proceso.
+[port]                 puerto en el que escuchará el servidor integrado.
 
-                      Habilitá esta opción si tu aplicación no es thread-safe.
-                      Se encuentra deshabilitada por defecto.
+[prefixed_redirects]   define si inserta <tt>request.script_name</tt> en las
+                       redirecciones cuando no se proporciona un path absoluto.
+                       De esta manera, cuando está habilitada,
+                       <tt>redirect '/foo'</tt> se comporta de la misma manera
+                       que <tt>redirect to('/foo')</tt>.  Se encuentra
+                       deshabilitada por defecto.
 
-[method_override]     utiliza el parámetro <tt>_method</tt> para permtir
-                      formularios put/delete en navegadores que no los soportan.
+[protection]           define si deben activarse las protecciones para los
+                       ataques web más comunes.  Para más detalles mirá la
+                       sección sobre la configuración de protección de ataques
+                       más arriba.
 
-[port]                puerto en el que escuchará el servidor integrado.
+[public_folder]        path del directorio desde donde se sirven los archivos
+                       públicos.  Solo se utiliza cuando se sirven archivos
+                       estáticos (ver la opción <tt>static</tt>).  Si no
+                       está presente, se infiere del valor de la opción
+                       <tt>app_file</tt>.
 
-[prefixed_redirects]  define si inserta <tt>request.script_name</tt> en las
-                      redirecciones cuando no se proporciona un path absoluto.
-                      De esta manera, cuando está habilitada,
-                      <tt>redirect '/foo'</tt> se comporta de la misma manera
-                      que <tt>redirect to('/foo')</tt>.  Se encuentra
-                      deshabilitada por defecto.
+[reload_templates]     define si se recargan las plantillas entre peticiones.
 
-[public]              directorio desde donde se sirven los archivos públicos.
+                       Se encuentra activado en el entorno de desarrollo.
 
-[reload_templates]    define si se recargan las plantillas entre peticiones.
+[root]                 path del directorio raíz del proyecto.  Si no está
+                       presente, se infiere del valor de la opción
+                       <tt>app_file</tt>.
 
-                      Se encuentra activado en el entorno de desarrollo y en
-                      Ruby 1.8.6 (para compoensar un bug en Ruby que provoca una
-                      pérdida de memoria).
+[raise_errors]         elevar excepciones (detiene la aplicación).  Se
+                       encuentra activada por defecto cuando el valor de
+                       <tt>environment</tt>  es <tt>"test"</tt>.  En caso
+                       contrario estará desactivada.
 
-[root]                directorio raíz del proyecto.
+[run]                  cuando está habilitada, Sinatra se va a encargar de
+                       iniciar el servidor web, no la habilités cuando estés
+                       usando rackup o algún otro medio.
 
-[raise_errors]        elevar excepciones (detiene la aplicación).
+[running]              indica si el servidor integrado está ejecutandose, ¡no
+                       cambiés esta configuración!.
 
-[run]                 cuando está habilitada, Sinatra se va a encargar de
-                      iniciar el servidor web, no la habilités cuando estés
-                      usando rackup o algún otro medio.
+[server]               servidor, o lista de servidores, para usar como servidor
+                       integrado.  Por defecto: ['thin', 'mongrel', 'webrick'],
+                       el orden establece la prioridad.
 
-[running]             indica si el servidor integrado está ejecutandose, ¡no
-                      cambiés esta configuración!.
+[sessions]             habilita el soporte de sesiones basadas en cookies a
+                       través de <tt>Rack::Session::Cookie</tt>.  Ver la
+                       sección 'Usando Sesiones' para más información.
 
-[server]              servidor, o lista de servidores, para usar como servidor
-                      integrado.  Por defecto: ['thin', 'mongrel', 'webrick'],
-                      el orden establece la prioridad.
+[show_exceptions]      muestra un stack trace en el navegador cuando ocurre una
+                       excepción.  Se encuentra activada por defecto cuando el
+                       valor de <tt>environment</tt> es <tt>"development"</tt>.
+                       En caso contrario estará desactivada.
 
-[sessions]            habilita sesiones basadas en cookies.
+[static]               define si Sinatra debe encargarse de servir archivos
+                       estáticos.
 
-[show_exceptions]     muestra un stack trace en el navegador.
+                       Deshabilitala cuando usés un servidor capaz de
+                       hacerlo por sí solo, porque mejorará el
+                       rendimiento.  Se encuentra habilitada por
+                       defecto en el estilo clásico y desactivado en el
+                       el modular.
 
-[static]              define si Sinatra debe encargarse de servir archivos
-                      estáticos.
+[static_cache_control] cuando Sinatra está sirviendo archivos estáticos, y
+                       está opción está habilitada, les va a agregar encabezados
+                       <tt>Cache-Control</tt> a las respuestas.  Para esto
+                       utiliza el helper +cache_control+.  Se encuentra
+                       deshabilitada por defecto.  Notar que es necesario
+                       utilizar un array cuando se asignan múltiples valores: 
+                       <tt>set :static_cache_control, [:public, :max_age => 300]</tt>.
 
-                      Deshabilitala cuando usés un servidor capaz de
-                      hacerlo por sí solo, porque mejorará el
-                      rendimiento.  Se encuentra habilitada por
-                      defecto en el estilo clásico y desactivado en el
-                      el modular.
+[views]                path del directorio de las vistas.  Si no está presente,
+                       se infiere del valor de la opción <tt>app_file</tt>.
 
-[views]               directorio de las vistas.
 
 == Manejo de Errores
 
@@ -1435,11 +1543,17 @@ 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.
 
+Podés encontrar middleware útil en
+{rack}[https://github.com/rack/rack/tree/master/lib/rack],
+{rack-contrib}[https://github.com/rack/rack-contrib#readme],
+con {CodeRack}[http://coderack.org/] o en la
+{Rack wiki}[https://github.com/rack/rack/wiki/List-of-Middleware].
+
 == 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}[http://gitrdoc.com/brynary/rack-test]:
+{Rack::Test}[http://rdoc.info/github/brynary/rack-test/master/frames]:
 
   require 'mi_app_sinatra'
   require 'test/unit'
@@ -1468,9 +1582,6 @@ cualquier framework o librería de pruebas basada en Rack. Se recomienda usar
     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
@@ -1479,8 +1590,8 @@ 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:
+directorios <tt>./public</tt> y <tt>./views</tt>, logging, página con detalles
+de excepción, etc.). Ahí es donde <tt>Sinatra::Base</tt> entra en el juego:
 
   require 'sinatra/base'
 
@@ -1493,16 +1604,16 @@ etc.). Ahí es donde Sinatra::Base entra en el juego:
     end
   end
 
-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:
+Las subclases de <tt>Sinatra::Base</tt> 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
+<tt>Sinatra::Base</tt> con dos modificaciones:
 
 * Tu archivo debe requerir <tt>sinatra/base</tt> 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.
+  en una subclase de <tt>Sinatra::Base</tt>.
 
 <tt>Sinatra::Base</tt> es una pizarra en blanco. La mayoría de las opciones están
 desactivadas por defecto, incluyendo el servidor incorporado. Mirá
@@ -1531,7 +1642,7 @@ entre sus configuraciones:
 
   Configuración       Clásica                      Modular
 
-  app_file            archivo que carga sinatra    nil
+  app_file            archivo que carga sinatra    archivo con la subclase de Sinatra::Base
   run                 $0 == app_file               false
   logging             true                         false
   method_override     true                         false
@@ -1560,7 +1671,7 @@ Iniciar con:
 O, con un archivo <tt>config.ru</tt>, que permite usar cualquier handler Rack:
 
   # config.ru
-  require 'mi_app'
+  require './mi_app'
   run MiApp
 
 Después ejecutar:
@@ -1580,7 +1691,7 @@ Escribí el archivo de tu aplicación:
 
 Y el <tt>config.ru</tt> correspondiente:
 
-  require 'app'
+  require './app'
   run Sinatra::Application
 
 === ¿Cuándo Usar config.ru?
@@ -1611,7 +1722,7 @@ aplicación basada en Rack (Rails/Ramaze/Camping/...):
     get('/login') { haml :login }
 
     post('/login') do
-      if params[:nombre] = 'admin' and params[:password] = 'admin'
+      if params[:nombre] == 'admin' && params[:password] == 'admin'
         session['nombre_de_usuario'] = params[:nombre]
       else
         redirect '/login'
@@ -1645,6 +1756,7 @@ esto tenés <tt>Sinatra.new</tt>:
 Acepta como argumento opcional una aplicación desde la que se
 heredará:
 
+  # config.ru
   require 'sinatra/base'
 
   controller = Sinatra.new do
@@ -1681,12 +1793,12 @@ 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 (<tt>require 'sinatra'</tt>), 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.
+Cada aplicación Sinatra es una subclase de <tt>Sinatra::Base</tt>. Si estás
+usando el DSL de top-level (<tt>require 'sinatra'</tt>), entonces esta clase es
+<tt>Sinatra::Application</tt>, 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:
 
@@ -1775,42 +1887,47 @@ Las opciones son:
   -s # especifica el servidor/manejador rack (thin es usado por defecto)
   -x # activa el mutex lock (está desactivado por defecto)
 
-== Requerimientos
-
-Se recomienda instalar Sinatra en Ruby 1.8.7, 1.9.2, JRuby o Rubinius.
+== Versiones de Ruby Soportadas
 
 Las siguientes versiones de Ruby son soportadas oficialmente:
 
-[ Ruby 1.8.6 ]
-  No se recomienda utilizar Sinatra en 1.8.6. Sin embargo, esta versión será
-  soportada oficialmente hasta que se libere Sinatra 1.3.0.  RDoc y CoffeeScript
-  no son soportadas por esta versión de Ruby. 1.8.6 contiene una falla
-  importante de pérdida de memoria en su implementación de Hash, que afecta a
-  las versiones de Sinatra anteriores a 1.1.1.  La versión actual evita
-  explícitamente esta falla a expensas de una disminución del rendimiento.  Por
-  último, Rack >= 1.2 dejó de soportar 1.8.6, por lo que vas a tener que usar
-  alguna versión 1.1.x.
-
 [ Ruby 1.8.7 ]
   1.8.7 es soportado completamente.  Sin embargo, si no hay nada que te lo
-  prohíba, te recomendamos que usés 1.9.2 o cambies a JRuby o Rubinius.
+  prohíba, te recomendamos que usés 1.9.2 o cambies a JRuby o Rubinius.  No se
+  dejará de dar soporte a 1.8.7 hasta Sinatra 2.0 y Ruby 2.0, aunque si se
+  libera la versión 1.8.8 de Ruby las cosas podrían llegar a cambiar.  Sin
+  embargo, que eso ocurra es muy poco probable, e incluso el caso de que lo
+  haga, puede que se siga dando soporte a 1.8.7.  <b>Hemos dejado de soportar
+  Ruby 1.8.6.</b> Si querés ejecutar Sinatra sobre 1.8.6, podés utilizar la
+  versión 1.2, pero tené en cuenta que una vez que Sinatra 1.4.0 sea liberado,
+  ya no se corregirán errores por más que se reciban reportes de los mismos.
 
 [ Ruby 1.9.2 ]
-  1.9.2 es soportado y recomendado.  Tené en cuenta que Radius y Markaby no
-  son compatibles con 1.9 actualmente.  Además, no usés 1.9.2p0, porque produce
-  fallos de segmentación cuando se utiliza Sinatra.
+  1.9.2 es soportado y recomendado.  Tené en cuenta que Radius y Markaby no son
+  compatibles con 1.9 actualmente.  Además, no usés 1.9.2p0, porque se producen
+  fallos de segmentación cuando se ejecuta Sinatra.  El soporte se mantendrá al
+  menos hasta que se libere la versión 1.9.4/2.0 de Ruby.  El soporte para la
+  última versión de la serie 1.9 se mantendrá mientras lo haga el core team de
+  Ruby.
+
+[ Ruby 1.9.3 ]
+  1.9.3 es soportado completamente.  De todas maneras, recomendamos esperar a
+  que se liberen niveles de parche superiores (el actual es p0) antes de usarlo
+  en producción.  Es importante notar que el cambio desde una versión anterior a
+  1.9.3 va a invalidar todas las sesiones.
 
 [ Rubinius ]
-  Rubinius es soportado oficialmente (Rubinius >= 1.2.3).  Todo
-  funciona correctamente, incluyendo los lenguajes de plantillas.
+  Rubinius es soportado oficialmente (Rubinius >= 1.2.4).  Todo funciona
+  correctamente, incluyendo los lenguajes de plantillas.  La próxima versión,
+  2.0, también es soportada.
 
 [ JRuby ]
-  JRuby es soportado oficialmente (JRuby >= 1.6.0).  No se conocen
-  problemas con librerías de plantillas de terceras partes.  Sin
-  embargo, si elegís usar JRuby, deberías examinar sus Rack handlers
-  porque el servidor web Thin no es soportado completamente.  El
-  soporte de JRuby para extensiones C se encuentra en una etapa
-  experimental, sin embargo, de momento solo RDiscount se ve afectada.
+  JRuby es soportado oficialmente (JRuby >= 1.6.5).  No se conocen problemas
+  con librerías de plantillas de terceras partes.  Sin embargo, si elegís usar
+  JRuby, deberías examinar sus Rack handlers porque el servidor web Thin no es
+  soportado completamente.  El soporte de JRuby para extensiones C se encuentra
+  en una etapa experimental, sin embargo, de momento solamente RDiscount,
+  Redcarpet y RedCloth se ven afectadas.
 
 Siempre le prestamos atención a las nuevas versiones de Ruby.
 
@@ -1818,21 +1935,25 @@ Las siguientes implementaciones de Ruby no se encuentran soportadas
 oficialmente.  De cualquier manera, pueden ejecutar Sinatra:
 
 * Versiones anteriores de JRuby y Rubinius
+* Ruby Enterprise Edition
 * MacRuby, Maglev e IronRuby
-* Ruby 1.9.0 y 1.9.1
+* Ruby 1.9.0 y 1.9.1 (pero no te recomendamos que los usés)
 
 No estar soportada oficialmente, significa que si las cosas solamente se rompen
 ahí y no en una plataforma soportada, asumimos que no es nuestro problema sino
 el suyo.
 
-Nuestro servidor CI también se ejecuta sobre ruby-head (que será la
-próxima versión 1.9.3).  Como está en movimiento constante, no podemos
-garantizar nada.  De todas formas, podés contar con que 1.9.3-p0 sea
-soportada.
+Nuestro servidor CI también se ejecuta sobre ruby-head (que será la próxima
+versión 2.0.0) y la rama 1.9.4.  Como están en movimiento constante, no podemos
+garantizar nada.  De todas formas, podés contar con que tanto 1.9.4-p0 como
+2.0.0-p0 sea soportadas.
 
 Sinatra debería funcionar en cualquier sistema operativo soportado por la
 implementación de Ruby elegida.
 
+En este momento, no vas a poder ejecutar Sinatra en Cardinal, SmallRuby,
+BlueRuby o cualquier versión de Ruby anterior a 1.8.7.
+
 == A la Vanguardia
 
 Si querés usar el código de Sinatra más reciente, sentite libre de ejecutar
@@ -1912,7 +2033,11 @@ siguiendo las especificaciones SemVer y SemVerTag.
 * {Twitter}[http://twitter.com/sinatra]
 * {Lista de Correo}[http://groups.google.com/group/sinatrarb/topics]
 * {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] en http://freenode.net
+* {Sinatra Book}[http://sinatra-book.gittr.com] Tutorial (en inglés).
+* {Sinatra Recipes}[http://recipes.sinatrarb.com/] Recetas contribuidas
+  por la comunidad (en inglés).
 * Documentación de la API para la
   {última versión liberada}[http://rubydoc.info/gems/sinatra] o para la
   {rama de desarrollo actual}[http://rubydoc.info/github/sinatra/sinatra]
   en http://rubydoc.info/
+* {Servidor de IC}[http://ci.rkh.im/view/Sinatra/]
diff --git a/README.fr.rdoc b/README.fr.rdoc
index 627f035..159efa7 100644
--- a/README.fr.rdoc
+++ b/README.fr.rdoc
@@ -1,31 +1,32 @@
-= Sinatra
-<i>Attention: Ce document correspond à la traduction de la version anglaise et
+= Sinatra
+
+<i>Attention : Ce document correspond à la traduction de la version anglaise et
 il n'est peut être plus à jour.</i>
 
-Sinatra est un DSL pour créer rapidement des applications web en Ruby et sans
-effort:
+Sinatra est un DSL pour créer rapidement et facilement des applications web en
+Ruby :
 
   # mon_application.rb
   require 'sinatra'
+
   get '/' do
-    'Bonjour Monde!'
+    'Bonjour le monde !'
   end
 
-Installez le gem et lancez avec:
+Installez la gem et lancez avec :
 
   gem install sinatra
   ruby -rubygems mon_application.rb
 
-Le résultat est visible sur: http://localhost:4567
+Le résultat est visible sur : http://localhost:4567
 
-Il est également recommandé d'exécuter <tt>gem install thin</tt>, que Sinatra
-utilisera si disponible.
+Il est recommandé d'exécuter également <tt>gem install thin</tt>, pour que
+Sinatra utilise le server Thin quand il est disponible.
 
 == Routes
 
 Dans Sinatra, une route est une méthode HTTP couplée à un masque (pattern)
-URL.
-Chaque route est associée à un bloc:
+URL. Chaque route est associée à un bloc :
 
   get '/' do
     .. montrer quelque chose ..
@@ -36,6 +37,10 @@ Chaque route est associée à un bloc:
   end
 
   put '/' do
+    .. remplacer quelque chose ..
+  end
+
+  patch '/' do
     .. changer quelque chose ..
   end
 
@@ -47,54 +52,66 @@ Chaque route est associée à un bloc:
     .. apaiser quelquechose ..
   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 routes sont évaluées  dans l'ordre où elles ont été définies. La première
+route qui correspond à la requête est appelée.
 
 Les masques peuvent inclure des paramètres nommés, accessibles par
-l'intermédiaire du hash <tt>params</tt>:
+l'intermédiaire du hash <tt>params</tt> :
 
   get '/bonjour/:nom' do
     # répond aux requêtes "GET /bonjour/foo" et "GET /bonjour/bar"
     # params[:nom] est 'foo' ou 'bar'
-    "Bonjour #{params[:nom]}!"
+    "Bonjour #{params[:nom]} !"
   end
 
-Vous pouvez aussi les nommer directement dans les paramètres du bloc comme
-ceci:
+Vous pouvez aussi accéder aux paramètres nommés directement grâce aux
+paramètres du bloc comme ceci :
 
   get '/bonjour/:nom' do |n|
-    "Bonjour #{n}!"
+    "Bonjour #{n} !"
   end
 
 Une route peut contenir un splat (caractère joker), accessible par
-l'intermédiaire de la liste <tt>params[:splat]</tt>:
+l'intermédiaire du tableau <tt>params[:splat]</tt> :
 
   get '/dire/*/a/*' do
-    # répondrait à /dire/bonjour/a/monde
+    # répond à /dire/bonjour/a/monde
     params[:splat] # => ["bonjour", "monde"]
   end
 
   get '/telecharger/*.*' do
-    # répondrait à /telecharger/chemin/vers/fichier.xml
+    # répond à /telecharger/chemin/vers/fichier.xml
     params[:splat] # => ["chemin/vers/fichier", "xml"]
   end
 
-Une route peut s'exprimer avec une Expression Régulière:
+Ou par l'intermédiaire des paramètres du bloc :
+
+  get '/telecharger/*.*' do |chemin, ext|
+    [chemin, ext] # => ["path/to/file", "xml"]
+  end
+
+Une route peut aussi être définie par une Expression Régulière :
 
   get %r{/bonjour/([\w]+)} do
-    "Bonjour, #{params[:captures].first}!"
+    "Bonjour, #{params[:captures].first} !"
   end
 
-Là aussi on peut utiliser les paramètres de bloc:
+Là encore on peut utiliser les paramètres de bloc :
 
   get %r{/bonjour/([\w]+)} do |c|
-    "Bonjour, #{c}!"
+    "Bonjour, #{c} !"
+  end
+
+Les routes peuvent aussi comporter des paramètres optionnels :
+
+  get '/posts.?:format?' do
+    # répond à "GET /posts" et aussi à "GET /posts.json", "GET /posts.xml" etc...
   end
 
 === Conditions
 
 Les routes peuvent définir toutes sortes de conditions, comme par exemple le
-"user agent":
+"user agent" :
 
   get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
     "Vous utilisez Songbird version #{params[:agent][0]}"
@@ -104,10 +121,10 @@ Les routes peuvent définir toutes sortes de conditions, comme par exemple le
     # Correspond à tous les autres navigateurs
   end
 
-Les autres conditions disponibles sont +host_name+ et +provides+:
+Les autres conditions disponibles sont +host_name+ et +provides+ :
 
   get '/', :host_name => /^admin\./ do
-    "Zone Administrateur, Accès refusé!"
+    "Zone Administrateur, Accès refusé !"
   end
 
   get '/', :provides => 'html' do
@@ -118,38 +135,57 @@ Les autres conditions disponibles sont +host_name+ et +provides+:
     builder :feed
   end
 
-Vous pouvez facilement définir vos propres conditions:
+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é!"
+    "Vous avez gagné !"
   end
 
   get '/gagner_une_voiture' do
     "Désolé, vous avez perdu."
   end
 
+Utilisez un splat (caractère joker) dans le cas d'une condition qui prend
+plusieurs valeurs :
+
+  set(:auth) do |*roles|   # <- ici on utilise un splat
+    condition do
+      unless logged_in? && roles.any? {|role| current_user.in_role? role }
+        redirect "/login/", 303 
+      end
+    end
+  end
+
+  get "/mon/compte/", :auth => [:user, :admin] do
+    "Informations sur votre compte"
+  end
+
+  get "/reserve/aux/admins/", :auth => :admin do  
+    "Seuls les administrateurs sont acceptés ici !"
+  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,
+La valeur renvoyée par le bloc correspondant à une route constitue le corps de
+la réponse qui sera transmise au client HTTP ou du moins au prochain middleware
+dans la pile Rack. Le plus souvent, 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:
+Vous pouvez renvoyer n'importe quel objet qu'il s'agisse d'une réponse Rack
+valide, d'un corps de réponse Rack ou d'un code statut HTTP :
 
-* Un tableau de 3 éléments: <tt>[code retour (Fixnum), entêtes (Hash), corps
-  de réponse (répondant à #each)]</tt>
-* Un tableau de 2 élements: <tt>[code retour (Fixnum), corps de réponse
+* Un tableau de 3 éléments : <tt>[code statut (Fixnum), entêtes (Hash), corps
+  de la réponse (répondant à #each)]</tt>
+* Un tableau de 2 élements : <tt>[code statut (Fixnum), corps de la réponse
   (répondant à #each)]</tt>
 * Un objet qui répond à <tt>#each</tt> et qui ne transmet que des chaînes de
   caractères au bloc fourni
-* Un Fixnum représentant le code retour
+* Un Fixnum représentant le code statut
 
-Ainsi, on peut facilement implémenter un streaming par exemple :
+Avec cela, on peut facilement implémenter un streaming par exemple :
 
     class Stream
       def each
@@ -159,11 +195,15 @@ Ainsi, on peut facilement implémenter un streaming par exemple :
 
     get('/') { Stream.new }
 
+Vous pouvez aussi utiliser le helper +stream+ (présenté un peu plus loin) pour
+éviter la surcharge et intégrer le traitement relatif au streaming dans le bloc
+de code de la route.
+
 === Masques de route spécifiques
 
-Comme montré plus haut, Sinatra embarque le support pour l'utilisation de
-masques utilisant des chaînes de caractères ou des expressions régulières
-pour définir les routes. Toutefois, cela ne s'arrête pas là. Vous pouvez
+Comme cela a été vu auparavant, Sinatra offre la possibilité d'utiliser des
+masques sous forme de chaines de caractères ou des expressions régulières
+pour définir les routes. Mais il est possible de faire bien plus. Vous pouvez
 facilement définir vos propres masques :
 
   class MasqueToutSauf
@@ -187,7 +227,7 @@ facilement définir vos propres masques :
     # ...
   end
 
-Notez que l'exemple ci-dessus est bien trop compliqué et le même résultat
+Notez que l'exemple ci-dessus est bien trop compliqué et que le même résultat
 peut être obtenu avec :
 
   get // do
@@ -203,460 +243,323 @@ Ou bien en utilisant la forme négative :
 
 == Fichiers statiques
 
-Par défaut, le dossier <tt>./public</tt> est utilisé pour servir les fichiers
-statiques. Vous pouvez changer ce dossier pour un autre nom grâce au paramètre
-<tt>:public</tt>:
+Les fichiers du dossier <tt>./public</tt> sont servis de façon statique. Vous
+avez la possibilité d'utiliser un autre répertoire en définissant le paramètre
+<tt>:public_folder</tt> :
 
-  set :public, File.dirname(__FILE__) + '/statique'
+  set :public_folder, File.dirname(__FILE__) + '/statique'
 
-Notez que le nom du dossier public n'est pas inclus dans l'URL. Un fichier
-sous <tt>./public/css/style.css</tt> est appelé avec l'URL :
+Notez que le nom du dossier public n'apparait pas dans l'URL. Le fichier
+<tt>./public/css/style.css</tt> sera appelé via l'URL : 
 <tt>http://exemple.com/css/style.css</tt>.
 
-== Vues / Templates
-
-Par défaut, les templates sont cherchés dans le dossier <tt>./views</tt>.
-Pour utiliser un autre dossier, il faut le déclarer:
-
-  set :views, File.dirname(__FILE__) + '/templates'
-
-Il est important de noter que les templates sont toujours référencés
-sous forme de symboles, même s'il s'agit d'un sous-répertoire (dans ce
-cas, utilisez <tt>:'sous_repertoire/template'</tt>). Vous devez utiliser un
-symbole car les méthodes de rendu évalueront le contenu des chaînes de
-caractères au lieu de les considérer comme un chemin vers un fichier.
+Utilisez le paramètre <tt>:static_cache_control</tt> pour ajouter l'information
+d'en-tête <tt>Cache-Control</tt> (voir plus loin).
 
-=== Templates Haml
-
-Le gem <tt>haml</tt> est nécessaire pour utiliser la fonction de rendu Haml:
+== Vues / Templates
 
-  # Chargez la bibliothèque haml dans votre application
-  require 'haml'
+Chaqie langage de template est disponible via sa propre méthode de rendu,
+lesquelles renvoient tout simplement une chaîne de caractères.
 
   get '/' do
-    haml :index
+    erb :index
   end
 
-Utilisera le template: <tt>./views/index.haml</tt>.
+Ceci effectue le rendu de la vue <tt>views/index.erb</tt>.
 
-{Les options de Haml}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options]
-peuvent se manipuler directement avec la configuration de Sinatra,
-voir {Options et Configuration}[http://www.sinatrarb.com/configuration.html],
-et supportent aussi la réécriture (surcharge) comme dans cet exemple.
-
-  set :haml, :format => :html5 # le format par défaut dans Haml est :xhtml
+Plutôt que d'utiliser le nom d'un template, vous pouvez directement passer
+le contenu du template :
 
   get '/' do
-    haml :index, :format => :html4 # surcharge
+    code = "<%= Time.now %>"
+    erb code
   end
 
-
-=== Templates Erb
-
-  # Chargez la bibliothèque erb dans votre application
-  require 'erb'
+Les méthodes de templates acceptent un second paramètre, un hash d'options :
 
   get '/' do
-    erb :index
+    erb :index, :layout => :post
   end
 
-Utilisera le template: <tt>./views/index.erb</tt>.
+Ceci effectuera le rendu de la vue <tt>views/index.erb</tt> en l'intégrant
+au +layout+ <tt>views/post.erb</tt> (les vues Erb sont intégrées par défaut
+au +layout+ <tt>views/layout.erb</tt> quand ce fichier existe).
 
-=== Templates Erubis
-
-Le gem <tt>erubis</tt> est nécessaire pour utiliser la fonction de rendu
-erubis:
-
-  # Chargez la bibliothèque erubis dans votre application
-  require 'erubis'
+Toute option que Sinatra ne comprend pas sera passée au moteur de rendu :
 
   get '/' do
-    erubis :index
+    haml :index, :format => :html5
   end
 
-Utilisera le template: <tt>./views/index.erubis</tt>
+Vous pouvez également définir des options par langage de template de façon
+générale :
 
-Il est également possible de remplacer Erb par Erubis:
-
-  require 'erubis'
-  Tilt.register :erb, Tilt[:erubis]
+  set :haml, :format => html5
 
   get '/' do
-    erb :index
+    haml :index
   end
 
-Utilisera le template <tt>./views/index.erb</tt> avec Erubis.
+Les options passées à la méthode de rendu prennent le pas sur les options
+définies au moyen de +set+.
 
-=== Templates Builder
+Options disponibles :
 
-Le gem <tt>builder</tt> est nécessaire pour utiliser la fonction de rendu
-builder:
+[locals]
+  Liste de variables locales passées au document. Pratique pour les vues
+  partielles.
+  Exemple : <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>.
 
-  # Chargez la bibliothèque builder dans votre application
-  require 'builder'
+[default_encoding]
+  Encodage de caractères à utiliser en cas d'incertitude. Par défaut, c'est
+  <tt>settings.default_encoding</tt>.
 
-  get '/' do
-    builder :index
-  end
+[views]
+  Dossier de vues dans lequel chercher les templates. Par défaut
+  <tt>settings.views</tt>.
 
-Utilisera le template: <tt>./views/index.builder</tt>.
+[layout]
+  S'il faut ou non utiliser un +layout+ (+true+ or +false+). Indique le
+  template à utiliser lorsque c'est un symbole. Exemple : <tt>erb :index,
+  :layout => !request.xhr?</tt>.
 
-=== Templates Nokogiri
+[content_type]
+  Content-Type que le template produit, dépend par défaut du langage de
+  template.
 
-Le gem <tt>nokogiri</tt> est nécessaire pour utiliser la fonction de rendu
-nokogiri:
+[scope]
+  Contexte sous lequel effectuer le rendu du template. Par défaut il s'agit
+  de l'instance de l'application. Si vous changez cela, les variables
+  d'instance et les méthodes utilitaires ne seront pas disponibles.
 
-  # Chargez la bibliothèque nokogiri dans votre application
-  require 'nokogiri'
+[layout_engine]
+  Moteur de rendu à utiliser pour le +layout+. Utile pour les langages ne
+  supportant pas les +layouts+. Il s'agit par défaut du moteur utilisé pour
+  le rendu du template. Exemple : <tt>set :rdoc, :layout_engine => :erb</tt>
 
-  get '/' do
-    nokogiri :index
-  end
+Les templates sont supposés se trouver directement dans le dossier
+<tt>./views</tt>. Pour utiliser un dossier de vues différent :
 
-Utilisera le template: <tt>./views/index.nokogiri</tt>.
+  set :views, settings.root + '/templates'
 
-=== Templates Sass
+Il est important de se souvenir que les templates sont toujours référencés
+sous forme de symboles, même lorsqu'ils sont dans un sous-répertoire (dans
+ce cas, utilisez <tt>:'sous_repertoire/template'</tt>). Il faut utiliser
+un symbole car les méthodes de rendu évaluent le contenu des chaînes de
+caractères au lieu de les considérer comme un chemin vers un fichier.
 
-Le gem <tt>haml</tt> ou <tt>sass</tt> est nécessaire pour utiliser la fonction
-de rendu Sass:
+=== Langages de template disponibles
 
-  # Chargez la bibliothèque haml ou sass dans votre application
-  require 'sass'
+Certains langages ont plusieurs implémentations. Pour préciser l'implémentation
+à utiliser (et garantir l'aspect thread-safe), vous devez simplement l'avoir
+chargée au préalable :
 
-  get '/stylesheet.css' do
-    sass :stylesheet
-  end
+  require 'rdiscount' # ou require 'bluecloth'
+  get('/') { markdown :index }
 
-Utilisera le template: <tt>./views/stylesheet.sass</tt>.
-
-{Les options de Sass}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
-peuvent se manipuler directement avec la configuration de Sinatra,
-voir {Options et Configuration}[http://www.sinatrarb.com/configuration.html],
-et supportent aussi la réécriture (surcharge) comme dans cet exemple.
+=== Templates Haml
 
-  set :sass, :style => :compact # le style par défaut dans Sass est :nested
+Dépendances::             {haml}[http://haml-lang.com/]
+Extensions de fichier::   <tt>.haml</tt>
+Exemple::                 <tt>haml :index, :format => :html5</tt>
 
-  get '/stylesheet.css' do
-    sass :stylesheet, :style => :expanded # surcharge
-  end
+=== Templates Erb
 
-=== Scss Templates
+Dépendances::             {erubis}[http://www.kuwata-lab.com/erubis/] ou
+                          erb (inclus avec Ruby)
+Extensions de fichier::   <tt>.erb</tt>, <tt>.rhtml</tt> ou <tt>.erubis</tt>
+                          (Erubis seulement)
+Exemple::                 <tt>erb :index</tt>
 
-Le gem <tt>haml</tt> ou <tt>sass</tt> est nécessaire pour utiliser la fonction
-de rendu Scss:
+=== Templates Builder
 
-  # Chargez la bibliothèque haml ou sass dans votre application
-  require 'sass'
+Dépendances::             {builder}[http://builder.rubyforge.org/]
+Extensions de fichier::   <tt>.builder</tt>
+Exemple::                 <tt>builder { |xml| xml.em "salut" }</tt>
 
-  get '/stylesheet.css' do
-    scss :stylesheet
-  end
+Ce moteur accepte également un bloc pour des templates en ligne (voir exemple).
 
-Utilisera le template <tt>./views/stylesheet.scss</tt>.
+=== Templates Nokogiri
 
-{Les options de Scss}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
-peuvent se manipuler directement avec la configuration de Sinatra,
-voir {Options et Configuration}[http://www.sinatrarb.com/configuration.html],
-et supportent aussi la réécriture (surcharge) comme dans cet exemple.
+Dépendances::             {nokogiri}[http://nokogiri.org/]
+Extensions de fichier::   <tt>.nokogiri</tt>
+Exemple::                 <tt>builder { |xml| xml.em "salut" }</tt>
 
-  set :scss, :style => :compact # le style par défaut de Scss est :nested
+Ce moteur accepte également un bloc pour des templates en ligne (voir exemple).
 
-  get '/stylesheet.css' do
-    scss :stylesheet, :style => :expanded # surcharge
-  end
+=== Templates Sass
 
-=== Templates Less
+Dépendances::             {sass}[http://sass-lang.com/]
+Extensions de fichier::   <tt>.sass</tt>
+Exemple::                 <tt>sass :stylesheet, :style => :expanded</tt>
 
-Le gem <tt>less</tt> est nécessaire pour utiliser la fonction de rendu Less:
+=== Templates SCSS
 
-  # Chargez la bibliothèque less dans votre application
-  require 'less'
+Dépendances::             {sass}[http://sass-lang.com/]
+Extensions de fichier::   <tt>.scss</tt>
+Exemple::                 <tt>scss :stylesheet, :style => :expanded</tt>
 
-  get '/stylesheet.css' do
-    less :stylesheet
-  end
+=== Templates Less
 
-Utilisera le template: <tt>./views/stylesheet.less</tt>.
+Dépendances::             {less}[http://www.lesscss.org/]
+Extensions de fichier::   <tt>.less</tt>
+Exemple::                 <tt>less :stylesheet</tt>
 
 === Templates Liquid
 
-Le gem <tt>liquid</tt> est nécessaire pour utiliser la fonction de rendu
-Liquid:
-
-  # Chargez la bibliothèque liquid dans votre application
-  require 'liquid'
+Dépendances::             {liquid}[http://www.liquidmarkup.org/]
+Extensions de fichier::   <tt>.liquid</tt>
+Exemple::                 <tt>liquid :index, :locals => { :key => 'value' }</tt>
 
-  get '/' do
-    liquid :index
-  end
-
-Utilisera <tt>./views/index.liquid</tt>.
-
-Comme vous ne pouvez pas appeler des méthodes Ruby (excepté +yield+) dans un
-template Liquid, il sera toujours nécessaire de lui passer des variables
-locales:
-
-  liquid :index, :locals => { :key => 'value' }
+Comme vous ne pouvez appeler de méthodes Ruby (autres que +yield+) dans un
+template Liquid, vous aurez sûrement à lui passer des variables locales.
 
 === Templates Markdown
 
-Le gem <tt>rdiscount</tt> est nécessaire pour utiliser la fonction de rendu
-Markdown:
+Dépendances::             {rdiscount}[https://github.com/rtomayko/rdiscount],
+                          {redcarpet}[https://github.com/tanoku/redcarpet],
+                          {bluecloth}[http://deveiate.org/projects/BlueCloth],
+                          {kramdown}[http://kramdown.rubyforge.org/] *ou*
+                          {maruku}[http://maruku.rubyforge.org/]
+Extensions de fichier::   <tt>.markdown</tt>, <tt>.mkd</tt> et <tt>.md</tt>
+Exemple::                 <tt>markdown :index, :layout_engine => :erb</tt>
 
-  # Chargez la bibliothèque rdiscount dans votre application
-  require "rdiscount"
+Il n'est pas possible d'appeler des méthodes depuis markdown, ni de lui
+passer des variables locales. Par conséquent, il sera souvent utilisé en
+combinaison avec un autre moteur de rendu :
 
-  get '/' do
-    markdown :index
-  end
-
-Utilisera <tt>./views/index.markdown</tt> (les extensions de fichier +md+ et
-+mkd+ sont également acceptées).
-
-Il n'est pas possible d'appeler des méthodes depuis markdown, ni même de lui
-passer des variables locales. Par conséquent, il sera le plus souvent utilisé
-en combinaison avec un autre moteur de rendu:
-
-  erb :vuedensemble, :locals => { :texte => markdown(:introduction) }
+  erb :overview, :locals => { :text => markdown(:introduction) }
 
 Notez que vous pouvez également appeler la méthode +markdown+ au sein d'autres
-templates:
-
-  %h1 Bonjour Depuis Haml!
-  %p= markdown(:salutations)
-
-Comme vous ne pouvez pas faire d'appels Ruby au sein de Markdown, vous ne
-pouvez pas utiliser des layouts écrits en Markdown. Il est toutefois possible
-d'utiliser un autre moteur de rendu pour le layout en passant l'option
-<tt>:layout_engine</tt> :
-
-  get '/' do
-    markdown :index, :layout_engine => :erb
-  end
-
-Ceci utilisera <tt>./views/index.md</tt> avec <tt>./views/layout.erb</tt> pour
-layout.
-
-Souvenez vous que vous pouvez spécifier de telles options de rendu
-globalement :
-
-  set :markdown, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    markdown :index
-  end
-
-Ceci utilisera <tt>./views/index.md</tt> (et tout autre template Markdown)
-avec <tt>./views/post.haml</tt> pour layout.
-
-Il est également possible de traduire le Markdown avec BlueCloth plutôt que
-RDiscount :
-
-  require 'bluecloth'
+templates :
 
-  Tilt.register 'markdown', BlueClothTemplate
-  Tilt.register 'mkd',      BlueClothTemplate
-  Tilt.register 'md',       BlueClothTemplate
-
-  get '/' do
-    markdown :index
-  end
+  %h1 Hello From Haml !
+  %p= markdown(:greetings)
 
-Utilisera <tt>./views/index.md</tt> avec BlueCloth.
+Comme vous ne pouvez pas appeler de Ruby au sein de Markdown, vous ne pouvez
+pas utiliser de +layouts+ écrits en Markdown. Toutefois, il est possible
+d'utiliser un moteur de rendu différent pour le template et pour le +layout+
+en utilisant l'option <tt>:layout_engine</tt>.
 
 === Templates Textile
 
-Le gem <tt>RedCloth</tt> est nécessaire pour utiliser la fonction de rendu
-Textile:
+Dépendances::             {RedCloth}[http://redcloth.org/]
+Extensions de fichier::   <tt>.textile</tt>
+Exemple::                 <tt>textile :index, :layout_engine => :erb</tt>
 
-  # Chargez la bibliothèqye redcloth dans votre application
-  require "redcloth"
+Il n'est pas possible d'appeler des méthodes depuis textile, ni de lui
+passer des variables locales. Par conséquent, il sera souvent utilisé en
+combinaison avec un autre moteur de rendu :
 
-  get '/' do
-    textile :index
-  end
-
-Utilisera <tt>./views/index.textile</tt>.
-
-Il n'est pas possible d'appeler des méthodes depuis textile, ni même de lui
-passer des variables locales. Par conséquent, il sera le plus souvent utilisé
-en combinaison avec un autre moteur de rendu:
-
-  erb :vuedensemble, :locals => { :texte => textile(:introduction) }
+  erb :overview, :locals => { :text => textile(:introduction) }
 
 Notez que vous pouvez également appeler la méthode +textile+ au sein d'autres
-templates:
+templates :
 
-  %h1 Bonjour Depuis Haml!
-  %p= textile(:salutations)
+  %h1 Hello From Haml !
+  %p= textile(:greetings)
 
-Comme vous ne pouvez pas faire d'appels Ruby au sein de Textile, vous ne
-pouvez pas utiliser des layouts écrits en Textile. Il est toutefois possible
-d'utiliser un autre moteur de rendu pour le layout en passant l'option
-<tt>:layout_engine</tt> :
-
-  get '/' do
-    textile :index, :layout_engine => :erb
-  end
-
-Ceci utilisera <tt>./views/index.textile</tt> avec <tt>./views/layout.erb</tt>
-pour layout.
-
-Souvenez vous que vous pouvez spécifier de telles options de rendu
-globalement :
-
-  set :textile, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    textile :index
-  end
-
-Ceci utilisera <tt>./views/index.textile</tt> (et tout autre template Textile)
-avec <tt>./views/post.haml</tt> pour layout.
+Comme vous ne pouvez pas appeler de Ruby au sein de Textile, vous ne pouvez
+pas utiliser de +layouts+ écrits en Textile. Toutefois, il est possible
+d'utiliser un moteur de rendu différent pour le template et pour le +layout+
+en utilisant l'option <tt>:layout_engine</tt>.
 
 === Templates RDoc
 
-Le gem <tt>rdoc</tt> est nécessaire pour utiliser la fonction de rendu RDoc:
-
-  # Chargez la bibliothèque rdoc/markup/to_html dans votre application
-  require "rdoc/markup/to_html"
-
-  get '/' do
-    rdoc :index
-  end
-
-Utilisera <tt>./views/index.rdoc</tt>.
+Dépendances::             {rdoc}[http://rdoc.rubyforge.org/]
+Extensions de fichier::   <tt>.rdoc</tt>
+Exemple::                 <tt>rdoc :README, :layout_engine => :erb</tt>
 
-Il n'est pas possible d'appeler des méthodes depuis rdoc, ni même de lui
-passer des variables locales. Par conséquent, il sera le plus souvent utilisé
-en combinaison avec un autre moteur de rendu:
+Il n'est pas possible d'appeler des méthodes depuis rdoc, ni de lui
+passer des variables locales. Par conséquent, il sera souvent utilisé en
+combinaison avec un autre moteur de rendu :
 
-  erb :vuedensemble, :locals => { :texte => rdoc(:introduction) }
+  erb :overview, :locals => { :text => rdoc(:introduction) }
 
 Notez que vous pouvez également appeler la méthode +rdoc+ au sein d'autres
-templates:
+templates :
 
-  %h1 Bonjour Depuis Haml!
-  %p= rdoc(:salutations)
+  %h1 Hello From Haml !
+  %p= rdoc(:greetings)
 
-Comme vous ne pouvez pas faire d'appels Ruby au sein de RDoc, vous ne
-pouvez pas utiliser des layouts écrits en RDoc. Il est toutefois possible
-d'utiliser un autre moteur de rendu pour le layout en passant l'option
-<tt>:layout_engine</tt> :
-
-  get '/' do
-    rdoc :index, :layout_engine => :erb
-  end
-
-Ceci utilisera <tt>./views/index.rdoc</tt> avec <tt>./views/layout.erb</tt>
-pour layout.
-
-Souvenez vous que vous pouvez spécifier de telles options de rendu
-globalement :
-
-  set :rdoc, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    rdoc :index
-  end
-
-Ceci utilisera <tt>./views/index.rdoc</tt> (et tout autre template RDoc)
-avec <tt>./views/post.haml</tt> pour layout.
+Comme vous ne pouvez pas appeler de Ruby au sein de RDoc, vous ne pouvez
+pas utiliser de +layouts+ écrits en RDoc. Toutefois, il est possible
+d'utiliser un moteur de rendu différent pour le template et pour le +layout+
+en utilisant l'option <tt>:layout_engine</tt>.
 
 === Templates Radius
 
-Le gem radius est nécessaire pour utiliser la fonction de rendu Radius:
-
-  # Chargez la bibliothèque radius dans votre application
-  require 'radius'
-
-  get '/' do
-    radius :index
-  end
-
-Utilisera <tt>./views/index.radius</tt>.
-
-Comme vous ne pouvez pas appeler des méthodes Ruby (excepté +yield+) dans un
-template Radius, il sera toujours nécessaire de lui passer des variables
-locales:
+Dépendances::             {radius}[http://radius.rubyforge.org/]
+Extensions de fichier::   <tt>.radius</tt>
+Exemple::                 <tt>radius :index, :locals => { :key => 'value' }</tt>
 
-  radius :index, :locals => { :key => 'value' }
+Comme vous ne pouvez pas appeler de méthodes Ruby depuis un template Radius,
+vous aurez sûrement à lui passer des variables locales.
 
 === Templates Markaby
 
-Le gem markaby est nécessaire pour utiliser la fonction de rendu Markaby:
-
-  # Chargez la bibliothèque markaby dans votre application
-  require 'markaby'
-
-  get '/' do
-    markaby :index
-  end
-
-Utilisera <tt>./views/index.mab</tt>.
+Dépendances::             {markaby}[http://markaby.github.com/]
+Extensions de fichier::   <tt>.mab</tt>
+Exemple::                 <tt>markaby { h1 "Bienvenue !" }</tt>
 
-Vous pouvez également utiliser Markaby en ligne :
-
-  get '/' do
-    markaby { h1 "Salut !" }
-  end
+Ce moteur accepte également un bloc pour des templates en ligne (voir exemple).
 
 === Templates Slim
 
-Le gem slim est nécessaire pour utiliser la fonction de rendu Slim:
+Dépendances::             {slim}[http://slim-lang.com/]
+Extensions de fichier::   <tt>.slim</tt>
+Exemple::                 <tt>slim :index</tt>
 
-  # Chargez la bibliothèque slim dans votre application
-  require 'slim'
+=== Templates Creole
 
-  get '/' do
-    slim :index
-  end
-
-Utilisera <tt>./views/index.slim</tt>.
+Dépendances::             {creole}[https://github.com/minad/creole]
+Extensions de fichier::   <tt>.creole</tt>
+Exemple::                 <tt>creole :wiki, :layout_engine => :erb</tt>
 
-=== Templates CoffeeScript
+Il n'est pas possible d'appeler des méthodes depuis creole, ni de lui
+passer des variables locales. Par conséquent, il sera souvent utilisé en
+combinaison avec un autre moteur de rendu :
 
-Le gem <tt>coffee-script</tt> est nécessaire ainsi que l'<b>une</b> des
-options suivantes permettant l'exécution de Java script :
+  erb :overview, :locals => { :text => creole(:introduction) }
 
-* +node+ (de Node.js) dans votre path
-* vous êtes sous OSX
-* le gem +therubyracer+
+Notez que vous pouvez également appeler la méthode +creole+ au sein d'autres
+templates :
 
-Voir http://github.com/josh/ruby-coffee-script pour une liste à jour d'options
-possibles.
+  %h1 Hello From Haml !
+  %p= creole(:greetings)
 
-Maintenant vous pouvez générer des templates CoffeeScript :
+Comme vous ne pouvez pas appeler de Ruby au sein de Creole, vous ne pouvez
+pas utiliser de +layouts+ écrits en Creole. Toutefois, il est possible
+d'utiliser un moteur de rendu différent pour le template et pour le +layout+
+en utilisant l'option <tt>:layout_engine</tt>.
 
-  # Chargez la bibliothèque coffee-script dans votre application
-  require 'coffee-script'
-
-  get '/application.js' do
-    coffee :application
-  end
+=== Templates CoffeeScript
 
-Utilisera <tt>./views/application.coffee</tt>.
+Dépendances::             {coffee-script}[https://github.com/josh/ruby-coffee-script]
+                          et un {moyen d'exécuter javascript}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
+Extensions de fichier::   <tt>.coffee</tt>
+Exemple::                 <tt>coffee :index</tt>
 
 === Templates embarqués
 
   get '/' do
-    haml '%div.title Bonjour Monde'
+    haml '%div.title Bonjour le monde'
   end
 
-Générera le template embarqué spécifié dans la chaîne de caractères.
+Générera le code du template spécifié dans la chaîne de caractères.
 
 === 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:
+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:
+Alternativement, on peut passer un hash contenant des variables locales :
 
   get '/:id' do
     foo = Foo.find(params[:id])
@@ -669,7 +572,7 @@ noms de variables.
 
 === Templates dans le fichier source
 
-Des templates peuvent être définis dans le fichier source comme ceci:
+Des templates peuvent être définis dans le fichier source comme ceci :
 
   require 'sinatra'
 
@@ -684,24 +587,24 @@ Des templates peuvent être définis dans le fichier source comme ceci:
     = yield
 
   @@ index
-  %div.title Bonjour Monde!!!!!
+  %div.title Bonjour le monde !
 
-NOTE: Les templates du fichier source qui contient <tt>require 'sinatra'</tt>
+NOTE : Les templates du fichier source qui contient <tt>require 'sinatra'</tt>
 sont automatiquement chargés. Si vous avez des templates dans d'autres
-fichiers source, il faut explicitement les déclarer via:
+fichiers source, il faut explicitement les déclarer avec
 <tt>enable :inline_templates</tt>.
 
 === Templates nommés
 
 Les templates peuvent aussi être définis grâce à la méthode de haut niveau
-<tt>template</tt>:
+<tt>template</tt> :
 
   template :layout do
     "%html\n  =yield\n"
   end
 
   template :index do
-    '%div.title Bonjour Monde!'
+    '%div.title Bonjour le monde !'
   end
 
   get '/' do
@@ -711,7 +614,7 @@ Les templates peuvent aussi être définis grâce à la méthode de haut niveau
 Si un template nommé "layout" existe, il sera utilisé à chaque fois qu'un
 template sera affiché. Vous pouvez désactivez les layouts au cas par cas en
 passant <tt>:layout => false</tt> ou bien les désactiver par défaut au moyen
-de <tt>set :haml, :layout => false</tt>:
+de <tt>set :haml, :layout => false</tt> :
 
   get '/' do
     haml :index, :layout => !request.xhr?
@@ -748,22 +651,22 @@ https://github.com/rtomayko/tilt pour en savoir plus sur Tilt.
 Un filtre <tt>before</tt> est évalué avant n'importe quelle requête, dans le
 contexte de celle-ci, et peut modifier la requête ou la réponse. Les variables
 d'instance déclarées dans le filtre sont accessibles au gestionnaire de route
-et au template:
+et au template :
 
   before do
-    @note = 'Coucou!'
+    @note = 'Coucou !'
     request.path_info = '/foo/bar/baz'
   end
 
   get '/foo/*' do
-    @note #=> 'Coucou!'
+    @note #=> 'Coucou !'
     params[:splat] #=> 'bar/baz'
   end
 
 Un filtre <tt>after</tt> est évalué après chaque requête, dans le contexte
 de celle-ci et peut également modifier la requête et/ou la réponse. Toutes les
 variables d'instance déclarées dans un filtre <tt>before</tt> et dans le
-gestionnaire de route sont accessibles dans le filtre <tt>after</tt>:
+gestionnaire de route sont accessibles dans le filtre <tt>after</tt> :
 
   after do
     puts response.status
@@ -775,7 +678,7 @@ pas disponible dans le filtre <tt>after</tt>, étant donné qu'il est généré
 plus tard.
 
 En option, on peut passer un masque au filtre, ce qui le rend actif uniquement
-si la requête correspond au masque en question:
+si la requête correspond au masque en question :
 
   before '/secret/*' do
     authentification!
@@ -812,7 +715,7 @@ qui seront accessibles dans vos gestionnaires de route et dans vos templates :
 
 === Utiliser les sessions
 
-Une session est utilisé pour conserver un état entre les requêtes. Une fois
+Une session est utilisée pour conserver un état entre les requêtes. Une fois
 activées, vous avez un +hash+ de session par session utilisateur :
 
   enable :sessions
@@ -842,10 +745,23 @@ choix comme vous le feriez pour n'importe quel autre +middleware+ :
     session[:valeur] = params[:valeur]
   end
 
-== Halt
+Pour renforcer la sécurité, les données de session dans le cookie sont signées
+avec une clé secrète de session. Une clé secrète est générée pour vous au
+hasard par Sinatra. Toutefois, comme cette clé change à chaque démarrage de
+votre application, vous pouvez définir cette clé vous-même afin que toutes
+les instances de votre application la partage :
+
+  set :session_secret, 'super secret'
+
+Si vous souhaitez avoir plus de contrôle, vous pouvez également enregistrer un
++hash+ avec des options lors de la configuration de +sessions+ :
+
+  set :sessions, :domain => 'foo.com'
+
+=== Halt
 
 Pour arrêter immédiatement la requête dans un filtre ou un gestionnaire de
-route:
+route :
 
   halt
 
@@ -859,28 +775,28 @@ Ou le texte ...
 
 Ou les deux ...
 
-  halt 401, 'Partez!'
+  halt 401, 'Partez !'
 
 Ainsi que les entêtes ...
 
   halt 402, {'Content-Type' => 'text/plain'}, 'revanche'
 
-Bien sûr il est possible de cominer un template avec +halt+:
+Bien sûr il est possible de combiner un template avec +halt+ :
 
   halt erb(:erreur)
 
-== Passer
+=== Passer
 
 Une route peut passer le relais aux autres routes qui correspondent également
-avec <tt>pass</tt>:
+avec <tt>pass</tt> :
 
   get '/devine/:qui' do
     pass unless params[:qui] == 'Frank'
-    "Tu m'as eu!"
+    "Tu m'as eu !"
   end
 
   get '/devine/*' do
-    'Manqué!'
+    'Manqué !'
   end
 
 On sort donc immédiatement de ce gestionnaire et on continue à chercher,
@@ -940,21 +856,98 @@ retour et les entêtes :
   get '/foo' do
     status 418
     headers \
-      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN"
+      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN",
       "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
-    body "I'm a tea pot!"
+    body "Je suis une théière !"
   end
 
 Comme +body+, +headers+ et +status+ peuvent être utilisés sans arguments
 pour accéder à leurs valeurs.
 
+=== Faire du streaming
+
+Il y a des cas où vous voulez commencer à renvoyer des données pendant que
+vous êtes en train de générer le reste de la réponse. Dans les cas les plus
+extrèmes, vous souhaitez continuer à envoyer des données tant que le client
+n'abandonne pas la connection. Vous pouvez alors utiliser le helper +stream+
+pour éviter de créer votre propre système :
+
+  get '/' do
+    stream do |out|
+      out << "Ca va être hallu -\n"
+      sleep 0.5
+      out << " (attends la suite) \n"
+      sleep 1
+      out << "- cinant !\n"
+    end
+  end
+
+Cela permet d'implémenter des API de streaming ou de
+{Server Sent Events}[http://dev.w3.org/html5/eventsource/] et peut servir de
+base pour des {WebSockets}[http://en.wikipedia.org/wiki/WebSocket]. Vous
+pouvez aussi l'employer pour augmenter le débit quand une partie du contenu
+provient d'une resource lente.
+
+Le fonctionnement du streaming, notamment le nombre de requêtes simultanées,
+dépend énormément du serveur web utilisé. Certains ne prennent pas du tout en
+charge le streaming (WEBRick par exemple). Lorsque le serveur ne gère pas le
+streaming, la partie body de la réponse sera envoyée au client en une seule
+fois, après que l'exécution du bloc passé au helper +stream+ sera terminée.
+
+En utilisant le helper +stream+ avec le paramètre +keep_open+, il n'appelera
+pas la méthode +close+ du flux, vous laissant la possibilité de le fermer à
+tout moment au cours de l'exécution. Ceci ne fonctionne qu'avec les serveurs
+evented (ie non threadés) tels que Thin et Rainbows. Les autres serveurs
+fermeront malgré tout le flux.
+
+  set :server, :thin
+  connections = []
+
+  get '/' do
+    # conserve le flux ouvert
+    stream(:keep_open) { |out| connections << out }
+  end
+
+  post '/' do
+    # écrit dans tous les flux ouverts
+    connections.each { |out| out << params[:message] << "\n" }
+    "message sent"
+  end
+
+=== Journalisation (Logging)
+
+Dans le contexte de la requête, la méthode utilitaire +logger+ expose une
+instance de +logger+ :
+
+  get '/' do
+    logger.info "chargement des données"
+    # ...
+  end
+
+Ce +logger+ va automatiquement prendre en compte les paramètres de
+configuration pour la journalisation de votre gestionnaire Rack. Si la
+journalisation est désactivée, cette méthode renverra un objet factice et
+vous n'avez pas à vous en inquiéter dans vos routes en le filtrant.
+
+Notez que la journalisation est seulement activée par défaut pour
+<tt>Sinatra::Application</tt>, donc si vous héritez de <tt>Sinatra::Base</tt>,
+vous aurez à l'activer vous-même :
+
+  class MonApp < Sinatra::Base
+    configure(:production, :development) do
+      enable :logging
+    end
+  end
+
 === Types Mime
 
 Quand vous utilisez <tt>send_file</tt> ou des fichiers statiques, vous
 pouvez rencontrer des types mime que Sinatra ne connaît pas. Utilisez
 +mime_type+ pour les déclarer par extension de fichier :
 
-  mime_type :foo, 'text/foo'
+  configure do
+    mime_type :foo, 'text/foo'
+  end
 
 Vous pouvez également les utiliser avec la méthode +content_type+ :
 
@@ -991,7 +984,7 @@ Tout paramètre additionnel est géré comme des arguments pour la méthode
   redirect 'http://google.com', 'mauvais endroit mon pote'
 
 Vous pouvez aussi rediriger vers la page dont l'utilisateur venait au moyen de
-<tt>redirect back</tt>:
+<tt>redirect back</tt> :
 
   get '/foo' do
     "<a href='/bar'>faire quelque chose</a>"
@@ -1008,7 +1001,7 @@ Pour passer des arguments à une redirection, ajoutez-les soit à la requête :
 
 Ou bien utilisez une session :
 
-  enable :session
+  enable :sessions
 
   get '/foo' do
     session[:secret] = 'foo'
@@ -1021,7 +1014,7 @@ Ou bien utilisez une session :
 
 === Contrôle du cache
 
-Définir correctement vos entêtes à la base pour un bon cahce HTTP.
+Définir correctement vos entêtes à la base pour un bon cache HTTP.
 
 Vous pouvez facilement définir l'entête Cache-Control de la manière suivante :
 
@@ -1030,7 +1023,7 @@ Vous pouvez facilement définir l'entête Cache-Control de la manière suivante
     "met le en cache !"
   end
 
-Conseil de pro : définir le cache dans un filtre +before+:
+Conseil de pro : définir le cache dans un filtre +before+ :
 
   before do
     cache_control :public, :must_revalidate, :max_age => 60
@@ -1043,10 +1036,10 @@ Si vous utilisez la méthode +expires+ pour définir l'entête correspondant,
     expires 500, :public, :must_revalidate
   end
 
-Pour utiliser correctement les caches, vous devriez utiliser +etag+ et
+Pour utiliser correctement les caches, vous devriez utiliser +etag+ ou
 +last_modified+. Il est recommandé d'utiliser ces méthodes *avant* de faire
-d'important modifications, car elles vont immédiatement déclencher la réponse
-si le client a déjà la version courante dans son cache:
+d'importantes modifications, car elles vont immédiatement déclencher la réponse
+si le client a déjà la version courante dans son cache :
 
   get '/article/:id' do
     @article = Article.find params[:id]
@@ -1056,14 +1049,14 @@ si le client a déjà la version courante dans son cache:
   end
 
 Il est également possible d'utiliser un
-{weak ETag}[http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation]:
+{weak ETag}[http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation] :
 
   etag @article.sha1, :weak
 
 Ces méthodes ne sont pas chargées de mettre des données en cache, mais elles
 fournissent les informations nécessaires pour votre cache. Si vous êtes à la
-recherche de solutions rapides de cache, essayez
-{rack-cache}[http://rtomayko.github.com/rack-cache/]:
+recherche de solutions rapides pour un reverse-proxy de cache, essayez
+{rack-cache}[http://rtomayko.github.com/rack-cache/] :
 
   require "rack/cache"
   require "sinatra"
@@ -1113,44 +1106,48 @@ Si le gestionnaire Rack le supporte, d'autres moyens que le +streaming+ via le
 processus Ruby seront utilisés. Si vous utilisez cette méthode, Sinatra gérera
 automatiquement les requêtes de type +range+.
 
-== Accéder à l'objet requête
+=== Accéder à l'objet requête
 
 L'objet correspondant à la requête envoyée peut être récupéré dans le contexte
 de la requête (filtres, routes, gestionnaires d'erreur) au moyen de la méthode
-`request`:
++request+ :
 
   # application tournant à l'adresse http://exemple.com/exemple
   get '/foo' do
-    request.body              # corps de la requête envoyée par le client
-                              # (voir ci-dessous)
-    request.scheme            # "http"
-    request.script_name       # "/exemple"
-    request.path_info         # "/foo"
-    request.port              # 80
-    request.request_method    # "GET"
-    request.query_string      # ""
-    request.content_length    # taille de request.body
-    request.media_type        # type de média pour request.body
-    request.host              # "exemple.com"
-    request.get?              # true (méthodes similaires pour les autres
-                              # verbes HTTP)
-    request.form_data?        # false
-    request["UN_ENTETE"]      # valeur de l'entête UN_ENTETE
-    request.referer           # référant du client ou '/'
-    request.user_agent        # user agent (utilisé par la condition :agent)
-    request.cookies           # tableau contenant les cookies du navigateur
-    request.xhr?              # requête AJAX ?
-    request.url               # "http://exemple.com/exemple/foo"
-    request.path              # "/exemple/foo"
-    request.ip                # adresse IP du client
-    request.secure?           # false
-    request.forwarded?        # vrai (si on est derrière un proxy inverse)
-    request.env               # tableau brut de l'environnement fourni par
-                              # Rack
+    t = %w[text/css text/html application/javascript]
+    request.accept              # ['text/html', '*/*']
+    request.accept? 'text/xml'  # true
+    request.preferred_type(t)   # 'text/html'
+    request.body                # corps de la requête envoyée par le client
+                                # (voir ci-dessous)
+    request.scheme              # "http"
+    request.script_name         # "/exemple"
+    request.path_info           # "/foo"
+    request.port                # 80
+    request.request_method      # "GET"
+    request.query_string        # ""
+    request.content_length      # taille de request.body
+    request.media_type          # type de média pour request.body
+    request.host                # "exemple.com"
+    request.get?                # true (méthodes similaires pour les autres
+                                # verbes HTTP)
+    request.form_data?          # false
+    request["UN_ENTETE"]        # valeur de l'entête UN_ENTETE
+    request.referer             # référant du client ou '/'
+    request.user_agent          # user agent (utilisé par la condition :agent)
+    request.cookies             # tableau contenant les cookies du navigateur
+    request.xhr?                # requête AJAX ?
+    request.url                 # "http://exemple.com/exemple/foo"
+    request.path                # "/exemple/foo"
+    request.ip                  # adresse IP du client
+    request.secure?             # false
+    request.forwarded?          # vrai (si on est derrière un proxy inverse)
+    request.env                 # tableau brut de l'environnement fourni par
+                                # Rack
   end
 
 Certaines options, telles que <tt>script_name</tt> ou <tt>path_info</tt>
-peuvent également être modifiées:
+peuvent également être modifiées :
 
   before { request.path_info = "/" }
 
@@ -1158,18 +1155,18 @@ peuvent également être modifiées:
     "toutes les requêtes arrivent ici"
   end
 
-<tt>request.body</tt> est un objet IO ou StringIO:
+<tt>request.body</tt> est un objet IO ou StringIO :
 
   post "/api" do
     request.body.rewind  # au cas où il a déjà été lu
     donnees = JSON.parse request.body.read
-    "Bonjour #{donnees['nom']}!"
+    "Bonjour #{donnees['nom']} !"
   end
 
 === Fichiers joints
 
 Vous pouvez utiliser la méthode +attachment+ pour indiquer au navigateur que
-la réponse devrait être stockée sur le disque plutôt qu'affichée:
+la réponse devrait être stockée sur le disque plutôt qu'affichée :
 
   get '/' do
     attachment
@@ -1183,6 +1180,38 @@ Vous pouvez également lui passer un nom de fichier :
     "enregistre-le !"
   end
 
+=== Gérer Date et Time
+
+Sinatra fourni un helper +time_for+ pour convertir une valeur donnée en
+objet +Time+. Il peut aussi faire la conversion à partir d'objets +DateTime+,
++Date+ ou de classes similaires.
+
+  get '/' do
+    pass if Time.now > time_for('Dec 23, 2012')
+    "encore temps"
+  end
+
+Cette méthode est utilisée en interne par +expires+, +last_modified+ et
+consorts. Par conséquent, vous pouvez très facilement étendre le
+fonctionnement de ces méthodes en surchargeant le helper +time_for+ dans
+votre application :
+
+  helpers do
+    def time_for(value)
+      case value
+      when :yesterday then Time.now - 24*60*60
+      when :tomorrow  then Time.now + 24*60*60
+      else super
+      end
+    end
+  end
+
+  get '/' do
+    last_modified :yesterday
+    expires :tomorrow
+    "salut"
+  end
+
 === Chercher les fichiers de templates
 
 La méthode <tt>find_template</tt> est utilisée pour trouver les fichiers de
@@ -1229,7 +1258,7 @@ devriez garder cela en tête si vous écrivez une méthode vraiment dingue.
 
 == Configuration
 
-Lancé une seule fois au démarrage de tous les environnements:
+Lancé une seule fois au démarrage de tous les environnements :
 
   configure do
     # définir un paramètre
@@ -1238,10 +1267,10 @@ Lancé une seule fois au démarrage de tous les environnements:
     # définir plusieurs paramètre
     set :a => 1, :b => 2
 
-    # identique à `set :option, true`
+    # identique à "set :option, true"
     enable :option
 
-    # identique à `set :option, false`
+    # identique à "set :option, false""
     disable :option
 
     # vous pouvez également avoir des paramètres dynamiques avec des blocs
@@ -1249,14 +1278,14 @@ Lancé une seule fois au démarrage de tous les environnements:
   end
 
 Lancé si l'environnement (variable d'environnement RACK_ENV) est défini comme
-<tt>:production</tt>:
+<tt>:production</tt> :
 
   configure :production do
     ...
   end
 
 Lancé si l'environnement est <tt>:production</tt> ou
-<tt>:test</tt>:
+<tt>:test</tt> :
 
   configure :production, :test do
     ...
@@ -1303,7 +1332,7 @@ Vous pouvez accéder à ces paramètres via <tt>settings</tt> :
                       dossier de vues ainsi que pour les templates en ligne.
 
 [bind]                adresse IP sur laquelle se brancher
-                      (par défaut: 0.0.0.0).
+                      (par défaut : 0.0.0.0).
                       Utiliser seulement pour le serveur intégré.
 
 [default_encoding]    encodage à utiliser si inconnu (par défaut
@@ -1336,12 +1365,10 @@ Vous pouvez accéder à ces paramètres via <tt>settings</tt> :
                       comportera comme <tt>redirect to('/foo')</tt>.
                       Désactivé par défaut.
 
-[public]              dossier duquel les fichiers publics sont servis
+[public_folder]       dossier duquel les fichiers publics sont servis
 
 [reload_templates]    si oui ou non les templates doivent être rechargés
-                      entre les requêtes. Activé en mode développement
-                      et sur Ruby 1.8.6 (pour compenser un bug Ruby
-                      occasionnant une fuite de mémoire).
+                      entre les requêtes. Activé en mode développement.
 
 [root]                dossier racine du projet.
 
@@ -1367,7 +1394,11 @@ Vous pouvez accéder à ces paramètres via <tt>settings</tt> :
                       fichiers statiques.
                       Désactivez si vous utilisez un serveur capable de le
                       gérer lui même. Le désactiver augmentera la performance.
-                      Activé par défaut.
+                      Activé par défaut pour le style classique, désactivé pour
+                      le style modulaire.
+
+[threaded]            à définir à +true+ pour indiquer à Thin d'utiliser
+                      <tt>EventMachine.defer</tt> pour traiter la requête.
 
 [views]               dossier des vues.
 
@@ -1377,43 +1408,43 @@ Les gestionnaires d'erreur s'exécutent dans le même contexte que les routes ou
 les filtres, ce qui veut dire que vous avez accès (entre autres) aux bons
 vieux <tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt>, etc.
 
-=== Pas Trouvé
+=== NotFound
 
 Quand une exception <tt>Sinatra::NotFound</tt> est soulevée, ou que le code
-retour est 404, le gestionnaire <tt>not_found</tt> est invoqué:
+retour est 404, le gestionnaire <tt>not_found</tt> est invoqué :
 
   not_found do
     'Pas moyen de trouver ce que vous cherchez'
   end
 
-=== Erreur
+=== Error
 
 Le gestionnaire +error+ est invoqué à chaque fois qu'une exception est
 soulevée dans une route ou un filtre. L'objet exception est accessible via la
-variable Rack <tt>sinatra.error</tt>:
+variable Rack <tt>sinatra.error</tt> :
 
   error do
     'Désolé mais une méchante erreur est survenue - ' + env['sinatra.error'].name
   end
 
-Erreur sur mesure:
+Erreur sur mesure :
 
   error MonErreurSurMesure do
     'Donc il est arrivé ceci...' + env['sinatra.error'].message
   end
 
-Donc si ceci arrive:
+Donc si ceci arrive :
 
   get '/' do
     raise MonErreurSurMesure, 'quelque chose de mal'
   end
 
-Vous obtenez ça:
+Vous obtenez ça :
 
   Donc il est arrivé ceci... quelque chose de mal
 
 Alternativement, vous pouvez avoir un gestionnaire d'erreur associé à un code
-particulier:
+particulier :
 
   error 403 do
     'Accès interdit'
@@ -1423,7 +1454,7 @@ particulier:
     403
   end
 
-Ou un intervalle:
+Ou un intervalle :
 
   error 400..510 do
     'Boom'
@@ -1443,7 +1474,7 @@ 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+:
+haut niveau +use+ :
 
   require 'sinatra'
   require 'mon_middleware_perso'
@@ -1452,13 +1483,13 @@ haut niveau +use+:
   use MonMiddlewarePerso
 
   get '/bonjour' do
-    'Bonjour Monde'
+    'Bonjour le monde'
   end
 
 La sémantique de +use+ est identique à celle définie dans le DSL de
 Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html]
 (le plus souvent utilisé dans un fichier rackup). Par exemple, la méthode
-+use+ accepte divers arguments ainsi que des blocs:
++use+ accepte divers arguments ainsi que des blocs :
 
   use Rack::Auth::Basic do |login, password|
     login == 'admin' && password == 'secret'
@@ -1473,7 +1504,7 @@ 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}[http://gitrdoc.com/brynary/rack-test] est
-recommandé:
+recommandé :
 
   require 'mon_application_sinatra'
   require 'test/unit'
@@ -1488,23 +1519,20 @@ recommandé:
 
     def test_ma_racine
       get '/'
-      assert_equal 'Bonjour Monde!', last_response.body
+      assert_equal 'Bonjour le monde !', last_response.body
     end
 
     def test_avec_des_parametres
       get '/rencontrer', :name => 'Frank'
-      assert_equal 'Salut Frank!', last_response.body
+      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
+      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
@@ -1513,8 +1541,8 @@ 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:
+pour l'application, les dossiers <tt>./public</tt> et <tt>./views</tt>, les
+logs, pages d'erreur, etc.). C'est là que <tt>Sinatra::Base</tt> entre en jeu :
 
   require 'sinatra/base'
 
@@ -1523,20 +1551,20 @@ d'erreur, etc.). C'est là que Sinatra::Base entre en jeu:
     set :foo, 'bar'
 
     get '/' do
-      'Bonjour Monde!'
+      'Bonjour le monde !'
     end
   end
 
-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:
+Les méthodes disponibles dans <tt>Sinatra::Base</tt> sont exactement identiques
+à celles disponibles dans le DSL de haut niveau. La plupart des applications
+de haut niveau peuvent être converties en composant <tt>Sinatra::Base</tt> avec
+deux modifications :
 
-* Votre fichier doit charger +sinatra/base+ au lieu de +sinatra+;
+* 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.
+  de noms.
 * Mettre vos gestionnaires de route, vos gestionnaires d'erreur, vos filtres
-	et options dans une sous-classe de Sinatra::Base.
+  et options dans une sous-classe de <tt>Sinatra::Base</tt>.
 
 <tt>Sinatra::Base</tt> est plutôt épuré. La plupart des options sont
 désactivées par défaut, ceci inclus le serveur. Voir {Options et
@@ -1561,7 +1589,7 @@ Il n'y a que deux inconvénient au style classique comparé au style modulaire :
 Il n'y pas d'empêchement à mélanger style classic et style modulaire.
 
 Si vous passez d'un style à l'autre, vous devriez être vigilant à quelques
-légères différences dans les paramètres :
+légères différences dans les paramètres par défaut :
 
   Paramètre           Classique                     Modulaire
 
@@ -1570,6 +1598,7 @@ légères différences dans les paramètres :
   logging             true                          false
   method_override     true                          false
   inline_templates    true                          false
+  static              true                          false
 
 
 === Servir une application modulaire
@@ -1595,7 +1624,7 @@ Ou alors avec un fichier <tt>config.ru</tt>, qui permet d'utiliser n'importe
 quel gestionnaire Rack :
 
   # config.ru
-  require 'my_app'
+  require './my_app'
   run MyApp
 
 Exécutez :
@@ -1610,12 +1639,12 @@ Ecrivez votre application :
   require 'sinatra'
 
   get '/' do
-    'Hello world!'
+    'Bonjour le monde !'
   end
 
 Et un fichier <tt>config.ru</tt> correspondant :
 
-  require 'app'
+  require './app'
   run Sinatra::Application
 
 === Quand utiliser un fichier config.ru ?
@@ -1638,7 +1667,7 @@ 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/...):
+(Rails/Ramaze/Camping/...) :
 
   require 'sinatra/base'
 
@@ -1648,7 +1677,7 @@ application Sinatra, ou n'importe quelle application basée sur Rack
     get('/connexion') { haml :connexion }
 
     post('/connexion') do
-      if params[:nom] = 'admin' and params[:motdepasse] = 'admin'
+      if params[:nom] = 'admin' && params[:motdepasse] = 'admin'
         session['nom_utilisateur'] = params[:nom]
       else
         redirect '/connexion'
@@ -1669,6 +1698,47 @@ application Sinatra, ou n'importe quelle application basée sur Rack
     get('/') { "Bonjour #{session['nom_utilisateur']}." }
   end
 
+=== Création dynamique d'applications
+
+Il se peut que vous ayez besoin de créer une nouvelle application à l'exécution
+sans avoir à les assigner à une constante, vous pouvez le faire grâce à
+<tt>Sinatra.new</tt> :
+
+  require 'sinatra/base'
+  mon_app = Sinatra.new { get('/') { "salut" } }
+  mon_app.run!
+
+L'application dont elle hérite peut être passé en argument optionnel :
+
+  # config.ru
+  require 'sinatra/base'
+
+  controleur = Sinatra.new do
+    enable :logging
+    helpers MyHelpers
+  end
+
+  map('/a') do
+    run Sinatra.new(controleur) { get('/') { 'a' } }
+  end
+
+  map('/b') do
+    run Sinatra.new(controleur) { get('/') { 'b' } }
+  end
+
+C'est notamment utile pour tester des extensions à Sinatra ou bien pour
+utiliser Sinatra dans votre propre bibliothèque.
+
+Cela permet également d'utiliser très facilement Sinatra comme middleware :
+
+  require 'sinatra/base'
+
+  use Sinatra do
+    get('/') { ... }
+  end
+
+  run RailsProject::Application
+
 == Contextes et Binding
 
 Le contexte dans lequel vous êtes détermine les méthodes et variables
@@ -1676,49 +1746,50 @@ disponibles.
 
 === Contexte de l'application/classe
 
-Toute application Sinatra correspond à une sous-classe de Sinatra::Base. Si
-vous utilisez le DSL haut niveau (<tt>require 'sinatra'</tt>), alors cette
-classe est Sinatra::Application, sinon il s'agit de la sous-classe que vous
-avez définie. Dans le contexte de la classe, vous avez accès aux méthodes
-telles que `get` ou `before`, mais vous n'avez pas accès aux objets `request`
-ou `session` car c'est la même classe d'application qui traitera toutes les
+Toute application Sinatra correspond à une sous-classe de <tt>Sinatra::Base</tt>.
+Si vous utilisez le DSL haut niveau (<tt>require 'sinatra'</tt>), alors cette
+classe est <tt>Sinatra::Application</tt>, 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:
+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!
+      # 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!
+        # Eh, je ne suis plus dans le contexte de l'application !
       end
     end
 
-Vous avez le binding du contexte de l'application dans:
+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`
+* Le bloc passé à +helpers+
+* Les procs/blocs utilisés comme argument pour +set+
+* Le bloc passé à <tt>Sinatra.new</tt>
 
-Vous pouvez atteindre ce contexte (donc la classe) de la façon suivante:
+Vous pouvez atteindre ce contexte (donc la classe) de la façon suivante :
 
-* Via l'objet passé dans les blocs `configure` (<tt>configure { |c| ... }</tt>)
-* En utilisant `settings` dans le contexte de la requête
+* Via l'objet passé dans les blocs +configure+ (<tt>configure { |c| ... }</tt>)
+* En utilisant +settings+ dans le contexte de la requête
 
 === Contexte de la requête/instance
 
 Pour tout traitement d'une requête, une nouvelle instance de votre classe
 d'application est créée et tous vos gestionnaires sont exécutés dans ce
-contexte. Dans ce dernier, vous pouvez accéder aux objets `request` et
-`session` et faire appel aux fonctions de rendu telles que `erb` ou `haml`.
+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`:
+requête au moyen de +settings+ :
 
   class MonApp < Sinatra::Base
-    # Eh, je suis dans le contexte de l'application!
+    # 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
@@ -1728,44 +1799,43 @@ requête au moyen de `settings`:
         @value # => nil (on est pas au sein de la même requête)
       end
 
-      "Route ajoutée!"
+      "Route ajoutée !"
     end
   end
 
-Vous avez le binding du contexte de la requête dans:
+Vous avez le binding du contexte de la requête dans :
 
 * les blocs get/head/post/put/delete/options
 * les filtres before/after
-* les méthodes utilitaires (définies au moyen de `helpers`)
+* les méthodes utilitaires (définies au moyen de +helpers+)
 * les vues/templates
 
 === Le contexte de délégation
 
 Le contexte de délégation se contente de transmettre les appels de méthodes au
 contexte de classe. Toutefois, il ne se comporte pas à 100% comme le contexte
-de classe car vous n'avez pas le binding de la classe: seules les méthodes
+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
+(comprenez : +self+ n'est pas le même). Vous pouvez ajouter des délégation de
 méthodes en appelant <tt>Sinatra::Delegator.delegate :method_name</tt>.
 
-Vous avez le binding du contexte de délégation dans:
+Vous avez le binding du contexte de délégation dans :
 
 * Le binding de haut niveau, si vous avez utilisé <tt>require "sinatra"</tt>
-* Un objet qui inclut le module `Sinatra::Delegator`
+* Un objet qui inclut le module +Sinatra::Delegator+
 
-Jetez un oeil
-pour vous faire une idée: voici le mixin
+Jetez un oeil pour vous faire une idée : voici le mixin
 {Sinatra::Delegator}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128]
 qui est {inclus dans l'espace de noms principal}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28]
 
 == Ligne de commande
 
-Les applications en Sinatra peuvent être lancées directement:
+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:
+Les options sont :
 
   -h # aide
   -p # déclare le port (4567 par défaut)
@@ -1776,21 +1846,8 @@ Les options sont:
 
 == Configuration nécessaire
 
-Il est recommandé d'installer Sinatra sur Ruby 1.8.7, 1.9.2, JRuby ou
-Rubinius.
-
 Les versions suivantes de Ruby sont officiellement supportées :
 
-[ Ruby 1.8.6 ]
-  Il n'est pas recommandé d'utiliser 1.8.6 pour Sinatra. Toutefois, ce sera
-  officiellement supporté jusqu'à l'arrivée Sinatra 1.3.0. Les templates RDoc
-  et CoffeeScript ne sont pas supportés par cette version de Ruby. 1.8.6
-  contient un défaut majeur de fuite de mémoire dans l'implémentation de Hash,
-  qui est déclenché par les versions de Sinatra antérieure à 1.1.1. La version
-  actuelle prévient explicitement ce risque au prix de la performance. Vous
-  devrez utiliser Rack 1.1.x dans la mesure où Rack >= 1.2 ne supporte plus
-  1.8.6.
-
 [ Ruby 1.8.7 ]
   1.8.7 est complètement supporté, toutefois si rien ne vous y retient, nous
   vous recommandons de passer à 1.9.2 ou bien de passer à JRuby ou Rubinius.
@@ -1801,14 +1858,18 @@ Les versions suivantes de Ruby sont officiellement supportées :
   réputé causer des erreurs de segmentation lorque Sinatra est utilisé.
 
 [ Rubinius ]
-  Rubinius est officiellement supporté (Rubinius >= 1.2.2), à l'exception
-  des templates Textile.
+  Rubinius est officiellement supporté (Rubinius >= 1.2.3), tout fonctionne,
+  y compris tous les langages de template.
 
 [ JRuby ]
-  JRuby est officiellement supporté (JRuby >= 1.5.6). Aucune anomalie avec
+  JRuby est officiellement supporté (JRuby >= 1.6.1). Aucune anomalie avec
   des bibliothèques de templates tierces ne sont connues. Toutefois, si vous
   choisissez JRuby, alors tournez vous vers des gestionnaires Rack JRuby car
-  le serveur Thin n'est pas (encore) supporté par JRuby.
+  le serveur Thin n'est pas complètement supporté par JRuby. Le support des
+  extensions C dans JRuby est encore expérimental, ce qui n'affecte que
+  RDiscount.
+
+<b>Ruby 1.8.6 n'est plus supporté.</b>
 
 Nous gardons également un oeil sur les versions Ruby à venir.
 
@@ -1816,15 +1877,17 @@ Les implémentations Ruby suivantes ne sont pas officiellement supportées mais
 sont toujours connues comme permettant à Sinatra de fonctionner :
 
 * Plus anciennes versions de JRuby et Rubinius
-* MacRuby
-* Maglev
-* IronRuby
+* MacRuby, Maglev, IronRuby
 * Ruby 1.9.0 et 1.9.1
 
 Ne pas être officiellement supporté signifie que si les choses se passent mal
 sur ces plateformes et non sur celles supportées, nous considérons que
 l'anomalie est de le ressort, pas du nôtre.
 
+Nous faisons également notre intégration continue (CI) avec ruby-head (la
+future 1.9.3), mais nous ne pouvons rien garantir étant donné que c'est
+constant mouvement. Vous pouvez vous attendre à ce que 1.9.3p0 soit supporté.
+
 Sinatra devrait fonctionner sur n'importe quel système d'exploitation
 supportant l'implémentation Ruby choisie.
 
@@ -1834,12 +1897,12 @@ Si vous voulez utiliser la toute dernière version de Sinatra, n'ayez pas peur
 de faire tourner votre application sur la branche master, cela devrait être
 stable.
 
-Nous publions également une gem de +prerelease+ de temps en temps donc vous
-pouvez faire la chose suivante :
+Nous publions également une gem de +prerelease+ de temps en temps que vous 
+pouvez installer comme suit :
 
   gem install sinatra --pre
 
-afin d'avoir certaines des toutes dernières fonctionnalités.
+afin d'avoir les toutes dernières fonctionnalités.
 
 === Avec Bundler
 
@@ -1905,8 +1968,16 @@ SemVer que SemVerTag.
 * {Site internet}[http://www.sinatrarb.com/] - Plus de documentation,
   de news, et des liens vers d'autres ressources.
 * {Contribuer}[http://www.sinatrarb.com/contributing] - Vous avez trouvé un
-  bug? Besoin d'aide? Vous avez un patch?
+  bug ? Besoin d'aide ? Vous avez un patch ?
 * {Suivi des problèmes}[http://github.com/sinatra/sinatra/issues]
 * {Twitter}[http://twitter.com/sinatra]
 * {Mailing List}[http://groups.google.com/group/sinatrarb/topics]
-* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] sur http://freenode.net
+* {IRC : #sinatra}[irc://chat.freenode.net/#sinatra] sur http://freenode.net
+* {IRC : #sinatra}[irc://chat.freenode.net/#sinatra] on http://freenode.net
+* {Sinatra Book}[http://sinatra-book.gittr.com] Tutoriels et recettes
+* {Sinatra Recipes}[http://recipes.sinatrarb.com/] Recettes contribuées
+  par la communauté
+* Documentation API de la {dernière version}[http://rubydoc.info/gems/sinatra]
+  ou du {HEAD courant}[http://rubydoc.info/github/sinatra/sinatra] sur
+  http://rubydoc.info/
+
diff --git a/README.hu.rdoc b/README.hu.rdoc
index e6deeca..c562d6a 100644
--- a/README.hu.rdoc
+++ b/README.hu.rdoc
@@ -99,9 +99,9 @@ tervezhetők, így például az user agent karakterláncot alapul véve:
 
 A statikus fájlok kiszolgálása a <tt>./public</tt> könyvtárból
 történik, de természetesen más könyvtárat is megadhatsz erre a célra,
-mégpedig a <tt>:public</tt> kapcsoló beállításával:
+mégpedig a <tt>:public_folder</tt> kapcsoló beállításával:
 
-  set :public, File.dirname(__FILE__) + '/static'
+  set :public_folder, File.dirname(__FILE__) + '/static'
 
 Fontos mgejegyezni, hogy a nyilvános könyvtár neve nem szerepel az URL-ben.
 A <tt>./public/css/style.css</tt> fájl az
diff --git a/README.jp.rdoc b/README.jp.rdoc
index 5e5d414..1765bb9 100644
--- a/README.jp.rdoc
+++ b/README.jp.rdoc
@@ -67,6 +67,12 @@ Sinatraでは、ルートはHTTPメソッドとURLマッチングパターンが
     params[:splat] # => ["path/to/file", "xml"]
   end
 
+ブロックパラーメータを使用した場合:
+
+  get '/download/*.*' do |path, ext|
+    [path, ext] # => ["path/to/file", "xml"]
+  end
+
 正規表現を使ったルート:
 
   get %r{/hello/([\w]+)} do
@@ -147,9 +153,9 @@ Rackレスポンス、Rackボディオブジェクト、HTTPステータスコ
 == 静的ファイル
 
 静的ファイルは<tt>./public</tt>ディレクトリから配信されます。
-<tt>:public</tt>オプションを指定することで別の場所を指定することができます。
+<tt>:public_folder</tt>オプションを指定することで別の場所を指定することができます。
 
-  set :public, File.dirname(__FILE__) + '/static'
+  set :public_folder, File.dirname(__FILE__) + '/static'
 
 注意: この静的ファイル用のディレクトリ名はURL中に含まれません。
 例えば、<tt>./public/css/style.css</tt>は<tt>http://example.com/css/style.css</tt>でアクセスできます。
@@ -370,6 +376,7 @@ textileからメソッドを呼び出すことも、localsに変数を渡すこ
 RDocテンプレートを使うにはRDocライブラリが必要です:
 
   # rdoc/markup/to_htmlを読み込みます
+  require "rdoc"
   require "rdoc/markup/to_html"
 
   get '/' do
@@ -432,6 +439,19 @@ Slimテンプレートを使うにはslimライブラリが必要です:
 
 <tt>./views/index.slim</tt>を表示します。
 
+=== Creole テンプレート
+
+Creoleテンプレートを使うにはcreoleライブラリが必要です:
+
+  # creoleを読み込みます
+  require 'creole'
+
+  get '/' do
+    creole :index
+  end
+
+<tt>./views/index.creole</tt>を表示します。
+
 === CoffeeScript テンプレート
 
 CoffeeScriptテンプレートを表示するにはcoffee-scriptライブラリと`coffee`バイナリが必要です:
@@ -990,7 +1010,7 @@ Sinatraの開発版を使いたい場合は、ローカルに開発版を落と
   git clone git://github.com/sinatra/sinatra.git
   ruby -Isinatra/lib myapp.rb
 
-<tt>sinatra/lib</tt>ディレクトリをto the<tt>LOAD_PATH</tt>に追加する方法もあります。
+<tt>sinatra/lib</tt>ディレクトリをアプリケーションの<tt>LOAD_PATH</tt>に追加する方法もあります。
 
   $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib'
   require 'rubygems'
diff --git a/README.pt-br.rdoc b/README.pt-br.rdoc
index 5a3e7e7..6b3ba2e 100644
--- a/README.pt-br.rdoc
+++ b/README.pt-br.rdoc
@@ -95,9 +95,9 @@ Rotas podem incluir uma variedade de condições correspondes, tal como o agente
 == Arquivos estáticos
 
 Arquivos estáticos são disponibilizados a partir do diretório <tt>./public</tt>. Você pode especificar
-um local diferente pela opção <tt>:public</tt>
+um local diferente pela opção <tt>:public_folder</tt>
 
-  set :public, File.dirname(__FILE__) + '/estatico'
+  set :public_folder, File.dirname(__FILE__) + '/estatico'
 
 Note que o nome do diretório público não é incluido na URL. Um arquivo
 <tt>./public/css/style.css</tt> é disponibilizado como
diff --git a/README.pt-pt.rdoc b/README.pt-pt.rdoc
index f8a8872..5b19082 100644
--- a/README.pt-pt.rdoc
+++ b/README.pt-pt.rdoc
@@ -95,9 +95,9 @@ Rotas podem incluir uma variedade de condições correspondentes, por exemplo, o
 == Arquivos estáticos
 
 Arquivos estáticos são disponibilizados a partir do directório <tt>./public</tt>. Você pode especificar
-um local diferente através da opção <tt>:public</tt>
+um local diferente através da opção <tt>:public_folder</tt>
 
-  set :public, File.dirname(__FILE__) + '/estatico'
+  set :public_folder, File.dirname(__FILE__) + '/estatico'
 
 Note que o nome do directório público não é incluido no URL. Um arquivo
 <tt>./public/css/style.css</tt> é disponibilizado como
diff --git a/README.rdoc b/README.rdoc
index 301b066..b64a899 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -34,13 +34,17 @@ Each route is associated with a block:
   end
 
   put '/' do
-    .. update something ..
+    .. replace something ..
+  end
+
+  patch '/' do
+    .. modify something ..
   end
 
   delete '/' do
     .. annihilate something ..
   end
-  
+
   options '/' do
     .. appease something ..
   end
@@ -76,6 +80,12 @@ via the <tt>params[:splat]</tt> array:
     params[:splat] # => ["path/to/file", "xml"]
   end
 
+Or with block parameters:
+
+  get '/download/*.*' do |path, ext|
+    [path, ext] # => ["path/to/file", "xml"]
+  end
+
 Route matching with Regular Expressions:
 
   get %r{/hello/([\w]+)} do
@@ -88,6 +98,15 @@ Or with a block parameter:
     "Hello, #{c}!"
   end
 
+Route patterns may have optional parameters:
+
+  get '/posts.?:format?' do
+    # matches "GET /posts" and any extension "GET /posts.json", "GET /posts.xml" etc.
+  end
+
+By the way, unless you disable the path traversal attack protection (see below),
+the request path might be modified before matching against your routes.
+
 === Conditions
 
 Routes may include a variety of matching conditions, such as the user agent:
@@ -125,7 +144,25 @@ You can easily define your own conditions:
   get '/win_a_car' do
     "Sorry, you lost."
   end
+  
+For a condition that takes multiple values use a splat:
+
+  set(:auth) do |*roles|   # <- notice the splat here
+    condition do
+      unless logged_in? && roles.any? {|role| current_user.in_role? role }
+        redirect "/login/", 303 
+      end
+    end
+  end
 
+  get "/my/account/", :auth => [:user, :admin] do
+    "Your Account Details"
+  end
+  
+  get "/only/admin/", :auth => :admin do
+    "Only admins are allowed here!"
+  end
+  
 === Return Values
 
 The return value of a route block determines at least the response body passed
@@ -136,9 +173,12 @@ also accepted.
 You can return any object that would either be a valid Rack response, Rack
 body object or HTTP status code:
 
-* An Array with three elements: <tt>[status (Fixnum), headers (Hash), response body (responds to #each)]</tt>
-* An Array with two elements: <tt>[status (Fixnum), response body (responds to #each)]</tt>
-* An object that responds to <tt>#each</tt> and passes nothing but strings to the given block
+* An Array with three elements: <tt>[status (Fixnum), headers (Hash), response
+  body (responds to #each)]</tt>
+* An Array with two elements: <tt>[status (Fixnum), response body (responds to
+  #each)]</tt>
+* An object that responds to <tt>#each</tt> and passes nothing but strings to 
+  the given block
 * A Fixnum representing the status code
 
 That way we can, for instance, easily implement a streaming example:
@@ -151,6 +191,9 @@ That way we can, for instance, easily implement a streaming example:
 
     get('/') { Stream.new }
 
+You can also use the +stream+ helper method (described below) to reduce boiler
+plate and embed the streaming logic in the route.
+
 === Custom Route Matchers
 
 As shown above, Sinatra ships with built-in support for using String patterns
@@ -195,205 +238,176 @@ Or, using negative look ahead:
 == Static Files
 
 Static files are served from the <tt>./public</tt> directory. You can specify
-a different location by setting the <tt>:public</tt> option:
+a different location by setting the <tt>:public_folder</tt> option:
 
-  set :public, File.dirname(__FILE__) + '/static'
+  set :public_folder, File.dirname(__FILE__) + '/static'
 
 Note that the public directory name is not included in the URL. A file
 <tt>./public/css/style.css</tt> is made available as
 <tt>http://example.com/css/style.css</tt>.
 
-== Views / Templates
+Use the <tt>:static_cache_control</tt> setting (see below) to add
+<tt>Cache-Control</tt> header info.
 
-Templates are assumed to be located directly under the <tt>./views</tt>
-directory. To use a different views directory:
-
-  set :views, File.dirname(__FILE__) + '/templates'
-
-One important thing to remember is that you always have to reference
-templates with symbols, even if they're in a subdirectory (in this
-case, use <tt>:'subdir/template'</tt>). You must use a symbol because
-otherwise rendering methods will render any strings passed to them
-directly.
-
-=== Haml Templates
-
-The <tt>haml</tt> gem/library is required to render HAML templates:
+== Views / Templates
 
-  # You'll need to require haml in your app
-  require 'haml'
+Each template language is exposed as via its own rendering method. These
+methods simply return a string:
 
   get '/' do
-    haml :index
+    erb :index
   end
 
-Renders <tt>./views/index.haml</tt>.
+This renders <tt>views/index.erb</tt>.
 
-{Haml's options}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options]
-can be set globally through Sinatra's configurations,
-see {Options and Configurations}[http://www.sinatrarb.com/configuration.html],
-and overridden on an individual basis.
-
-  set :haml, :format => :html5 # default Haml format is :xhtml
+Instead of a template name, you can also just pass in the template content
+directly:
 
   get '/' do
-    haml :index, :format => :html4 # overridden
+    code = "<%= Time.now %>"
+    erb code
   end
 
-
-=== Erb Templates
-
-  # You'll need to require erb in your app
-  require 'erb'
+Templates take a second argument, the options hash:
 
   get '/' do
-    erb :index
+    erb :index, :layout => :post
   end
 
-Renders <tt>./views/index.erb</tt>.
-
-=== Erubis Templates
+This will render <tt>views/index.erb</tt> embedded in the
+<tt>views/post.erb</tt> (default is <tt>views/layout.erb</tt>, if it exists).
 
-The <tt>erubis</tt> gem/library is required to render Erubis templates:
-
-  # You'll need to require erubis in your app
-  require 'erubis'
+Any options not understood by Sinatra will be passed on to the template
+engine:
 
   get '/' do
-    erubis :index
+    haml :index, :format => :html5
   end
 
-Renders <tt>./views/index.erubis</tt>.
+You can also set options per template language in general:
 
-It is also possible to replace Erb with Erubis:
+  set :haml, :format => :html5
 
-  require 'erubis'
-  Tilt.register :erb, Tilt[:erubis]
-  
   get '/' do
-    erb :index
+    haml :index
   end
 
-Renders <tt>./views/index.erb</tt> with Erubis.
+Options passed to the render method override options set via +set+.
 
-=== Builder Templates
+Available Options:
 
-The <tt>builder</tt> gem/library is required to render builder templates:
+[locals]
+  List of locals passed to the document. Handy with partials.
+  Example: <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>
 
-  # You'll need to require builder in your app
-  require 'builder'
+[default_encoding]
+  String encoding to use if uncertain. Defaults to
+  <tt>settings.default_encoding</tt>.
 
-  get '/' do
-    builder :index
-  end
+[views]
+  Views folder to load templates from. Defaults to <tt>settings.views</tt>.
 
-Renders <tt>./views/index.builder</tt>.
+[layout]
+  Whether to use a layout (+true+ or +false+), if it's a Symbol, specifies
+  what template to use. Example: <tt>erb :index, :layout => !request.xhr?</tt>
 
-=== Nokogiri Templates
+[content_type]
+  Content-Type the template produces, default depends on template language.
 
-The <tt>nokogiri</tt> gem/library is required to render nokogiri templates:
+[scope]
+  Scope to render template under. Defaults to the application instance. If you
+  change this, instance variables and helper methods will not be available.
 
-  # You'll need to require nokogiri in your app
-  require 'nokogiri'
+[layout_engine]
+  Template engine to use for rendering the layout. Useful for languages that
+  do not support layouts otherwise. Defaults to the engine used for the
+  template. Example: <tt>set :rdoc, :layout_engine => :erb</tt>
 
-  get '/' do
-    nokogiri :index
-  end
-
-Renders <tt>./views/index.nokogiri</tt>.
+Templates are assumed to be located directly under the <tt>./views</tt>
+directory. To use a different views directory:
 
-=== Sass Templates
+  set :views, settings.root + '/templates'
 
-The <tt>haml</tt> or <tt>sass</tt> gem/library is required to render Sass templates:
+One important thing to remember is that you always have to reference
+templates with symbols, even if they're in a subdirectory (in this
+case, use <tt>:'subdir/template'</tt>). You must use a symbol because
+otherwise rendering methods will render any strings passed to them
+directly.
 
-  # You'll need to require haml or sass in your app
-  require 'sass'
+=== Available Template Languages
 
-  get '/stylesheet.css' do
-    sass :stylesheet
-  end
+Some languages have multiple implementations. To specify what implementation
+to use (and to be thread-safe), you should simply require it first:
 
-Renders <tt>./views/stylesheet.sass</tt>.
+  require 'rdiscount' # or require 'bluecloth'
+  get('/') { markdown :index }
 
-{Sass's options}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
-can be set globally through Sinatra's configurations,
-see {Options and Configurations}[http://www.sinatrarb.com/configuration.html],
-and overridden on an individual basis.
+=== Haml Templates
 
-  set :sass, :style => :compact # default Sass style is :nested
+Dependency::        {haml}[http://haml-lang.com/]
+File Extensions::   <tt>.haml</tt>
+Example::           <tt>haml :index, :format => :html5</tt>
 
-  get '/stylesheet.css' do
-    sass :stylesheet, :style => :expanded # overridden
-  end
+=== Erb Templates
 
-=== Scss Templates
+Dependency::        {erubis}[http://www.kuwata-lab.com/erubis/] or
+                    erb (included in Ruby)
+File Extensions::   <tt>.erb</tt>, <tt>.rhtml</tt> or <tt>.erubis</tt> (Erubis
+                    only)
+Example::           <tt>erb :index</tt>
 
-The <tt>haml</tt> or <tt>sass</tt> gem/library is required to render Scss templates:
+=== Builder Templates
 
-  # You'll need to require haml or sass in your app
-  require 'sass'
+Dependency::        {builder}[http://builder.rubyforge.org/]
+File Extensions::   <tt>.builder</tt>
+Example::           <tt>builder { |xml| xml.em "hi" }</tt>
 
-  get '/stylesheet.css' do
-    scss :stylesheet
-  end
+It also takes a block for inline templates (see example).
 
-Renders <tt>./views/stylesheet.scss</tt>.
+=== Nokogiri Templates
 
-{Scss's options}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
-can be set globally through Sinatra's configurations,
-see {Options and Configurations}[http://www.sinatrarb.com/configuration.html],
-and overridden on an individual basis.
+Dependency::        {nokogiri}[http://nokogiri.org/]
+File Extensions::   <tt>.nokogiri</tt>
+Example::           <tt>nokogiri { |xml| xml.em "hi" }</tt>
 
-  set :scss, :style => :compact # default Scss style is :nested
+It also takes a block for inline templates (see example).
 
-  get '/stylesheet.css' do
-    scss :stylesheet, :style => :expanded # overridden
-  end
+=== Sass Templates
 
-=== Less Templates
+Dependency::        {sass}[http://sass-lang.com/]
+File Extensions::   <tt>.sass</tt>
+Example::           <tt>sass :stylesheet, :style => :expanded</tt>
 
-The <tt>less</tt> gem/library is required to render Less templates:
+=== SCSS Templates
 
-  # You'll need to require less in your app
-  require 'less'
+Dependency::        {sass}[http://sass-lang.com/]
+File Extensions::   <tt>.scss</tt>
+Example::           <tt>scss :stylesheet, :style => :expanded</tt>
 
-  get '/stylesheet.css' do
-    less :stylesheet
-  end
+=== Less Templates
 
-Renders <tt>./views/stylesheet.less</tt>.
+Dependency::        {less}[http://www.lesscss.org/]
+File Extensions::   <tt>.less</tt>
+Example::           <tt>less :stylesheet</tt>
 
 === Liquid Templates
 
-The <tt>liquid</tt> 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 <tt>./views/index.liquid</tt>.
+Dependency::        {liquid}[http://www.liquidmarkup.org/]
+File Extensions::   <tt>.liquid</tt>
+Example::           <tt>liquid :index, :locals => { :key => 'value' }</tt>
 
 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' }
+template, you almost always want to pass locals to it.
 
 === Markdown Templates
 
-The <tt>rdiscount</tt> 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 <tt>./views/index.markdown</tt> (+md+ and +mkd+ are also valid file
-extensions).
+Dependency::        {rdiscount}[https://github.com/rtomayko/rdiscount],
+                    {redcarpet}[https://github.com/tanoku/redcarpet],
+                    {bluecloth}[http://deveiate.org/projects/BlueCloth],
+                    {kramdown}[http://kramdown.rubyforge.org/] *or*
+                    {maruku}[http://maruku.rubyforge.org/]
+File Extensions::   <tt>.markdown</tt>, <tt>.mkd</tt> and <tt>.md</tt>
+Example::           <tt>markdown :index, :layout_engine => :erb</tt>
 
 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
@@ -408,52 +422,13 @@ Note that you may also call the +markdown+ method from within other templates:
 
 Since you cannot call Ruby from Markdown, you cannot use layouts written in
 Markdown. However, it is possible to use another rendering engine for the
-template than for the layout by passing the <tt>:layout_engine</tt> option:
-
-  get '/' do
-    markdown :index, :layout_engine => :erb
-  end
-
-This will render <tt>./views/index.md</tt> with <tt>./views/layout.erb</tt> as
-layout.
-
-Remember that you can set such rendering options globally:
-
-  set :markdown, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    markdown :index
-  end
-
-This will render <tt>./views/index.md</tt> (and any other Markdown template)
-with <tt>./views/post.haml</tt> as layout.
-
-It is also possible to parse Markdown with BlueCloth rather than RDiscount:
-
-  require 'bluecloth'
-  
-  Tilt.register 'markdown', BlueClothTemplate
-  Tilt.register 'mkd',      BlueClothTemplate
-  Tilt.register 'md',       BlueClothTemplate
-  
-  get '/' do
-    markdown :index
-  end
-
-Renders <tt>./views/index.md</tt> with BlueCloth.
+template than for the layout by passing the <tt>:layout_engine</tt> option.
 
 === Textile Templates
 
-The <tt>RedCloth</tt> 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 <tt>./views/index.textile</tt>.
+Dependency::        {RedCloth}[http://redcloth.org/]
+File Extensions::   <tt>.textile</tt>
+Example::           <tt>textile :index, :layout_engine => :erb</tt>
 
 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:
@@ -467,38 +442,13 @@ Note that you may also call the +textile+ method from within other templates:
 
 Since you cannot call Ruby from Textile, you cannot use layouts written in
 Textile. However, it is possible to use another rendering engine for the
-template than for the layout by passing the <tt>:layout_engine</tt> option:
-
-  get '/' do
-    textile :index, :layout_engine => :erb
-  end
-
-This will render <tt>./views/index.textile</tt> with
-<tt>./views/layout.erb</tt> as layout.
-
-Remember that you can set such rendering options globally:
-
-  set :textile, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    textile :index
-  end
-
-This will render <tt>./views/index.textile</tt> (and any other Textile
-template) with <tt>./views/post.haml</tt> as layout.
+template than for the layout by passing the <tt>:layout_engine</tt> option.
 
 === RDoc Templates
 
-The <tt>rdoc</tt> gem/library is required to render RDoc templates:
-
-  # You'll need to require rdoc/markup/to_html in your app
-  require "rdoc/markup/to_html"
-
-  get '/' do
-    rdoc :index
-  end
-
-Renders <tt>./views/index.rdoc</tt>.
+Dependency::        {rdoc}[http://rdoc.rubyforge.org/]
+File Extensions::   <tt>.rdoc</tt>
+Example::           <tt>rdoc :README, :layout_engine => :erb</tt>
 
 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:
@@ -512,97 +462,57 @@ Note that you may also call the +rdoc+ method from within other templates:
 
 Since you cannot call Ruby from RDoc, you cannot use layouts written in
 RDoc. However, it is possible to use another rendering engine for the
-template than for the layout by passing the <tt>:layout_engine</tt> option:
-
-  get '/' do
-    rdoc :index, :layout_engine => :erb
-  end
-
-This will render <tt>./views/index.rdoc</tt> with <tt>./views/layout.erb</tt> as
-layout.
-
-Remember that you can set such rendering options globally:
-
-  set :rdoc, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    rdoc :index
-  end
-
-This will render <tt>./views/index.rdoc</tt> (and any other RDoc template)
-with <tt>./views/post.haml</tt> as layout.
+template than for the layout by passing the <tt>:layout_engine</tt> option.
 
 === Radius Templates
 
-The <tt>radius</tt> 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 <tt>./views/index.radius</tt>.
+Dependency::        {radius}[http://radius.rubyforge.org/]
+File Extensions::   <tt>.radius</tt>
+Example::           <tt>radius :index, :locals => { :key => 'value' }</tt>
 
-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' }
+Since you cannot call Ruby methods directly from a Radius template, you almost
+always want to pass locals to it.
 
 === Markaby Templates
 
-The <tt>markaby</tt> 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 <tt>./views/index.mab</tt>.
+Dependency::        {markaby}[http://markaby.github.com/]
+File Extensions::   <tt>.mab</tt>
+Example::           <tt>markaby { h1 "Welcome!" }</tt>
 
-You may also use inline Markaby:
-
-  get '/' do
-    markaby { h1 "Welcome!" }
-  end
+It also takes a block for inline templates (see example).
 
 === Slim Templates
 
-The <tt>slim</tt> gem/library is required to render Slim templates:
-
-  # You'll need to require slim in your app
-  require 'slim'
+Dependency::        {slim}[http://slim-lang.com/]
+File Extensions::   <tt>.slim</tt>
+Example::           <tt>slim :index</tt>
 
-  get '/' do
-    slim :index
-  end
+=== Creole Templates
 
-Renders <tt>./views/index.slim</tt>.
+Dependency::        {creole}[https://github.com/minad/creole]
+File Extensions::   <tt>.creole</tt>
+Example::           <tt>creole :wiki, :layout_engine => :erb</tt>
 
-=== CoffeeScript Templates
-
-The <tt>coffee-script</tt> gem/library and at least <b>one</b> of the
-following options to execute JavaScript:
+It is not possible to call methods from creole, nor to pass locals to it. You
+therefore will usually use it in combination with another rendering engine:
 
-* +node+ (from Node.js) in your path
-* you must be running on OSX
-* +therubyracer+ gem/library
+  erb :overview, :locals => { :text => creole(:introduction) }
 
-See http://github.com/josh/ruby-coffee-script for an updated list of options.
+Note that you may also call the +creole+ method from within other templates:
 
-Now you can render CoffeeScript templates:
+  %h1 Hello From Haml!
+  %p= creole(:greetings)
 
-  # You'll need to require coffee-script in your app
-  require 'coffee-script'
+Since you cannot call Ruby from Creole, you cannot use layouts written in
+Creole. However, it is possible to use another rendering engine for the
+template than for the layout by passing the <tt>:layout_engine</tt> option.
 
-  get '/application.js' do
-    coffee :application
-  end
+=== CoffeeScript Templates
 
-Renders <tt>./views/application.coffee</tt>.
+Dependency::        {coffee-script}[https://github.com/josh/ruby-coffee-script]
+                    and a {way to execute javascript}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
+File Extensions::   <tt>.coffee</tt>
+Example::           <tt>coffee :index</tt>
 
 === Embedded Templates
 
@@ -626,7 +536,7 @@ Or, specify an explicit Hash of local variables:
 
   get '/:id' do
     foo = Foo.find(params[:id])
-    haml '%h1= foo.name', :locals => { :foo => foo }
+    haml '%h1= bar.name', :locals => { :bar => foo }
   end
 
 This is typically used when rendering templates as partials from within
@@ -672,8 +582,9 @@ Templates may also be defined using the top-level <tt>template</tt> method:
   end
 
 If a template named "layout" exists, it will be used each time a template
-is rendered. You can individually disable layouts by passing <tt>:layout => false</tt>
-or disable them by default via <tt>set :haml, :layout => false</tt>:
+is rendered. You can individually disable layouts by passing 
+<tt>:layout => false</tt> or disable them by default via 
+<tt>set :haml, :layout => false</tt>:
 
   get '/' do
     haml :index, :layout => !request.xhr?
@@ -785,9 +696,9 @@ session hash per user session:
 
 Note that <tt>enable :sessions</tt> actually stores all data in a cookie. This
 might not always be what you want (storing lots of data will increase your
-traffic, for instance). You can use any Rack session middleware, in order to
+traffic, for instance). You can use any Rack session middleware: in order to
 do so, do *not* call <tt>enable :sessions</tt>, but instead pull in your
-middleware of choice how you would any other middleware:
+middleware of choice as you would any other middleware:
 
   use Rack::Session::Pool, :expire_after => 2592000
 
@@ -806,6 +717,11 @@ set the secret yourself, so all your application instances share it:
 
   set :session_secret, 'super secret'
 
+If you want to configure it further, you may also store a hash with options in
+the +sessions+ setting:
+
+  set :sessions, :domain => 'foo.com'
+
 === Halting
 
 To immediately stop a request within a filter or route use:
@@ -887,15 +803,15 @@ access the body:
     puts body
   end
 
-It is also possible to pass a block to +body+, which will be executed by the Rack
-handler (this can be used to implement streaming, see "Return Values").
+It is also possible to pass a block to +body+, which will be executed by the 
+Rack handler (this can be used to implement streaming, see "Return Values").
 
 Similar to the body, you can also set the status code and headers:
 
   get '/foo' do
     status 418
     headers \
-      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN"
+      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN",
       "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
     body "I'm a tea pot!"
   end
@@ -903,12 +819,90 @@ Similar to the body, you can also set the status code and headers:
 Like +body+, +headers+ and +status+ with no arguments can be used to access
 their current values.
 
+=== Streaming Responses
+
+Sometimes you want to start sending out data while still generating parts of
+the response body. In extreme examples, you want to keep sending data until
+the client closes the connection. You can use the +stream+ helper to avoid
+creating your own wrapper:
+
+  get '/' do
+    stream do |out|
+      out << "It's gonna be legen -\n"
+      sleep 0.5
+      out << " (wait for it) \n"
+      sleep 1
+      out << "- dary!\n"
+    end
+  end
+
+This allows you to implement streaming APIs,
+{Server Sent Events}[http://dev.w3.org/html5/eventsource/] and can be used as
+basis for {WebSockets}[http://en.wikipedia.org/wiki/WebSocket]. It can also be
+used to increase throughput if some but not all content depends on a slow
+resource.
+
+Note that the streaming behavior, especially the number of concurrent request,
+highly depends on the web server used to serve the application. Some servers,
+like WEBRick, might not even support streaming at all. If the server does not
+support streaming, the body will be sent all at once after the block passed to
++stream+ finished executing. Streaming does not work at all with Shotgun.
+
+If the optional parameter is set to +keep_open+, it will not call +close+ on
+the stream object, allowing you to close it at any later point in the
+execution flow. This only works on evented servers, like Thin and Rainbows.
+Other servers will still close the stream:
+
+  set :server, :thin
+  connections = []
+
+  get '/' do
+    # keep stream open
+    stream(:keep_open) { |out| connections << out }
+  end
+
+  post '/' do
+    # write to all open streams
+    connections.each { |out| out << params[:message] << "\n" }
+    "message sent"
+  end
+
+=== Logging
+
+In the request scope, the +logger+ helper exposes a +Logger+ instance:
+
+  get '/' do
+    logger.info "loading data"
+    # ...
+  end
+
+This logger will automatically take your Rack handler's logging settings into
+account. If logging is disabled, this method will return a dummy object, so
+you do not have to worry in your routes and filters about it.
+
+Note that logging is only enabled for <tt>Sinatra::Application</tt> by
+default, so if you inherit from <tt>Sinatra::Base</tt>, you probably want to
+enable it yourself:
+
+  class MyApp < Sinatra::Base
+    configure :production, :development do
+      enable :logging
+    end
+  end
+
+To avoid any logging middleware to be set up, set the +logging+ setting to
++nil+. However, keep in mind that +logger+ will in that case return +nil+. A
+common use case is when you want to set your own logger. Sinatra will use
+whatever it will find in <tt>env['rack.logger']</tt>.
+
 === Mime Types
 
 When using <tt>send_file</tt> or static files you may have mime types Sinatra
 doesn't understand. Use +mime_type+ to register them by file extension:
 
-  mime_type :foo, 'text/foo'
+  configure do
+    mime_type :foo, 'text/foo'
+  end
 
 You can also use it with the +content_type+ helper:
 
@@ -959,7 +953,7 @@ To pass arguments with a redirect, either add them to the query:
 
 Or use a session:
 
-  enable :session
+  enable :sessions
   
   get '/foo' do
     session[:secret] = 'foo'
@@ -994,7 +988,7 @@ If you are using the +expires+ helper to set the corresponding header,
     expires 500, :public, :must_revalidate
   end
 
-To properly use caches, you should consider using +etag+ and +last_modified+.
+To properly use caches, you should consider using +etag+ or +last_modified+.
 It is recommended to call those helpers *before* doing heavy lifting, as they
 will immediately flush a response if the client already has the current
 version in its cache:
@@ -1012,8 +1006,8 @@ It is also possible to use a
   etag @article.sha1, :weak
 
 These helpers will not do any caching for you, but rather feed the necessary
-information to your cache. If you are looking for a quick caching solutions, try
-{rack-cache}[http://rtomayko.github.com/rack-cache/]:
+information to your cache. If you are looking for a quick reverse-proxy caching solution, 
+try {rack-cache}[http://rtomayko.github.com/rack-cache/]:
 
   require "rack/cache"
   require "sinatra"
@@ -1026,6 +1020,26 @@ information to your cache. If you are looking for a quick caching solutions, try
     "hello"
   end
 
+Use the <tt>:static_cache_control</tt> setting (see below) to add
+<tt>Cache-Control</tt> header info to static files.
+
+According to RFC 2616 your application should behave differently if the If-Match
+or If-None-Match header is set to <tt>*</tt> depending on whether the resource
+requested is already in existence. Sinatra assumes resources for safe (like get)
+and idempotent (like put) requests are already in existence, whereas other
+resources (for instance for post requests), are treated as new resources. You
+can change this behavior by passing in a <tt>:new_resource</tt> option:
+
+  get '/create' do
+    etag '', :new_resource => true
+    Article.create
+    erb :new_article
+  end
+
+If you still want to use a weak ETag, pass in a <tt>:kind</tt> option:
+
+  etag '', :new_resource => true, :kind => :weak
+
 === Sending Files
 
 For sending files, you can use the <tt>send_file</tt> helper method:
@@ -1067,29 +1081,33 @@ error handlers) through the <tt>request</tt> method:
 
   # app running on http://example.com/example
   get '/foo' do
-    request.body              # request body sent by the client (see below)
-    request.scheme            # "http"
-    request.script_name       # "/example"
-    request.path_info         # "/foo"
-    request.port              # 80
-    request.request_method    # "GET"
-    request.query_string      # ""
-    request.content_length    # length of request.body
-    request.media_type        # media type of request.body
-    request.host              # "example.com"
-    request.get?              # true (similar methods for other verbs)
-    request.form_data?        # false
-    request["SOME_HEADER"]    # value of SOME_HEADER header
-    request.referrer          # the referrer of the client or '/'
-    request.user_agent        # user agent (used by :agent condition)
-    request.cookies           # hash of browser cookies
-    request.xhr?              # is this an ajax request?
-    request.url               # "http://example.com/example/foo"
-    request.path              # "/example/foo"
-    request.ip                # client IP address
-    request.secure?           # false (would be true over ssl)
-    request.forwarded?        # true (if running behind a reverse proxy)
-    request.env               # raw env hash handed in by Rack
+    t = %w[text/css text/html application/javascript]
+    request.accept              # ['text/html', '*/*']
+    request.accept? 'text/xml'  # true
+    request.preferred_type(t)   # 'text/html'
+    request.body                # request body sent by the client (see below)
+    request.scheme              # "http"
+    request.script_name         # "/example"
+    request.path_info           # "/foo"
+    request.port                # 80
+    request.request_method      # "GET"
+    request.query_string        # ""
+    request.content_length      # length of request.body
+    request.media_type          # media type of request.body
+    request.host                # "example.com"
+    request.get?                # true (similar methods for other verbs)
+    request.form_data?          # false
+    request["SOME_HEADER"]      # value of SOME_HEADER header
+    request.referrer            # the referrer of the client or '/'
+    request.user_agent          # user agent (used by :agent condition)
+    request.cookies             # hash of browser cookies
+    request.xhr?                # is this an ajax request?
+    request.url                 # "http://example.com/example/foo"
+    request.path                # "/example/foo"
+    request.ip                  # client IP address
+    request.secure?             # false (would be true over ssl)
+    request.forwarded?          # true (if running behind a reverse proxy)
+    request.env                 # raw env hash handed in by Rack
   end
 
 Some options, like <tt>script_name</tt> or <tt>path_info</tt>, can also be
@@ -1126,6 +1144,37 @@ You can also pass it a file name:
     "store it!"
   end
 
+=== Dealing with Date and Time
+
+Sinatra offers a +time_for+ helper method, which, from the given value
+generates a Time object. It is also able to convert +DateTime+, +Date+ and
+similar classes:
+
+  get '/' do
+    pass if Time.now > time_for('Dec 23, 2012')
+    "still time"
+  end
+
+This method is used internally by +expires+, +last_modified+ and akin. You can
+therefore easily extend the behavior of those methods by overriding +time_for+
+in your application:
+
+  helpers do
+    def time_for(value)
+      case value
+      when :yesterday then Time.now - 24*60*60
+      when :tomorrow  then Time.now + 24*60*60
+      else super
+      end
+    end
+  end
+
+  get '/' do
+    last_modified :yesterday
+    expires :tomorrow
+    "hello"
+  end
+
 === Looking Up Template Files
 
 The <tt>find_template</tt> helper is used to find template files for rendering:
@@ -1214,88 +1263,140 @@ You can access those options via <tt>settings</tt>:
     ...
   end
 
+=== Configuring attack protection
+
+Sinatra is using
+{Rack::Protection}[https://github.com/rkh/rack-protection#readme] to defend
+you application against common, opportunistic attacks. You can easily disable
+this behavior (which should result in performance gains):
+
+  disable :protection
+
+To skip a single defense layer, set +protection+ to an options hash:
+
+  set :protection, :except => :path_traversal
+
+You can also hand in an array in order to disable a list of protections:
+
+  set :protection, :except => [:path_traversal, :session_hijacking]
+
 === Available Settings
 
-[absolute_redirects]  If disabled, Sinatra will allow relative redirects,
-                      however, Sinatra will no longer conform with RFC 2616
-                      (HTTP 1.1), which only allows absolute redirects.
-                      
-                      Enable if your app is running behind a reverse proxy that
-                      has not been set up properly. Note that the +url+ helper
-                      will still produce absolute URLs, unless you pass in
-                      +false+ as second parameter.
-                      
-                      Disabled per default.
+[absolute_redirects]   If disabled, Sinatra will allow relative redirects,
+                       however, Sinatra will no longer conform with RFC 2616
+                       (HTTP 1.1), which only allows absolute redirects.
+
+                       Enable if your app is running behind a reverse proxy that
+                       has not been set up properly. Note that the +url+ helper
+                       will still produce absolute URLs, unless you pass in
+                       +false+ as second parameter.
+
+                       Disabled per default.
+
+[add_charsets]         mime types the <tt>content_type</tt> helper will
+                       automatically add the charset info to.
+
+                       You should add to it rather than overriding this option:
+
+                         settings.add_charsets << "application/foobar"
+
+[app_file]             Path to the main application file, used to detect project root,
+                       views and public folder and inline templates.
+
+[bind]                 IP address to bind to (default: 0.0.0.0).
+                       Only used for built-in server.
+
+[default_encoding]     encoding to assume if unknown
+                       (defaults to <tt>"utf-8"</tt>).
+
+[dump_errors]          display errors in the log.
+
+[environment]          current environment, defaults to <tt>ENV['RACK_ENV']</tt>,
+                       or <tt>"development"</tt> if not available.
 
-[add_charsets]        mime types the <tt>content_type</tt> helper will
-                      automatically add the charset info to.
-                      
-                      You should add to it rather than overriding this option:
-                      
-                        settings.add_charsets << "application/foobar"
+[logging]              use the logger.
 
-[app_file]            main application file, used to detect project root,
-                      views and public folder and inline templates.
+[lock]                 Places a lock around every request, only running
+                       processing on request per Ruby process concurrently.
 
-[bind]                IP address to bind to (default: 0.0.0.0).
-                      Only used for built-in server.
+                       Enabled if your app is not thread-safe.
+                       Disabled per default.
 
-[default_encoding]    encoding to assume if unknown
-                      (defaults to <tt>"utf-8"</tt>).
+[method_override]      use <tt>_method</tt> magic to allow put/delete forms in
+                       browsers that don't support it.
 
-[dump_errors]         display errors in the log.
+[port]                 Port to listen on. Only used for built-in server.
 
-[environment]         current environment, defaults to <tt>ENV['RACK_ENV']</tt>,
-                      or <tt>"development"</tt> if not available.
+[prefixed_redirects]   Whether or not to insert <tt>request.script_name</tt>
+                       into redirects if no absolute path is given. That way
+                       <tt>redirect '/foo'</tt> would behave like
+                       <tt>redirect to('/foo')</tt>. Disabled per default.
 
-[logging]             use the logger.
+[protection]           Whether or not to enable web attack protections. See
+                       protection section above.
 
-[lock]                Places a lock around every request, only running
-                      processing on request per Ruby process concurrently.
-                      
-                      Enabled if your app is not thread-safe.
-                      Disabled per default.
+[public_folder]        Path to the folder public files are served from. Only
+                       used if static file serving is enabled (see
+                       <tt>static</tt> setting below). Inferred from
+                       <tt>app_file</tt> setting if not set.
 
-[method_override]     use <tt>_method</tt> magic to allow put/delete forms in
-                      browsers that don't support it.
+[reload_templates]     whether or not to reload templates between requests.
+                       Enabled in development mode.
 
-[port]                Port to listen on. Only used for built-in server.
+[root]                 Path to project root folder. Inferred from +app_file+
+                       setting if not set.
 
-[prefixed_redirects]  Whether or not to insert <tt>request.script_name</tt> into
-                      redirects if no absolute path is given. That way
-                      <tt>redirect '/foo'</tt> would behave like
-                      <tt>redirect to('/foo')</tt>. Disabled per default.
+[raise_errors]         raise exceptions (will stop application). Enabled
+                       by default when <tt>environment</tt> is set to <tt>"test"</tt>,
+                       disabled otherwise.
 
-[public]              folder public files are served from
+[run]                  if enabled, Sinatra will handle starting the web server,
+                       do not enable if using rackup or other means.
 
-[reload_templates]    whether or not to reload templates between requests.
-                      Enabled in development mode and on Ruby 1.8.6 (to
-                      compensate a bug in Ruby causing a memory leak).
+[running]              is the built-in server running now?
+                       do not change this setting!
 
-[root]                project root folder.
+[server]               server or list of servers to use for built-in server.
+                       defaults to ['thin', 'mongrel', 'webrick'], order
+                       indicates priority.
 
-[raise_errors]        raise exceptions (will stop application).
+[sessions]             enable cookie based sessions support using
+                       <tt>Rack::Session::Cookie</tt>. See 'Using Sessions'
+                       section for more information.
 
-[run]                 if enabled, Sinatra will handle starting the web server,
-                      do not enable if using rackup or other means.
+[show_exceptions]      show a stack trace in the browser when an exception
+                       happens. Enabled by default when <tt>environment</tt>
+                       is set to <tt>"development"</tt>, disabled otherwise.
 
-[running]             is the built-in server running now?
-                      do not change this setting!
+[static]               Whether Sinatra should handle serving static files.
+                       Disable when using a Server able to do this on its own.
+                       Disabling will boost performance.
+                       Enabled per default in classic style, disabled for
+                       modular apps.
 
-[server]              server or list of servers to use for built-in server.
-                      defaults to ['thin', 'mongrel', 'webrick'], order indicates
-                      priority.
+[static_cache_control] When Sinatra is serving static files, set this to add
+                       <tt>Cache-Control</tt> headers to the responses. Uses the
+                       +cache_control+ helper. Disabled by default.
+                       Use an explicit array when setting multiple values:
+                       <tt>set :static_cache_control, [:public, :max_age => 300]</tt>
 
-[sessions]            enable cookie based sessions.
+[threaded]             If set to +true+, will tell Thin to use
+                       <tt>EventMachine.defer</tt> for processing the request.
 
-[show_exceptions]     show a stack trace in the browser.
+[views]                Path to the views folder. Inferred from <tt>app_file</tt>
+                       setting if not set.
 
-[static]              Whether Sinatra should handle serving static files.
-                      Disable when using a Server able to do this on its own.
-                      Disabling will boost performance.
-                      Enabled per default.
+== Environments
 
-[views]               views folder.
+There are three predefined +environments+: <tt>development</tt>,  <tt>production</tt> and <tt>test</tt>. Environment can be set by RACK_ENV environment variable, and default value is <tt>development</tt>.
+
+You can also run different environemnt using <tt>-e</tt> option:
+
+  ruby my_app.rb -e [ENVIRONMENT]
+
+You can use predefinied methods: +development?+, +test?+ and +production?+, to check which enviroment is set.
+
++Developemnt+ is default setting. In this mode, all templates are being reloaded between requests. Special <tt>not_found</tt> and <tt>error</tt> handlers are installed for this enviroment, so you will see nice error page. In +production+ and +test+ templates are being cached.
 
 == Error Handling
 
@@ -1392,11 +1493,17 @@ debugging, URL routing, authentication, and session handling. Sinatra uses
 many of these components automatically based on configuration so you
 typically don't have to +use+ them explicitly.
 
+You can find useful middleware in
+{rack}[https://github.com/rack/rack/tree/master/lib/rack],
+{rack-contrib}[https://github.com/rack/rack-contrib#readme],
+with {CodeRack}[http://coderack.org/] or in the
+{Rack wiki}[https://github.com/rack/rack/wiki/List-of-Middleware].
+
 == Testing
 
 Sinatra tests can be written using any Rack-based testing library
-or framework. {Rack::Test}[http://gitrdoc.com/brynary/rack-test] is
-recommended:
+or framework. {Rack::Test}[http://rdoc.info/github/brynary/rack-test/master/frames] 
+is recommended:
 
   require 'my_sinatra_app'
   require 'test/unit'
@@ -1425,9 +1532,6 @@ recommended:
     end
   end
 
-NOTE: The built-in Sinatra::Test module and Sinatra::TestHarness class
-are deprecated as of the 0.9.2 release.
-
 == Sinatra::Base - Middleware, Libraries, and Modular Apps
 
 Defining your app at the top-level works well for micro-apps but has
@@ -1435,8 +1539,8 @@ considerable drawbacks when building reusable components such as Rack
 middleware, Rails metal, simple libraries with a server component, or
 even Sinatra extensions. The top-level DSL pollutes the Object namespace
 and assumes a micro-app style configuration (e.g., a single application
-file, ./public and ./views directories, logging, exception detail page,
-etc.). That's where Sinatra::Base comes into play:
+file, <tt>./public</tt> and <tt>./views</tt> directories, logging, exception
+detail page, etc.). That's where <tt>Sinatra::Base</tt> comes into play:
 
   require 'sinatra/base'
 
@@ -1449,15 +1553,15 @@ etc.). That's where Sinatra::Base comes into play:
     end
   end
 
-The methods available to Sinatra::Base subclasses are exactly as those
+The methods available to <tt>Sinatra::Base</tt> subclasses are exactly as those
 available via the top-level DSL. Most top-level apps can be converted to
-Sinatra::Base components with two modifications:
+<tt>Sinatra::Base</tt> components with two modifications:
 
 * Your file should require <tt>sinatra/base</tt> instead of +sinatra+;
   otherwise, all of Sinatra's DSL methods are imported into the main
   namespace.
 * Put your app's routes, error handlers, filters, and options in a subclass
-  of Sinatra::Base.
+  of <tt>Sinatra::Base</tt>.
 
 <tt>Sinatra::Base</tt> is a blank slate. Most options are disabled by default,
 including the built-in server. See {Options and Configuration}[http://sinatra.github.com/configuration.html]
@@ -1483,11 +1587,12 @@ different default settings:
 
   Setting             Classic                 Modular
 
-  app_file            file loading sinatra    nil
+  app_file            file loading sinatra    file subclassing Sinatra::Base
   run                 $0 == app_file          false
   logging             true                    false
   method_override     true                    false
   inline_templates    true                    false
+  static              true                    false
 
 
 === Serving a Modular Application
@@ -1512,7 +1617,7 @@ Start with:
 Or with a <tt>config.ru</tt>, which allows using any Rack handler:
 
   # config.ru
-  require 'my_app'
+  require './my_app'
   run MyApp
 
 Run:
@@ -1532,7 +1637,7 @@ Write your app file:
 
 And a corresponding <tt>config.ru</tt>:
 
-  require 'app'
+  require './app'
   run Sinatra::Application
 
 === When to use a config.ru?
@@ -1563,7 +1668,7 @@ application (Rails/Ramaze/Camping/...):
     get('/login') { haml :login }
     
     post('/login') do
-      if params[:name] = 'admin' and params[:password] = 'admin'
+      if params[:name] == 'admin' && params[:password] == 'admin'
         session['user_name'] = params[:name]
       else
         redirect '/login'
@@ -1595,6 +1700,7 @@ assign them to a constant, you can do this with <tt>Sinatra.new</tt>:
 
 It takes the application to inherit from as optional argument:
 
+  # config.ru
   require 'sinatra/base'
 
   controller = Sinatra.new do
@@ -1630,12 +1736,12 @@ available.
 
 === Application/Class Scope
 
-Every Sinatra application corresponds to a subclass of Sinatra::Base. If you
-are using the top-level DSL (<tt>require 'sinatra'</tt>), then this class is
-Sinatra::Application, otherwise it is the subclass you created explicitly. At
-class level you have methods like +get+ or +before+, but you cannot access the
-+request+ object or the +session+, as there only is a single application class
-for all requests.
+Every Sinatra application corresponds to a subclass of <tt>Sinatra::Base</tt>.
+If you are using the top-level DSL (<tt>require 'sinatra'</tt>), then this 
+class is <tt>Sinatra::Application</tt>, otherwise it is the subclass you 
+created explicitly. At class level you have methods like +get+ or +before+, but
+you cannot access the +request+ object or the +session+, as there only is a 
+single application class for all requests.
 
 Options created via +set+ are methods at class level:
 
@@ -1725,40 +1831,42 @@ Options are:
   -s # specify rack server/handler (default is thin)
   -x # turn on the mutex lock (default is off)
 
-== Requirements
-
-It is recommended to install Sinatra on Ruby 1.8.7, 1.9.2, JRuby or Rubinius.
+== Requirement
 
 The following Ruby versions are officially supported:
 
-[ Ruby 1.8.6 ]
-  It is not recommended to use 1.8.6 for Sinatra. However, it will be
-  officially supported until Sinatra 1.3.0 is released. RDoc and CoffeeScript
-  templates are not supported by this Ruby version. 1.8.6 includes a major
-  memory leak in its Hash implementation, which is triggered by Sinatra
-  versions prior to 1.1.1. The current version explicitly prevents this leak
-  at the cost of performance. You will have to downgrade Rack to 1.1.x, as
-  Rack >= 1.2 no longer supports 1.8.6.
-
 [ Ruby 1.8.7 ]
   1.8.7 is fully supported, however, if nothing is keeping you from it, we
-  recommend upgrading to 1.9.2 or switching to JRuby or Rubinius.
+  recommend upgrading to 1.9.2 or switching to JRuby or Rubinius. Support for
+  1.8.7 will not be dropped before Sinatra 2.0 and Ruby 2.0 except maybe for
+  the unlikely event of 1.8.8 being released. Even then, we might continue
+  supporting it. <b>Ruby 1.8.6 is no longer supported.</b> If you want to run
+  with 1.8.6, downgrade to Sinatra 1.2, which will receive bug fixes until
+  Sinatra 1.4.0 is released.
 
 [ Ruby 1.9.2 ]
-  1.9.2 is supported and recommended. Note that Radius and Markaby are
-  currently not 1.9 compatible. Do not use 1.9.2p0, it is known to cause
-  segmentation faults when using Sinatra.
+  1.9.2 is fully supported and recommended. Note that Radius and Markaby
+  are currently not 1.9 compatible. Do not use 1.9.2p0, it is known to cause
+  segmentation faults when running Sinatra. Support will continue at least
+  until the release of Ruby 1.9.4/2.0 and support for the latest 1.9 release
+  will continue as long as it is still supported by the Ruby core team.
+
+[ Ruby 1.9.3 ]
+  1.9.3 is fully supported. We recommend waiting for higher patch levels to be
+  released (current one is p0) before using it in production. Please note that
+  switching to 1.9.3 from an earlier version will invalidate all sessions.
 
 [ Rubinius ]
-  Rubinius is officially supported (Rubinius >= 1.2.3), everything, including
-  all template languages, works.
+  Rubinius is officially supported (Rubinius >= 1.2.4), everything, including
+  all template languages, works. The upcoming 2.0 release is supported as
+  well.
 
 [ JRuby ]
-  JRuby is officially supported (JRuby >= 1.6.0). No issues with third party
+  JRuby is officially supported (JRuby >= 1.6.5). No issues with third party
   template libraries are known, however, if you choose to use JRuby, please
   look into JRuby rack handlers, as the Thin web server is not fully supported
   on JRuby. JRuby's support for C extensions is still experimental, which only
-  affects RDiscount at the moment.
+  affects RDiscount, Redcarpet and RedCloth at the moment.
 
 We also keep an eye on upcoming Ruby versions.
 
@@ -1766,20 +1874,25 @@ The following Ruby implementations are not officially supported but still are
 known to run Sinatra:
 
 * Older versions of JRuby and Rubinius
+* Ruby Enterprise Edition
 * MacRuby, Maglev, IronRuby
-* Ruby 1.9.0 and 1.9.1
+* Ruby 1.9.0 and 1.9.1 (but we do recommend against using those)
 
 Not being officially supported means if things only break there and not on a
 supported platform, we assume it's not our issue but theirs.
 
-We also run our CI against ruby-head (the upcoming 1.9.3), but we can't
-guarantee anything, since it is constantly moving. Expect 1.9.3p0 to be
-supported.
+We also run our CI against ruby-head (the upcoming 2.0.0) and the 1.9.4
+branch, but we can't guarantee anything, since it is constantly moving. Expect
+both 1.9.4p0 and 2.0.0p0 to be supported.
 
 Sinatra should work on any operating system supported by the chosen Ruby
 implementation.
 
+You will not be able to run Sinatra on Cardinal, SmallRuby, BlueRuby or any
+Ruby version prior to 1.8.7 as of the time being.
+
 == The Bleeding Edge
+
 If you would like to use Sinatra's latest bleeding code, feel free to run your
 application against the master branch, it should be rather stable.
 
@@ -1790,6 +1903,7 @@ We also push out prerelease gems from time to time, so you can do a
 To get some of the latest features.
 
 === With Bundler
+
 If you want to run your application with the latest Sinatra, using
 {Bundler}[http://gembundler.com/] is the recommended way.
 
@@ -1815,6 +1929,7 @@ Now you can run your app like this:
   bundle exec ruby myapp.rb
 
 === Roll Your Own
+
 Create a local clone and run your app with the <tt>sinatra/lib</tt> directory
 on the <tt>$LOAD_PATH</tt>:
 
@@ -1856,8 +1971,9 @@ SemVerTag.
 * {Mailing List}[http://groups.google.com/group/sinatrarb/topics]
 * {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] on http://freenode.net
 * {Sinatra Book}[http://sinatra-book.gittr.com] Cookbook Tutorial
-* {Sinatra Book Contrib}[http://sinatra-book-contrib.com/] Community contributed recipes
+* {Sinatra Recipes}[http://recipes.sinatrarb.com/] Community 
+  contributed recipes
 * API documentation for the {latest release}[http://rubydoc.info/gems/sinatra]
   or the {current HEAD}[http://rubydoc.info/github/sinatra/sinatra] on
-  http://rubydoc.info/
-
+  http://rubydoc.info
+* {CI server}[http://ci.rkh.im/view/Sinatra/]
diff --git a/README.ru.rdoc b/README.ru.rdoc
index 9e02167..fdbf7a4 100644
--- a/README.ru.rdoc
+++ b/README.ru.rdoc
@@ -1,8 +1,7 @@
 = Sinatra
-<i>Внимание: Этот документ является переводом Английской версии и может быть устаревшим</i>
+<i>Внимание: Этот документ является переводом английской версии и может быть устаревшим</i>
 
-Sinatra — это предметно-ориентированный язык (DSL) для быстрого создания приложений на Ruby с
-приложением минимума усилий:
+Sinatra — это предметно-ориентированный каркас (DSL) для быстрого создания функциональных веб-приложений на Ruby:
 
   # myapp.rb
   require 'sinatra'
@@ -11,17 +10,23 @@ Sinatra — это предметно-ориентированный язык (D
     'Hello world!'
   end
 
-Установите gem и запустите приложение с помощью:
+Установите gem:
 
   gem install sinatra
+
+и запустите приложение с помощью:
+
   ruby -rubygems myapp.rb
 
-Результат будет тут: http://localhost:4567
+Результат можно увидеть: http://localhost:4567
+
+Рекомендуется также установить thin, сделать это можно командой: <tt>gem install thin</tt>.
+Thin - это более производительный и функциональный сервер для разработки приложений на Sinatra.
 
 == Маршруты
 
-В Sinatra маршрут — это пара: HTTP метод и шаблон (образец) URL.
-Каждый маршрут ассоциирован с блоком:
+В Sinatra маршрут — это пара: <HTTP метод> и <шаблон URL>.
+Каждый маршрут ассоциирован с блоком кода, исполняемого внутри, пример:
 
   get '/' do
     .. что-то показать ..
@@ -32,7 +37,11 @@ Sinatra — это предметно-ориентированный язык (D
   end
 
   put '/' do
-    .. что-то обновить ..
+    .. что-то заменить ..
+  end
+
+  patch '/' do
+    .. что-то изменить ..
   end
 
   delete '/' do
@@ -43,7 +52,8 @@ Sinatra — это предметно-ориентированный язык (D
     .. что-то ответить ..
   end
 
-Маршруты сверяются с запросом по очередности определения. Первый же совпавший с запросом маршрут и будет вызван.
+Маршруты сверяются с запросом в порядке очередности их записи в файле приложения. По умолчанию
+будет вызван первый совпавший с запросом маршрут.
 
 Шаблоны маршрутов могут включать в себя параметры доступные в
 <tt>params</tt> xэше:
@@ -60,8 +70,8 @@ Sinatra — это предметно-ориентированный язык (D
     "Hello #{n}!"
   end
 
-Шаблоны маршрутов также могут включать splat (wildcard, *, любая строка символов) параметры доступные
-в <tt>params[:splat]</tt> массиве.
+Шаблоны маршрутов также могут включать splat (или '*' маску, обозначающую любой символ) параметры доступные
+в массиве <tt>params[:splat]</tt>:
 
   get '/say/*/to/*' do
     # соответствует /say/hello/to/world
@@ -73,7 +83,13 @@ Sinatra — это предметно-ориентированный язык (D
     params[:splat] # => ["path/to/file", "xml"]
   end
 
-Маршруты также могут использовать регулярные выражения в качестве шаблона URL:
+Или с параметрами блока:
+
+  get '/download/*.*' do |path, ext|
+    [path, ext] # => ["path/to/file", "xml"]
+  end
+
+Маршруты также могут содержать регулярные выражения:
 
   get %r{/hello/([\w]+)} do
     "Hello, #{params[:captures].first}!"
@@ -111,7 +127,7 @@ Sinatra — это предметно-ориентированный язык (D
     builder :feed
   end
 
-Довольно легко можно задать собственные условия:
+Вы можете задать собственные условия:
 
   set(:probability) { |value| condition { rand <= value } }
   
@@ -126,18 +142,18 @@ Sinatra — это предметно-ориентированный язык (D
 === Возвращаемые значения
 
 Возвращаемое значение блока маршрута ограничивается телом ответа, которое будет передано HTTP клиенту,
-или следующей "прослойкой" (middleware, промежуточная программа) в Rack стеке. Чаще всего это строка, как в вышеизложенных примерах.
+или следующей "прослойкой" (middleware) в Rack стеке. Чаще всего это строка, как в примерах выше.
 Но и другие значения также приемлемы.
 
-Вы можете вернуть любой объект, который будет либо корректным Rack ответом, Rack
-телом ответа, либо кодом состояния HTTP:
+Вы можете вернуть любой объект, который будет либо корректным Rack ответом, объектом Rack body,
+либо кодом состояния HTTP:
 
-* Массив с тремя переменными: <tt>[status (Fixnum), headers (Hash), response body (должен отвечать на #each)]</tt>
-* Массив с двумя переменными: <tt>[status (Fixnum), response body (должен отвечать на #each)]</tt>
-* Объект, отвечающий на <tt>#each</tt>, который передает только строковые типы данных в этот блок
-* Fixnum, соответствующий коду состояния HTTP
+* массив с тремя переменными: <tt>[status (Fixnum), headers (Hash), response body (должен отвечать на #each)]</tt>;
+* массив с двумя переменными: <tt>[status (Fixnum), response body (должен отвечать на #each)]</tt>;
+* объект, отвечающий на <tt>#each</tt>, который передает только строковые типы данных в этот блок;
+* Fixnum, представляющий код состояния HTTP.
 
-Таким образом мы легко можем создать поточный пример:
+Таким образом, мы легко можем создать поточный пример:
 
     class Stream
       def each
@@ -147,211 +163,220 @@ Sinatra — это предметно-ориентированный язык (D
 
     get('/') { Stream.new }
 
-== Статические файлы
-
-Статические файлы отдаются из <tt>./public</tt> директории. Вы можете указать другое место,
-используя <tt>:public</tt> опцию:
+=== Собственные детекторы совпадений для маршрутов
 
-  set :public, File.dirname(__FILE__) + '/static'
+Как показано выше, Sinatra поставляется со встроенной поддержкой строк и
+регулярных выражений в качестве шаблонов URL. Но и это еще не все. Вы можете
+легко определить свои собственные детекторы совпадений (matchers) для маршрутов:
 
-Учтите, что имя директории со статическими файлами не включено в URL. Например, файл
-<tt>./public/css/style.css</tt> будет доступен как
-<tt>http://example.com/css/style.css</tt>.
+  class AllButPattern
+    Match = Struct.new(:captures)
 
-== Виды / Шаблоны
+    def initialize(except)
+      @except   = except
+      @captures = Match.new([])
+    end
 
-Шаблоны по умолчанию будут использованы из директории <tt>./views</tt>.
-Для использования другой директории:
+    def match(str)
+      @captures unless @except === str
+    end
+  end
 
-  set :views, File.dirname(__FILE__) + '/templates'
+  def all_but(pattern)
+    AllButPattern.new(pattern)
+  end
 
-Важно помнить, что вы всегда должны указывать шаблоны
-с помощью символов, даже если это подкаталог (в этом случае
-используйте <tt>:'subdir/template'</tt>). Вы должны использовать
-символ, иначе методы, ответственные за рендеринг, отобразят просто переданную им строку.
+  get all_but("/index") do
+    # ...
+  end
 
-=== Haml шаблоны
+Заметьте, что предыдущий пример возможно чересчур сложен, он также может быть представлен как:
 
-<tt>haml</tt> gem/библиотека необходима для рендеринга HAML шаблонов:
+  get // do
+    pass if request.path_info == "/index"
+    # ...
+  end
 
-  # Вам нужно будет подключить haml gem в приложении
-  require 'haml'
+Или с использованием негативного просмотра вперед:
 
-  get '/' do
-    haml :index
+  get %r{^(?!/index$)} do
+    # ...
   end
 
-Отрисует <tt>./views/index.haml</tt>.
+== Статические файлы
 
-{Опции Haml}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options]
-могут быть установлены глобально через конфигурацию Sinatra,
-см. {Опции и Конфигурация}[http://www.sinatrarb.com/configuration.html],
-и переопределены локально.
+Статические файлы отдаются из <tt>./public</tt> директории. Вы можете указать другое место,
+указав его через опцию <tt>:public_folder</tt>:
 
-  set :haml, :format => :html5 # :xhtml - Haml формат по умолчанию
+  set :public_folder, File.dirname(__FILE__) + '/static'
 
-  get '/' do
-    haml :index, :format => :html4 # переопределен
-  end
+Учтите, что имя директории со статическими файлами не включено в URL. Например, файл
+<tt>./public/css/style.css</tt> будет доступен как
+<tt>http://example.com/css/style.css</tt>.
 
+Используйте опцию <tt>:static_cache_control</tt> (см. ниже), чтобы
+добавить заголовок <tt>Cache-Control</tt>.
 
-=== Erb шаблоны
+== Представления / Шаблоны
 
-  # Вам нужно будет подключить erb в приложении
-  require 'erb'
+Каждый шаблонизатор представлен своим собственным методом. Эти методы
+попросту возвращают строку:
 
   get '/' do
     erb :index
   end
 
-Отрисует <tt>./views/index.erb</tt>.
+Отобразит <tt>views/index.erb</tt>.
 
-=== Erubis шаблоны
+Вместо имени шаблона вы так же можете передавать непосредственно само
+содержимое шаблона
 
-<tt>erubis</tt> gem/библиотека необходима для рендеринга Erubis шаблонов:
+  get '/' do
+    code = "<%= Time.now %>"
+    erb code
+  end
 
-  # Вам нужно будет подключить Erubis в приложении
-  require 'erubis'
+Эти методы принимают второй аргумент, хеш с опциями:
 
   get '/' do
-    erubis :index
+    erb :index, :layout => :post
   end
 
-Отрисует <tt>./views/index.erubis</tt>.
+Отобразит <tt>views/index.erb</tt>, вложенным в
+<tt>views/post.erb</tt> (по умолчанию: <tt>views/layout.erb</tt>, если существует).
 
-Также возможно заменить Erb на Erubis:
-
-  require 'erubis'
-  Tilt.register :erb, Tilt[:erubis]
+Любые опции, не понимаемые Sinatra, будут переданы в шаблонизатор
 
   get '/' do
-    erb :index
+    haml :index, :format => :html5
   end
 
-Отрисует <tt>./views/index.erb</tt> с помощью Erubis.
-
-=== Builder шаблоны
-
-<tt>builder</tt> gem/библиотека необходима для рендеринга builder шаблонов:
+Вы также можете задавать опции для шаблонизаторов в общем:
 
-  # Вам нужно будет подключить builder в приложении
-  require 'builder'
+  set :haml, :format => :html5
 
   get '/' do
-    builder :index
+    haml :index
   end
 
-Отрисует <tt>./views/index.builder</tt>.
+Опции, переданные в метод, переопределяют опции, заданные с помощью
++set+.
 
-=== Nokogiri шаблоны
+Доступные опции:
 
-<tt>nokogiri</tt> gem/библиотека необходима для рендеринга nokogiri шаблонов:
+[locals]
+  Список локальных переменных, передаваемых в документ.
+  Например: <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>
 
-  # Вам нужно будет подключить nokogiri в приложении
-  require 'nokogiri'
+[default_encoding]
+  Кодировка, которую следует использовать, если не удалось определить
+  оригинальную. По умолчанию: <tt>settings.default_encoding</tt>.
 
-  get '/' do
-    nokogiri :index
-  end
+[views]
+  Директория с шаблонами. По умолчанию: <tt>settings.views</tt>.
 
-Отрисует <tt>./views/index.nokogiri</tt>.
+[layout]
+  Использовать или нет лэйаут (+true+ или +false+). Если же значение Symbol,
+  то указывает, какой шаблон использовать в качестве лэйаута. Например:
+  <tt>erb :index, :layout => !request.xhr?</tt>
 
-=== Sass шаблоны
+[content_type]
+  Content-Type отображенного шаблона. По умолчанию: задается шаблонизатором.
 
-<tt>haml</tt> gem/библиотека необходима для рендеринга Sass шаблонов:
+[scope]
+  Область видимости, в которой рендерятся шаблоны. По умолчанию: экземпляр
+  приложения. Если вы измените эту опцию, то переменные экземпляра и
+  методы-помощники станут недоступными в ваших шаблонах.
 
-  # Вам нужно будет подключить haml или sass в приложении
-  require 'sass'
+[layout_engine]
+  Шаблонизатор, который следует использовать для отображения лэйаута. Полезная
+  опция для шаблонизаторов, в которых нет никакой поддержки лэйаутов. По
+  умолчанию: тот же шаблонизатор, что используется и для самого шаблона.
+  Пример: <tt>set :rdoc, :layout_engine => :erb</tt>
 
-  get '/stylesheet.css' do
-    sass :stylesheet
-  end
+По умолчанию считается, что шаблоны находятся в директории <tt>./views</tt>.
+Чтобы использовать другую директорию с шаблонами:
 
-Отрисует <tt>./views/stylesheet.sass</tt>.
+  set :views, settings.root + '/templates'
 
-{Опции Sass}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
-могут быть установлены глобально через конфигурацию Sinatra,
-см. {Опции и Конфигурация}[http://www.sinatrarb.com/configuration.html],
-и переопределены локально.
+Важное замечание: вы всегда должны ссылаться на шаблоны с помощью символов
+(Symbol), даже когда они в поддиректории (в этом случае используйте
+<tt>:'subdir/template'</tt>). Вы должны использовать символы, потому что
+иначе шаблонизаторы попросту отображают любые строки, переданные им.
 
-  set :sass, :style => :compact # :nested - стиль Sass по умолчанию
+=== Доступные шаблонизаторы
 
-  get '/stylesheet.css' do
-    sass :stylesheet, :style => :expanded # переопределен
-  end
+Некоторые языки шаблонов имеют несколько реализаций. Чтобы указать, какую
+реализацию использовать, вам следует просто подключить нужную
+библиотеку:
 
-=== Scss шаблоны
+  require 'rdiscount' # или require 'bluecloth'
+  get('/') { markdown :index }
 
-<tt>haml</tt> gem/библиотека необходима для рендеринга Scss шаблонов:
+=== Haml шаблоны
 
-  # Вам нужно будет подключить haml или sass в приложении
-  require 'sass'
+Зависимости::         {haml}[http://haml-lang.com/]
+Расширения файлов::   <tt>.haml</tt>
+Пример::              <tt>haml :index, :format => :html5</tt>
 
-  get '/stylesheet.css' do
-    scss :stylesheet
-  end
+=== Erb шаблоны
 
-Отрисует <tt>./views/stylesheet.scss</tt>.
+Зависимости::         {erubis}[http://www.kuwata-lab.com/erubis/] или erb (включен в Ruby)
+Расширения файлов::   <tt>.erb</tt>, <tt>.rhtml</tt> or <tt>.erubis</tt> (только Erubis)
+Пример::              <tt>erb :index</tt>
 
-{Опции Scss}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
-могут быть установлены глобально через конфигурацию Sinatra,
-см. {Опции и Конфигурация}[http://www.sinatrarb.com/configuration.html],
-и переопределены локально.
+=== Builder шаблоны
 
-  set :scss, :style => :compact # :nested - стиль Scss по умолчанию
+Зависимости::         {builder}[http://builder.rubyforge.org/]
+Расширения файлов::   <tt>.builder</tt>
+Пример::              <tt>builder { |xml| xml.em "hi" }</tt>
 
-  get '/stylesheet.css' do
-    scss :stylesheet, :style => :expanded # переопределен
-  end
+Блок также используется и для встроенных шаблонов (см. пример).
 
-=== Less шаблоны
+=== Nokogiri шаблоны
 
-<tt>less</tt> gem/библиотека необходима для рендеринга Less шаблонов:
+Зависимости::         {nokogiri}[http://nokogiri.org/]
+Расширения файлов::   <tt>.nokogiri</tt>
+Пример::              <tt>nokogiri { |xml| xml.em "hi" }</tt>
 
-  # Вам нужно будет подключить less в приложении
-  require 'less'
+Блок также используется и для встроенных шаблонов (см. пример).
 
-  get '/stylesheet.css' do
-    less :stylesheet
-  end
+=== Sass шаблоны
 
-Отрисует <tt>./views/stylesheet.less</tt>.
+Зависимости::         {sass}[http://sass-lang.com/]
+Расширения файлов::   <tt>.sass</tt>
+Пример::              <tt>sass :stylesheet, :style => :expanded</tt>
 
-=== Liquid шаблоны
+=== SCSS шаблоны
 
-<tt>liquid</tt> gem/библиотека необходима для рендеринга liquid шаблонов:
+Зависимости::         {sass}[http://sass-lang.com/]
+Расширения файлов::   <tt>.scss</tt>
+Пример::              <tt>scss :stylesheet, :style => :expanded</tt>
 
-  # Вам нужно будет подключить liquid в приложении
-  require 'liquid'
+=== Less шаблоны
 
-  get '/' do
-    liquid :index
-  end
+Зависимости::         {less}[http://www.lesscss.org/]
+Расширения файлов::   <tt>.less</tt>
+Пример::              <tt>less :stylesheet</tt>
 
-Отрисует <tt>./views/index.liquid</tt>.
+=== Liquid шаблоны
 
-Так как в Liquid шаблонах невозможно вызывать методы из Ruby (кроме +yield+),
-то вы почти всегда будете передавать локальные переменные:
+Зависимости::         {liquid}[http://www.liquidmarkup.org/]
+Расширения файлов::   <tt>.liquid</tt>
+Пример::              <tt>liquid :index, :locals => { :key => 'value' }</tt>
 
-  liquid :index, :locals => { :key => 'value' }
+Так как в Liquid шаблонах невозможно вызывать методы из Ruby (кроме yield), то
+вы почти всегда будете передавать в шаблон локальные переменные.
 
 === Markdown шаблоны
 
-<tt>rdiscount</tt> gem/библиотека необходима для рендеринга Markdown шаблонов:
-
-  # Вам нужно будет подключить rdiscount в приложении
-  require "rdiscount"
-  
-  get '/' do
-    markdown :index
-  end
-
-Отрисует <tt>./views/index.markdown</tt> (+md+ и +mkd+ также являются допустимыми файловыми
-расширениями).
+Зависимости::          {rdiscount}[https://github.com/rtomayko/rdiscount], {redcarpet}[https://github.com/tanoku/redcarpet], {bluecloth}[http://deveiate.org/projects/BlueCloth], {kramdown}[http://kramdown.rubyforge.org/] или {maruku}[http://maruku.rubyforge.org/]
+Расширения файлов::   <tt>.markdown</tt>, <tt>.mkd</tt> and <tt>.md</tt>
+Пример::              <tt>markdown :index, :layout_engine => :erb</tt>
 
 В Markdown невозможно вызывать методы или передавать локальные переменные.
-Следовательно, вам, скорее всего, придется использовать этот шаблон совместно с другим
-движком рендеринга:
+Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
+с другим шаблонизатором:
 
   erb :overview, :locals => { :text => markdown(:introduction) }
 
@@ -360,57 +385,20 @@ Sinatra — это предметно-ориентированный язык (D
   %h1 Hello From Haml!
   %p= markdown(:greetings)
 
-Вы не можете вызывать Ruby из Markdown, соответственно, вы не можете использовать
-лэйаут-шаблоны (layouts) на Markdown. Тем не менее, есть возможность использовать один
-движок рендеринга для шаблона, а другой для лэйаута с помощью опции `:layout_engine`:
-
-  get '/' do
-    markdown :index, :layout_engine => :erb
-  end
-
-Отрисует <tt>./views/index.md</tt> с <tt>./views/layout.erb</tt> в качестве
-лэйаута.
-
-Также вы можете задать такие опции рендеринга глобально:
-
-  set :markdown, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    markdown :index
-  end
-
-Отрисует <tt>./views/index.md</tt> (и любой другой шаблон на Markdown) с
-<tt>./views/post.haml</tt> в качестве лэйаута.
-
-Также возможно обрабатывать Markdown с помощью BlueCloth, а не RDiscount:
-
-  require 'bluecloth'
-
-  Tilt.register 'markdown', BlueClothTemplate
-  Tilt.register 'mkd',      BlueClothTemplate
-  Tilt.register 'md',       BlueClothTemplate
-
-  get '/' do
-    markdown :index
-  end
-
-Отрисует <tt>./views/index.md</tt> с помощью BlueCloth.
+Вы не можете вызывать Ruby из Markdown, соответственно, вы не можете
+использовать лэйауты на Markdown. Тем не менее, есть возможность использовать
+один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
+опции <tt>:layout_engine</tt>.
 
 === Textile шаблоны
 
-<tt>RedCloth</tt> gem/библиотека необходима для рендеринга Textile шаблонов:
-
-  # Вам нужно будет подключить redcloth в приложении
-  require "redcloth"
-
-  get '/' do
-    textile :index
-  end
-
-Отрисует <tt>./views/index.textile</tt>.
+Зависимости::         {RedCloth}[http://redcloth.org/]
+Расширения файлов::   <tt>.textile</tt>
+Пример::              <tt>textile :index, :layout_engine => :erb</tt>
 
-В Textile невозможно вызывать методы или передавать локальные переменные. Следовательно, вам, скорее всего,
-придется использовать этот шаблон совместно с другим движком рендеринга:
+В Textile невозможно вызывать методы или передавать локальные переменные.
+Следовательно, вам, скорее всего, придется использовать этот шаблон совместно с
+другим шаблонизатором:
 
   erb :overview, :locals => { :text => textile(:introduction) }
 
@@ -419,43 +407,20 @@ Sinatra — это предметно-ориентированный язык (D
   %h1 Hello From Haml!
   %p= textile(:greetings)
 
-Вы не можете вызывать Ruby из Textile, соответственно, вы не можете использовать
-лэйаут-шаблоны на Textile. Тем не менее, есть возможность использовать один
-движок рендеринга для шаблона, а другой для лэйаута с помощью опции `:layout_engine`:
-
-  get '/' do
-    textile :index, :layout_engine => :erb
-  end
-
-Отрисует <tt>./views/index.textile</tt> с <tt>./views/layout.erb</tt> в качестве
-лэйаута.
-
-Также вы можете задать такие опции рендеринга глобально:
-
-  set :textile, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    textile :index
-  end
-
-Отрисует <tt>./views/index.textile</tt> (и любой другой шаблон на Textile) с
-<tt>./views/post.haml</tt> в качестве лэйаута.
+Вы не можете вызывать Ruby из Textile, соответственно, вы не можете
+использовать лэйауты на Textile. Тем не менее, есть возможность использовать
+один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
+опции <tt>:layout_engine</tt>.
 
 === RDoc шаблоны
 
-<tt>rdoc</tt> gem/библиотека необходима для рендеринга RDoc шаблонов:
+Зависимости::         {rdoc}[http://rdoc.rubyforge.org/]
+Расширения файлов::   <tt>.rdoc</tt>
+Пример::              <tt>textile :README, :layout_engine => :erb</tt>
 
-  # Вам нужно будет подключить rdoc/markup/to_html в приложении
-  require "rdoc/markup/to_html"
-
-  get '/' do
-    rdoc :index
-  end
-
-Отрисует <tt>./views/index.rdoc</tt>.
-
-В RDoc невозможно вызывать методы или передавать локальные переменные. Следовательно, вам, скорее всего,
-придется использовать этот шаблон совместно с другим движком рендеринга:
+В RDoc невозможно вызывать методы или передавать локальные переменные.
+Следовательно, вам, скорее всего, придется использовать этот шаблон совместно с
+другим шаблонизатором:
 
   erb :overview, :locals => { :text => rdoc(:introduction) }
 
@@ -464,99 +429,61 @@ Sinatra — это предметно-ориентированный язык (D
   %h1 Hello From Haml!
   %p= rdoc(:greetings)
 
-Вы не можете вызывать Ruby из RDoc, соответственно, вы не можете использовать
-лэйаут-шаблоны на RDoc. Тем не менее, есть возможность использовать один
-движок рендеринга для шаблона, а другой для лэйаута с помощью опции `:layout_engine`:
-
-  get '/' do
-    rdoc :index, :layout_engine => :erb
-  end
-
-Отрисует <tt>./views/index.rdoc</tt> с <tt>./views/layout.erb</tt> в качестве
-лэйаута.
-
-Также вы можете задать такие опции рендеринга глобально:
-
-  set :rdoc, :layout_engine => :haml, :layout => :post
-
-  get '/' do
-    rdoc :index
-  end
-
-Отрисует <tt>./views/index.rdoc</tt> (и любой другой шаблон на RDoc) с
-<tt>./views/post.haml</tt> в качестве лэйаута.
+Вы не можете вызывать Ruby из RDoc, соответственно, вы не можете
+использовать лэйауты на RDoc. Тем не менее, есть возможность использовать
+один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
+опции <tt>:layout_engine</tt>.
 
 === Radius шаблоны
 
-<tt>radius</tt> gem/библиотека необходима для рендеринга Radius шаблонов:
-
-  # Вам нужно будет подключить radius в приложении
-  require 'radius'
+Зависимости::         {radius}[http://radius.rubyforge.org/]
+Расширения файлов::   <tt>.radius</tt>
+Пример::              <tt>radius :index, :locals => { :key => 'value' }</tt>
 
-  get '/' do
-    radius :index
-  end
-
-Отрисует <tt>./views/index.radius</tt>.
-
-Так как в Radius шаблоне невозможно вызывать методы из Ruby (кроме +yield+), 
-то вы почти всегда будете передавать локальные переменные:
-
-  radius :index, :locals => { :key => 'value' }
+Так как в Radius шаблонах невозможно вызывать методы из Ruby напрямую, то
+вы почти всегда будете передавать в шаблон локальные переменные.
 
 === Markaby шаблоны
 
-<tt>markaby</tt> gem/библиотека необходима для рендеринга Markaby шаблонов:
+Зависимости::         {markaby}[http://markaby.github.com/]
+Расширения файлов::   <tt>.mab</tt>
+Пример::              <tt>markaby { h1 "Welcome!" }</tt>
 
-  # Вам нужно будет подключить markaby в приложении
-  require 'markaby'
-
-  get '/' do
-    markaby :index
-  end
-
-Отрисует <tt>./views/index.mab</tt>.
-
-Вы также можете использовать внутристроковые Markaby шаблоны:
-
-  get '/' do
-    markaby { h1 "Welcome!" }
-  end
+Блок также используется и для встроенных шаблонов (см. пример).
 
 === Slim шаблоны
 
-<tt>slim</tt> gem/библиотека необходима для рендеринга slim шаблонов:
+Зависимости::         {slim}[http://slim-lang.com/]
+Расширения файлов::   <tt>.slim</tt>
+Пример::              <tt>slim :index</tt>
 
-  # Вам нужно будет подключить slim в приложении
-  require 'slim'
+=== Creole шаблоны
 
-  get '/' do
-    slim :index
-  end
-
-Отрисует <tt>./views/index.slim</tt>.
-
-=== CoffeeScript шаблоны
+Зависимости::         {creole}[https://github.com/minad/creole]
+Расширения файлов::   <tt>.creole</tt>
+Пример::              <tt>creole :wiki, :layout_engine => :erb</tt>
 
-Вам понадобится <tt>coffee-script</tt> gem/библиотека и что-то <b>одно</b> из следующего списка,
-чтобы запускать JavaScript:
+В Creole невозможно вызывать методы или передавать локальные переменные.
+Следовательно, вам, скорее всего, придется использовать этот шаблон совместно с
+другим шаблонизатором:
 
-* +node+ (из Node.js)
-* вы можете использовать OSX (есть встроенные средства для выполнения JavaScript)
-* +therubyracer+ gem/библиотека
+  erb :overview, :locals => { :text => creole(:introduction) }
 
-Подробнее смотрите на {странице проекта}[http://github.com/josh/ruby-coffee-script].
+Заметьте, что вы можете вызывать метод +creole+ из других шаблонов:
 
-Таким образом вы можете использовать CoffeeScript шаблоны.
+  %h1 Hello From Haml!
+  %p= creole(:greetings)
 
-  # Вам нужно будет подключить coffee-script в приложении
-  require 'coffee-script'
+Вы не можете вызывать Ruby из Creole, соответственно, вы не можете
+использовать лэйауты на Creole. Тем не менее, есть возможность использовать
+один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
+опции <tt>:layout_engine</tt>.
 
-  get '/application.js' do
-    coffee :application
-  end
+=== CoffeeScript шаблоны
 
-Отрисует <tt>./views/application.coffee</tt>.
+Зависимости::         {coffee-script}[https://github.com/josh/ruby-coffee-script] и {способ запускать javascript}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
+Расширения файлов::   <tt>.coffee</tt>
+Пример::              <tt>coffee :index</tt>
 
 === Встроенные шаблоны
 
@@ -564,7 +491,7 @@ Sinatra — это предметно-ориентированный язык (D
     haml '%div.title Hello World'
   end
 
-Отрисует встроенный (строчный) шаблон.
+Отобразит встроенный шаблон, переданный строкой.
 
 === Доступ к переменным в шаблонах
 
@@ -580,14 +507,14 @@ Sinatra — это предметно-ориентированный язык (D
 
   get '/:id' do
     foo = Foo.find(params[:id])
-    haml '%h1= foo.name', :locals => { :foo => foo }
+    haml '%h1= bar.name', :locals => { :bar => foo }
   end
 
-Это обычный подход, когда шаблоны рендерятся как частные (partials) из других шаблонов.
+Это обычный подход, когда шаблоны рендерятся как части других шаблонов.
 
 === Вложенные шаблоны
 
-Шаблоны также могут быть определены в конце файла-исходника:
+Шаблоны также могут быть определены в конце исходного файла:
 
   require 'sinatra'
 
@@ -604,13 +531,13 @@ Sinatra — это предметно-ориентированный язык (D
   @@ index
   %div.title Hello world!!!!!
 
-Заметьте: Вложенные шаблоны, определенные в файле-исходнике, который подключил Sinatra, будут
+Заметьте: вложенные шаблоны, определенные в исходном файле, который подключила Sinatra, будут
 автоматически загружены. Вызовите <tt>enable :inline_templates</tt> напрямую, если у вас вложенные
 шаблоны в других файлах.
 
 === Именные шаблоны
 
-Шаблоны также могут быть определены, используя <tt>template</tt> метод:
+Шаблоны также могут быть определены при помощи <tt>template</tt> метода:
 
   template :layout do
     "%html\n  =yield\n"
@@ -624,9 +551,10 @@ Sinatra — это предметно-ориентированный язык (D
     haml :index
   end
 
-Если шаблон с именем "layout" существует, то он будет использован каждый раз,
-когда шаблоны будут отрисовываться. Вы можете отключать лэйаут в каждом конкретном случае с помощью
-<tt>:layout => false</tt> или отключить его для всего приложения, например, так: <tt>set :haml, :layout => false</tt>.
+Если шаблон с именем "layout" существует, то он будет использоваться каждый раз
+при рендеринге. Вы можете отключать лэйаут в каждом конкретном случае с помощью
+<tt>:layout => false</tt> или отключить его для всего приложения, например, так:
+<tt>set :haml, :layout => false</tt>:
 
   get '/' do
     haml :index, :layout => !request.xhr?
@@ -634,7 +562,7 @@ Sinatra — это предметно-ориентированный язык (D
 
 === Привязка файловых расширений
 
-Чтобы связать расширение файла и движок рендеринга, используйте
+Чтобы связать расширение файла с движком рендеринга, используйте
 <tt>Tilt.register</tt>. Например, если вы хотите использовать расширение +tt+
 для шаблонов Textile:
 
@@ -654,27 +582,12 @@ Sinatra — это предметно-ориентированный язык (D
     myat :index
   end
 
-Отрисует <tt>./views/index.myat</tt>. Чтобы узнать больше о Tilt,
+Отобразит <tt>./views/index.myat</tt>. Чтобы узнать больше о Tilt,
 смотрите https://github.com/rtomayko/tilt
 
-== Методы-помощники
-
-Используйте метод <tt>helpers</tt>, чтобы определить методы-помощники, которые
-в дальнейшем можно будет использовать в обработчиках маршрутов и шаблонах:
-
-  helpers do
-    def bar(name)
-      "#{name}bar"
-    end
-  end
-
-  get '/:name' do
-    bar(params[:name])
-  end
-
 == Фильтры
 
-Before-фильтры выполняются перед каждым запросом в том же контексте, что и маршруты. Фильтры могут изменять
++before+-фильтры выполняются перед каждым запросом в том же контексте, что и маршруты. Фильтры могут изменять
 как запрос, так и ответ на него. Переменные экземпляра, установленные в фильтрах, доступны в маршрутах и шаблонах:
 
   before do
@@ -687,14 +600,18 @@ Before-фильтры выполняются перед каждым запро
     params[:splat] #=> 'bar/baz'
   end
 
-After-фильтры выполняются после каждого запроса в том же контексте, что и пути. Фильтры могут изменять
-как запрос, так и ответ на него. Переменные экземпляра, установленные в before-фильтрах и маршрутах, будут доступны в after-фильтрах:
++after+-фильтры выполняются после каждого запроса в том же контексте, что и пути. Фильтры могут изменять
+как запрос, так и ответ на него. Переменные экземпляра, установленные в +before+-фильтрах и маршрутах,
+будут доступны в +after+-фильтрах:
 
   after do
     puts response.status
   end
 
-Фильтры могут использовать шаблоны URL и будут интерпретированы, только если путь запроса совпадет с этим шаблоном:
+Заметьте: если вы используете метод +body+, а не просто возвращаете строку из
+маршрута, то тело ответа не будет доступно в +after+-фильтрах, так как оно будет сгенерировано позднее.
+
+Фильтры могут использовать шаблоны URL и будут интерпретированы только если путь запроса совпадет с этим шаблоном:
 
   before '/protected/*' do
     authenticate!
@@ -714,7 +631,66 @@ After-фильтры выполняются после каждого запро
     # ...
   end
 
-== Прерывание
+== Методы-помощники
+
+Используйте метод <tt>helpers</tt>, чтобы определить методы-помощники, которые
+в дальнейшем можно будет использовать в обработчиках маршрутов и шаблонах:
+
+  helpers do
+    def bar(name)
+      "#{name}bar"
+    end
+  end
+
+  get '/:name' do
+    bar(params[:name])
+  end
+
+=== Использование сессий
+
+Сессия используется, чтобы сохранять состояние между запросами. Если эта опция
+включена, то у вас будет один хеш сессии на одну пользовательскую сессию:
+
+  enable :sessions
+
+  get '/' do
+    "value = " << session[:value].inspect
+  end
+
+  get '/:value' do
+    session[:value] = params[:value]
+  end
+
+Заметьте, что при использовании <tt>enable :sessions</tt> все данные
+сохраняются в cookies. Это может быть не совсем то, что вы хотите (например,
+сохранение больших объемов данных увеличит ваш трафик). В таком случае вы
+можете использовать альтернативную Rack "прослойку" (middleware), реализующую
+механизм сессий. Для этого *не надо* вызывать <tt>enable :sessions</tt>,
+вместо этого следует подключить ее так же, как и любую другую "прослойку":
+
+  use Rack::Session::Pool, :expire_after => 2592000
+
+  get '/' do
+    "value = " << session[:value].inspect
+  end
+
+  get '/:value' do
+    session[:value] = params[:value]
+  end
+
+Для повышения безопасности данные сессии в куках подписываются секретным
+ключом. Секретный ключ генерируется Sinatra. Тем не менее, так как этот
+ключ будет меняться с каждым запуском приложения, вы, возможно, захотите
+установить ключ вручную, чтобы у всех экземпляров вашего приложения
+был один и тот же ключ:
+
+  set :session_secret, 'super secret'
+
+Если вы хотите больше настроек для сессий, вы можете задать их, передав хеш опций в параметр +sessions+:
+
+  set :sessions, :domain => 'foo.com'
+
+=== Прерывание
 
 Чтобы незамедлительно прервать обработку запроса внутри фильтра или маршрута, используйте:
 
@@ -736,7 +712,11 @@ After-фильтры выполняются после каждого запро
 
   halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
 
-== Передача
+И, конечно, можно использовать шаблоны с +halt+:
+
+  halt erb(:error)
+
+=== Передача
 
 Маршрут может передать обработку запроса следующему совпадающему маршруту, используя <tt>pass</tt>:
 
@@ -752,38 +732,285 @@ After-фильтры выполняются после каждого запро
 Блок маршрута сразу же прерывается, и контроль переходит к следующему совпадающему маршруту.
 Если соответствующий маршрут не найден, то ответом на запрос будет 404.
 
-== Доспут к объекту запроса
+=== Вызов другого маршрута
+
+Иногда +pass+ не подходит, например, если вы хотите получить результат
+вызова другого обработчика маршрута. В таком случае просто используйте +call+:
+
+  get '/foo' do
+    status, headers, body = call env.merge("PATH_INFO" => '/bar')
+    [status, headers, body.map(&:upcase)]
+  end
+
+  get '/bar' do
+    "bar"
+  end
+
+Заметьте, что в предыдущем примере можно облегчить тестирование и повысить
+производительность, перенеся <tt>"bar"</tt> в метод-помощник, используемый
+и в <tt>/foo</tt>, и в <tt>/bar</tt>.
+
+Если вы хотите, чтобы запрос был отправлен в тот же экземпляр приложения, а не
+в его копию, используйте <tt>call!</tt> вместо <tt>call</tt>.
+
+Если хотите узнать больше о <tt>call</tt>, смотрите спецификацию Rack.
+
+=== Задание тела, кода и заголовков ответа
+
+Хорошим тоном является установка кода состояния HTTP и тела ответа в возвращаемом
+значении обработчика маршрута. Тем не менее, в некоторых ситуациях вам, возможно,
+понадобится задать тело ответа в произвольной точке потока исполнения. Вы можете
+сделать это с помощью метода-помощника +body+. Если вы задействуете метод +body+,
+то вы можете использовать его и в дальнейшем, чтобы получить доступ к телу ответа.
+
+  get '/foo' do
+    body "bar"
+  end
+
+  after do
+    puts body
+  end
+
+Также можно передать блок в метод +body+, который затем будет вызван
+обработчиком Rack (такой подход может быть использован для реализации поточного
+ответа, см. "Возвращаемые значения").
+
+Аналогично вы можете установить код ответа и его заголовки:
+
+  get '/foo' do
+    status 418
+    headers \
+      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN",
+      "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
+    body "I'm a tea pot!"
+  end
+
+Как и +body+, методы +headers+ и +status+, вызванные без аргументов, возвращают
+свои текущие значения.
+
+=== Логирование
+
+В области видимости запроса метод +logger+ предоставляет доступ к экземпляру +Logger+:
+
+  get '/' do
+    logger.info "loading data"
+    # ...
+  end
+
+Этот логер автоматически учитывает ваши настройки логирования в Rack. Если
+логирование выключено, то этот метод вернет пустой (dummy) объект, поэтому вы можете
+смело использовать его в маршрутах и фильтрах.
+
+Заметьте, что логирование включено по умолчанию только для <tt>Sinatra::Application</tt>,
+а если ваше приложение -- подкласс <tt>Sinatra::Base</tt>, то вы, наверное, захотите включить
+его вручную:
+
+  class MyApp < Sinatra::Base
+    configure(:production, :development) do
+      enable :logging
+    end
+  end
+
+=== Mime-типы
+
+Когда вы используете <tt>send_file</tt> или статические файлы, у вас могут быть mime-типы, которые Sinatra
+не понимает по умолчанию. Используйте +mime_type+ для их регистрации по расширению файла:
+
+  configure do
+    mime_type :foo, 'text/foo'
+  end
+
+Вы также можете использовать это в +content_type+ методе-помощнике:
+
+  get '/' do
+    content_type :foo
+    "foo foo foo"
+  end
+
+=== Генерирование URL
+
+Чтобы сформировать URL вам следует использовать метод +url+, например, в Haml:
+
+  %a{:href => url('/foo')} foo
+
+Этот метод учитывает обратные прокси и маршрутизаторы Rack, если они присутствуют.
+
+Наряду с +url+ вы можете использовать +to+ (смотрите пример ниже).
+
+=== Перенаправление (редирект)
+
+Вы можете перенаправить браузер пользователя с помощью метода +redirect+:
+
+  get '/foo' do
+    redirect to('/bar')
+  end
+
+Любые дополнительные параметры используются по аналогии с аргументами метода +halt+:
+
+  redirect to('/bar'), 303
+  redirect 'http://google.com', 'wrong place, buddy'
+
+Вы также можете перенаправить пользователя обратно, на страницу с которой он пришел,
+с помощью <tt>redirect back</tt>:
+
+  get '/foo' do
+    "<a href='/bar'>do something</a>"
+  end
+
+  get '/bar' do
+    do_something
+    redirect back
+  end
+
+Чтобы передать какие-либо параметры вместе с перенаправлением, либо добавьте их в строку запроса:
+
+  redirect to('/bar?sum=42')
+
+либо используйте сессию:
+
+  enable :sessions
+
+  get '/foo' do
+    session[:secret] = 'foo'
+    redirect to('/bar')
+  end
+
+  get '/bar' do
+    session[:secret]
+  end
+
+=== Управление кэшированием
+
+Установка корректных заголовков — основа правильного HTTP кэширования.
+
+Вы можете легко выставить заголовок Cache-Control таким образом:
+
+  get '/' do
+    cache_control :public
+    "cache it!"
+  end
+
+Совет: задавайте кэширование в +before+-фильтре:
+
+  before do
+    cache_control :public, :must_revalidate, :max_age => 60
+  end
+
+Если вы используете метод +expires+ для задания соответствующего заголовка,
+то <tt>Cache-Control</tt> будет выставлен автоматически:
+
+  before do
+    expires 500, :public, :must_revalidate
+  end
+
+Чтобы как следует использовать кэширование, вам следует подумать об использовании
++etag+ и +last_modified+. Рекомендуется использовать эти методы-помощники *до*
+выполнения ресурсоемких вычислений, так как они немедленно отправят ответ клиенту,
+если текущая версия уже есть в их кэше:
+
+  get '/article/:id' do
+    @article = Article.find params[:id]
+    last_modified @article.updated_at
+    etag @article.sha1
+    erb :article
+  end
+
+Также вы можете использовать
+{weak ETag}[http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation]:
+
+  etag @article.sha1, :weak
+
+Эти методы-помощники не станут ничего кэшировать для вас, но они дадут
+необходимую информацию для вашего кэша. Если вы ищите легкое решение для
+кэширования, попробуйте {rack-cache}[http://rtomayko.github.com/rack-cache/]:
+
+  require 'rack/cache'
+  require 'sinatra'
+
+  use Rack::Cache
+
+  get '/' do
+    cache_control :public, :max_age => 36000
+    sleep 5
+    "hello"
+  end
+
+Используйте опцию <tt>:static_cache_control</tt> (см. ниже), чтобы
+добавить заголовок <tt>Cache-Control</tt> к статическим файлам.
+
+=== Отправка файлов
+
+Для отправки файлов пользователю вы можете использовать метод <tt>send_file</tt>:
+
+  get '/' do
+    send_file 'foo.png'
+  end
+
+Этот метод имеет несколько опций:
+
+  send_file 'foo.png', :type => :jpg
+
+Возможные опции:
+
+[filename]
+  имя файла, по умолчанию: реальное имя файла.
 
-Объект входящего запроса доступен на уровне обработки запроса (в фильтрах, маршрутах, обработчиках ошибок)
-с помощью `request` метода:
+[last_modified]
+  значение для заголовка Last-Modified, по умолчанию: mtime (время изменения) файла.
+
+[type]
+  тип файла, по умолчанию: предполагается по расширению файла.
+
+[disposition]
+  используется для заголовка Content-Disposition, возможные значения:
+  +nil+ (по умолчанию), <tt>:attachment</tt> и <tt>:inline</tt>.
+
+[length]
+  значения для заголовка Content-Length, по умолчанию: размер файла.
+
+
+Этот метод будет использовать возможности Rack сервера для отправки файлов, если они
+доступны, а в противном случае, будет напрямую отдавать файл из Ruby процесса.
+Метод <tt>send_file</tt> также обеспечивает автоматическую обработку частичных (range)
+запросов с помощью Sinatra.
+
+=== Доступ к объекту запроса
+
+Объект входящего запроса доступен на уровне обработки запроса (в фильтрах, маршрутах,
+обработчиках ошибок) с помощью <tt>request</tt> метода:
 
   # приложение запущено на http://example.com/example
   get '/foo' do
-    request.body              # тело запроса, посланное клиентом (см. ниже)
-    request.scheme            # "http"
-    request.script_name       # "/example"
-    request.path_info         # "/foo"
-    request.port              # 80
-    request.request_method    # "GET"
-    request.query_string      # ""
-    request.content_length    # длина тела запроса
-    request.media_type        # медиатип тела запроса
-    request.host              # "example.com"
-    request.get?              # true (есть аналоги для других методов HTTP)
-    request.form_data?        # false
-    request["SOME_HEADER"]    # значение заголовка SOME_HEADER
-    request.referer           # источник запроса клиента либо '/'
-    request.user_agent        # user agent (используется для :agent условия)
-    request.cookies           # хеш с куками браузера
-    request.xhr?              # является ли запрос ajax запросом?
-    request.url               # "http://example.com/example/foo"
-    request.path              # "/example/foo"
-    request.ip                # IP-адрес клиента
-    request.secure?           # false
-    request.env               # "сырой" env хеш, полученный Rack
-  end
-
-Некоторые опции, такие как <tt>script_name</tt> или <tt>path_info</tt> доступны для записи:
+    t = %w[text/css text/html application/javascript]
+    request.accept              # ['text/html', '*/*']
+    request.accept? 'text/xml'  # true
+    request.preferred_type(t)   # 'text/html'
+    request.body                # тело запроса, посланное клиентом (см. ниже)
+    request.scheme              # "http"
+    request.script_name         # "/example"
+    request.path_info           # "/foo"
+    request.port                # 80
+    request.request_method      # "GET"
+    request.query_string        # ""
+    request.content_length      # длина тела запроса
+    request.media_type          # медиатип тела запроса
+    request.host                # "example.com"
+    request.get?                # true (есть аналоги для других методов HTTP)
+    request.form_data?          # false
+    request["SOME_HEADER"]      # значение заголовка SOME_HEADER
+    request.referrer            # источник запроса клиента либо '/'
+    request.user_agent          # user agent (используется для :agent условия)
+    request.cookies             # хеш, содержащий cookies браузера
+    request.xhr?                # является ли запрос ajax запросом?
+    request.url                 # "http://example.com/example/foo"
+    request.path                # "/example/foo"
+    request.ip                  # IP-адрес клиента
+    request.secure?             # false (true, если запрос сделан через SSL)
+    request.forwarded?          # true (если сервер работает за обратным прокси)
+    request.env                 # "сырой" env хеш, полученный Rack
+  end
+
+Некоторые опции, такие как <tt>script_name</tt> или <tt>path_info</tt> доступны для изменения:
 
   before { request.path_info = "/" }
   
@@ -799,16 +1026,86 @@ After-фильтры выполняются после каждого запро
     "Hello #{data['name']}!"
   end
 
+=== Вложения
+
+Вы можете использовать метод +attachment+, чтобы сказать браузеру, что ответ
+сервера должен быть сохранен на диск, а не отображен:
+
+  get '/' do
+    attachment
+    "store it!"
+  end
+
+Вы также можете указать имя файла:
+
+  get '/' do
+    attachment "info.txt"
+    "store it!"
+  end
+
+=== Поиск шаблонов
+
+Для поиска шаблонов и их последующего рендеринга используется метод <tt>find_template</tt>:
+
+  find_template settings.views, 'foo', Tilt[:haml] do |file|
+    puts "could be #{file}"
+  end
+
+Это не слишком полезный пример. Зато полезен тот факт, что вы можете переопределить
+этот метод, чтобы использовать свой собственный механизм поиска. Например, если вы
+хотите, чтобы можно было использовать несколько директорий с шаблонами:
+
+  set :views, ['views', 'templates']
+
+  helpers do
+    def find_template(views, name, engine, &block)
+      Array(views).each { |v| super(v, name, engine, &block) }
+    end
+  end
+
+Другой пример, в котором используются разные директории для движков рендеринга:
+
+  set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views'
+
+  helpers do
+    def find_template(views, name, engine, &block)
+      _, folder = views.detect { |k,v| engine == Tilt[k] }
+      folder ||= views[:default]
+      super(folder, name, engine, &block)
+    end
+  end
+
+Вы можете легко вынести этот код в расширение и поделиться им с остальными!
+
+Заметьте, что <tt>find_template</tt> не проверяет, существует ли файл на самом деле,
+а вызывает заданный блок для всех возможных путей. Дело тут не в производительности,
+дело в том, что +render+ вызовет +break+, как только файл не будет найден.
+Содержимое и местонахождение шаблонов будет закэшировано, если приложение запущено не
+в режиме разработки (set :environment, :development). Вы должны помнить об этих нюансах, 
+если пишите по-настоящему "сумасшедший" метод.
+
 == Конфигурация
 
 Этот блок исполняется один раз при старте в любом окружении, режиме (environment):
 
   configure do
-    ...
+    # задание одной опции
+    set :option, 'value'
+
+    # устанавливаем несколько опций
+    set :a => 1, :b => 2
+
+    # то же самое, что и `set :option, true`
+    enable :option
+
+    # то же самое, что и `set :option, false`
+    disable :option
+
+    # у вас могут быть "динамические" опции с блоками
+    set(:css_dir) { File.join(views, 'css') }
   end
 
-Будет запущено, когда окружение (RACK_ENV переменная) установлена в
-<tt>:production</tt>:
+Будет запущено, когда окружение (RACK_ENV переменная) <tt>:production</tt>:
 
   configure :production do
     ...
@@ -820,14 +1117,116 @@ After-фильтры выполняются после каждого запро
     ...
   end
 
+Вы можете получить доступ к этим опциям с помощью <tt>settings</tt>:
+
+  configure do
+    set :foo, 'bar'
+  end
+
+  get '/' do
+    settings.foo? # => true
+    settings.foo  # => 'bar'
+    ...
+  end
+
+=== Доступные настройки
+
+[absolute_redirects]   если отключено, то Sinatra будет позволять использование
+                       относительных перенаправлений, но при этом перестанет
+                       соответствовать RFC 2616 (HTTP 1.1), который разрешает только
+                       абсолютные перенаправления.
+
+                       Включайте эту опцию, если ваше приложение работает за обратным прокси,
+                       который настроен не совсем корректно. Обратите внимание, метод +url+
+                       все равно будет генерировать абсолютные URL, если вы не передадите
+                       +false+ вторым аргументом.
+
+                       Отключено по умолчанию.
+
+[add_charsets]         mime-типы, к которым метод <tt>content_type</tt> будет автоматически
+                       добавлять информацию о кодировке.
+
+                       Вам следует добавлять значения к этой опции вместо ее переопределения:
+
+                         settings.add_charsets << "application/foobar"
+
+[app_file]             главный файл приложения, используется для определения корневой директории
+                       проекта, директорий с шаблонами и статическими файлами, вложенных шаблонов.
+
+[bind]                 используемый IP-адрес (по умолчанию: 0.0.0.0). Используется только
+                       встроенным сервером.
+
+[default_encoding]     кодировка, если неизвестна (по умолчанию: <tt>"utf-8"</tt>).
+
+[dump_errors]          отображать ошибки в логе.
+
+[environment]          текущее окружение, по умолчанию, значение <tt>ENV['RACK_ENV']</tt>
+                       или <tt>"development"</tt>, если <tt>ENV['RACK_ENV']</tt> не доступна.
+
+[logging]              использовать логер.
+
+[lock]                 создает блокировку для каждого запроса, которая гарантирует обработку
+                       только одного запроса в текущий момент времени в Ruby процессе.
+
+                       Включайте, если ваше приложение не потоко-безопасно (thread-safe).
+                       Отключено по умолчанию.
+
+[method_override]      использовать "магический" параметр <tt>_method</tt>, чтобы позволить
+                       использование PUT/DELETE форм в браузерах, которые не поддерживают
+                       эти методы.
+
+[port]                 порт, на котором будет работать сервер. Используется только
+                       встроенным сервером.
+
+[prefixed_redirects]   добавлять или нет параметр <tt>request.script_name</tt> к редиректам,
+                       если не задан абсолютный путь. Таким образом,  <tt>redirect '/foo'</tt>
+                       будет вести себя как <tt>redirect to('/foo')</tt>. Отключено по умолчанию.
+
+[public_folder]        директория, откуда будут раздаваться статические файлы.
+
+[reload_templates]     перезагружать или нет шаблоны на каждый запрос.
+                       Включено в режиме разработки.
+
+[root]                 корневая директория проекта.
+
+[raise_errors]         выбрасывать исключения (будет останавливать приложение).
+
+[run]                  если включено, Sinatra будет самостоятельно запускать веб-сервер.
+                       Не включайте, если используете rackup или аналогичные средства.
+
+[running]              работает ли сейчас встроенный сервер?
+                       Не меняйте эту опцию!
+
+[server]               сервер или список серверов, которые следует использовать в качестве
+                       встроенного сервера. По умолчанию: ['thin', 'mongrel', 'webrick'],
+                       порядок задает приоритет.
+
+[sessions]             включить сессии на основе кук (cookie).
+
+[show_exceptions]      показывать исключения/стек вызовов (stack trace) в браузере.
+
+[static]               должна ли Sinatra осуществлять раздачу статических файлов.
+                       Отключите, когда используете какой-либо веб-сервер для этой цели.
+                       Отключение значительно улучшит производительность приложения.
+                       По умолчанию включено в классических и отключено в модульных
+                       приложениях.
+
+[static_cache_control] Когда Sinatra отдает статические файлы, используйте эту опцию,
+                       чтобы добавить им заголовок <tt>Cache-Control</tt>. Для этого
+                       используется метод-помощник +cache_control+. По умолчанию отключено.
+                       Используйте массив, когда надо задать несколько значений:
+                       <tt>set :static_cache_control, [:public, :max_age => 300]</tt>
+
+[views]               директория с шаблонами.
+
 == Обработка ошибок
 
-Обработчики ошибок исполняются в том же контексте, что и маршруты, before-фильтры, а это означает, что всякие
+Обработчики ошибок исполняются в том же контексте, что и маршруты, и +before+-фильтры, а это означает, что всякие
 прелести вроде <tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt> и т.д. доступны и им.
 
-=== NotFound
+=== Not Found
 
-Когда возбуждено исключение <tt>Sinatra::NotFound</tt>, или кодом ответа является 404,
+Когда выброшено исключение <tt>Sinatra::NotFound</tt>, или кодом ответа является 404,
 то будет вызван <tt>not_found</tt> обработчик:
 
   not_found do
@@ -836,7 +1235,7 @@ After-фильтры выполняются после каждого запро
 
 === Ошибки
 
-Обработчик ошибок +error+ будет вызван, когда исключение возбуждено из блока маршрута, либо из фильтра.
+Обработчик ошибок +error+ будет вызван, когда исключение выброшено из блока маршрута, либо из фильтра.
 Объект-исключение доступен как переменная <tt>sinatra.error</tt> в Rack:
 
   error do
@@ -846,7 +1245,7 @@ After-фильтры выполняются после каждого запро
 Частные ошибки:
 
   error MyCustomError do
-    'So what happened was...' + request.env['sinatra.error'].message
+    'So what happened was...' + env['sinatra.error'].message
   end
 
 Тогда, если это произошло:
@@ -875,26 +1274,15 @@ After-фильтры выполняются после каждого запро
     'Boom'
   end
 
-Sinatra устанавливает специальные <tt>not_found</tt> и <tt>error</tt> обработчики, когда запущена в режиме
+Sinatra устанавливает специальные <tt>not_found</tt> и <tt>error</tt> обработчики, когда приложение запущено в режиме
 разработки (окружение <tt>:development</tt>).
 
-== Mime-типы
-
-Когда вы используете <tt>send_file</tt> или статические файлы, у вас могут быть mime-типы, которые Sinatra
-не понимает по умолчанию. Используйте +mime_type+ для их регистрации по расширению файла:
-
-  mime_type :foo, 'text/foo'
-
-Вы также можете использовать это в +content_type+ помощнике:
-
-  content_type :foo
-
 == Rack "прослойки"
 
 Sinatra использует Rack[http://rack.rubyforge.org/], минимальный стандартный 
 интерфейс для веб-фреймворков на Ruby. Одной из самых интересных для разработчиков возможностей Rack
 является поддержка "прослоек" ("middleware") — компонентов,
-"сидящих" между сервером и вашим приложением, которые отслеживают и/или манипулируют
+находящихся "между" сервером и вашим приложением, которые отслеживают и/или манипулируют
 HTTP запросами/ответами для предоставления различной функциональности.
 
 В Sinatra очень просто использовать такие "прослойки" с помощью метода +use+:
@@ -911,8 +1299,8 @@ HTTP запросами/ответами для предоставления р
 
 Семантика +use+ идентична той, что определена для
 Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSL
-(чаще всего используется в rackup файлах). Например, +use+ метод принимает
-множественные переменные, также как и блоки:
+(чаще всего используется в rackup файлах). Например, метод +use+ принимает
+как множественные переменные, так и блоки:
 
   use Rack::Auth::Basic do |username, password|
     username == 'admin' && password == 'secret'
@@ -921,12 +1309,18 @@ Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSL
 Rack распространяется с различными стандартными "прослойками"
 для логирования, отладки, маршрутизации URL, аутентификации, обработки сессий. Sinatra использует
 многие из этих компонентов автоматически, основываясь на конфигурации, чтобы вам не приходилось
-регистрировать/использовать (+use+) их вручную.
+подключать (+use+) их вручную.
+
+Вы можете найти полезные прослойки в
+{rack}[https://github.com/rack/rack/tree/master/lib/rack],
+{rack-contrib}[https://github.com/rack/rack-contrib#readme],
+{CodeRack}[http://coderack.org/] или в
+{Rack wiki}[https://github.com/rack/rack/wiki/List-of-Middleware].
 
 == Тестирование
 
 Тесты для Sinatra приложений могут быть написаны с помощью библиотек, фреймворков, поддерживающих
-тестирование Rack. {Rack::Test}[http://gitrdoc.com/brynary/rack-test] рекомендован:
+тестирование Rack. {Rack::Test}[http://rdoc.info/github/brynary/rack-test/master/frames] рекомендован:
 
   require 'my_sinatra_app'
   require 'test/unit'
@@ -955,19 +1349,18 @@ Rack распространяется с различными стандартн
     end
   end
 
-Обратите внимание: Встроенные модуль Sinatra::Test и класс Sinatra::TestHarness являются
-устаревшими, начиная с релиза 0.9.2.
-
 == Sinatra::Base — "прослойки", библиотеки и модульные приложения
 
-Описание своего приложения самым простейшим способом (с помощью DSL верхнего уровня, как в примерах выше)
-работает отлично для крохотных приложений, но имеет множество недостатков, когда надо создать компоненты, такие как
-Rack middleware ("прослойки"), Rails metal, простые библиотеки с серверными компонентами,
-расширения Sinatra.
-DSL верхнего уровня загрязняет пространство имен <tt>Object</tt> и подразумевает стиль конфигурации
-микро-приложения (например, единый файл приложения, ./public и
-./views директории, создание логов, страницу деталей об исключениях 
-и т.д.). И тут на помощь приходит Sinatra::Base:
+Описание своего приложения самым простейшим способом (с помощью DSL верхнего уровня,
+как в примерах выше) отлично работает для крохотных приложений. Тем не менее,
+такой метод имеет множество недостатков при создании компонентов, таких как
+Rack middleware ("прослоек"), Rails metal, простых библиотек с серверными компонентами,
+расширений Sinatra.
+
+DSL верхнего уровня "загрязняет" пространство имен <tt>Object</tt> и подразумевает стиль конфигурации
+микро-приложения (например, единый файл приложения, <tt>./public</tt> и
+<tt>./views</tt> директории, логирование, страницу деталей об исключениях
+и т.д.). И тут на помощь приходит <tt>Sinatra::Base</tt>:
 
   require 'sinatra/base'
 
@@ -980,18 +1373,47 @@ DSL верхнего уровня загрязняет пространство
     end
   end
 
-Методы, доступные Sinatra::Base сабклассам идентичны тем, что доступны
+Методы, доступные <tt>Sinatra::Base</tt> подклассам идентичны тем, что доступны
 в DSL верхнего уровня. Большинство приложений верхнего уровня могут быть
-конвертированы в Sinatra::Base компоненты с помощью двух модификаций:
+конвертированы в <tt>Sinatra::Base</tt> компоненты с помощью двух модификаций:
 
-* Вы должны подключать +sinatra/base+ вместо +sinatra+,
-  иначе все методы предоставляемые Sinatra будут импортированные в глобальное пространство имен.
-* Поместите все маршруты, обработчики ошибок, фильтры и опции в сабкласс Sinatra::Base.
+* Вы должны подключать <tt>sinatra/base</tt> вместо +sinatra+,
+  иначе все методы, предоставляемые Sinatra, будут импортированы в глобальное пространство имен.
+* Поместите все маршруты, обработчики ошибок, фильтры и опции в подкласс <tt>Sinatra::Base</tt>.
 
 <tt>Sinatra::Base</tt> — это чистый лист. Большинство опций, включая встроенный сервер, по умолчанию отключены.
-Смотрите {Опции и Конфигурация}[http://www.sinatrarb.com/configuration.html] для детальной информации
+Смотрите {Опции и конфигурация}[http://www.sinatrarb.com/configuration.html] для детальной информации
 об опциях и их поведении.
 
+=== Модульные приложения против классических
+
+Вопреки всеобщему убеждению, в классическом стиле (самом простом) нет ничего плохого.
+Если этот стиль подходит вашему приложению, вы не обязаны переписывать его в модульное
+приложение.
+
+У классического стиля есть всего два недостатка относительно модульного:
+
+* У вас может быть только одно приложение Sinatra на один Ruby процесс. Если вы планируете
+  использовать больше, то переключайтесь на модульный стиль.
+
+* Приложения, использующие классический стиль, добавляют методы к Object. Если вы
+  планируете поставлять свое приложение в виде библиотеки/gem, то переходите
+  на модульный стиль.
+
+Не существует причин, по которым вы не могли бы смешивать модульный и классический стили.
+
+Переходя с одного стиля на другой, примите во внимание следующие изменения в настройках:
+
+  Опция               Классический            Модульный
+
+  app_file            файл с приложением      файл с подклассом Sinatra::Base
+  run                 $0 == app_file          false
+  logging             true                    false
+  method_override     true                    false
+  inline_templates    true                    false
+  static              true                    false
+
+
 === Запуск модульных приложений
 
 Есть два общепринятых способа запускать модульные приложения: запуск напрямую с помощью <tt>run!</tt>:
@@ -1014,14 +1436,14 @@ DSL верхнего уровня загрязняет пространство
 Rack-совместимый сервер приложений.
 
   # config.ru
-  require 'my_app'
+  require './my_app'
   run MyApp
 
 Запускаем:
 
   rackup -p 4567
 
-=== Запуск "классических" приложений с config.ru
+=== Запуск классических приложений с config.ru
 
 Файл приложения:
 
@@ -1034,7 +1456,7 @@ Rack-совместимый сервер приложений.
 
 И соответствующий <tt>config.ru</tt>:
 
-  require 'app'
+  require './app'
   run Sinatra::Application
 
 === Когда использовать config.ru?
@@ -1042,8 +1464,8 @@ Rack-совместимый сервер приложений.
 Вот несколько причин, по которым вы, возможно, захотите использовать <tt>config.ru</tt>:
 
 * вы хотите разворачивать свое приложение на различных Rack-совместимых серверах (Passenger, Unicorn,
-  Heroku, ...).
-* вы хотите использовать более одного сабкласса <tt>Sinatra::Base</tt>.
+  Heroku, ...);
+* вы хотите использовать более одного подкласса <tt>Sinatra::Base</tt>;
 * вы хотите использовать Sinatra только в качестве "прослойки" Rack.
 
 <b>Совсем необязательно переходить на использование <tt>config.ru</tt> лишь потому, что вы стали
@@ -1053,8 +1475,8 @@ Rack-совместимый сервер приложений.
 === Использование Sinatra в качестве "прослойки"
 
 Не только сама Sinatra может использовать "прослойки" Rack, но и любое Sinatra приложение
-само может быть добавлено к любому Rack эндпоинту в качестве "прослойки". Этим эндпоинтом
-может быть другое Sinatra приложение, или приложение, основанное на Rack (Rails/Ramaze/Camping/...).
+само может быть добавлено к любому Rack endpoint в качестве "прослойки". Этим endpoint (конечной точкой)
+может быть другое Sinatra приложение, или приложение, основанное на Rack (Rails/Ramaze/Camping/...):
 
   require 'sinatra/base'
   
@@ -1064,7 +1486,7 @@ Rack-совместимый сервер приложений.
     get('/login') { haml :login }
     
     post('/login') do
-      if params[:name] = 'admin' and params[:password] = 'admin'
+      if params[:name] == 'admin' && params[:password] == 'admin'
         session['user_name'] = params[:name]
       else
         redirect '/login'
@@ -1085,6 +1507,48 @@ Rack-совместимый сервер приложений.
     get('/') { "Hello #{session['user_name']}." }
   end
 
+=== Создание приложений "на лету"
+
+Иногда требуется создавать Sinatra приложения "на лету" (например,
+из другого приложения). Это возможно с помощью <tt>Sinatra.new</tt>:
+
+  require 'sinatra/base'
+  my_app = Sinatra.new { get('/') { "hi" } }
+  my_app.run!
+
+Этот метод может принимать аргументом приложение, от которого
+следует наследоваться:
+
+  # config.ru
+  require 'sinatra/base'
+
+  controller = Sinatra.new do
+    enable :logging
+    helpers MyHelpers
+  end
+
+  map('/a') do
+    run Sinatra.new(controller) { get('/') { 'a' } }
+  end
+
+  map('/b') do
+    run Sinatra.new(controller) { get('/') { 'b' } }
+  end
+
+Это особенно полезно для тестирования расширений Sinatra и при
+использовании Sinatra внутри вашей библиотеки.
+
+Благодаря этому, использовать Sinatra как "прослойку" очень просто:
+
+  require 'sinatra/base'
+
+  use Sinatra do
+    get('/') { ... }
+  end
+
+  run RailsProject::Application
+
+
 == Области видимости и привязка
 
 Текущая область видимости определяет методы и переменные, доступные
@@ -1092,15 +1556,14 @@ Rack-совместимый сервер приложений.
 
 === Область видимости приложения / класса
 
-Любое Sinatra приложение соответствует сабклассу Sinatra::Base. Если вы 
-используете DSL верхнего 
-уровня (<tt>require 'sinatra'</tt>), то этим классом будет 
-Sinatra::Application, иначе это будет сабкласс, который вы создали вручную.
-На уровне класса вам будут доступны такие методы, как `get` или `before`, но вы
-не сможете иметь доступ к объектам `request` или `session`, так как существует
-только единый класс приложения для всех запросов.
+Любое Sinatra приложение соответствует подклассу <tt>Sinatra::Base</tt>. Если вы
+используете DSL верхнего уровня (<tt>require 'sinatra'</tt>), то этим классом будет 
+<tt>Sinatra::Application</tt>, иначе это будет подкласс, который вы создали вручную.
+На уровне класса вам будут доступны такие методы, как +get+ или +before+, но вы
+не сможете получить доступ к объектам +request+ или +session+, так как существует
+только один класс приложения для всех запросов.
 
-Опции, созданные с помощью `set`, являются методами уровня класса:
+Опции, созданные с помощью +set+, являются методами уровня класса:
 
     class MyApp < Sinatra::Base
       # Я в области видимости приложения!
@@ -1114,23 +1577,24 @@ Sinatra::Application, иначе это будет сабкласс, котор
 
 У вас будет область видимости приложения внутри:
 
-* Тела вашего класса приложения
-* Методов, определенных расширениями
-* Блока, переданного в `helpers`
-* Блоков, использованных как значения для `set`
+* тела вашего класса приложения;
+* методов, определенных расширениями;
+* блока, переданного в +helpers+;
+* блоков, использованных как значения для +set+;
+* блока, переданного в <tt>Sinatra.new</tt>.
 
 Вы можете получить доступ к объекту области видимости (классу приложения) следующими способами:
 
-* объект, переданный блокам конфигурации (<tt>configure { |c| ... }</tt>)
-* `settings` внутри области видимости запроса
+* через объект, переданный блокам конфигурации (<tt>configure { |c| ... }</tt>);
+* +settings+ внутри области видимости запроса.
 
 === Область видимости запроса/экземпляра
 
 Для каждого входящего запроса будет создан новый экземпляр вашего приложения,
 и все блоки обработчика будут запущены в этом контексте. В этой области
-видимости вам доступны `request` и `session` объекты, вызовы методов
-рендеринга, такие как `erb` или `haml`. Вы можете получить доступ к
-области видимости приложения из контекста запроса, используя помощник `settings`:
+видимости вам доступны +request+ и +session+ объекты, вызовы методов
+рендеринга, такие как +erb+ или +haml+. Вы можете получить доступ к
+области видимости приложения из контекста запроса, используя метод-помощник +settings+:
 
   class MyApp < Sinatra::Base
     # Я в области видимости приложения!
@@ -1147,27 +1611,27 @@ Sinatra::Application, иначе это будет сабкласс, котор
     end
   end
 
-У вас будет область видимости запроса внутри:
+У вас будет область видимости запроса в:
 
-* get/head/post/put/delete/options блоков
-* before/after фильтрах
-* методах помощниках 
-* шаблонах/видах
+* get/head/post/put/delete/options блоках;
+* before/after фильтрах;
+* методах-помощниках;
+* шаблонах/отображениях.
 
 === Область видимости делегирования
 
 Область видимости делегирования просто перенаправляет методы в область видимости класса.
-Однако, оно не полностью на 100% ведет себя как область видимости класса,
-так как у вас нету привязки к классу: только методы, явно помеченные для
-делегирования, будут доступны, а переменных/состояний области видимости класса
-не будет (иначе говоря, у вас будет другой `self` объект). Вы можете
+Однако, она не полностью ведет себя как область видимости класса, так как у вас нет
+привязки к классу. Только методы, явно помеченные для делегирования, будут доступны,
+а переменных/состояний области видимости класса не будет (иначе говоря,
+у вас будет другой +self+ объект). Вы можете
 непосредственно добавить методы делегирования, используя
 <tt>Sinatra::Delegator.delegate :method_name</tt>.
 
 У вас будет контекст делегирования внутри:
 
-* Привязки верхнего уровня, если вы сделали <tt>require "sinatra"</tt>
-* Объекта, расширенного с помощью примеси `Sinatra::Delegator`
+* привязки верхнего уровня, если вы сделали <tt>require 'sinatra'</tt>;
+* объекта, расширенного с помощью <tt>Sinatra::Delegator</tt>.
 
 Посмотрите сами в код: тут
 {Sinatra::Delegator примесь}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128]
@@ -1181,23 +1645,71 @@ Sinatra приложения могут быть запущены напряму
 
 Опции включают:
 
-  -h # помощь
-  -p # настроить порт (по умолчанию 4567)
-  -o # настроить хост (по умолчанию 0.0.0.0)
-  -e # настроить окружение, режим (по умолчанию development)
-  -s # настроить rack сервер/обработчик (по умолчанию thin)
-  -x # включить мьютекс (по умолчанию выключен)
+  -h # раздел помощи
+  -p # указание порта (по умолчанию 4567)
+  -o # указание хоста (по умолчанию 0.0.0.0)
+  -e # указание окружения, режима (по умолчанию development)
+  -s # указание rack сервера/обработчика (по умолчанию thin)
+  -x # включить мьютекс-блокировку (по умолчанию выключена)
+
+== Системные требования
+
+Следующие версии Ruby официально поддерживаются:
+
+[ Ruby 1.8.7 ]
+  1.8.7 полностью поддерживается, тем не менее, если вас ничто не держит на
+  этой версии, рекомендуем обновиться до 1.9.2 или перейти на JRuby или Rubinius.
+
+[ Ruby 1.9.2 ]
+  1.9.2 поддерживается и рекомендована к использованию. Заметьте, что Radius и Markaby
+  пока несовместимы с 1.9.2. Не используйте 1.9.2p0, известно, что эта
+  версия весьма нестабильна при использовании Sinatra.
+
+[ Rubinius ]
+  Rubinius официально поддерживается (Rubinius >= 1.2.3), всё, включая все
+  языки шаблонов, работает.
+
+[ JRuby ]
+  JRuby официально поддерживается (JRuby >= 1.6.1). Нет никаких проблем с
+  использованием альтернативных шаблонов. Тем не менее, если вы выбираете
+  JRuby, то, пожалуйста, посмотрите на JRuby Rack-сервера, так как Thin не
+  поддерживается полностью на JRuby. Поддержка расширений на C в JRuby все
+  еще экспериментальная, что на данный момент затрагивает только RDiscount и
+  Redcarpet.
+
+<b>Ruby 1.8.6 больше не поддерживается.</b> Если вы хотите запускать свое
+приложение на 1.8.6, откатитесь до Sinatra 1.2, которая будет получать все
+исправления ошибок до тех пор, пока не будет выпущена Sinatra 1.4.0
+
+Мы также следим за предстоящими к выходу версиями Ruby.
+
+Следующие реализации Ruby не поддерживаются официально, но известно, что на
+них запускается Sinatra:
+
+* старые версии JRuby и Rubinius;
+* MacRuby, Maglev, IronRuby;
+* Ruby 1.9.0 и 1.9.1;
+* Ruby 1.8.6 с помощью {backports}[https://github.com/marcandre/backports/#readme].
+
+То, что версия официально не поддерживается, означает, что, если что-то не
+работает на этой версии, а на поддерживаемой работает - это не наша проблема, а их.
+
+Мы также запускаем наши CI-тесты на последней версии Ruby (предстоящей 1.9.3),
+но мы не можем ничего гарантировать, так как она постоянно развивается.
+Предполагается, что 1.9.3p0 будет поддерживаться.
+
+Sinatra должна работать на любой операционной системе, в которой есть одна из указанных выше версий Ruby.
 
 == На острие
 
 Если вы хотите использовать самый последний код Sinatra, не бойтесь запускать
-свое приложение вместе с master бранчем Sinatra, он весьма стабилен.
+свое приложение вместе с кодом из master ветки Sinatra, она весьма стабильна.
 
 Мы также время от времени выпускаем предварительные версии, так что вы можете делать так:
 
   gem install sinatra --pre
 
-Чтобы воспользоваться некоторыми самыми последними возможностям.
+Чтобы воспользоваться некоторыми самыми последними возможностями.
 
 === С помощью Bundler
 
@@ -1221,14 +1733,14 @@ Sinatra приложения могут быть запущены напряму
 в этом файле. Однако, непосредственные зависимости Sinatra (Rack и Tilt) Bundler
 автоматически скачает и добавит.
 
-Теперь вы можете запускать свое приложение примерно так:
+Теперь вы можете запускать свое приложение так:
 
   bundle exec ruby myapp.rb
 
 === Вручную
 
 Создайте локальный клон репозитория и запускайте свое приложение с <tt>sinatra/lib</tt>
-директорией в <tt>LOAD_PATH</tt>:
+директорией в <tt>$LOAD_PATH</tt>:
 
   cd myapp
   git clone git://github.com/sinatra/sinatra.git
@@ -1248,16 +1760,26 @@ Sinatra приложения могут быть запущены напряму
   rake sinatra.gemspec
   rake install
 
-Если вы устанавливаете пакеты (gem) от пользователя root, то вашим последним шагом должна быть команда
+Если вы устанавливаете пакеты (gem) от пользователя root, то вашим следующим шагом должна быть команда
 
   sudo rake install
 
+== Версии
+
+Sinatra использует {Semantic Versioning}[http://semver.org/], SemVer и
+SemVerTag.
+
 == Дальнейшее чтение
 
-* {Вебсайт проекта}[http://www.sinatrarb.com/] - Дополнительная документация,
+* {Веб-сайт проекта}[http://www.sinatrarb.com/] - Дополнительная документация,
   новости и ссылки на другие ресурсы.
-* {Участие}[http://www.sinatrarb.com/contributing] - Нашли баг? Нужна помощь?	Написали патч?
-* {Слежение за проблемами}[http://github.com/sinatra/sinatra/issues]
+* {Участие в проекте}[http://www.sinatrarb.com/contributing] - Обнаружили баг? Нужна помощь? Написали патч?
+* {Слежение за проблемами/ошибками}[http://github.com/sinatra/sinatra/issues]
 * {Twitter}[http://twitter.com/sinatra]
-* {Лист рассылки}[http://groups.google.com/group/sinatrarb/topics]
-* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] on http://freenode.net 
+* {Группы рассылки}[http://groups.google.com/group/sinatrarb/topics]
+* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] на http://freenode.net
+* {Sinatra Book}[http://sinatra-book.gittr.com] учебник и сборник рецептов
+* {Sinatra Recipes}[http://recipes.sinatrarb.com/] сборник рецептов
+* API документация к {последнему релизу}[http://rubydoc.info/gems/sinatra]
+  или {текущему HEAD}[http://rubydoc.info/github/sinatra/sinatra] на
+  http://rubydoc.info
diff --git a/README.zh.rdoc b/README.zh.rdoc
index 649f985..4b6fc37 100644
--- a/README.zh.rdoc
+++ b/README.zh.rdoc
@@ -194,10 +194,10 @@ Rack body对象或者HTTP状态码:
 
 == 静态文件
 
-静态文件是从 <tt>./public</tt> 目录提供服务。你可以通过设置<tt>:public</tt>
+静态文件是从 <tt>./public_folder</tt> 目录提供服务。你可以通过设置<tt>:public</tt>
 选项设定一个不同的位置:
 
-  set :public, File.dirname(__FILE__) + '/static'
+  set :public_folder, File.dirname(__FILE__) + '/static'
 
 请注意public目录名并没有被包含在URL之中。文件
 <tt>./public/css/style.css</tt>是通过
@@ -492,6 +492,7 @@ Rack body对象或者HTTP状态码:
 需要引入 <tt>RDoc</tt> gem/library 以渲染RDoc模板:
 
   # 需要在你的应用中引入rdoc/markup/to_html
+  require "rdoc"
   require "rdoc/markup/to_html"
 
   get '/' do
@@ -582,6 +583,19 @@ Rack body对象或者HTTP状态码:
 
 渲染 <tt>./views/index.slim</tt>。
 
+=== Creole 模板 
+
+需要引入 <tt>creole</tt> gem/library 来渲染 Creole 模板:
+
+  # 需要在你的应用中引入 creole
+  require 'creole'
+
+  get '/' do
+    creole :index
+  end
+
+渲染 <tt>./views/index.creole</tt>。
+
 === CoffeeScript 模板
 
 需要引入 <tt>coffee-script</tt> gem/library 并至少满足下面条件一项
@@ -884,7 +898,7 @@ Session被用来在请求之间保持状态。如果被激活,每一个用户
   get '/foo' do
     status 418
     headers \
-      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN"
+      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN",
       "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
     body "I'm a tea pot!"
   end
@@ -948,7 +962,7 @@ Sinatra并不理解。使用 +mime_type+ 通过文件扩展名来注册它们:
 
 或者使用session:
 
-  enable :session
+  enable :sessions
 
   get '/foo' do
     session[:secret] = 'foo'
@@ -1255,7 +1269,7 @@ Sinatra会自动处理range请求。
                       <tt>redirect '/foo'</tt> 会和
                       <tt>redirect to('/foo')</tt>起相同作用。默认禁用。
 
-[public]              public文件夹的位置。
+[public_folder]      public文件夹的位置。
 
 [reload_templates]    是否每个请求都重新载入模板。
                       在development mode和 Ruby 1.8.6 中被企业(用来
@@ -1500,7 +1514,7 @@ Sinatra::Base子类可用的方法实际上就是通过顶层 DSL
 或者使用一个 <tt>config.ru</tt>,允许你使用任何Rack处理器:
 
   # config.ru
-  require 'my_app'
+  require './my_app'
   run MyApp
 
 运行:
@@ -1520,7 +1534,7 @@ Sinatra::Base子类可用的方法实际上就是通过顶层 DSL
 
 加入相应的 <tt>config.ru</tt>:
 
-  require 'app'
+  require './app'
   run Sinatra::Application
 
 === 什么时候用 config.ru?
diff --git a/Rakefile b/Rakefile
index 044ea57..d7e0d8a 100644
--- a/Rakefile
+++ b/Rakefile
@@ -3,6 +3,12 @@ require 'rake/testtask'
 require 'fileutils'
 require 'date'
 
+# CI Reporter is only needed for the CI
+begin
+  require 'ci/reporter/rake/test_unit'
+rescue LoadError
+end
+
 task :default => :test
 task :spec => :test
 
@@ -10,8 +16,8 @@ CLEAN.include "**/*.rbc"
 
 def source_version
   @source_version ||= begin
-    line = File.read('lib/sinatra/base.rb')[/^\s*VERSION = .*/]
-    line.match(/.*VERSION = '(.*)'/)[1]
+    load './lib/sinatra/version.rb'
+    Sinatra::VERSION
   end
 end
 
@@ -37,6 +43,16 @@ Rake::TestTask.new(:test) do |t|
   t.ruby_opts << '-I.'
 end
 
+Rake::TestTask.new(:"test:core") do |t|
+  core_tests = %w[base delegator encoding extensions filter
+     helpers mapped_error middleware radius rdoc
+     readme request response result route_added_hook
+     routing server settings sinatra static templates]
+  t.test_files = core_tests.map {|n| "test/#{n}_test.rb"}
+  t.ruby_opts = ["-rubygems"] if defined? Gem
+  t.ruby_opts << "-I."
+end
+
 # Rcov ================================================================
 
 namespace :test do
@@ -67,7 +83,7 @@ task :add_template, [:name] do |t, args|
         puts "Liquid not found in #{file}"
       else
         puts "Adding section to #{file}"
-        template = template.gsub(/Liquid/, args.name.capitalize).gsub(/liquid/, args.name.downcase)        
+        template = template.gsub(/Liquid/, args.name.capitalize).gsub(/liquid/, args.name.downcase)
         code.gsub! /^(\s*===.*CoffeeScript)/, "\n" << template << "\n\\1"
         File.open(file, "w") { |f| f << code }
       end
@@ -90,18 +106,22 @@ task :thanks, [:release,:backports] do |t, a|
     "(based on commits included in #{a.release}, but not in #{a.backports})"
 end
 
-task :authors, [:format, :sep] do |t, a|
-  a.with_defaults :format => "%s (%d)", :sep => ', '
+desc "list of authors"
+task :authors, [:commit_range, :format, :sep] do |t, a|
+  a.with_defaults :format => "%s (%d)", :sep => ", ", :commit_range => '--all'
   authors = Hash.new { |h,k| h[k] = 0 }
   blake   = "Blake Mizerany"
+  overall = 0
   mapping = {
     "blake.mizerany at gmail.com" => blake, "bmizerany" => blake,
     "a_user at mac.com" => blake, "ichverstehe" => "Harry Vangberg",
     "Wu Jiang (nouse)" => "Wu Jiang" }
-  `git shortlog -s`.lines.map do |line|
+  `git shortlog -s #{a.commit_range}`.lines.map do |line|
     num, name = line.split("\t", 2).map(&:strip)
     authors[mapping[name] || name] += num.to_i
+    overall += num.to_i
   end
+  puts "#{overall} commits by #{authors.count} authors:"
   puts authors.sort_by { |n,c| -c }.map { |e| a.format % e }.join(a.sep)
 end
 
@@ -143,29 +163,11 @@ if defined?(Gem)
     SH
   end
 
-  task 'sinatra.gemspec' => FileList['{lib,test,compat}/**','Rakefile','CHANGES','*.rdoc'] do |f|
-    # read spec file and split out manifest section
-    spec = File.read(f.name)
-    head, manifest, tail = spec.split("  # = MANIFEST =\n")
-    # replace version and date
-    head.sub!(/\.version = '.*'/, ".version = '#{source_version}'")
-    head.sub!(/\.date = '.*'/, ".date = '#{Date.today.to_s}'")
-    # determine file list from git ls-files
-    files = `git ls-files`.
-      split("\n").
-      sort.
-      reject{ |file| file =~ /^\./ }.
-      reject { |file| file =~ /^doc/ }.
-      map{ |file| "    #{file}" }.
-      join("\n")
-    # piece file back together and write...
-    manifest = "  s.files = %w[\n#{files}\n  ]\n"
-    spec = [head,manifest,tail].join("  # = MANIFEST =\n")
-    File.open(f.name, 'w') { |io| io.write(spec) }
-    puts "updated #{f.name}"
-  end
-
   task 'release' => ['test', package('.gem')] do
+    if File.read("CHANGES") =~ /= \d\.\d\.\d . not yet released$/i
+      fail 'please update changes first'
+    end
+
     sh <<-SH
       gem install #{package('.gem')} --local &&
       gem push #{package('.gem')}  &&
diff --git a/debian/changelog b/debian/changelog
index 457aeaa..58b80e1 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+ruby-sinatra (1.3.2-1) unstable; urgency=low
+
+  * New upstream version: 1.3.2
+
+ -- Youhei SASAKI <uwabami at gfd-dennou.org>  Fri, 06 Jan 2012 02:43:30 +0900
+
 ruby-sinatra (1.2.6-1) unstable; urgency=low
 
   * New upstream version: 1.2.6
diff --git a/examples/chat.rb b/examples/chat.rb
new file mode 100755
index 0000000..e0575f3
--- /dev/null
+++ b/examples/chat.rb
@@ -0,0 +1,61 @@
+#!/usr/bin/env ruby -I ../lib -I lib
+# coding: utf-8
+require 'sinatra'
+set :server, 'thin'
+connections = []
+
+get '/' do
+  halt erb(:login) unless params[:user]
+  erb :chat, :locals => { :user => params[:user].gsub(/\W/, '') }
+end
+
+get '/stream', :provides => 'text/event-stream' do
+  stream :keep_open do |out|
+    connections << out
+    out.callback { connections.delete(out) }
+  end
+end
+
+post '/' do
+  connections.each { |out| out << "data: #{params[:msg]}\n\n" }
+  204 # response without entity body
+end
+
+__END__
+
+@@ layout
+<html>
+  <head> 
+    <title>Super Simple Chat with Sinatra</title> 
+    <meta charset="utf-8" />
+    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> 
+  </head> 
+  <body><%= yield %></body>
+</html>
+
+@@ login
+<form action='/'>
+  <label for='user'>User Name:</label>
+  <input name='user' value='' />
+  <input type='submit' value="GO!" />
+</form>
+
+@@ chat
+<pre id='chat'></pre>
+
+<script>
+  // reading
+  var es = new EventSource('/stream');
+  es.onmessage = function(e) { $('#chat').append(e.data + "\n") };
+
+  // writing
+  $("form").live("submit", function(e) {
+    $.post('/', {msg: "<%= user %>: " + $('#msg').val()});
+    $('#msg').val(''); $('#msg').focus();
+    e.preventDefault();
+  });
+</script>
+
+<form>
+  <input id='msg' placeholder='type message here...' />
+</form>
\ No newline at end of file
diff --git a/examples/simple.rb b/examples/simple.rb
new file mode 100755
index 0000000..2697f94
--- /dev/null
+++ b/examples/simple.rb
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby -I ../lib -I lib
+require 'sinatra'
+get('/') { 'this is a simple app' }
diff --git a/examples/stream.ru b/examples/stream.ru
new file mode 100644
index 0000000..074c66b
--- /dev/null
+++ b/examples/stream.ru
@@ -0,0 +1,26 @@
+# this example does *not* work properly with WEBrick
+#
+# run *one* of these:
+#
+#   rackup -s mongrel stream.ru   # gem install mongrel
+#   thin -R stream.ru start       # gem install thin
+#   unicorn stream.ru             # gem install unicorn
+#   puma stream.ru                # gem install puma
+
+require 'sinatra/base'
+
+class Stream < Sinatra::Base
+  get '/' do
+    content_type :txt
+
+    stream do |out|
+      out << "It's gonna be legen -\n"
+      sleep 0.5
+      out << " (wait for it) \n"
+      sleep 1
+      out << "- dary!\n"
+    end
+  end
+end
+
+run Stream
diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb
index 52115e7..893016e 100644
--- a/lib/sinatra/base.rb
+++ b/lib/sinatra/base.rb
@@ -1,14 +1,18 @@
+# external dependencies
+require 'rack'
+require 'tilt'
+require "rack/protection"
+
+# stdlib dependencies
 require 'thread'
 require 'time'
 require 'uri'
-require 'rack'
-require 'rack/builder'
+
+# other files we need
 require 'sinatra/showexceptions'
-require 'tilt'
+require 'sinatra/version'
 
 module Sinatra
-  VERSION = '1.2.6'
-
   # The request object. See Rack::Request for more info:
   # http://rack.rubyforge.org/doc/classes/Rack/Request.html
   class Request < Rack::Request
@@ -29,37 +33,27 @@ module Sinatra
       end
     end
 
-    if Rack.release <= "1.2"
-      # Whether or not the web server (or a reverse proxy in front of it) is
-      # using SSL to communicate with the client.
-      def secure?
-        @env['HTTPS'] == 'on' or
-        @env['HTTP_X_FORWARDED_PROTO'] == 'https' or
-        @env['rack.url_scheme'] == 'https'
-      end
-    else
-      alias secure? ssl?
-    end
+    alias accept? preferred_type
+    alias secure? ssl?
 
     def forwarded?
       @env.include? "HTTP_X_FORWARDED_HOST"
     end
 
-    def route
-      @route ||= Rack::Utils.unescape(path_info)
+    def safe?
+      get? or head? or options? or trace?
     end
 
-    def path_info=(value)
-      @route = nil
-      super
+    def idempotent?
+      safe? or put? or delete?
     end
 
     private
 
     def accept_entry(entry)
-      type, *options = entry.gsub(/\s/, '').split(';')
-      quality = 0 # we sort smalles first
-      options.delete_if { |e| quality = 1 - e[2..-1].to_f if e =~ /^q=/ }
+      type, *options = entry.delete(' ').split(';')
+      quality = 0 # we sort smallest first
+      options.delete_if { |e| quality = 1 - e[2..-1].to_f if e.start_with? 'q=' }
       [type, [quality, type.count('*'), 1 - options.size]]
     end
   end
@@ -69,20 +63,27 @@ module Sinatra
   # http://rack.rubyforge.org/doc/classes/Rack/Response.html
   # http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
   class Response < Rack::Response
+    def body=(value)
+      value = value.body while Rack::Response === value
+      @body = String === value ? [value.to_str] : value
+    end
+
+    def each
+      block_given? ? super : enum_for(:each)
+    end
+
     def finish
-      @body = block if block_given?
-      if [204, 304].include?(status.to_i)
-        header.delete "Content-Type"
-        [status.to_i, header.to_hash, []]
-      else
-        body = @body || []
-        body = [body] if body.respond_to? :to_str
-        if body.respond_to?(:to_ary)
-          header["Content-Length"] = body.to_ary.
-            inject(0) { |len, part| len + Rack::Utils.bytesize(part) }.to_s
-        end
-        [status.to_i, header.to_hash, body]
+      if status.to_i / 100 == 1
+        headers.delete "Content-Length"
+        headers.delete "Content-Type"
+      elsif Array === body and not [204, 304].include?(status.to_i)
+        headers["Content-Length"] = body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
       end
+
+      # Rack::Response#finish sometimes returns self as response body. We don't want that.
+      status, headers, result = super
+      result = body if result == self
+      [status, headers, result]
     end
   end
 
@@ -113,7 +114,11 @@ module Sinatra
 
     # Halt processing and redirect to the URI provided.
     def redirect(uri, *args)
-      status 302
+      if env['HTTP_VERSION'] == 'HTTP/1.1' and env["REQUEST_METHOD"] != 'GET'
+        status 303
+      else
+        status 302
+      end
 
       # According to RFC 2616 section 14.30, "the field value consists of a
       # single absolute URI"
@@ -127,9 +132,7 @@ module Sinatra
       return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
       uri = [host = ""]
       if absolute
-        host << 'http'
-        host << 's' if request.secure?
-        host << "://"
+        host << "http#{'s' if request.secure?}://"
         if request.forwarded? or request.port != (request.secure? ? 443 : 80)
           host << request.host_with_port
         else
@@ -167,6 +170,11 @@ module Sinatra
       request.session
     end
 
+    # Access shared logger object.
+    def logger
+      request.logger
+    end
+
     # Look up a media type by file extension in Rack's mime registry.
     def mime_type(type)
       Base.mime_type(type)
@@ -198,14 +206,13 @@ module Sinatra
       if filename
         params = '; filename="%s"' % File.basename(filename)
         response['Content-Disposition'] << params
+        ext = File.extname(filename)
+        content_type(ext) unless response['Content-Type'] or ext.empty?
       end
     end
 
     # Use the contents of the file at +path+ as the response body.
     def send_file(path, opts={})
-      stat = File.stat(path)
-      last_modified(opts[:last_modified] || stat.mtime)
-
       if opts[:type] or not response['Content-Type']
         content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
       end
@@ -216,88 +223,83 @@ module Sinatra
         response['Content-Disposition'] = 'inline'
       end
 
-      file_length = opts[:length] || stat.size
-      sf = StaticFile.open(path, 'rb')
-      if ! sf.parse_ranges(env, file_length)
-        response['Content-Range'] = "bytes */#{file_length}"
-        halt 416
-      elsif r=sf.range
-        response['Content-Range'] = "bytes #{r.begin}-#{r.end}/#{file_length}"
-        response['Content-Length'] = (r.end - r.begin + 1).to_s
-        halt 206, sf
-      else
-        response['Content-Length'] ||= file_length.to_s
-        halt sf
-      end
+      last_modified opts[:last_modified] if opts[:last_modified]
+
+      file      = Rack::File.new nil
+      file.path = path
+      result    = file.serving env
+      result[1].each { |k,v| headers[k] ||= v }
+      halt result[0], result[2]
     rescue Errno::ENOENT
       not_found
     end
 
-    # Rack response body used to deliver static files. The file contents are
-    # generated iteratively in 8K chunks.
-    class StaticFile < ::File #:nodoc:
-      alias_method :to_path, :path
-
-      attr_accessor :range  # a Range or nil
-
-      # Checks for byte-ranges in the request and sets self.range appropriately.
-      # Returns false if the ranges are unsatisfiable and the request should return 416.
-      def parse_ranges(env, size)
-        #r = Rack::Utils::byte_ranges(env, size)  # TODO: not available yet in released Rack
-        r = byte_ranges(env, size)
-        return false if r == []  # Unsatisfiable; report error
-        @range = r[0] if r && r.length == 1  # Ignore multiple-range requests for now
-        return true
-      end
-
-      # TODO: Copied from the new method Rack::Utils::byte_ranges; this method can be removed once
-      # a version of Rack with that method is released and Sinatra can depend on it.
-      def byte_ranges(env, size)
-        # See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
-        http_range = env['HTTP_RANGE']
-        return nil unless http_range
-        ranges = []
-        http_range.split(/,\s*/).each do |range_spec|
-          matches = range_spec.match(/bytes=(\d*)-(\d*)/)
-          return nil  unless matches
-          r0,r1 = matches[1], matches[2]
-          if r0.empty?
-            return nil  if r1.empty?
-            # suffix-byte-range-spec, represents trailing suffix of file
-            r0 = [size - r1.to_i, 0].max
-            r1 = size - 1
-          else
-            r0 = r0.to_i
-            if r1.empty?
-              r1 = size - 1
-            else
-              r1 = r1.to_i
-              return nil  if r1 < r0  # backwards range is syntactically invalid
-              r1 = size-1  if r1 >= size
-            end
+    # Class of the response body in case you use #stream.
+    #
+    # Three things really matter: The front and back block (back being the
+    # blog generating content, front the one sending it to the client) and
+    # the scheduler, integrating with whatever concurrency feature the Rack
+    # handler is using.
+    #
+    # Scheduler has to respond to defer and schedule.
+    class Stream
+      def self.schedule(*) yield end
+      def self.defer(*)    yield end
+
+      def initialize(scheduler = self.class, keep_open = false, &back)
+        @back, @scheduler, @keep_open = back.to_proc, scheduler, keep_open
+        @callbacks, @closed = [], false
+      end
+
+      def close
+        return if @closed
+        @closed = true
+        @scheduler.schedule { @callbacks.each { |c| c.call }}
+      end
+
+      def each(&front)
+        @front = front
+        @scheduler.defer do
+          begin
+            @back.call(self)
+          rescue Exception => e
+            @scheduler.schedule { raise e }
           end
-          ranges << (r0..r1)  if r0 <= r1
+          close unless @keep_open
         end
-        ranges
       end
 
-      CHUNK_SIZE = 8192
+      def <<(data)
+        @scheduler.schedule { @front.call(data.to_s) }
+        self
+      end
 
-      def each
-        if @range
-          self.pos = @range.begin
-          length = @range.end - @range.begin + 1
-          while length > 0 && (buf = read([CHUNK_SIZE,length].min))
-            yield buf
-            length -= buf.length
-          end
-        else
-          rewind
-          while buf = read(CHUNK_SIZE)
-            yield buf
-          end
+      def callback(&block)
+        @callbacks << block
+      end
+
+      alias errback callback
+    end
+
+    # Allows to start sending data to the client even though later parts of
+    # the response body have not yet been generated.
+    #
+    # The close parameter specifies whether Stream#close should be called
+    # after the block has been executed. This is only relevant for evented
+    # servers like Thin or Rainbows.
+    def stream(keep_open = false)
+      scheduler = env['async.callback'] ? EventMachine : Stream
+      current   = @params.dup
+      block     = proc do |out|
+        begin
+          original, @params = @params, current
+          yield(out)
+        ensure
+          @params = original if original
         end
       end
+
+      body Stream.new(scheduler, keep_open, &block)
     end
 
     # Specify response freshness policy for HTTP caches (Cache-Control header).
@@ -319,7 +321,7 @@ module Sinatra
         hash = {}
       end
 
-      values = values.map { |value| value.to_s.tr('_','-') }
+      values.map! { |value| value.to_s.tr('_','-') }
       hash.each do |key, value|
         key = key.to_s.tr('_', '-')
         value = value.to_i if key == "max-age"
@@ -366,8 +368,19 @@ module Sinatra
       return unless time
       time = time_for time
       response['Last-Modified'] = time.httpdate
-      # compare based on seconds since epoch
-      halt 304 if Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i >= time.to_i
+      return if env['HTTP_IF_NONE_MATCH']
+
+      if status == 200 and env['HTTP_IF_MODIFIED_SINCE']
+        # compare based on seconds since epoch
+        since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i
+        halt 304 if since >= time.to_i
+      end
+
+      if (success? or status == 412) and env['HTTP_IF_UNMODIFIED_SINCE']
+        # compare based on seconds since epoch
+        since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i
+        halt 412 if since < time.to_i
+      end
     rescue ArgumentError
     end
 
@@ -380,16 +393,28 @@ module Sinatra
     # When the current request includes an 'If-None-Match' header with a
     # matching etag, execution is immediately halted. If the request method is
     # GET or HEAD, a '304 Not Modified' response is sent.
-    def etag(value, kind=:strong)
-      raise TypeError, ":strong or :weak expected" if ![:strong,:weak].include?(kind)
+    def etag(value, options = {})
+      # Before touching this code, please double check RFC 2616 14.24 and 14.26.
+      options      = {:kind => options} unless Hash === options
+      kind         = options[:kind] || :strong
+      new_resource = options.fetch(:new_resource) { request.post? }
+
+      unless [:strong, :weak].include?(kind)
+        raise ArgumentError, ":strong or :weak expected"
+      end
+
       value = '"%s"' % value
       value = 'W/' + value if kind == :weak
       response['ETag'] = value
 
-      # Conditional GET check
-      if etags = env['HTTP_IF_NONE_MATCH']
-        etags = etags.split(/\s*,\s*/)
-        halt 304 if etags.include?(value) || etags.include?('*')
+      if success? or status == 304
+        if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource
+          halt(request.safe? ? 304 : 412)
+        end
+
+        if env['HTTP_IF_MATCH']
+          halt 412 unless etag_matches? env['HTTP_IF_MATCH'], new_resource
+        end
       end
     end
 
@@ -398,11 +423,38 @@ module Sinatra
       request.referer
     end
 
-    private
+    # whether or not the status is set to 1xx
+    def informational?
+      status.between? 100, 199
+    end
+
+    # whether or not the status is set to 2xx
+    def success?
+      status.between? 200, 299
+    end
+
+    # whether or not the status is set to 3xx
+    def redirect?
+      status.between? 300, 399
+    end
+
+    # whether or not the status is set to 4xx
+    def client_error?
+      status.between? 400, 499
+    end
 
-    # Ruby 1.8 has no #to_time method.
-    # This can be removed and calls to it replaced with to_time,
-    # if 1.8 support is dropped.
+    # whether or not the status is set to 5xx
+    def server_error?
+      status.between? 500, 599
+    end
+
+    # whether or not the status is set to 404
+    def not_found?
+      status == 404
+    end
+
+    # Generates a Time object from the given value.
+    # Used by #expires and #last_modified.
     def time_for(value)
       if value.respond_to? :to_time
         value.to_time
@@ -426,8 +478,18 @@ module Sinatra
     rescue Exception
       raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
     end
+
+    private
+
+    # Helper method checking if a ETag value list includes the current ETag.
+    def etag_matches?(list, new_resource = request.post?)
+      return !new_resource if list == '*'
+      list.to_s.split(/\s*,\s*/).include? response['ETag']
+    end
   end
 
+  private
+
   # Template rendering methods. Each method takes the name of a template
   # to render as a Symbol and returns a String with the rendered output,
   # as well as an optional hash with additional options.
@@ -451,11 +513,18 @@ module Sinatra
       attr_accessor :content_type
     end
 
+    def initialize
+      super
+      @default_layout = :layout
+    end
+
     def erb(template, options={}, locals={})
       render :erb, template, options, locals
     end
 
     def erubis(template, options={}, locals={})
+      warn "Sinatra::Templates#erubis is deprecated and will be removed, use #erb instead.\n" \
+        "If you have Erubis installed, it will be used automatically."
       render :erubis, template, options, locals
     end
 
@@ -521,12 +590,16 @@ module Sinatra
       render :slim, template, options, locals
     end
 
+    def creole(template, options={}, locals={})
+      render :creole, template, options, locals
+    end
+
     # Calls the given block for every possible template file in views,
     # named name.ext, where ext is registered on engine.
     def find_template(views, name, engine)
       yield ::File.join(views, "#{name}.#{@preferred_extension}")
       Tilt.mappings.each do |ext, engines|
-        next unless ext != @preferred_extension and Array(engines).include? engine
+        next unless ext != @preferred_extension and engines.include? engine
         yield ::File.join(views, "#{name}.#{ext}")
       end
     end
@@ -548,7 +621,6 @@ module Sinatra
       # extract generic options
       locals          = options.delete(:locals) || locals         || {}
       views           = options.delete(:views)  || settings.views || "./views"
-      @default_layout = :layout if @default_layout.nil?
       layout          = options.delete(:layout)
       eat_errors      = layout.nil?
       layout          = @default_layout if layout.nil? or layout == true
@@ -557,11 +629,14 @@ module Sinatra
       scope           = options.delete(:scope)         || self
 
       # compile and render template
-      layout_was      = @default_layout
-      @default_layout = false
-      template        = compile_template(engine, data, options, views)
-      output          = template.render(scope, locals, &block)
-      @default_layout = layout_was
+      begin
+        layout_was      = @default_layout
+        @default_layout = false
+        template        = compile_template(engine, data, options, views)
+        output          = template.render(scope, locals, &block)
+      ensure
+        @default_layout = layout_was
+      end
 
       # render layout
       if layout
@@ -579,8 +654,8 @@ module Sinatra
         template = Tilt[engine]
         raise "Template engine not found: #{engine}" if template.nil?
 
-        case
-        when data.is_a?(Symbol)
+        case data
+        when Symbol
           body, path, line = settings.templates[data]
           if body
             body = body.call if body.respond_to?(:call)
@@ -598,12 +673,12 @@ module Sinatra
             throw :layout_missing if eat_errors and not found
             template.new(path, 1, options)
           end
-        when data.is_a?(Proc) || data.is_a?(String)
+        when Proc, String
           body = data.is_a?(String) ? Proc.new { data } : data
           path, line = settings.caller_locations.first
           template.new(path, line.to_i, options, &body)
         else
-          raise ArgumentError
+          raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
         end
       end
     end
@@ -619,6 +694,7 @@ module Sinatra
     attr_reader   :template_cache
 
     def initialize(app=nil)
+      super()
       @app = app
       @template_cache = Tilt::Cache.new
       yield self if block_given?
@@ -637,15 +713,15 @@ module Sinatra
       @response = Response.new
       @params   = indifferent_params(@request.params)
       template_cache.clear if settings.reload_templates
-      force_encoding(@request.route)
       force_encoding(@params)
 
       @response['Content-Type'] = nil
       invoke { dispatch! }
       invoke { error_block!(response.status) }
+
       unless @response['Content-Type']
-        if body.respond_to?(:to_ary) and body.first.respond_to? :content_type
-          content_type body.first.content_type
+        if Array === body and body[0].respond_to? :content_type
+          content_type body[0].content_type
         else
           content_type :html
         end
@@ -664,9 +740,10 @@ module Sinatra
       self.class.settings
     end
 
-    alias_method :options, :settings
-    class << self
-      alias_method :options, :settings
+    def options
+      warn "Sinatra::Base#options is deprecated and will be removed, " \
+        "use #settings instead."
+      settings
     end
 
     # Exit the current block, halts any further processing
@@ -697,15 +774,15 @@ module Sinatra
     # Run filters defined on the class and all superclasses.
     def filter!(type, base = settings)
       filter! type, base.superclass if base.superclass.respond_to?(:filters)
-      base.filters[type].each { |block| instance_eval(&block) }
+      base.filters[type].each { |args| process_route(*args) }
     end
 
     # Run routes defined on the class and all superclasses.
     def route!(base = settings, pass_block=nil)
       if routes = base.routes[@request.request_method]
         routes.each do |pattern, keys, conditions, block|
-          pass_block = process_route(pattern, keys, conditions) do
-            route_eval(&block)
+          pass_block = process_route(pattern, keys, conditions) do |*args|
+            route_eval { block[*args] }
           end
         end
       end
@@ -720,8 +797,8 @@ module Sinatra
     end
 
     # Run a route block and throw :halt with the result.
-    def route_eval(&block)
-      throw :halt, instance_eval(&block)
+    def route_eval
+      throw :halt, yield
     end
 
     # If the current request matches pattern and conditions, fill params
@@ -729,37 +806,23 @@ module Sinatra
     # Revert params afterwards.
     #
     # Returns pass block.
-    def process_route(pattern, keys, conditions)
-      @original_params ||= @params
-      route = @request.route
+    def process_route(pattern, keys, conditions, block = nil, values = [])
+      route = @request.path_info
       route = '/' if route.empty? and not settings.empty_path_info?
-      if match = pattern.match(route)
-        values = match.captures.to_a
-        params =
-          if keys.any?
-            keys.zip(values).inject({}) do |hash,(k,v)|
-              if k == 'splat'
-                (hash[k] ||= []) << v
-              else
-                hash[k] = v
-              end
-              hash
-            end
-          elsif values.any?
-            {'captures' => values}
-          else
-            {}
-          end
-        @params = @original_params.merge(params)
-        @block_params = values
-        catch(:pass) do
-          conditions.each { |cond|
-            throw :pass if instance_eval(&cond) == false }
-          yield
-        end
+      return unless match = pattern.match(route)
+      values += match.captures.to_a.map { |v| force_encoding URI.decode(v) if v }
+
+      if values.any?
+        original, @params = params, params.merge('splat' => [], 'captures' => values)
+        keys.zip(values) { |k,v| (@params[k] ||= '') << v if v }
+      end
+
+      catch(:pass) do
+        conditions.each { |c| throw :pass if c.bind(self).call == false }
+        block ? block[self, values] : yield(self, values)
       end
     ensure
-      @params = @original_params
+      @params = original if original
     end
 
     # No matching route was found or all routes passed. The default
@@ -778,14 +841,14 @@ module Sinatra
     # Attempt to serve static files from public directory. Throws :halt when
     # a matching file is found, returns nil otherwise.
     def static!
-      return if (public_dir = settings.public).nil?
+      return if (public_dir = settings.public_folder).nil?
       public_dir = File.expand_path(public_dir)
 
       path = File.expand_path(public_dir + unescape(request.path_info))
-      return if path[0, public_dir.length] != public_dir
-      return unless File.file?(path)
+      return unless path.start_with?(public_dir) and File.file?(path)
 
       env['sinatra.static_file'] = path
+      cache_control(*settings.static_cache_control) if settings.static_cache_control?
       send_file path, :disposition => nil
     end
 
@@ -804,36 +867,16 @@ module Sinatra
     end
 
     # Run the block with 'throw :halt' support and apply result to the response.
-    def invoke(&block)
-      res = catch(:halt) { instance_eval(&block) }
-      return if res.nil?
-
-      case
-      when res.respond_to?(:to_str)
-        @response.body = [res]
-      when res.respond_to?(:to_ary)
-        res = res.to_ary
-        if Fixnum === res.first
-          if res.length == 3
-            @response.status, headers, body = res
-            @response.body = body if body
-            headers.each { |k, v| @response.headers[k] = v } if headers
-          elsif res.length == 2
-            @response.status = res.first
-            @response.body   = res.last
-          else
-            raise TypeError, "#{res.inspect} not supported"
-          end
-        else
-          @response.body = res
-        end
-      when res.respond_to?(:each)
-        @response.body = res
-      when (100..599) === res
-        @response.status = res
+    def invoke
+      res = catch(:halt) { yield }
+      res = [res] if Fixnum === res or String === res
+      if Array === res and Fixnum === res.first
+        status(res.shift)
+        body(res.pop)
+        headers(*res)
+      elsif res.respond_to? :each
+        body res
       end
-
-      res
     end
 
     # Dispatch a request with error handling.
@@ -841,60 +884,47 @@ module Sinatra
       static! if settings.static? && (request.get? || request.head?)
       filter! :before
       route!
-    rescue NotFound => boom
-      handle_not_found!(boom)
     rescue ::Exception => boom
       handle_exception!(boom)
     ensure
       filter! :after unless env['sinatra.static_file']
     end
 
-    # Special treatment for 404s in order to play nice with cascades.
-    def handle_not_found!(boom)
-      @env['sinatra.error']          = boom
-      @response.status               = 404
-      @response.headers['X-Cascade'] = 'pass'
-      @response.body                 = ['<h1>Not Found</h1>']
-      error_block! boom.class, NotFound
-    end
-
     # Error handling during requests.
     def handle_exception!(boom)
       @env['sinatra.error'] = boom
+      status boom.respond_to?(:code) ? Integer(boom.code) : 500
 
-      dump_errors!(boom) if settings.dump_errors?
-      raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
+      if server_error?
+        dump_errors! boom if settings.dump_errors?
+        raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
+      end
 
-      @response.status = 500
-      if res = error_block!(boom.class)
-        res
-      elsif settings.raise_errors?
-        raise boom
-      else
-        error_block!(Exception)
+      if not_found?
+        headers['X-Cascade'] = 'pass'
+        body '<h1>Not Found</h1>'
       end
+
+      res = error_block!(boom.class, boom) || error_block!(status, boom)
+      return res if res or not server_error?
+      raise boom if settings.raise_errors? or settings.show_exceptions?
+      error_block! Exception, boom
     end
 
     # Find an custom error block for the key(s) specified.
-    def error_block!(*keys)
-      keys.each do |key|
-        base = settings
-        while base.respond_to?(:errors)
-          if block = base.errors[key]
-            # found a handler, eval and return result
-            return instance_eval(&block)
-          else
-            base = base.superclass
-          end
-        end
+    def error_block!(key, *block_params)
+      base = settings
+      while base.respond_to?(:errors)
+        next base = base.superclass unless args = base.errors[key]
+        args += [block_params]
+        return process_route(*args)
       end
-      raise boom if settings.show_exceptions? and keys == Exception
-      nil
+      return false unless key.respond_to? :superclass and key.superclass < Exception
+      error_block!(key.superclass, *block_params)
     end
 
     def dump_errors!(boom)
-      msg = ["#{boom.class} - #{boom.message}:",
-        *boom.backtrace].join("\n ")
+      msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
       @env['rack.errors'].puts(msg)
     end
 
@@ -939,20 +969,43 @@ module Sinatra
 
       # Sets an option to the given value.  If the value is a proc,
       # the proc will be called every time the option is accessed.
-      def set(option, value = (not_set = true), &block)
+      def set(option, value = (not_set = true), ignore_setter = false, &block)
         raise ArgumentError if block and !not_set
-        value = block if block
-        if value.kind_of?(Proc)
-          metadef(option, &value)
-          metadef("#{option}?") { !!__send__(option) }
-          metadef("#{option}=") { |val| metadef(option, &Proc.new{val}) }
-        elsif not_set
+        value, not_set = block, false if block
+
+        if not_set
           raise ArgumentError unless option.respond_to?(:each)
           option.each { |k,v| set(k, v) }
-        elsif respond_to?("#{option}=")
-          __send__ "#{option}=", value
-        else
-          set option, Proc.new{value}
+          return self
+        end
+
+        if respond_to?("#{option}=") and not ignore_setter
+          return __send__("#{option}=", value)
+        end
+
+        setter = proc { |val| set option, val, true }
+        getter = proc { value }
+
+        case value
+        when Proc
+          getter = value
+        when Symbol, Fixnum, FalseClass, TrueClass, NilClass
+          # we have a lot of enable and disable calls, let's optimize those
+          class_eval "def self.#{option}() #{value.inspect} end"
+          getter = nil
+        when Hash
+          setter = proc do |val|
+            val = value.merge val if Hash === val
+            set option, val, true
+          end
+        end
+
+        (class << self; self; end).class_eval do
+          define_method("#{option}=", &setter) if setter
+          define_method(option,       &getter) if getter
+          unless method_defined? "#{option}?"
+            class_eval "def #{option}?() !!#{option} end"
+          end
         end
         self
       end
@@ -970,8 +1023,11 @@ module Sinatra
       # Define a custom error handler. Optionally takes either an Exception
       # class, or an HTTP status code to specify which errors should be
       # handled.
-      def error(codes=Exception, &block)
-        Array(codes).each { |code| @errors[code] = block }
+      def error(*codes, &block)
+        args  = compile! "ERROR", //, block
+        codes = codes.map { |c| Array(c) }.flatten
+        codes << Exception if codes.empty?
+        codes.each { |c| @errors[c] = args }
       end
 
       # Sugar for `error(404) { ... }`
@@ -1055,18 +1111,19 @@ module Sinatra
 
       # add a filter
       def add_filter(type, path = nil, options = {}, &block)
-        return filters[type] << block unless path
         path, options = //, path if path.respond_to?(:each_pair)
-        block, *arguments = compile!(type, path, block, options)
-        add_filter(type) do
-          process_route(*arguments) { instance_eval(&block) }
-        end
+        filters[type] << compile!(type, path || //, block, options)
       end
 
       # Add a route condition. The route is considered non-matching when the
       # block returns false.
-      def condition(&block)
-        @conditions << block
+      def condition(name = "#{caller.first[/`.*'/]} condition", &block)
+        @conditions << generate_method(name, &block)
+      end
+
+      def public=(value)
+        warn ":public is no longer used to avoid overloading Module#public, use :public_folder instead"
+        set(:public_folder, value)
       end
 
    private
@@ -1119,57 +1176,55 @@ module Sinatra
       def delete(path, opts={}, &bk)  route 'DELETE',  path, opts, &bk end
       def head(path, opts={}, &bk)    route 'HEAD',    path, opts, &bk end
       def options(path, opts={}, &bk) route 'OPTIONS', path, opts, &bk end
+      def patch(path, opts={}, &bk)   route 'PATCH',   path, opts, &bk end
 
     private
       def route(verb, path, options={}, &block)
         # Because of self.options.host
         host_name(options.delete(:host)) if options.key?(:host)
         enable :empty_path_info if path == "" and empty_path_info.nil?
-
-        block, pattern, keys, conditions = compile! verb, path, block, options
+        signature = compile!(verb, path, block, options)
+        (@routes[verb] ||= []) << signature
         invoke_hook(:route_added, verb, path, block)
-
-        (@routes[verb] ||= []).
-          push([pattern, keys, conditions, block]).last
+        signature
       end
 
       def invoke_hook(name, *args)
         extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
       end
 
+      def generate_method(method_name, &block)
+        define_method(method_name, &block)
+        method = instance_method method_name
+        remove_method method_name
+        method
+      end
+
       def compile!(verb, path, block, options = {})
         options.each_pair { |option, args| send(option, *args) }
-        method_name = "#{verb} #{path}"
-
-        define_method(method_name, &block)
-        unbound_method          = instance_method method_name
-        pattern, keys           = compile(path)
+        method_name             = "#{verb} #{path}"
+        unbound_method          = generate_method(method_name, &block)
+        pattern, keys           = compile path
         conditions, @conditions = @conditions, []
-        remove_method method_name
 
-        [ block.arity != 0 ?
-            proc { unbound_method.bind(self).call(*@block_params) } :
-            proc { unbound_method.bind(self).call },
-          pattern, keys, conditions ]
+        [ pattern, keys, conditions, block.arity != 0 ?
+            proc { |a,p| unbound_method.bind(a).call(*p) } :
+            proc { |a,p| unbound_method.bind(a).call } ]
       end
 
       def compile(path)
         keys = []
         if path.respond_to? :to_str
-          special_chars = %w{. + ( ) $}
-          pattern =
-            path.to_str.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
-              case match
-              when "*"
-                keys << 'splat'
-                "(.*?)"
-              when *special_chars
-                Regexp.escape(match)
-              else
-                keys << $2[1..-1]
-                "([^/?#]+)"
-              end
+          pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c) }
+          pattern.gsub!(/((:\w+)|\*)/) do |match|
+            if match == "*"
+              keys << 'splat'
+              "(.*?)"
+            else
+              keys << $2[1..-1]
+              "([^/?#]+)"
             end
+          end
           [/^#{pattern}$/, keys]
         elsif path.respond_to?(:keys) && path.respond_to?(:match)
           [path, path.keys]
@@ -1182,11 +1237,18 @@ module Sinatra
         end
       end
 
+      def encoded(char)
+        enc = URI.encode(char)
+        enc = "(?:#{Regexp.escape enc}|#{URI.encode char, /./})" if enc == char
+        enc = "(?:#{enc}|#{encoded('+')})" if char == " "
+        enc
+      end
+
     public
       # Makes the methods defined in the block and in the Modules given
       # in `extensions` available to the handlers and templates
       def helpers(*extensions, &block)
-        class_eval(&block)  if block_given?
+        class_eval(&block)   if block_given?
         include(*extensions) if extensions.any?
       end
 
@@ -1220,23 +1282,28 @@ module Sinatra
       def quit!(server, handler_name)
         # Use Thin's hard #stop! if available, otherwise just #stop.
         server.respond_to?(:stop!) ? server.stop! : server.stop
-        puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
+        $stderr.puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
       end
 
       # Run the Sinatra app as a self-hosted server using
-      # Thin, Mongrel or WEBrick (in that order)
+      # Thin, Mongrel or WEBrick (in that order). If given a block, will call
+      # with the constructed handler once we have taken the stage.
       def run!(options={})
         set options
         handler      = detect_rack_handler
         handler_name = handler.name.gsub(/.*::/, '')
-        puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
-          "on #{port} for #{environment} with backup from #{handler_name}" unless handler_name =~/cgi/i
         handler.run self, :Host => bind, :Port => port do |server|
+          unless handler_name =~ /cgi/i
+            $stderr.puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
+            "on #{port} for #{environment} with backup from #{handler_name}"
+          end
           [:INT, :TERM].each { |sig| trap(sig) { quit!(server, handler_name) } }
+          server.threaded = settings.threaded if server.respond_to? :threaded=
           set :running, true
+          yield server if block_given?
         end
       rescue Errno::EADDRINUSE => e
-        puts "== Someone is already performing on port #{port}!"
+        $stderr.puts "== Someone is already performing on port #{port}!"
       end
 
       # The prototype instance used to process requests.
@@ -1251,19 +1318,14 @@ module Sinatra
       # pipeline. The object is guaranteed to respond to #call but may not be
       # an instance of the class new was called on.
       def new(*args, &bk)
-        build(*args, &bk).to_app
+        build(Rack::Builder.new, *args, &bk).to_app
       end
 
       # Creates a Rack::Builder instance with all the middleware set up and
       # an instance of this class as end point.
-      def build(*args, &bk)
-        builder = Rack::Builder.new
-        builder.use Rack::MethodOverride if method_override?
-        builder.use ShowExceptions       if show_exceptions?
-        builder.use Rack::CommonLogger   if logging?
-        builder.use Rack::Head
-        setup_sessions builder
-        middleware.each { |c,a,b| builder.use(c, *a, &b) }
+      def build(builder, *args, &bk)
+        setup_default_middleware builder
+        setup_middleware builder
         builder.run new!(*args, &bk)
         builder
       end
@@ -1273,16 +1335,66 @@ module Sinatra
       end
 
     private
+      def setup_default_middleware(builder)
+        builder.use ShowExceptions       if show_exceptions?
+        builder.use Rack::MethodOverride if method_override?
+        builder.use Rack::Head
+        setup_logging    builder
+        setup_sessions   builder
+        setup_protection builder
+      end
+
+      def setup_middleware(builder)
+        middleware.each { |c,a,b| builder.use(c, *a, &b) }
+      end
+
+      def setup_logging(builder)
+        if logging?
+          setup_common_logger(builder)
+          setup_custom_logger(builder)
+        elsif logging == false
+          setup_null_logger(builder)
+        end
+      end
+
+      def setup_null_logger(builder)
+        builder.use Rack::NullLogger
+      end
+
+      def setup_common_logger(builder)
+        return if ["development", "deployment", nil].include? ENV["RACK_ENV"]
+        builder.use Rack::CommonLogger
+      end
+
+      def setup_custom_logger(builder)
+        if logging.respond_to? :to_int
+          builder.use Rack::Logger, logging
+        else
+          builder.use Rack::Logger
+        end
+      end
+
+      def setup_protection(builder)
+        return unless protection?
+        options = Hash === protection ? protection.dup : {}
+        options[:except] = Array options[:except]
+        options[:except] += [:session_hijacking, :remote_token] unless sessions?
+        builder.use Rack::Protection, options
+      end
+
       def setup_sessions(builder)
         return unless sessions?
-        builder.use Rack::Session::Cookie, :secret => session_secret
+        options = {}
+        options[:secret] = session_secret if session_secret?
+        options.merge! sessions.to_hash if sessions.respond_to? :to_hash
+        builder.use Rack::Session::Cookie, options
       end
 
       def detect_rack_handler
         servers = Array(server)
         servers.each do |server_name|
           begin
-            return Rack::Handler.get(server_name.downcase)
+            return Rack::Handler.get(server_name.to_s)
           rescue LoadError
           rescue NameError
           end
@@ -1292,6 +1404,7 @@ module Sinatra
 
       def inherited(subclass)
         subclass.reset!
+        subclass.set :app_file, caller_files.first unless subclass.app_file?
         super
       end
 
@@ -1304,20 +1417,16 @@ module Sinatra
         end
       end
 
-      def metadef(message, &block)
-        (class << self; self; end).
-          send :define_method, message, &block
-      end
-
     public
       CALLERS_TO_IGNORE = [ # :nodoc:
         /\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code
         /lib\/tilt.*\.rb$/,                              # all tilt code
-        /\(.*\)/,                                        # generated code
+        /^\(.*\)$/,                                      # generated code
         /rubygems\/custom_require\.rb$/,                 # rubygems require hacks
         /active_support/,                                # active_support require hacks
         /bundler(\/runtime)?\.rb/,                       # bundler require hacks
-        /<internal:/                                     # internal in ruby >= 1.9.2
+        /<internal:/,                                    # internal in ruby >= 1.9.2
+        /src\/kernel\/bootstrap\/[A-Z]/                  # maglev kernel files
       ]
 
       # add rubinius (and hopefully other VM impls) ignore patterns ...
@@ -1326,16 +1435,26 @@ module Sinatra
       # Like Kernel#caller but excluding certain magic entries and without
       # line / method information; the resulting array contains filenames only.
       def caller_files
-        caller_locations.
-          map { |file,line| file }
+        cleaned_caller(1).flatten
       end
 
       # Like caller_files, but containing Arrays rather than strings with the
       # first element being the file, and the second being the line.
       def caller_locations
+        cleaned_caller 2
+      end
+
+    private
+      # used for deprecation warnings
+      def warn(message)
+        super message + "\n\tfrom #{cleaned_caller.first.join(':')}"
+      end
+
+      # Like Kernel#caller but excluding certain magic entries
+      def cleaned_caller(keep = 3)
         caller(1).
-          map    { |line| line.split(/:(?=\d|in )/)[0,2] }.
-          reject { |file,line| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
+          map    { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
+          reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
       end
     end
 
@@ -1350,7 +1469,7 @@ module Sinatra
       def self.force_encoding(data, encoding = default_encoding)
         return if data == settings || data.is_a?(Tempfile)
         if data.respond_to? :force_encoding
-          data.force_encoding encoding
+          data.force_encoding(encoding).encode!
         elsif data.respond_to? :each_value
           data.each_value { |v| force_encoding(v, encoding) }
         elsif data.respond_to? :each
@@ -1360,7 +1479,7 @@ module Sinatra
       end
     else
       def self.force_encoding(data, *) data end
-      end
+    end
 
     reset!
 
@@ -1370,12 +1489,20 @@ module Sinatra
     set :show_exceptions, Proc.new { development? }
     set :sessions, false
     set :logging, false
+    set :protection, true
     set :method_override, false
     set :default_encoding, "utf-8"
-    set :add_charset, [/^text\//, 'application/javascript', 'application/xml', 'application/xhtml+xml']
+    set :add_charset, %w[javascript xml xhtml+xml json].map { |t| "application/#{t}" }
+    settings.add_charset << /^text\//
 
-    # explicitly generating this eagerly to play nice with preforking
-    set :session_secret, '%x' % rand(2**255)
+    # explicitly generating a session secret eagerly to play nice with preforking
+    begin
+      require 'securerandom'
+      set :session_secret, SecureRandom.hex(64)
+    rescue LoadError, NotImplementedError
+      # SecureRandom raises a NotImplementedError if no random device is available
+      set :session_secret, "%064x" % Kernel.rand(2**256-1)
+    end
 
     class << self
       alias_method :methodoverride?, :method_override?
@@ -1395,11 +1522,13 @@ module Sinatra
     set :app_file, nil
     set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
     set :views, Proc.new { root && File.join(root, 'views') }
-    set :reload_templates, Proc.new { development? or RUBY_VERSION < '1.8.7' }
+    set :reload_templates, Proc.new { development? }
     set :lock, false
+    set :threaded, true
 
-    set :public, Proc.new { root && File.join(root, 'public') }
-    set :static, Proc.new { public && File.exist?(public) }
+    set :public_folder, Proc.new { root && File.join(root, 'public') }
+    set :static, Proc.new { public_folder && File.exist?(public_folder) }
+    set :static_cache_control, false
 
     error ::Exception do
       response.status = 500
@@ -1446,13 +1575,14 @@ module Sinatra
   #
   # The Application class should not be subclassed, unless you want to
   # inherit all settings, routes, handlers, and error pages from the
-  # top-level. Subclassing Sinatra::Base is heavily recommended for
+  # top-level. Subclassing Sinatra::Base is highly recommended for
   # modular applications.
   class Application < Base
     set :logging, Proc.new { ! test? }
     set :method_override, true
     set :run, Proc.new { ! test? }
     set :session_secret, Proc.new { super() unless development? }
+    set :app_file, nil
 
     def self.register(*extensions, &block) #:nodoc:
       added_methods = extensions.map {|m| m.public_instance_methods }.flatten
@@ -1465,32 +1595,17 @@ module Sinatra
   # methods to be delegated to the Sinatra::Application class. Used primarily
   # at the top-level.
   module Delegator #:nodoc:
-    TEMPLATE = <<-RUBY
-      def %1$s(*args, &b)
-        return super if respond_to? :%1$s
-        ::Sinatra::Delegator.target.send("%2$s", *args, &b)
-      end
-    RUBY
-
     def self.delegate(*methods)
       methods.each do |method_name|
-        # Replaced with way shorter and better implementation in 1.3.0
-        # using define_method instead, however, blocks cannot take block
-        # arguments on 1.8.6.
-        begin
-          code = TEMPLATE % [method_name, method_name]
-          eval code, binding, '(__DELEGATE__)', 1
-        rescue SyntaxError
-          code  = TEMPLATE % [:_delegate, method_name]
-          eval code, binding, '(__DELEGATE__)', 1
-          alias_method method_name, :_delegate
-          undef_method :_delegate
+        define_method(method_name) do |*args, &block|
+          return super(*args, &block) if respond_to? method_name
+          Delegator.target.send(method_name, *args, &block)
         end
         private method_name
       end
     end
 
-    delegate :get, :put, :post, :delete, :head, :options, :template, :layout,
+    delegate :get, :patch, :put, :post, :delete, :head, :options, :template, :layout,
              :before, :after, :error, :not_found, :configure, :set, :mime_type,
              :enable, :disable, :use, :development?, :test?, :production?,
              :helpers, :settings
@@ -1519,4 +1634,9 @@ module Sinatra
   def self.helpers(*extensions, &block)
     Delegator.target.helpers(*extensions, &block)
   end
+
+  # Use the middleware for classic applications.
+  def self.use(*args, &block)
+    Delegator.target.use(*args, &block)
+  end
 end
diff --git a/lib/sinatra/main.rb b/lib/sinatra/main.rb
index df0a679..48af1ad 100644
--- a/lib/sinatra/main.rb
+++ b/lib/sinatra/main.rb
@@ -8,16 +8,16 @@ module Sinatra
     # on this path by default.
     set :app_file, caller_files.first || $0
 
-    set :run, Proc.new { $0 == app_file }
+    set :run, Proc.new { File.expand_path($0) == File.expand_path(app_file) }
 
     if run? && ARGV.any?
       require 'optparse'
       OptionParser.new { |op|
-        op.on('-x')        {       set :lock, true }
-        op.on('-e env')    { |val| set :environment, val.to_sym }
-        op.on('-s server') { |val| set :server, val }
-        op.on('-p port')   { |val| set :port, val.to_i }
-        op.on('-o addr')   { |val| set :bind, val }
+        op.on('-p port',   'set the port (default is 4567)')                { |val| set :port, Integer(val) }
+        op.on('-o addr',   'set the host (default is 0.0.0.0)')             { |val| set :bind, val }
+        op.on('-e env',    'set the environment (default is development)')  { |val| set :environment, val.to_sym }
+        op.on('-s server', 'specify rack server/handler (default is thin)') { |val| set :server, val }
+        op.on('-x',        'turn on the mutex lock (default is off)')       {       set :lock, true }
       }.parse!(ARGV.dup)
     end
   end
diff --git a/lib/sinatra/showexceptions.rb b/lib/sinatra/showexceptions.rb
index 73558c0..993537b 100644
--- a/lib/sinatra/showexceptions.rb
+++ b/lib/sinatra/showexceptions.rb
@@ -252,7 +252,7 @@ TEMPLATE = <<-HTML # :nodoc:
 
     <div id="get">
       <h3 id="get-info">GET</h3>
-      <% unless req.GET.empty? %>
+      <% if req.GET and not req.GET.empty? %>
         <table class="req">
           <tr>
             <th>Variable</th>
@@ -273,7 +273,7 @@ TEMPLATE = <<-HTML # :nodoc:
 
     <div id="post">
       <h3 id="post-info">POST</h3>
-      <% unless req.POST.empty? %>
+      <% if req.POST and not req.POST.empty? %>
         <table class="req">
           <tr>
             <th>Variable</th>
diff --git a/lib/sinatra/version.rb b/lib/sinatra/version.rb
new file mode 100644
index 0000000..03836e2
--- /dev/null
+++ b/lib/sinatra/version.rb
@@ -0,0 +1,3 @@
+module Sinatra
+  VERSION = '1.3.2'
+end
diff --git a/metadata.yml b/metadata.yml
index a05c029..578d83c 100644
--- a/metadata.yml
+++ b/metadata.yml
@@ -1,10 +1,10 @@
---- !ruby/object:Gem::Specification 
+--- !ruby/object:Gem::Specification
 name: sinatra
-version: !ruby/object:Gem::Version 
+version: !ruby/object:Gem::Version
+  version: 1.3.2
   prerelease: 
-  version: 1.2.6
 platform: ruby
-authors: 
+authors:
 - Blake Mizerany
 - Ryan Tomayko
 - Simon Rozet
@@ -12,61 +12,66 @@ authors:
 autorequire: 
 bindir: bin
 cert_chain: []
-
-date: 2011-05-01 00:00:00 Z
-dependencies: 
-- !ruby/object:Gem::Dependency 
+date: 2011-12-30 00:00:00.000000000 Z
+dependencies:
+- !ruby/object:Gem::Dependency
   name: rack
-  requirement: &id001 !ruby/object:Gem::Requirement 
+  requirement: &2153237200 !ruby/object:Gem::Requirement
     none: false
-    requirements: 
+    requirements:
     - - ~>
-      - !ruby/object:Gem::Version 
-        version: "1.1"
+      - !ruby/object:Gem::Version
+        version: '1.3'
+    - - ! '>='
+      - !ruby/object:Gem::Version
+        version: 1.3.6
   type: :runtime
   prerelease: false
-  version_requirements: *id001
-- !ruby/object:Gem::Dependency 
-  name: tilt
-  requirement: &id002 !ruby/object:Gem::Requirement 
+  version_requirements: *2153237200
+- !ruby/object:Gem::Dependency
+  name: rack-protection
+  requirement: &2153236060 !ruby/object:Gem::Requirement
     none: false
-    requirements: 
-    - - ">="
-      - !ruby/object:Gem::Version 
-        version: 1.2.2
-    - - <
-      - !ruby/object:Gem::Version 
-        version: "2.0"
+    requirements:
+    - - ~>
+      - !ruby/object:Gem::Version
+        version: '1.2'
   type: :runtime
   prerelease: false
-  version_requirements: *id002
-- !ruby/object:Gem::Dependency 
-  name: shotgun
-  requirement: &id003 !ruby/object:Gem::Requirement 
+  version_requirements: *2153236060
+- !ruby/object:Gem::Dependency
+  name: tilt
+  requirement: &2153235320 !ruby/object:Gem::Requirement
     none: false
-    requirements: 
+    requirements:
     - - ~>
-      - !ruby/object:Gem::Version 
-        version: "0.6"
-  type: :development
+      - !ruby/object:Gem::Version
+        version: '1.3'
+    - - ! '>='
+      - !ruby/object:Gem::Version
+        version: 1.3.3
+  type: :runtime
   prerelease: false
-  version_requirements: *id003
-description: Classy web-development dressed in a DSL
+  version_requirements: *2153235320
+description: Sinatra is a DSL for quickly creating web applications in Ruby with minimal
+  effort.
 email: sinatrarb at googlegroups.com
 executables: []
-
 extensions: []
-
-extra_rdoc_files: 
-- README.rdoc
+extra_rdoc_files:
 - README.de.rdoc
-- README.jp.rdoc
-- README.fr.rdoc
 - README.es.rdoc
+- README.fr.rdoc
 - README.hu.rdoc
+- README.jp.rdoc
+- README.pt-br.rdoc
+- README.pt-pt.rdoc
+- README.rdoc
+- README.ru.rdoc
 - README.zh.rdoc
 - LICENSE
-files: 
+files:
+- .yardopts
 - AUTHORS
 - CHANGES
 - Gemfile
@@ -82,27 +87,32 @@ files:
 - README.ru.rdoc
 - README.zh.rdoc
 - Rakefile
+- examples/chat.rb
+- examples/simple.rb
+- examples/stream.ru
 - lib/sinatra.rb
 - lib/sinatra/base.rb
 - lib/sinatra/images/404.png
 - lib/sinatra/images/500.png
 - lib/sinatra/main.rb
 - lib/sinatra/showexceptions.rb
+- lib/sinatra/version.rb
 - sinatra.gemspec
 - test/base_test.rb
 - test/builder_test.rb
 - test/coffee_test.rb
 - test/contest.rb
+- test/creole_test.rb
 - test/delegator_test.rb
 - test/encoding_test.rb
 - test/erb_test.rb
-- test/erubis_test.rb
 - test/extensions_test.rb
 - test/filter_test.rb
 - test/haml_test.rb
-- test/hello.mab
 - test/helper.rb
 - test/helpers_test.rb
+- test/integration/app.rb
+- test/integration_test.rb
 - test/less_test.rb
 - test/liquid_test.rb
 - test/mapped_error_test.rb
@@ -111,8 +121,10 @@ files:
 - test/middleware_test.rb
 - test/nokogiri_test.rb
 - test/public/favicon.ico
+- test/rack_test.rb
 - test/radius_test.rb
 - test/rdoc_test.rb
+- test/readme_test.rb
 - test/request_test.rb
 - test/response_test.rb
 - test/result_test.rb
@@ -125,6 +137,7 @@ files:
 - test/sinatra_test.rb
 - test/slim_test.rb
 - test/static_test.rb
+- test/streaming_test.rb
 - test/templates_test.rb
 - test/textile_test.rb
 - test/views/a/in_a.str
@@ -133,15 +146,14 @@ files:
 - test/views/calc.html.erb
 - test/views/error.builder
 - test/views/error.erb
-- test/views/error.erubis
 - test/views/error.haml
 - test/views/error.sass
 - test/views/explicitly_nested.str
 - test/views/foo/hello.test
 - test/views/hello.builder
 - test/views/hello.coffee
+- test/views/hello.creole
 - test/views/hello.erb
-- test/views/hello.erubis
 - test/views/hello.haml
 - test/views/hello.less
 - test/views/hello.liquid
@@ -158,7 +170,6 @@ files:
 - test/views/hello.textile
 - test/views/layout2.builder
 - test/views/layout2.erb
-- test/views/layout2.erubis
 - test/views/layout2.haml
 - test/views/layout2.liquid
 - test/views/layout2.mab
@@ -169,53 +180,55 @@ files:
 - test/views/layout2.test
 - test/views/nested.str
 - test/views/utf8.erb
-homepage: http://sinatra.rubyforge.org
+homepage: http://www.sinatrarb.com/
 licenses: []
-
 post_install_message: 
-rdoc_options: 
+rdoc_options:
 - --line-numbers
 - --inline-source
 - --title
 - Sinatra
 - --main
 - README.rdoc
-require_paths: 
+require_paths:
 - lib
-required_ruby_version: !ruby/object:Gem::Requirement 
+required_ruby_version: !ruby/object:Gem::Requirement
   none: false
-  requirements: 
-  - - ">="
-    - !ruby/object:Gem::Version 
-      hash: -2370709599790999687
-      segments: 
+  requirements:
+  - - ! '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+      segments:
       - 0
-      version: "0"
-required_rubygems_version: !ruby/object:Gem::Requirement 
+      hash: 2805773660361067313
+required_rubygems_version: !ruby/object:Gem::Requirement
   none: false
-  requirements: 
-  - - ">="
-    - !ruby/object:Gem::Version 
-      version: "0"
+  requirements:
+  - - ! '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+      segments:
+      - 0
+      hash: 2805773660361067313
 requirements: []
-
-rubyforge_project: sinatra
-rubygems_version: 1.7.2
+rubyforge_project: 
+rubygems_version: 1.8.10
 signing_key: 
-specification_version: 2
+specification_version: 3
 summary: Classy web-development dressed in a DSL
-test_files: 
+test_files:
 - test/base_test.rb
 - test/builder_test.rb
 - test/coffee_test.rb
+- test/creole_test.rb
 - test/delegator_test.rb
 - test/encoding_test.rb
 - test/erb_test.rb
-- test/erubis_test.rb
 - test/extensions_test.rb
 - test/filter_test.rb
 - test/haml_test.rb
 - test/helpers_test.rb
+- test/integration_test.rb
 - test/less_test.rb
 - test/liquid_test.rb
 - test/mapped_error_test.rb
@@ -223,8 +236,10 @@ test_files:
 - test/markdown_test.rb
 - test/middleware_test.rb
 - test/nokogiri_test.rb
+- test/rack_test.rb
 - test/radius_test.rb
 - test/rdoc_test.rb
+- test/readme_test.rb
 - test/request_test.rb
 - test/response_test.rb
 - test/result_test.rb
@@ -237,5 +252,6 @@ test_files:
 - test/sinatra_test.rb
 - test/slim_test.rb
 - test/static_test.rb
+- test/streaming_test.rb
 - test/templates_test.rb
 - test/textile_test.rb
diff --git a/sinatra.gemspec b/sinatra.gemspec
index ada5d90..bd9aa9a 100644
--- a/sinatra.gemspec
+++ b/sinatra.gemspec
@@ -1,134 +1,18 @@
-Gem::Specification.new do |s|
-  s.specification_version = 2 if s.respond_to? :specification_version=
-  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
+require 'sinatra/version'
 
-  s.name = 'sinatra'
-  s.version = '1.2.6'
-  s.date = '2011-05-01'
+Gem::Specification.new 'sinatra', Sinatra::VERSION do |s|
+  s.description       = "Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort."
+  s.summary           = "Classy web-development dressed in a DSL"
+  s.authors           = ["Blake Mizerany", "Ryan Tomayko", "Simon Rozet", "Konstantin Haase"]
+  s.email             = "sinatrarb at googlegroups.com"
+  s.homepage          = "http://www.sinatrarb.com/"
+  s.files             = `git ls-files`.split("\n") - %w[.gitignore .travis.yml]
+  s.test_files        = s.files.select { |p| p =~ /^test\/.*_test.rb/ }
+  s.extra_rdoc_files  = s.files.select { |p| p =~ /^README/ } << 'LICENSE'
+  s.rdoc_options      = %w[--line-numbers --inline-source --title Sinatra --main README.rdoc]
 
-  s.description = "Classy web-development dressed in a DSL"
-  s.summary     = "Classy web-development dressed in a DSL"
-
-  s.authors = ["Blake Mizerany", "Ryan Tomayko", "Simon Rozet", "Konstantin Haase"]
-  s.email = "sinatrarb at googlegroups.com"
-
-  # = MANIFEST =
-  s.files = %w[
-    AUTHORS
-    CHANGES
-    Gemfile
-    LICENSE
-    README.de.rdoc
-    README.es.rdoc
-    README.fr.rdoc
-    README.hu.rdoc
-    README.jp.rdoc
-    README.pt-br.rdoc
-    README.pt-pt.rdoc
-    README.rdoc
-    README.ru.rdoc
-    README.zh.rdoc
-    Rakefile
-    lib/sinatra.rb
-    lib/sinatra/base.rb
-    lib/sinatra/images/404.png
-    lib/sinatra/images/500.png
-    lib/sinatra/main.rb
-    lib/sinatra/showexceptions.rb
-    sinatra.gemspec
-    test/base_test.rb
-    test/builder_test.rb
-    test/coffee_test.rb
-    test/contest.rb
-    test/delegator_test.rb
-    test/encoding_test.rb
-    test/erb_test.rb
-    test/erubis_test.rb
-    test/extensions_test.rb
-    test/filter_test.rb
-    test/haml_test.rb
-    test/hello.mab
-    test/helper.rb
-    test/helpers_test.rb
-    test/less_test.rb
-    test/liquid_test.rb
-    test/mapped_error_test.rb
-    test/markaby_test.rb
-    test/markdown_test.rb
-    test/middleware_test.rb
-    test/nokogiri_test.rb
-    test/public/favicon.ico
-    test/radius_test.rb
-    test/rdoc_test.rb
-    test/request_test.rb
-    test/response_test.rb
-    test/result_test.rb
-    test/route_added_hook_test.rb
-    test/routing_test.rb
-    test/sass_test.rb
-    test/scss_test.rb
-    test/server_test.rb
-    test/settings_test.rb
-    test/sinatra_test.rb
-    test/slim_test.rb
-    test/static_test.rb
-    test/templates_test.rb
-    test/textile_test.rb
-    test/views/a/in_a.str
-    test/views/ascii.erb
-    test/views/b/in_b.str
-    test/views/calc.html.erb
-    test/views/error.builder
-    test/views/error.erb
-    test/views/error.erubis
-    test/views/error.haml
-    test/views/error.sass
-    test/views/explicitly_nested.str
-    test/views/foo/hello.test
-    test/views/hello.builder
-    test/views/hello.coffee
-    test/views/hello.erb
-    test/views/hello.erubis
-    test/views/hello.haml
-    test/views/hello.less
-    test/views/hello.liquid
-    test/views/hello.mab
-    test/views/hello.md
-    test/views/hello.nokogiri
-    test/views/hello.radius
-    test/views/hello.rdoc
-    test/views/hello.sass
-    test/views/hello.scss
-    test/views/hello.slim
-    test/views/hello.str
-    test/views/hello.test
-    test/views/hello.textile
-    test/views/layout2.builder
-    test/views/layout2.erb
-    test/views/layout2.erubis
-    test/views/layout2.haml
-    test/views/layout2.liquid
-    test/views/layout2.mab
-    test/views/layout2.nokogiri
-    test/views/layout2.radius
-    test/views/layout2.slim
-    test/views/layout2.str
-    test/views/layout2.test
-    test/views/nested.str
-    test/views/utf8.erb
-  ]
-  # = MANIFEST =
-
-  s.test_files = s.files.select {|path| path =~ /^test\/.*_test.rb/}
-
-  s.extra_rdoc_files = %w[README.rdoc README.de.rdoc README.jp.rdoc README.fr.rdoc README.es.rdoc README.hu.rdoc README.zh.rdoc LICENSE]
-  s.add_dependency 'rack', '~> 1.1'
-  s.add_dependency 'tilt', '>= 1.2.2', '< 2.0'
-  s.add_development_dependency 'shotgun', '~> 0.6'
-
-  s.homepage = "http://sinatra.rubyforge.org"
-  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Sinatra", "--main", "README.rdoc"]
-  s.require_paths = %w[lib]
-  s.rubyforge_project = 'sinatra'
-  s.rubygems_version = '1.1.1'
+  s.add_dependency 'rack',            '~> 1.3', '>= 1.3.6'
+  s.add_dependency 'rack-protection', '~> 1.2'
+  s.add_dependency 'tilt',            '~> 1.3', '>= 1.3.3'
 end
diff --git a/test/base_test.rb b/test/base_test.rb
index 9cb4123..32a0d48 100644
--- a/test/base_test.rb
+++ b/test/base_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 class BaseTest < Test::Unit::TestCase
   def test_default
diff --git a/test/builder_test.rb b/test/builder_test.rb
index 40c777f..2450e81 100644
--- a/test/builder_test.rb
+++ b/test/builder_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
 require 'builder'
diff --git a/test/coffee_test.rb b/test/coffee_test.rb
index c8c8442..b87d23f 100644
--- a/test/coffee_test.rb
+++ b/test/coffee_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
 require 'coffee-script'
@@ -71,7 +71,7 @@ class CoffeeTest < Test::Unit::TestCase
   it "passes coffee options to the coffee engine" do
     coffee_app { coffee "alert 'Aye!'\n", :no_wrap => true }
     assert ok?
-    assert_equal "alert('Aye!');", body
+    assert_body "alert('Aye!');"
   end
 
   it "passes default coffee options to the coffee engine" do
@@ -83,7 +83,7 @@ class CoffeeTest < Test::Unit::TestCase
     end
     get '/'
     assert ok?
-    assert_equal "alert('Aye!');", body
+    assert_body "alert('Aye!');"
   end
 end
 
diff --git a/test/contest.rb b/test/contest.rb
index de71601..ab09d6e 100644
--- a/test/contest.rb
+++ b/test/contest.rb
@@ -1,38 +1,67 @@
+# Copyright (c) 2009 Damian Janowski and Michel Martens for Citrusbyte
+# 
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+# 
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
 require "test/unit"
 
-# Test::Unit loads a default test if the suite is empty, and the only
-# purpose of that test is to fail. As having empty contexts is a common
-# practice, we decided to overwrite TestSuite#empty? in order to
-# allow them. Having a failure when no tests have been defined seems
-# counter-intuitive.
+# Test::Unit loads a default test if the suite is empty, whose purpose is to
+# fail. Since having empty contexts is a common practice, we decided to
+# overwrite TestSuite#empty? in order to allow them. Having a failure when no
+# tests have been defined seems counter-intuitive.
 class Test::Unit::TestSuite
-  unless method_defined?(:empty?)
-    def empty?
-      false
-    end
+  def empty?
+    false
   end
 end
 
-# We added setup, test and context as class methods, and the instance
-# method setup now iterates on the setup blocks. Note that all setup
-# blocks must be defined with the block syntax. Adding a setup instance
-# method defeats the purpose of this library.
+# Contest adds +teardown+, +test+ and +context+ as class methods, and the
+# instance methods +setup+ and +teardown+ now iterate on the corresponding
+# blocks. Note that all setup and teardown blocks must be defined with the
+# block syntax. Adding setup or teardown instance methods defeats the purpose
+# of this library.
 class Test::Unit::TestCase
-  def self.setup(&block)
-    setup_blocks << block
+  def self.setup(&block)     setup_blocks    << block  end
+  def self.teardown(&block)  teardown_blocks << block  end
+  def self.setup_blocks()    @setup_blocks    ||= []   end
+  def self.teardown_blocks() @teardown_blocks ||= []   end
+
+  def setup_blocks(base = self.class)
+    setup_blocks base.superclass if base.superclass.respond_to? :setup_blocks
+    base.setup_blocks.each do |block|
+      instance_eval(&block)
+    end
   end
 
-  def setup
-    self.class.setup_blocks.each do |block|
+  def teardown_blocks(base = self.class)
+    teardown_blocks base.superclass if base.superclass.respond_to? :teardown_blocks
+    base.teardown_blocks.each do |block|
       instance_eval(&block)
     end
   end
 
-  def self.context(name, &block)
-    subclass = Class.new(self.superclass)
-    subclass.setup_blocks.unshift(*setup_blocks)
-    subclass.class_eval(&block)
-    const_set(context_name(name), subclass)
+  alias setup setup_blocks
+  alias teardown teardown_blocks
+
+  def self.context(*name, &block)
+    subclass = Class.new(self)
+    remove_tests(subclass)
+    subclass.class_eval(&block) if block_given?
+    const_set(context_name(name.join(" ")), subclass)
   end
 
   def self.test(name, &block)
@@ -46,12 +75,10 @@ class Test::Unit::TestCase
 
 private
 
-  def self.setup_blocks
-    @setup_blocks ||= []
-  end
-
   def self.context_name(name)
-    "Test#{sanitize_name(name).gsub(/(^| )(\w)/) { $2.upcase }}".to_sym
+    # "Test#{sanitize_name(name).gsub(/(^| )(\w)/) { $2.upcase }}".to_sym
+    name = "Test#{sanitize_name(name).gsub(/(^| )(\w)/) { $2.upcase }}"
+    name.tr(" ", "_").to_sym
   end
 
   def self.test_name(name)
@@ -59,6 +86,13 @@ private
   end
 
   def self.sanitize_name(name)
-    name.gsub(/\W+/, ' ').strip
+    # name.gsub(/\W+/, ' ').strip
+    name.gsub(/\W+/, ' ')
+  end
+
+  def self.remove_tests(subclass)
+    subclass.public_instance_methods.grep(/^test_/).each do |meth|
+      subclass.send(:undef_method, meth.to_sym)
+    end
   end
 end
diff --git a/test/creole_test.rb b/test/creole_test.rb
new file mode 100644
index 0000000..6de3ccb
--- /dev/null
+++ b/test/creole_test.rb
@@ -0,0 +1,65 @@
+require File.expand_path('../helper', __FILE__)
+
+begin
+require 'creole'
+
+class CreoleTest < Test::Unit::TestCase
+  def creole_app(&block)
+    mock_app do
+      set :views, File.dirname(__FILE__) + '/views'
+      get '/', &block
+    end
+    get '/'
+  end
+
+  it 'renders inline creole strings' do
+    creole_app { creole '= Hiya' }
+    assert ok?
+    assert_body "<h1>Hiya</h1>"
+  end
+
+  it 'renders .creole files in views path' do
+    creole_app { creole :hello }
+    assert ok?
+    assert_body "<h1>Hello From Creole</h1>"
+  end
+
+  it "raises error if template not found" do
+    mock_app { get('/') { creole :no_such_template } }
+    assert_raise(Errno::ENOENT) { get('/') }
+  end
+
+  it "renders with inline layouts" do
+    mock_app do
+      layout { 'THIS. IS. #{yield.upcase}!' }
+      get('/') { creole 'Sparta', :layout_engine => :str }
+    end
+    get '/'
+    assert ok?
+    assert_like 'THIS. IS. <P>SPARTA</P>!', body
+  end
+
+  it "renders with file layouts" do
+    creole_app { creole 'Hello World', :layout => :layout2, :layout_engine => :erb }
+    assert ok?
+    assert_body "ERB Layout!\n<p>Hello World</p>"
+  end
+
+  it "can be used in a nested fashion for partials and whatnot" do
+    mock_app do
+      template(:inner) { "hi" }
+      template(:outer) { "<outer><%= creole :inner %></outer>" }
+      get '/' do
+        erb :outer
+      end
+    end
+
+    get '/'
+    assert ok?
+    assert_like '<outer><p>hi</p></outer>', body
+  end
+end
+
+rescue LoadError
+  warn "#{$!.to_s}: skipping creole tests"
+end
diff --git a/test/delegator_test.rb b/test/delegator_test.rb
index e35b738..25eb6e8 100644
--- a/test/delegator_test.rb
+++ b/test/delegator_test.rb
@@ -1,3 +1,5 @@
+require File.expand_path('../helper', __FILE__)
+
 class DelegatorTest < Test::Unit::TestCase
   class Mirror
     attr_reader :last_call
@@ -10,18 +12,18 @@ class DelegatorTest < Test::Unit::TestCase
   def self.delegates(name)
     it "delegates #{name}" do
       m = mirror { send name }
-      assert_equal m.last_call, [name.to_s]
+      assert_equal [name.to_s], m.last_call
     end
 
     it "delegates #{name} with arguments" do
       m = mirror { send name, "foo", "bar" }
-      assert_equal m.last_call, [name.to_s, "foo", "bar"]
+      assert_equal [name.to_s, "foo", "bar"], m.last_call
     end
 
     it "delegates #{name} with block" do
       block = proc { }
       m = mirror { send(name, &block) }
-      assert_equal m.last_call, [name.to_s, block]
+      assert_equal [name.to_s, block], m.last_call
     end
   end
 
@@ -55,10 +57,10 @@ class DelegatorTest < Test::Unit::TestCase
   end
 
   it 'defaults to Sinatra::Application as target' do
-    assert_equal Sinatra::Delegator.target, Sinatra::Application
+    assert_equal Sinatra::Application, Sinatra::Delegator.target
   end
 
-  %w[get put post delete options].each do |verb|
+  %w[get put post delete options patch].each do |verb|
     it "delegates #{verb} correctly" do
       delegation_app do
         send verb, '/hello' do
@@ -91,13 +93,19 @@ class DelegatorTest < Test::Unit::TestCase
   it "registers extensions with the delegation target" do
     app, mixin = mirror, Module.new
     Sinatra.register mixin
-    assert_equal app.last_call, ["register", mixin.to_s ]
+    assert_equal ["register", mixin.to_s], app.last_call
   end
 
   it "registers helpers with the delegation target" do
     app, mixin = mirror, Module.new
     Sinatra.helpers mixin
-    assert_equal app.last_call, ["helpers", mixin.to_s ]
+    assert_equal ["helpers", mixin.to_s], app.last_call
+  end
+
+  it "registers helpers with the delegation target" do
+    app, mixin = mirror, Module.new
+    Sinatra.use mixin
+    assert_equal ["use", mixin.to_s], app.last_call
   end
 
   it "should work with method_missing proxies for options" do
@@ -128,6 +136,7 @@ class DelegatorTest < Test::Unit::TestCase
   end
 
   delegates 'get'
+  delegates 'patch'
   delegates 'put'
   delegates 'post'
   delegates 'delete'
diff --git a/test/encoding_test.rb b/test/encoding_test.rb
index 70e3bbf..a2e9f69 100644
--- a/test/encoding_test.rb
+++ b/test/encoding_test.rb
@@ -1,5 +1,5 @@
 # encoding: UTF-8
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 require 'erb'
 
 class BaseTest < Test::Unit::TestCase
diff --git a/test/erb_test.rb b/test/erb_test.rb
index cc68c5c..c0b5cba 100644
--- a/test/erb_test.rb
+++ b/test/erb_test.rb
@@ -1,6 +1,15 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 class ERBTest < Test::Unit::TestCase
+  def engine
+    Tilt::ERBTemplate
+  end
+
+  def setup
+    Tilt.prefer engine, :erb
+    super
+  end
+
   def erb_app(&block)
     mock_app {
       set :views, File.dirname(__FILE__) + '/views'
@@ -9,6 +18,10 @@ class ERBTest < Test::Unit::TestCase
     get '/'
   end
 
+  it 'uses the correct engine' do
+    assert_equal engine, Tilt[:erb]
+  end
+
   it 'renders inline ERB strings' do
     erb_app { erb '<%= 1 + 1 %>' }
     assert ok?
@@ -45,7 +58,7 @@ class ERBTest < Test::Unit::TestCase
       erb 'Hello World', :layout => :layout2
     }
     assert ok?
-    assert_equal "ERB Layout!\nHello World\n", body
+    assert_body "ERB Layout!\nHello World"
   end
 
   it "renders erb with blocks" do
@@ -79,3 +92,13 @@ class ERBTest < Test::Unit::TestCase
     assert_equal '<outer><inner>hi</inner></outer>', body
   end
 end
+
+
+begin
+  require 'erubis'
+  class ErubisTest < ERBTest
+    def engine; Tilt::ErubisTemplate end
+  end
+rescue LoadError
+  warn "#{$!.to_s}: skipping erubis tests"
+end
diff --git a/test/erubis_test.rb b/test/erubis_test.rb
deleted file mode 100644
index 6e2323c..0000000
--- a/test/erubis_test.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-require File.dirname(__FILE__) + '/helper'
-
-begin
-require 'erubis'
-
-class ERubisTest < Test::Unit::TestCase
-  def erubis_app(&block)
-    mock_app {
-      set :views, File.dirname(__FILE__) + '/views'
-      get '/', &block
-    }
-    get '/'
-  end
-
-  it 'renders inline ERubis strings' do
-    erubis_app { erubis '<%= 1 + 1 %>' }
-    assert ok?
-    assert_equal '2', body
-  end
-
-  it 'renders .erubis files in views path' do
-    erubis_app { erubis :hello }
-    assert ok?
-    assert_equal "Hello World\n", body
-  end
-
-  it 'takes a :locals option' do
-    erubis_app {
-      locals = {:foo => 'Bar'}
-      erubis '<%= foo %>', :locals => locals
-    }
-    assert ok?
-    assert_equal 'Bar', body
-  end
-
-  it "renders with inline layouts" do
-    mock_app {
-      layout { 'THIS. IS. <%= yield.upcase %>!' }
-      get('/') { erubis 'Sparta' }
-    }
-    get '/'
-    assert ok?
-    assert_equal 'THIS. IS. SPARTA!', body
-  end
-
-  it "renders with file layouts" do
-    erubis_app {
-      erubis 'Hello World', :layout => :layout2
-    }
-    assert ok?
-    assert_equal "ERubis Layout!\nHello World\n", body
-  end
-
-  it "renders erubis with blocks" do
-    mock_app {
-      def container
-        @_out_buf << "THIS."
-        yield
-        @_out_buf << "SPARTA!"
-      end
-      def is; "IS." end
-      get '/' do
-        erubis '<% container do %> <%= is %> <% end %>'
-      end
-    }
-    get '/'
-    assert ok?
-    assert_equal 'THIS. IS. SPARTA!', body
-  end
-
-  it "can be used in a nested fashion for partials and whatnot" do
-    mock_app {
-      template(:inner) { "<inner><%= 'hi' %></inner>" }
-      template(:outer) { "<outer><%= erubis :inner %></outer>" }
-      get '/' do
-        erubis :outer
-      end
-    }
-
-    get '/'
-    assert ok?
-    assert_equal '<outer><inner>hi</inner></outer>', body
-  end
-end
-
-rescue LoadError
-  warn "#{$!.to_s}: skipping erubis tests"
-end
diff --git a/test/extensions_test.rb b/test/extensions_test.rb
index 8e4a543..739c33e 100644
--- a/test/extensions_test.rb
+++ b/test/extensions_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 class ExtensionsTest < Test::Unit::TestCase
   module FooExtensions
diff --git a/test/filter_test.rb b/test/filter_test.rb
index ef7e9ae..fd9958d 100644
--- a/test/filter_test.rb
+++ b/test/filter_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 class BeforeFilterTest < Test::Unit::TestCase
   it "executes filters in the order defined" do
@@ -97,6 +97,17 @@ class BeforeFilterTest < Test::Unit::TestCase
     assert_equal 'cool', body
   end
 
+  it "properly unescapes parameters" do
+    mock_app {
+      before { @foo = params['foo'] }
+      get('/foo') { @foo }
+    }
+
+    get '/foo?foo=bar%3Abaz%2Fbend'
+    assert ok?
+    assert_equal 'bar:baz/bend', body
+  end
+
   it "runs filters defined in superclasses" do
     base = Class.new(Sinatra::Base)
     base.before { @foo = 'hello from superclass' }
@@ -114,7 +125,7 @@ class BeforeFilterTest < Test::Unit::TestCase
     mock_app {
       before { ran_filter = true }
       set :static, true
-      set :public, File.dirname(__FILE__)
+      set :public_folder, File.dirname(__FILE__)
     }
     get "/#{File.basename(__FILE__)}"
     assert ok?
@@ -221,9 +232,12 @@ class AfterFilterTest < Test::Unit::TestCase
     count = 2
     base = Class.new(Sinatra::Base)
     base.after { count *= 2 }
-    mock_app(base) {
-      get('/foo') { count += 2 }
-    }
+    mock_app(base) do
+      get('/foo') do
+        count += 2
+        "ok"
+      end
+    end
 
     get '/foo'
     assert_equal 8, count
@@ -234,7 +248,7 @@ class AfterFilterTest < Test::Unit::TestCase
     mock_app {
       after { ran_filter = true }
       set :static, true
-      set :public, File.dirname(__FILE__)
+      set :public_folder, File.dirname(__FILE__)
     }
     get "/#{File.basename(__FILE__)}"
     assert ok?
@@ -366,6 +380,26 @@ class AfterFilterTest < Test::Unit::TestCase
     assert ran
   end
 
+  it 'can add params' do
+    mock_app do
+      before { params['foo'] = 'bar' }
+      get('/') { params['foo'] }
+    end
+
+    get '/'
+    assert_body 'bar'
+  end
+
+  it 'can remove params' do
+    mock_app do
+      before { params.delete('foo') }
+      get('/') { params['foo'].to_s }
+    end
+
+    get '/?foo=bar'
+    assert_body ''
+  end
+
   it 'is possible to apply user_agent conditions to after filters with no path' do
     ran = false
     mock_app do
diff --git a/test/haml_test.rb b/test/haml_test.rb
index 7404251..e2779d2 100644
--- a/test/haml_test.rb
+++ b/test/haml_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
 require 'haml'
diff --git a/test/hello.mab b/test/hello.mab
deleted file mode 100644
index 6e0c9cb..0000000
--- a/test/hello.mab
+++ /dev/null
@@ -1 +0,0 @@
-h1 "Hello From Markaby"
diff --git a/test/helper.rb b/test/helper.rb
index 99ec824..dc8fd40 100644
--- a/test/helper.rb
+++ b/test/helper.rb
@@ -38,6 +38,13 @@ class Test::Unit::TestCase
 
   class << self
     alias_method :it, :test
+    alias_method :section, :context
+  end
+
+  def self.example(desc = nil, &block)
+    @example_count = 0 unless instance_variable_defined? :@example_count
+    @example_count += 1
+    it(desc || "Example #{@example_count}", &block)
   end
 
   alias_method :response, :last_response
@@ -62,13 +69,34 @@ class Test::Unit::TestCase
   end
 
   def assert_body(value)
-    assert_equal value.lstrip.gsub(/\s*\n\s*/, ""), body.lstrip.gsub(/\s*\n\s*/, "")
+    if value.respond_to? :to_str
+      assert_equal value.lstrip.gsub(/\s*\n\s*/, ""), body.lstrip.gsub(/\s*\n\s*/, "")
+    else
+      assert_match value, body
+    end
+  end
+
+  def assert_status(expected)
+    assert_equal Integer(expected), Integer(status)
+  end
+
+  def assert_like(a,b)
+    pattern = /id=['"][^"']*["']|\s+/
+    assert_equal a.strip.gsub(pattern, ""), b.strip.gsub(pattern, "")
   end
 
   def assert_include(str, substr)
     assert str.include?(substr), "expected #{str.inspect} to include #{substr.inspect}"
   end
 
+  def options(uri, params = {}, env = {}, &block)
+    request(uri, env.merge(:method => "OPTIONS", :params => params), &block)
+  end
+
+  def patch(uri, params = {}, env = {}, &block)
+    request(uri, env.merge(:method => "PATCH", :params => params), &block)
+  end
+
   # Delegate other missing methods to response.
   def method_missing(name, *args, &block)
     if response && response.respond_to?(name)
@@ -76,6 +104,8 @@ class Test::Unit::TestCase
     else
       super
     end
+  rescue Rack::Test::Error
+    super
   end
 
   # Also check response since we delegate there.
diff --git a/test/helpers_test.rb b/test/helpers_test.rb
index bb4dbae..42d1f9a 100644
--- a/test/helpers_test.rb
+++ b/test/helpers_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 require 'date'
 
 class HelpersTest < Test::Unit::TestCase
@@ -6,22 +6,117 @@ class HelpersTest < Test::Unit::TestCase
     assert true
   end
 
-  describe 'status' do
-    setup do
-      mock_app {
-        get '/' do
-          status 207
-          nil
-        end
-      }
+  def status_app(code, &block)
+    code += 2 if [204, 205, 304].include? code
+    block ||= proc { }
+    mock_app do
+      get '/' do
+        status code
+        instance_eval(&block).inspect
+      end
     end
+    get '/'
+  end
 
+  describe 'status' do
     it 'sets the response status code' do
-      get '/'
+      status_app 207
       assert_equal 207, response.status
     end
   end
 
+  describe 'not_found?' do
+    it 'is true for status == 404' do
+      status_app(404) { not_found? }
+      assert_body 'true'
+    end
+
+    it 'is false for status > 404' do
+      status_app(405) { not_found? }
+      assert_body 'false'
+    end
+
+    it 'is false for status < 404' do
+      status_app(403) { not_found? }
+      assert_body 'false'
+    end
+  end
+
+  describe 'informational?' do
+    it 'is true for 1xx status' do
+      status_app(100 + rand(100)) { informational? }
+      assert_body 'true'
+    end
+
+    it 'is false for status > 199' do
+      status_app(200 + rand(400)) { informational? }
+      assert_body 'false'
+    end
+  end
+
+  describe 'success?' do
+    it 'is true for 2xx status' do
+      status_app(200 + rand(100)) { success? }
+      assert_body 'true'
+    end
+
+    it 'is false for status < 200' do
+      status_app(100 + rand(100)) { success? }
+      assert_body 'false'
+    end
+
+    it 'is false for status > 299' do
+      status_app(300 + rand(300)) { success? }
+      assert_body 'false'
+    end
+  end
+
+  describe 'redirect?' do
+    it 'is true for 3xx status' do
+      status_app(300 + rand(100)) { redirect? }
+      assert_body 'true'
+    end
+
+    it 'is false for status < 300' do
+      status_app(200 + rand(100)) { redirect? }
+      assert_body 'false'
+    end
+
+    it 'is false for status > 399' do
+      status_app(400 + rand(200)) { redirect? }
+      assert_body 'false'
+    end
+  end
+
+  describe 'client_error?' do
+    it 'is true for 4xx status' do
+      status_app(400 + rand(100)) { client_error? }
+      assert_body 'true'
+    end
+
+    it 'is false for status < 400' do
+      status_app(200 + rand(200)) { client_error? }
+      assert_body 'false'
+    end
+
+    it 'is false for status > 499' do
+      status_app(500 + rand(100)) { client_error? }
+      assert_body 'false'
+    end
+  end
+
+  describe 'server_error?' do
+    it 'is true for 5xx status' do
+      status_app(500 + rand(100)) { server_error? }
+      assert_body 'true'
+    end
+
+    it 'is false for status < 500' do
+      status_app(200 + rand(300)) { server_error? }
+      assert_body 'false'
+    end
+  end
+
   describe 'body' do
     it 'takes a block for defered body generation' do
       mock_app {
@@ -112,6 +207,22 @@ class HelpersTest < Test::Unit::TestCase
       assert_equal 'http://example.org:444/foo', response['Location']
     end
 
+    it 'uses 303 for post requests if request is HTTP 1.1' do
+      mock_app { post('/') { redirect '/'} }
+      post '/', {}, 'HTTP_VERSION' => 'HTTP/1.1'
+      assert_equal 303, status
+      assert_equal '', body
+      assert_equal 'http://example.org/', response['Location']
+    end
+
+    it 'uses 302 for post requests if request is HTTP 1.0' do
+      mock_app { post('/') { redirect '/'} }
+      post '/', {}, 'HTTP_VERSION' => 'HTTP/1.0'
+      assert_equal 302, status
+      assert_equal '', body
+      assert_equal 'http://example.org/', response['Location']
+    end
+
     it 'works behind a reverse proxy' do
       mock_app do
         get '/' do
@@ -269,7 +380,7 @@ class HelpersTest < Test::Unit::TestCase
         enable :sessions
 
         get '/' do
-          assert session.empty?
+          assert session[:foo].nil?
           session[:foo] = 'bar'
           redirect '/hi'
         end
@@ -312,6 +423,33 @@ class HelpersTest < Test::Unit::TestCase
       get '/'
       assert_body 'ok'
     end
+
+    it 'allows disabling session secret' do
+      mock_app do
+        enable :sessions
+        disable :session_secret
+        get '/' do
+          assert !env['rack.session.options'].include?(:session_secret)
+          'ok'
+        end
+      end
+
+      get '/'
+      assert_body 'ok'
+    end
+
+    it 'accepts an options hash' do
+      mock_app do
+        set :sessions, :foo => :bar
+        get '/' do
+          assert_equal env['rack.session.options'][:foo], :bar
+          'ok'
+        end
+      end
+
+      get '/'
+      assert_body 'ok'
+    end
   end
 
   describe 'mime_type' do
@@ -419,6 +557,7 @@ class HelpersTest < Test::Unit::TestCase
           assert_equal content_type(:xml),    'application/xml;charset=utf-8'
           assert_equal content_type(:xhtml),  'application/xhtml+xml;charset=utf-8'
           assert_equal content_type(:js),     'application/javascript;charset=utf-8'
+          assert_equal content_type(:json),   'application/json;charset=utf-8'
           assert_equal content_type(:bar),    'application/bar'
           assert_equal content_type(:png),    'image/png'
           assert_equal content_type(:baz),    'application/baz;charset=utf-8'
@@ -453,6 +592,45 @@ class HelpersTest < Test::Unit::TestCase
     end
   end
 
+  describe 'attachment' do
+    def attachment_app(filename=nil)
+      mock_app {       
+        get '/attachment' do
+          attachment filename
+          response.write("<sinatra></sinatra>")
+        end
+      }
+    end
+    
+    it 'sets the Content-Type response header' do
+      attachment_app('test.xml')
+      get '/attachment'
+      assert_equal 'application/xml;charset=utf-8', response['Content-Type']
+      assert_equal '<sinatra></sinatra>', body
+    end 
+    
+    it 'sets the Content-Type response header without extname' do
+      attachment_app('test')
+      get '/attachment'
+      assert_equal 'text/html;charset=utf-8', response['Content-Type']
+      assert_equal '<sinatra></sinatra>', body   
+    end
+    
+    it 'sets the Content-Type response header without extname' do
+      mock_app do
+        get '/attachment' do
+          content_type :atom
+          attachment 'test.xml'
+          response.write("<sinatra></sinatra>")
+        end
+      end
+      get '/attachment'
+      assert_equal 'application/atom+xml', response['Content-Type']
+      assert_equal '<sinatra></sinatra>', body   
+    end
+    
+  end
+
   describe 'send_file' do
     setup do
       @file = File.dirname(__FILE__) + '/file.txt'
@@ -633,6 +811,10 @@ class HelpersTest < Test::Unit::TestCase
           expires obj, :public, :no_cache
           'Hello World'
         end
+
+        get '/boom' do
+          expires '9999'
+        end
       end
     end
 
@@ -660,6 +842,10 @@ class HelpersTest < Test::Unit::TestCase
       get '/blah'
       assert_equal ['public', 'no-cache', 'max-age=60'], response['Cache-Control'].split(', ')
     end
+
+    it 'fails when Time.parse raises an ArgumentError' do
+      assert_raise(ArgumentError) { get '/boom' }
+    end
   end
 
   describe 'last_modified' do
@@ -672,6 +858,20 @@ class HelpersTest < Test::Unit::TestCase
       assert ! response['Last-Modified']
     end
 
+    it 'does not change a status other than 200' do
+      mock_app do
+        get '/' do
+          status 299
+          last_modified Time.at(0)
+          'ok'
+        end
+      end
+
+      get('/', {}, 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 26 Sep 2030 23:43:52 GMT')
+      assert_status 299
+      assert_body 'ok'
+    end
+
     [Time.now, DateTime.now, Date.today, Time.now.to_i,
       Struct.new(:to_time).new(Time.now) ].each do |last_modified_time|
       describe "with #{last_modified_time.class.name}" do
@@ -683,7 +883,7 @@ class HelpersTest < Test::Unit::TestCase
             end
           end
           wrapper = Object.new.extend Sinatra::Helpers
-          @last_modified_time = wrapper.send :time_for, last_modified_time
+          @last_modified_time = wrapper.time_for last_modified_time
         end
 
         # fixes strange missing test error when running complete test suite.
@@ -738,7 +938,7 @@ class HelpersTest < Test::Unit::TestCase
             get '/compare', {}, { 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 26 Sep 2010 23:43:52 GMT' }
             assert_equal 200, status
             assert_equal 'foo', body
-            get '/compare', {}, { 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 26 Sep 2100 23:43:52 GMT' }
+            get '/compare', {}, { 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 26 Sep 2030 23:43:52 GMT' }
             assert_equal 304, status
             assert_equal '', body
           end
@@ -769,54 +969,654 @@ class HelpersTest < Test::Unit::TestCase
             assert_equal '', body
           end
         end
+
+        context "If-Unmodified-Since" do
+          it 'results in 200 if resource has not been modified' do
+            get '/', {}, { 'HTTP_IF_UNMODIFIED_SINCE' => 'Sun, 26 Sep 2030 23:43:52 GMT' }
+            assert_equal 200, status
+            assert_equal 'Boo!', body
+          end
+
+          it 'results in 412 if resource has been modified' do
+            get '/', {}, { 'HTTP_IF_UNMODIFIED_SINCE' => Time.at(0).httpdate }
+            assert_equal 412, status
+            assert_equal '', body
+          end
+        end
       end
     end
   end
 
   describe 'etag' do
-    setup do
-      mock_app {
-        get '/' do
-          body { 'Hello World' }
-          etag 'FOO'
-          'Boo!'
+    context "safe requests" do
+      it 'returns 200 for normal requests' do
+        mock_app do
+          get '/' do
+            etag 'foo'
+            'ok'
+          end
         end
-      }
-    end
 
-    it 'sets the ETag header' do
-      get '/'
-      assert_equal '"FOO"', response['ETag']
-    end
+        get('/')
+        assert_status 200
+        assert_body 'ok'
+      end
 
-    it 'returns a body when conditional get misses' do
-      get '/'
-      assert_equal 200, status
-      assert_equal 'Boo!', body
+      context "If-None-Match" do
+        it 'returns 304 when If-None-Match is *' do
+          mock_app do
+            get '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
+          assert_status 304
+          assert_body ''
+        end
+
+        it 'returns 200 when If-None-Match is * for new resources' do
+          mock_app do
+            get '/' do
+              etag 'foo', :new_resource => true
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 304 when If-None-Match is * for existing resources' do
+          mock_app do
+            get '/' do
+              etag 'foo', :new_resource => false
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
+          assert_status 304
+          assert_body ''
+        end
+
+        it 'returns 304 when If-None-Match is the etag' do
+          mock_app do
+            get '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
+          assert_status 304
+          assert_body ''
+        end
+
+        it 'returns 304 when If-None-Match includes the etag' do
+          mock_app do
+            get '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar", "foo"')
+          assert_status 304
+          assert_body ''
+        end
+
+        it 'returns 200 when If-None-Match does not include the etag' do
+          mock_app do
+            get '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'ignores If-Modified-Since if If-None-Match does not match' do
+          mock_app do
+            get '/' do
+              etag 'foo'
+              last_modified Time.at(0)
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'does not change a status code other than 2xx or 304' do
+          mock_app do
+            get '/' do
+              status 499
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
+          assert_status 499
+          assert_body 'ok'
+        end
+
+        it 'does change 2xx status codes' do
+          mock_app do
+            get '/' do
+              status 299
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
+          assert_status 304
+          assert_body ''
+        end
+
+        it 'does not send a body on 304 status codes' do
+          mock_app do
+            get '/' do
+              status 304
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
+          assert_status 304
+          assert_body ''
+        end
+      end
+
+      context "If-Match" do
+        it 'returns 200 when If-Match is the etag' do
+          mock_app do
+            get '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_MATCH' => '"foo"')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 200 when If-Match includes the etag' do
+          mock_app do
+            get '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_MATCH' => '"foo", "bar"')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 200 when If-Match is *' do
+          mock_app do
+            get '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_MATCH' => '*')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 412 when If-Match is * for new resources' do
+          mock_app do
+            get '/' do
+              etag 'foo', :new_resource => true
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_MATCH' => '*')
+          assert_status 412
+          assert_body ''
+        end
+
+        it 'returns 200 when If-Match is * for existing resources' do
+          mock_app do
+            get '/' do
+              etag 'foo', :new_resource => false
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_MATCH' => '*')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 412 when If-Match does not include the etag' do
+          mock_app do
+            get '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          get('/', {}, 'HTTP_IF_MATCH' => '"bar"')
+          assert_status 412
+          assert_body ''
+        end
+      end
     end
 
-    it 'halts when a conditional GET matches' do
-      get '/', {}, { 'HTTP_IF_NONE_MATCH' => '"FOO"' }
-      assert_equal 304, status
-      assert_equal '', body
+    context "idempotent requests" do
+      it 'returns 200 for normal requests' do
+        mock_app do
+          put '/' do
+            etag 'foo'
+            'ok'
+          end
+        end
+
+        put('/')
+        assert_status 200
+        assert_body 'ok'
+      end
+
+      context "If-None-Match" do
+        it 'returns 412 when If-None-Match is *' do
+          mock_app do
+            put '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
+          assert_status 412
+          assert_body ''
+        end
+
+        it 'returns 200 when If-None-Match is * for new resources' do
+          mock_app do
+            put '/' do
+              etag 'foo', :new_resource => true
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 412 when If-None-Match is * for existing resources' do
+          mock_app do
+            put '/' do
+              etag 'foo', :new_resource => false
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
+          assert_status 412
+          assert_body ''
+        end
+
+        it 'returns 412 when If-None-Match is the etag' do
+          mock_app do
+            put '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
+          assert_status 412
+          assert_body ''
+        end
+
+        it 'returns 412 when If-None-Match includes the etag' do
+          mock_app do
+            put '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar", "foo"')
+          assert_status 412
+          assert_body ''
+        end
+
+        it 'returns 200 when If-None-Match does not include the etag' do
+          mock_app do
+            put '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'ignores If-Modified-Since if If-None-Match does not match' do
+          mock_app do
+            put '/' do
+              etag 'foo'
+              last_modified Time.at(0)
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
+          assert_status 200
+          assert_body 'ok'
+        end
+      end
+
+      context "If-Match" do
+        it 'returns 200 when If-Match is the etag' do
+          mock_app do
+            put '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_MATCH' => '"foo"')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 200 when If-Match includes the etag' do
+          mock_app do
+            put '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_MATCH' => '"foo", "bar"')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 200 when If-Match is *' do
+          mock_app do
+            put '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_MATCH' => '*')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 412 when If-Match is * for new resources' do
+          mock_app do
+            put '/' do
+              etag 'foo', :new_resource => true
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_MATCH' => '*')
+          assert_status 412
+          assert_body ''
+        end
+
+        it 'returns 200 when If-Match is * for existing resources' do
+          mock_app do
+            put '/' do
+              etag 'foo', :new_resource => false
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_MATCH' => '*')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 412 when If-Match does not include the etag' do
+          mock_app do
+            put '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          put('/', {}, 'HTTP_IF_MATCH' => '"bar"')
+          assert_status 412
+          assert_body ''
+        end
+      end
     end
 
-    it 'should handle multiple ETag values in If-None-Match header' do
-      get '/', {}, { 'HTTP_IF_NONE_MATCH' => '"BAR", *' }
-      assert_equal 304, status
-      assert_equal '', body
+    context "post requests" do
+      it 'returns 200 for normal requests' do
+        mock_app do
+          post '/' do
+            etag 'foo'
+            'ok'
+          end
+        end
+
+        post('/')
+        assert_status 200
+        assert_body 'ok'
+      end
+
+      context "If-None-Match" do
+        it 'returns 200 when If-None-Match is *' do
+          mock_app do
+            post '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 200 when If-None-Match is * for new resources' do
+          mock_app do
+            post '/' do
+              etag 'foo', :new_resource => true
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 412 when If-None-Match is * for existing resources' do
+          mock_app do
+            post '/' do
+              etag 'foo', :new_resource => false
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
+          assert_status 412
+          assert_body ''
+        end
+
+        it 'returns 412 when If-None-Match is the etag' do
+          mock_app do
+            post '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
+          assert_status 412
+          assert_body ''
+        end
+
+        it 'returns 412 when If-None-Match includes the etag' do
+          mock_app do
+            post '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar", "foo"')
+          assert_status 412
+          assert_body ''
+        end
+
+        it 'returns 200 when If-None-Match does not include the etag' do
+          mock_app do
+            post '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'ignores If-Modified-Since if If-None-Match does not match' do
+          mock_app do
+            post '/' do
+              etag 'foo'
+              last_modified Time.at(0)
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
+          assert_status 200
+          assert_body 'ok'
+        end
+      end
+
+      context "If-Match" do
+        it 'returns 200 when If-Match is the etag' do
+          mock_app do
+            post '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_MATCH' => '"foo"')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 200 when If-Match includes the etag' do
+          mock_app do
+            post '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_MATCH' => '"foo", "bar"')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 412 when If-Match is *' do
+          mock_app do
+            post '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_MATCH' => '*')
+          assert_status 412
+          assert_body ''
+        end
+
+        it 'returns 412 when If-Match is * for new resources' do
+          mock_app do
+            post '/' do
+              etag 'foo', :new_resource => true
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_MATCH' => '*')
+          assert_status 412
+          assert_body ''
+        end
+
+        it 'returns 200 when If-Match is * for existing resources' do
+          mock_app do
+            post '/' do
+              etag 'foo', :new_resource => false
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_MATCH' => '*')
+          assert_status 200
+          assert_body 'ok'
+        end
+
+        it 'returns 412 when If-Match does not include the etag' do
+          mock_app do
+            post '/' do
+              etag 'foo'
+              'ok'
+            end
+          end
+
+          post('/', {}, 'HTTP_IF_MATCH' => '"bar"')
+          assert_status 412
+          assert_body ''
+        end
+      end
     end
 
     it 'uses a weak etag with the :weak option' do
-      mock_app {
+      mock_app do
         get '/' do
           etag 'FOO', :weak
           "that's weak, dude."
         end
-      }
+      end
       get '/'
       assert_equal 'W/"FOO"', response['ETag']
     end
+
+    it 'raises an ArgumentError for an invalid strength' do
+      mock_app do
+        get '/' do
+          etag 'FOO', :w00t
+          "that's weak, dude."
+        end
+      end
+      assert_raise(ArgumentError) { get '/' }
+    end
   end
 
   describe 'back' do
@@ -883,6 +1683,46 @@ class HelpersTest < Test::Unit::TestCase
     end
   end
 
+  describe 'logger' do
+    it 'logging works when logging is enabled' do
+      mock_app do
+        enable :logging
+        get '/' do
+          logger.info "Program started"
+          logger.warn "Nothing to do!"
+        end
+      end
+      io = StringIO.new
+      get '/', {}, 'rack.errors' => io
+      assert io.string.include?("INFO -- : Program started")
+      assert io.string.include?("WARN -- : Nothing to do")
+    end
+
+    it 'logging works when logging is disable, but no output is produced' do
+      mock_app do
+        disable :logging
+        get '/' do
+          logger.info "Program started"
+          logger.warn "Nothing to do!"
+        end
+      end
+      io = StringIO.new
+      get '/', {}, 'rack.errors' => io
+      assert !io.string.include?("INFO -- : Program started")
+      assert !io.string.include?("WARN -- : Nothing to do")
+    end
+
+    it 'does not create a logger when logging is set to nil' do
+      mock_app do
+        set :logging, nil
+        get('/') { logger.inspect }
+      end
+
+      get '/'
+      assert_body 'nil'
+    end
+  end
+
   module ::HelperOne; def one; '1'; end; end
   module ::HelperTwo; def two; '2'; end; end
 
diff --git a/test/integration/app.rb b/test/integration/app.rb
new file mode 100644
index 0000000..b13d6e9
--- /dev/null
+++ b/test/integration/app.rb
@@ -0,0 +1,6 @@
+require 'sinatra'
+
+get '/app_file' do
+  content_type :txt
+  settings.app_file
+end
\ No newline at end of file
diff --git a/test/integration_test.rb b/test/integration_test.rb
new file mode 100644
index 0000000..7d92d4a
--- /dev/null
+++ b/test/integration_test.rb
@@ -0,0 +1,65 @@
+require File.expand_path('../helper', __FILE__)
+require 'rbconfig'
+require 'open-uri'
+require 'timeout'
+
+class IntegrationTest < Test::Unit::TestCase
+  def app_file
+    File.expand_path('../integration/app.rb', __FILE__)
+  end
+
+  def port
+    5000 + (Process.pid % 1000)
+  end
+
+  def command
+    cmd = ['exec']
+    if RbConfig.respond_to? :ruby
+      cmd << RbConfig.ruby.inspect
+    else
+      file, dir = RbConfig::CONFIG.values_at('ruby_install_name', 'bindir')
+      cmd << File.expand_path(file, dir).inspect
+    end
+    cmd << "-I" << File.expand_path('../../lib', __FILE__).inspect
+    cmd << app_file.inspect << '-p' << port << '2>&1'
+    cmd.join(" ")
+  end
+
+  def display_output(pipe)
+    out = ""
+    loop { out <<  pipe.read_nonblock(1) }
+  rescue
+    $stderr.puts command, out unless out.empty?
+  end
+
+  def kill(pid, signal = "TERM")
+    Process.kill(signal, pid)
+  rescue NotImplementedError
+    system "kill -s #{signal} #{pid}"
+  end
+
+  def with_server
+    pipe = IO.popen(command)
+    error = nil
+    Timeout.timeout(120) do
+      begin
+        yield
+      rescue Errno::ECONNREFUSED => e
+        error = e
+        sleep 0.1
+        retry
+      end
+    end
+    kill(pipe.pid) if pipe
+  rescue Timeout::Error => e
+    display_output pipe
+    kill(pipe.pid, "KILL") if pipe
+    raise error || e
+  end
+
+  it 'starts a top level application' do
+    with_server do
+      assert_equal open("http://127.0.0.1:#{port}/app_file").read, app_file
+    end
+  end
+end
diff --git a/test/less_test.rb b/test/less_test.rb
index b761f4f..979f201 100644
--- a/test/less_test.rb
+++ b/test/less_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
 require 'less'
@@ -16,7 +16,7 @@ class LessTest < Test::Unit::TestCase
   it 'renders inline Less strings' do
     less_app { less "@white_color: #fff; #main { background-color: @white_color }" }
     assert ok?
-    assert_equal "#main { background-color: #ffffff; }\n", body
+    assert_equal "#main{background-color:#ffffff;}", body.gsub(/\s/, "")
   end
 
   it 'defaults content type to css' do
@@ -45,13 +45,13 @@ class LessTest < Test::Unit::TestCase
   it 'renders .less files in views path' do
     less_app { less :hello }
     assert ok?
-    assert_equal "#main { background-color: #ffffff; }\n", body
+    assert_equal "#main{background-color:#ffffff;}", body.gsub(/\s/, "")
   end
 
   it 'ignores the layout option' do
     less_app { less :hello, :layout => :layout2 }
     assert ok?
-    assert_equal "#main { background-color: #ffffff; }\n", body
+    assert_equal "#main{background-color:#ffffff;}", body.gsub(/\s/, "")
   end
 
   it "raises error if template not found" do
diff --git a/test/liquid_test.rb b/test/liquid_test.rb
index 9c226c7..ef398af 100644
--- a/test/liquid_test.rb
+++ b/test/liquid_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
 require 'liquid'
diff --git a/test/mapped_error_test.rb b/test/mapped_error_test.rb
index d613496..0a16af7 100644
--- a/test/mapped_error_test.rb
+++ b/test/mapped_error_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 class FooError < RuntimeError
 end
@@ -6,6 +6,10 @@ end
 class FooNotFound < Sinatra::NotFound
 end
 
+class FooSpecialError < RuntimeError
+  def code; 501 end
+end
+
 class MappedErrorTest < Test::Unit::TestCase
   def test_default
     assert true
@@ -25,6 +29,15 @@ class MappedErrorTest < Test::Unit::TestCase
       assert_equal 'Foo!', body
     end
 
+    it 'passes the exception object to the error handler' do
+      mock_app do
+        set :raise_errors, false
+        error(FooError) { |e| assert_equal(FooError, e.class) }
+        get('/') { raise FooError }
+      end
+      get('/')
+    end
+
     it 'uses the Exception handler if no matching handler found' do
       mock_app {
         set :raise_errors, false
@@ -39,6 +52,36 @@ class MappedErrorTest < Test::Unit::TestCase
       assert_equal 'Exception!', body
     end
 
+    it 'walks down inheritance chain for errors' do
+      mock_app {
+        set :raise_errors, false
+        error(RuntimeError) { 'Exception!' }
+        get '/' do
+          raise FooError
+        end
+      }
+
+      get '/'
+      assert_equal 500, status
+      assert_equal 'Exception!', body
+    end
+
+    it 'favors subclass handler over superclass handler if available' do
+      mock_app {
+        set :raise_errors, false
+        error(Exception) { 'Exception!' }
+        error(FooError) { 'FooError!' }
+        error(RuntimeError) { 'Exception!' }
+        get '/' do
+          raise FooError
+        end
+      }
+
+      get '/'
+      assert_equal 500, status
+      assert_equal 'FooError!', body
+    end
+
     it "sets env['sinatra.error'] to the rescued exception" do
       mock_app {
         set :raise_errors, false
@@ -78,12 +121,7 @@ class MappedErrorTest < Test::Unit::TestCase
     end
 
     it "never raises Sinatra::NotFound beyond the application" do
-      mock_app {
-        set :raise_errors, true
-        get '/' do
-          raise Sinatra::NotFound
-        end
-      }
+      mock_app(Sinatra::Application) { get('/') { raise Sinatra::NotFound }}
       assert_nothing_raised { get '/' }
       assert_equal 404, status
     end
@@ -143,6 +181,17 @@ class MappedErrorTest < Test::Unit::TestCase
       get '/'
       assert_equal 'subclass', body
     end
+
+    it 'honors Exception#code if present' do
+      mock_app do
+        set :raise_errors, false
+        error(501) { 'Foo!' }
+        get('/') { raise FooSpecialError }
+      end
+      get '/'
+      assert_equal 501, status
+      assert_equal 'Foo!', body
+    end
   end
 
   describe 'Custom Error Pages' do
@@ -172,6 +221,19 @@ class MappedErrorTest < Test::Unit::TestCase
       assert_equal 'Error: 507', body
     end
 
+    it 'allows passing more than one range' do
+      mock_app {
+        set :raise_errors, false
+        error(409..411, 503..509) { "Error: #{response.status}" }
+        get '/' do
+          [507, {}, 'A very special error']
+        end
+      }
+      get '/'
+      assert_equal 507, status
+      assert_equal 'Error: 507', body
+    end
+
     class FooError < RuntimeError
     end
 
diff --git a/test/markaby_test.rb b/test/markaby_test.rb
index 104295e..d4e06ee 100644
--- a/test/markaby_test.rb
+++ b/test/markaby_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
 require 'markaby'
diff --git a/test/markdown_test.rb b/test/markdown_test.rb
index 8e676db..f9f7a6b 100644
--- a/test/markdown_test.rb
+++ b/test/markdown_test.rb
@@ -1,10 +1,6 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
-begin
-fail LoadError, "rdiscount not available" if defined? JRuby
-require 'rdiscount'
-
-class MarkdownTest < Test::Unit::TestCase
+MarkdownTest = proc do
   def markdown_app(&block)
     mock_app do
       set :views, File.dirname(__FILE__) + '/views'
@@ -13,24 +9,73 @@ class MarkdownTest < Test::Unit::TestCase
     get '/'
   end
 
+  def setup
+    Tilt.prefer engine, 'markdown', 'mkd', 'md'
+    super
+  end
+
+  it 'uses the correct engine' do
+    assert_equal engine, Tilt[:md]
+    assert_equal engine, Tilt[:mkd]
+    assert_equal engine, Tilt[:markdown]
+  end
+
   it 'renders inline markdown strings' do
     markdown_app { markdown '# Hiya' }
     assert ok?
-    assert_equal "<h1>Hiya</h1>\n", body
+    assert_like "<h1>Hiya</h1>\n", body
   end
 
   it 'renders .markdown files in views path' do
     markdown_app { markdown :hello }
     assert ok?
-    assert_equal "<h1>Hello From Markdown</h1>\n", body
+    assert_like "<h1>Hello From Markdown</h1>", body
   end
 
   it "raises error if template not found" do
     mock_app { get('/') { markdown :no_such_template } }
     assert_raise(Errno::ENOENT) { get('/') }
   end
+
+  it "renders with inline layouts" do
+    mock_app do
+      layout { 'THIS. IS. #{yield.upcase}!' }
+      get('/') { markdown 'Sparta', :layout_engine => :str }
+    end
+    get '/'
+    assert ok?
+    assert_like 'THIS. IS. <P>SPARTA</P>!', body
+  end
+
+  it "renders with file layouts" do
+    markdown_app { markdown 'Hello World', :layout => :layout2, :layout_engine => :erb }
+    assert ok?
+    assert_body "ERB Layout!\n<p>Hello World</p>"
+  end
+
+  it "can be used in a nested fashion for partials and whatnot" do
+    mock_app do
+      template(:inner) { "hi" }
+      template(:outer) { "<outer><%= markdown :inner %></outer>" }
+      get '/' do
+        erb :outer
+      end
+    end
+
+    get '/'
+    assert ok?
+    assert_like '<outer><p>hi</p></outer>', body
+  end
 end
 
-rescue LoadError
-  warn "#{$!.to_s}: skipping markdown tests"
+# Will generate RDiscountTest, KramdownTest, etc.
+Tilt.mappings['md'].each do |t|
+  begin
+    t.new { "" }
+    klass = Class.new(Test::Unit::TestCase) { define_method(:engine) { t }}
+    klass.class_eval(&MarkdownTest)
+    Object.const_set t.name[/[^:]+(?=Template$)/] << "Test", klass
+  rescue LoadError
+    warn "#{$!}: skipping markdown tests with #{t}"
+  end
 end
diff --git a/test/middleware_test.rb b/test/middleware_test.rb
index ee34f35..7ba0c8b 100644
--- a/test/middleware_test.rb
+++ b/test/middleware_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 class MiddlewareTest < Test::Unit::TestCase
   setup do
diff --git a/test/nokogiri_test.rb b/test/nokogiri_test.rb
index 3123083..3d70817 100644
--- a/test/nokogiri_test.rb
+++ b/test/nokogiri_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
 require 'nokogiri'
@@ -15,7 +15,7 @@ class NokogiriTest < Test::Unit::TestCase
   it 'renders inline Nokogiri strings' do
     nokogiri_app { nokogiri 'xml' }
     assert ok?
-    assert_body %{<?xml version="1.0"?>\n}
+    assert_body %(<?xml version="1.0"?>\n)
   end
 
   it 'renders inline blocks' do
@@ -26,7 +26,7 @@ class NokogiriTest < Test::Unit::TestCase
       end
     end
     assert ok?
-    assert_body "<?xml version=\"1.0\"?>\n<couple>Frank & Mary</couple>\n"
+    assert_body %(<?xml version="1.0"?>\n<couple>Frank & Mary</couple>\n)
   end
 
   it 'renders .nokogiri files in views path' do
@@ -35,7 +35,7 @@ class NokogiriTest < Test::Unit::TestCase
       nokogiri :hello
     end
     assert ok?
-    assert_body %(<?xml version="1.0"?>\n<exclaim>You're my boy, Blue!</exclaim>\n)
+    assert_body "<?xml version=\"1.0\"?>\n<exclaim>You're my boy, Blue!</exclaim>\n"
   end
 
   it "renders with inline layouts" do
@@ -46,17 +46,16 @@ class NokogiriTest < Test::Unit::TestCase
     end
     get '/'
     assert ok?
-    assert_body "<?xml version=\"1.0\"?>\n<layout>\n  <em>Hello World</em>\n</layout>\n"
+    assert_body %(<?xml version="1.0"?>\n<layout>\n  <em>Hello World</em>\n</layout>\n)
   end
 
   it "renders with file layouts" do
     next if Tilt::VERSION <= "1.1"
     nokogiri_app do
-      @name = "Blue"
       nokogiri %(xml.em 'Hello World'), :layout => :layout2
     end
     assert ok?
-    assert_body "<?xml version=\"1.0\"?>\n<layout>\n  <em>Hello World</em>\n</layout>\n"
+    assert_body %(<?xml version="1.0"?>\n<layout>\n  <em>Hello World</em>\n</layout>\n)
   end
 
   it "raises error if template not found" do
diff --git a/test/rack_test.rb b/test/rack_test.rb
new file mode 100644
index 0000000..84734ea
--- /dev/null
+++ b/test/rack_test.rb
@@ -0,0 +1,45 @@
+require File.expand_path('../helper', __FILE__)
+require 'rack'
+
+class RackTest < Test::Unit::TestCase
+  setup do
+    @foo = Sinatra.new { get('/foo') { 'foo' }}
+    @bar = Sinatra.new { get('/bar') { 'bar' }}
+  end
+
+  def build(*middleware)
+    endpoint = middleware.pop
+    @app = Rack::Builder.app do
+      middleware.each { |m| use m }
+      run endpoint
+    end
+  end
+
+  def check(*middleware)
+    build(*middleware)
+    assert get('/foo').ok?
+    assert_body 'foo'
+    assert get('/bar').ok?
+    assert_body 'bar'
+  end
+
+  it 'works as middleware in front of Rack::Lock, with lock enabled' do
+    @foo.enable :lock
+    check(@foo, Rack::Lock, @bar)
+  end
+
+  it 'works as middleware behind Rack::Lock, with lock enabled' do
+    @foo.enable :lock
+    check(Rack::Lock, @foo, @bar)
+  end
+
+  it 'works as middleware in front of Rack::Lock, with lock disabled' do
+    @foo.disable :lock
+    check(@foo, Rack::Lock, @bar)
+  end
+
+  it 'works as middleware behind Rack::Lock, with lock disabled' do
+    @foo.disable :lock
+    check(Rack::Lock, @foo, @bar)
+  end
+end
diff --git a/test/radius_test.rb b/test/radius_test.rb
index b1107bf..0e99028 100644
--- a/test/radius_test.rb
+++ b/test/radius_test.rb
@@ -1,7 +1,6 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
-fail LoadError, 'Radius broken on 1.9.' if RUBY_VERSION >= '1.9.1'
 require 'radius'
 
 class RadiusTest < Test::Unit::TestCase
diff --git a/test/rdoc_test.rb b/test/rdoc_test.rb
index 7407b0e..0d58856 100644
--- a/test/rdoc_test.rb
+++ b/test/rdoc_test.rb
@@ -1,6 +1,7 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
+require 'rdoc'
 require 'rdoc/markup/to_html'
 
 class RdocTest < Test::Unit::TestCase
@@ -15,19 +16,49 @@ class RdocTest < Test::Unit::TestCase
   it 'renders inline rdoc strings' do
     rdoc_app { rdoc '= Hiya' }
     assert ok?
-    assert_body "<h1>Hiya</h1>"
+    assert_body /<h1[^>]*>Hiya<\/h1>/
   end
 
   it 'renders .rdoc files in views path' do
     rdoc_app { rdoc :hello }
     assert ok?
-    assert_body "<h1>Hello From RDoc</h1>"
+    assert_body /<h1[^>]*>Hello From RDoc<\/h1>/
   end
 
   it "raises error if template not found" do
     mock_app { get('/') { rdoc :no_such_template } }
     assert_raise(Errno::ENOENT) { get('/') }
   end
+
+  it "renders with inline layouts" do
+    mock_app do
+      layout { 'THIS. IS. #{yield.upcase}!' }
+      get('/') { rdoc 'Sparta', :layout_engine => :str }
+    end
+    get '/'
+    assert ok?
+    assert_like 'THIS. IS. <P>SPARTA</P>!', body
+  end
+
+  it "renders with file layouts" do
+    rdoc_app { rdoc 'Hello World', :layout => :layout2, :layout_engine => :erb }
+    assert ok?
+    assert_body "ERB Layout!\n<p>Hello World</p>"
+  end
+
+  it "can be used in a nested fashion for partials and whatnot" do
+    mock_app do
+      template(:inner) { "hi" }
+      template(:outer) { "<outer><%= rdoc :inner %></outer>" }
+      get '/' do
+        erb :outer
+      end
+    end
+
+    get '/'
+    assert ok?
+    assert_like '<outer><p>hi</p></outer>', body
+  end
 end
 
 rescue LoadError
diff --git a/test/readme_test.rb b/test/readme_test.rb
new file mode 100644
index 0000000..b0a6bce
--- /dev/null
+++ b/test/readme_test.rb
@@ -0,0 +1,136 @@
+# Tests to check if all the README examples work.
+require File.expand_path('../helper', __FILE__)
+
+class ReadmeTest < Test::Unit::TestCase
+  example do
+    mock_app { get('/') { 'Hello world!' } }
+    get '/'
+    assert_body 'Hello world!'
+  end
+
+  section "Routes" do
+    example do
+      mock_app do
+        get '/' do
+          ".. show something .."
+        end
+
+        post '/' do
+          ".. create something .."
+        end
+
+        put '/' do
+          ".. replace something .."
+        end
+
+        patch '/' do
+          ".. modify something .."
+        end
+
+        delete '/' do
+          ".. annihilate something .."
+        end
+
+        options '/' do
+          ".. appease something .."
+        end
+      end
+
+      get '/'
+      assert_body '.. show something ..'
+
+      post '/'
+      assert_body '.. create something ..'
+
+      put '/'
+      assert_body '.. replace something ..'
+
+      patch '/'
+      assert_body '.. modify something ..'
+
+      delete '/'
+      assert_body '.. annihilate something ..'
+
+      options '/'
+      assert_body '.. appease something ..'
+    end
+
+    example do
+      mock_app do
+        get '/hello/:name' do
+          # matches "GET /hello/foo" and "GET /hello/bar"
+          # params[:name] is 'foo' or 'bar'
+          "Hello #{params[:name]}!"
+        end
+      end
+
+      get '/hello/foo'
+      assert_body 'Hello foo!'
+
+      get '/hello/bar'
+      assert_body 'Hello bar!'
+    end
+
+    example do
+      mock_app do
+        get '/hello/:name' do |n|
+          "Hello #{n}!"
+        end
+      end
+
+      get '/hello/foo'
+      assert_body 'Hello foo!'
+
+      get '/hello/bar'
+      assert_body 'Hello bar!'
+    end
+
+    example do
+      mock_app do
+        get '/say/*/to/*' do
+          # matches /say/hello/to/world
+          params[:splat].inspect # => ["hello", "world"]
+        end
+
+        get '/download/*.*' do
+          # matches /download/path/to/file.xml
+          params[:splat].inspect # => ["path/to/file", "xml"]
+        end
+      end
+
+      get "/say/hello/to/world"
+      assert_body '["hello", "world"]'
+
+      get "/download/path/to/file.xml"
+      assert_body '["path/to/file", "xml"]'
+    end
+
+    example do
+      mock_app do
+        get %r{/hello/([\w]+)} do
+          "Hello, #{params[:captures].first}!"
+        end
+      end
+
+      get '/hello/foo'
+      assert_body 'Hello, foo!'
+
+      get '/hello/bar'
+      assert_body 'Hello, bar!'
+    end
+
+    example do
+      mock_app do
+        get %r{/hello/([\w]+)} do |c|
+          "Hello, #{c}!"
+        end
+      end
+
+      get '/hello/foo'
+      assert_body 'Hello, foo!'
+
+      get '/hello/bar'
+      assert_body 'Hello, bar!'
+    end
+  end
+end
diff --git a/test/request_test.rb b/test/request_test.rb
index 610f1a8..13abad4 100644
--- a/test/request_test.rb
+++ b/test/request_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 require 'stringio'
 
 class RequestTest < Test::Unit::TestCase
diff --git a/test/response_test.rb b/test/response_test.rb
index 5aa5f4e..9d80e4e 100644
--- a/test/response_test.rb
+++ b/test/response_test.rb
@@ -1,6 +1,6 @@
 # encoding: utf-8
 
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 class ResponseTest < Test::Unit::TestCase
   setup do
@@ -22,7 +22,7 @@ class ResponseTest < Test::Unit::TestCase
   it 'writes to body' do
     @response.body = 'Hello'
     @response.write ' World'
-    assert_equal 'Hello World', @response.body
+    assert_equal 'Hello World', @response.body.join
   end
 
   [204, 304].each do |status_code|
@@ -39,4 +39,23 @@ class ResponseTest < Test::Unit::TestCase
     assert_equal '14', headers['Content-Length']
     assert_equal @response.body, body
   end
+
+  it 'does not call #to_ary or #inject on the body' do
+    object = Object.new
+    def object.inject(*) fail 'called' end
+    def object.to_ary(*) fail 'called' end
+    def object.each(*) end
+    @response.body = object
+    assert @response.finish
+  end
+
+  it 'does not nest a Sinatra::Response' do
+    @response.body = Sinatra::Response.new ["foo"]
+    assert_equal @response.body, ["foo"]
+  end
+
+  it 'does not nest a Rack::Response' do
+    @response.body = Rack::Response.new ["foo"]
+    assert_equal @response.body, ["foo"]
+  end
 end
diff --git a/test/result_test.rb b/test/result_test.rb
index 5100abf..305bcda 100644
--- a/test/result_test.rb
+++ b/test/result_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 class ResultTest < Test::Unit::TestCase
   it "sets response.body when result is a String" do
@@ -54,12 +54,12 @@ class ResultTest < Test::Unit::TestCase
   it "sets status, headers, and body when result is a Rack response tuple" do
     mock_app {
       get '/' do
-        [205, {'Content-Type' => 'foo/bar'}, 'Hello World']
+        [203, {'Content-Type' => 'foo/bar'}, 'Hello World']
       end
     }
 
     get '/'
-    assert_equal 205, status
+    assert_equal 203, status
     assert_equal 'foo/bar', response['Content-Type']
     assert_equal 'Hello World', body
   end
@@ -76,14 +76,14 @@ class ResultTest < Test::Unit::TestCase
     assert_equal 'formula of', body
   end
 
-  it "raises a TypeError when result is a non two or three tuple Array" do
+  it "raises a ArgumentError when result is a non two or three tuple Array" do
     mock_app {
       get '/' do
         [409, 'formula of', 'something else', 'even more']
       end
     }
 
-    assert_raise(TypeError) { get '/' }
+    assert_raise(ArgumentError) { get '/' }
   end
 
   it "sets status when result is a Fixnum status code" do
diff --git a/test/route_added_hook_test.rb b/test/route_added_hook_test.rb
index 08fdd92..e621f1b 100644
--- a/test/route_added_hook_test.rb
+++ b/test/route_added_hook_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 module RouteAddedTest
   @routes, @procs = [], []
diff --git a/test/routing_test.rb b/test/routing_test.rb
index f1b5c6b..e3f4d0b 100644
--- a/test/routing_test.rb
+++ b/test/routing_test.rb
@@ -1,5 +1,5 @@
 # I like coding: UTF-8
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 # Helper method for easy route pattern matching testing
 def route_def(pattern)
@@ -23,7 +23,7 @@ class RegexpLookAlike
 end
 
 class RoutingTest < Test::Unit::TestCase
-  %w[get put post delete options].each do |verb|
+  %w[get put post delete options patch].each do |verb|
     it "defines #{verb.upcase} request handlers with #{verb}" do
       mock_app {
         send verb, '/hello' do
@@ -78,6 +78,13 @@ class RoutingTest < Test::Unit::TestCase
     assert_equal 200, status
   end
 
+  it "it handles encoded slashes correctly" do
+    mock_app { get("/:a") { |a| a } }
+    get '/foo%2Fbar'
+    assert_equal 200, status
+    assert_body "foo/bar"
+  end
+
   it "overrides the content-type in error handlers" do
     mock_app {
       before { content_type 'text/plain' }
@@ -107,6 +114,8 @@ class RoutingTest < Test::Unit::TestCase
 
   it 'matches empty PATH_INFO to "" if a route is defined for ""' do
     mock_app do
+      disable :protection
+
       get '/' do
         'did not work'
       end
@@ -1047,7 +1056,7 @@ class RoutingTest < Test::Unit::TestCase
     mock_app do
       get '/foo' do
         status, headers, body = call env.merge("PATH_INFO" => '/bar')
-        [status, headers, body.map(&:upcase)]
+        [status, headers, body.each.map(&:upcase)]
       end
 
       get '/bar' do
@@ -1073,4 +1082,17 @@ class RoutingTest < Test::Unit::TestCase
     assert ok?
     assert_body 'hello'
   end
+
+  it 'returns the route signature' do
+    signature = list = nil
+
+    mock_app do
+      signature = post('/') { }
+      list = routes['POST']
+    end
+
+    assert_equal Array, signature.class
+    assert_equal 4, signature.length
+    assert list.include?(signature)
+  end
 end
diff --git a/test/sass_test.rb b/test/sass_test.rb
index 9a0372e..c46f5a1 100644
--- a/test/sass_test.rb
+++ b/test/sass_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
 require 'sass'
diff --git a/test/scss_test.rb b/test/scss_test.rb
index 722f0dc..2b64a2c 100644
--- a/test/scss_test.rb
+++ b/test/scss_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
 require 'sass'
diff --git a/test/server_test.rb b/test/server_test.rb
index 886102a..d4ebdd4 100644
--- a/test/server_test.rb
+++ b/test/server_test.rb
@@ -1,4 +1,5 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
+require 'stringio'
 
 module Rack::Handler
   class Mock
@@ -25,11 +26,11 @@ class ServerTest < Test::Unit::TestCase
       set :bind, 'foo.local'
       set :port, 9001
     }
-    $stdout = File.open('/dev/null', 'wb')
+    $stderr = StringIO.new
   end
 
   def teardown
-    $stdout = STDOUT
+    $stderr = STDERR
   end
 
   it "locates the appropriate Rack handler and calls ::run" do
diff --git a/test/settings_test.rb b/test/settings_test.rb
index 71ceb84..b2e58de 100644
--- a/test/settings_test.rb
+++ b/test/settings_test.rb
@@ -1,12 +1,12 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 class SettingsTest < Test::Unit::TestCase
   setup do
     @base = Sinatra.new(Sinatra::Base)
-    @base.set :environment, :foo
+    @base.set :environment => :foo, :app_file => nil
 
     @application = Sinatra.new(Sinatra::Application)
-    @application.set :environment, :foo
+    @application.set :environment => :foo, :app_file => nil
   end
 
   it 'sets settings to literal values' do
@@ -115,6 +115,32 @@ class SettingsTest < Test::Unit::TestCase
     assert_equal 'oops', @base.foo
   end
 
+  it 'merges values of multiple set calls if those are hashes' do
+    @base.set :foo, :a => 1
+    sub = Class.new(@base)
+    sub.set :foo, :b => 2
+    assert_equal({:a => 1, :b => 2}, sub.foo)
+  end
+
+  it 'merging does not affect the superclass' do
+    @base.set :foo, :a => 1
+    sub = Class.new(@base)
+    sub.set :foo, :b => 2
+    assert_equal({:a => 1}, @base.foo)
+  end
+
+  it 'is possible to change a value from a hash to something else' do
+    @base.set :foo, :a => 1
+    @base.set :foo, :bar
+    assert_equal(:bar, @base.foo)
+  end
+
+  it 'merges values with values of the superclass if those are hashes' do
+    @base.set :foo, :a => 1
+    @base.set :foo, :b => 2
+    assert_equal({:a => 1, :b => 2}, @base.foo)
+  end
+
   it "sets multiple settings to true with #enable" do
     @base.enable :sessions, :foo, :bar
     assert @base.sessions
@@ -222,27 +248,30 @@ class SettingsTest < Test::Unit::TestCase
     end
 
     it 'does not override app-specified error handling when set to :after_handler' do
-      klass = Sinatra.new(Sinatra::Application)
-      mock_app(klass) {
+      ran = false
+      mock_app do
         set :show_exceptions, :after_handler
-        
-        error RuntimeError do
-          'Big mistake !'
-        end
-        
-        get '/' do
-          raise RuntimeError
-        end  
-      }
-      
+        error(RuntimeError) { ran = true }
+        get('/') { raise RuntimeError }
+      end
+
       get '/'
       assert_equal 500, status
+      assert ran
+    end
 
-      assert ! body.include?("<code>")
-      assert body.include? "Big mistake !"
-      
+    it 'does catch any other exceptions when set to :after_handler' do
+      ran = false
+      mock_app do
+        set :show_exceptions, :after_handler
+        error(RuntimeError) { ran = true }
+        get('/') { raise ArgumentError }
+      end
+
+      get '/'
+      assert_equal 500, status
+      assert !ran
     end
-    
   end
 
   describe 'dump_errors' do
@@ -277,6 +306,29 @@ class SettingsTest < Test::Unit::TestCase
       get '/'
       assert body.include?("RuntimeError") && body.include?("settings_test.rb")
     end
+
+    it 'does not dump 404 errors' do
+      klass = Sinatra.new(Sinatra::Application)
+
+      mock_app(klass) {
+        enable :dump_errors
+        disable :raise_errors
+
+        error do
+          error = @env['rack.errors'].instance_variable_get(:@error)
+          error.rewind
+
+          error.read
+        end
+
+        get '/' do
+          raise Sinatra::NotFound
+        end
+      }
+
+      get '/'
+      assert !body.include?("NotFound") && !body.include?("settings_test.rb")
+    end
   end
 
   describe 'sessions' do
@@ -307,13 +359,13 @@ class SettingsTest < Test::Unit::TestCase
       assert ! @base.static?
     end
 
-    it 'is enabled on Base when public is set and exists' do
+    it 'is enabled on Base when public_folder is set and exists' do
       @base.set :environment, :development
-      @base.set :public, File.dirname(__FILE__)
+      @base.set :public_folder, File.dirname(__FILE__)
       assert @base.static?
     end
 
-    it 'is enabled on Base when root is set and root/public exists' do
+    it 'is enabled on Base when root is set and root/public_folder exists' do
       @base.set :environment, :development
       @base.set :root, File.dirname(__FILE__)
       assert @base.static?
@@ -323,17 +375,36 @@ class SettingsTest < Test::Unit::TestCase
       assert ! @application.static?
     end
 
-    it 'is enabled on Application when public is set and exists' do
+    it 'is enabled on Application when public_folder is set and exists' do
       @application.set :environment, :development
-      @application.set :public, File.dirname(__FILE__)
+      @application.set :public_folder, File.dirname(__FILE__)
       assert @application.static?
     end
 
-    it 'is enabled on Application when root is set and root/public exists' do
+    it 'is enabled on Application when root is set and root/public_folder exists' do
       @application.set :environment, :development
       @application.set :root, File.dirname(__FILE__)
       assert @application.static?
     end
+
+    it 'is possible to use Module#public' do
+      @base.send(:define_method, :foo) { }
+      @base.send(:private, :foo)
+      assert !@base.public_method_defined?(:foo)
+      @base.send(:public, :foo)
+      assert @base.public_method_defined?(:foo)
+    end
+
+    it 'is possible to use the keyword public in a sinatra app' do
+      app = Sinatra.new do
+        private
+        def priv; end
+        public
+        def pub; end
+      end
+      assert !app.public_method_defined?(:priv)
+      assert app.public_method_defined?(:pub)
+    end
   end
 
   describe 'bind' do
@@ -358,9 +429,13 @@ class SettingsTest < Test::Unit::TestCase
   end
 
   describe 'app_file' do
-    it 'is nil' do
-      assert_nil @base.app_file
-      assert_nil @application.app_file
+    it 'is nil for base classes' do
+      assert_nil Sinatra::Base.app_file
+      assert_nil Sinatra::Application.app_file
+    end
+
+    it 'defaults to the file subclassing' do
+      assert_equal __FILE__, Sinatra.new.app_file
     end
   end
 
@@ -394,18 +469,18 @@ class SettingsTest < Test::Unit::TestCase
     end
   end
 
-  describe 'public' do
+  describe 'public_folder' do
     it 'is nil if root is not set' do
-      assert @base.public.nil?
-      assert @application.public.nil?
+      assert @base.public_folder.nil?
+      assert @application.public_folder.nil?
     end
 
     it 'is set to root joined with public/' do
       @base.root = File.dirname(__FILE__)
-      assert_equal File.dirname(__FILE__) + "/public", @base.public
+      assert_equal File.dirname(__FILE__) + "/public", @base.public_folder
 
       @application.root = File.dirname(__FILE__)
-      assert_equal File.dirname(__FILE__) + "/public", @application.public
+      assert_equal File.dirname(__FILE__) + "/public", @application.public_folder
     end
   end
 
@@ -415,4 +490,49 @@ class SettingsTest < Test::Unit::TestCase
       assert ! @application.lock?
     end
   end
+
+  describe 'protection' do
+    class MiddlewareTracker < Rack::Builder
+      def self.track
+        Rack.send :remove_const, :Builder
+        Rack.const_set :Builder, MiddlewareTracker
+        MiddlewareTracker.used.clear
+        yield
+      ensure
+        Rack.send :remove_const, :Builder
+        Rack.const_set :Builder, MiddlewareTracker.superclass
+      end
+
+      def self.used
+        @used ||= []
+      end
+
+      def use(middleware, *)
+        MiddlewareTracker.used << middleware
+        super
+      end
+    end
+
+    it 'sets up Rack::Protection' do
+      MiddlewareTracker.track do
+        Sinatra::Base.new
+        assert_include MiddlewareTracker.used, Rack::Protection
+      end
+    end
+
+    it 'sets up Rack::Protection::PathTraversal' do
+      MiddlewareTracker.track do
+        Sinatra::Base.new
+        assert_include MiddlewareTracker.used, Rack::Protection::PathTraversal
+      end
+    end
+
+    it 'does not set up Rack::Protection::PathTraversal when disabling it' do
+      MiddlewareTracker.track do
+        Sinatra.new { set :protection, :except => :path_traversal }.new
+        assert_include MiddlewareTracker.used, Rack::Protection
+        assert !MiddlewareTracker.used.include?(Rack::Protection::PathTraversal)
+      end
+    end
+  end
 end
diff --git a/test/sinatra_test.rb b/test/sinatra_test.rb
index a7fe1f3..3f8cede 100644
--- a/test/sinatra_test.rb
+++ b/test/sinatra_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 class SinatraTest < Test::Unit::TestCase
   it 'creates a new Sinatra::Base subclass on new' do
diff --git a/test/slim_test.rb b/test/slim_test.rb
index 96a8cab..d294d21 100644
--- a/test/slim_test.rb
+++ b/test/slim_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
 require 'slim'
@@ -52,44 +52,34 @@ class SlimTest < Test::Unit::TestCase
   HTML4_DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"
   
   it "passes slim options to the slim engine" do
-    mock_app {
-      get '/' do
-        slim "! doctype html\nh1 Hello World", :format => :html4
-      end
-    }
+    mock_app { get('/') { slim "x foo='bar'", :attr_wrapper => "'" }}
     get '/'
     assert ok?
-    assert_equal "#{HTML4_DOCTYPE}<h1>Hello World</h1>", body
+    assert_body "<x foo='bar'></x>"
   end
   
   it "passes default slim options to the slim engine" do
-    mock_app {
-      set :slim, {:format => :html4}
-      get '/' do
-        slim "! doctype html\nh1 Hello World"
-      end
-    }
+    mock_app do
+      set :slim, :attr_wrapper => "'"
+      get('/') { slim "x foo='bar'" }
+    end
     get '/'
     assert ok?
-    assert_equal "#{HTML4_DOCTYPE}<h1>Hello World</h1>", body
+    assert_body "<x foo='bar'></x>"
   end
   
   it "merges the default slim options with the overrides and passes them to the slim engine" do
-    mock_app {
-      set :slim, {:format => :html4}
-      get '/' do
-        slim "! doctype html\nh1.header Hello World"
-      end
-      get '/html5' do
-        slim "! doctype html\nh1.header Hello World", :format => :html5
-      end
-    }
+    mock_app do
+      set :slim, :attr_wrapper => "'"
+      get('/') { slim "x foo='bar'" }
+      get('/other') { slim "x foo='bar'", :attr_wrapper => '"' }
+    end
     get '/'
     assert ok?
-    assert_match(/^#{HTML4_DOCTYPE}/, body)
-    get '/html5'
+    assert_body "<x foo='bar'></x>"
+    get '/other'
     assert ok?
-    assert_equal "<!DOCTYPE html><h1 class=\"header\">Hello World</h1>", body
+    assert_body '<x foo="bar"></x>'
   end
 end
 
diff --git a/test/static_test.rb b/test/static_test.rb
index 50e3db0..3f5b671 100644
--- a/test/static_test.rb
+++ b/test/static_test.rb
@@ -1,10 +1,10 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 class StaticTest < Test::Unit::TestCase
   setup do
     mock_app {
       set :static, true
-      set :public, File.dirname(__FILE__)
+      set :public_folder, File.dirname(__FILE__)
     }
   end
 
@@ -36,7 +36,6 @@ class StaticTest < Test::Unit::TestCase
     head "/#{File.basename(__FILE__)}"
     assert ok?
     assert_equal '', body
-    assert_equal File.size(__FILE__).to_s, response['Content-Length']
     assert response.headers.include?('Last-Modified')
   end
 
@@ -66,7 +65,7 @@ class StaticTest < Test::Unit::TestCase
   end
 
   it 'passes to the next handler when the public option is nil' do
-    @app.set :public, nil
+    @app.set :public_folder, nil
     get "/#{File.basename(__FILE__)}"
     assert not_found?
   end
@@ -85,7 +84,7 @@ class StaticTest < Test::Unit::TestCase
   it '404s when .. path traverses outside of public directory' do
     mock_app {
       set :static, true
-      set :public, File.dirname(__FILE__) + '/data'
+      set :public_folder, File.dirname(__FILE__) + '/data'
     }
     get "/../#{File.basename(__FILE__)}"
     assert not_found?
@@ -154,4 +153,25 @@ class StaticTest < Test::Unit::TestCase
       assert_equal "bytes */#{length}",response['Content-Range'], "416 response should include actual length"
     end
   end
+
+  it 'does not include static cache control headers by default' do
+    env = Rack::MockRequest.env_for("/#{File.basename(__FILE__)}")
+    status, headers, body = @app.call(env)
+    assert !headers.has_key?('Cache-Control')
+  end
+
+  it 'sets cache control headers on static files if set' do
+    @app.set :static_cache_control, :public
+    env = Rack::MockRequest.env_for("/#{File.basename(__FILE__)}")
+    status, headers, body = @app.call(env)
+    assert headers.has_key?('Cache-Control')
+    assert_equal headers['Cache-Control'], 'public'
+
+    @app.set :static_cache_control, [:public, :must_revalidate, {:max_age => 300}]
+    env = Rack::MockRequest.env_for("/#{File.basename(__FILE__)}")
+    status, headers, body = @app.call(env)
+    assert headers.has_key?('Cache-Control')
+    assert_equal headers['Cache-Control'], 'public, must-revalidate, max-age=300'
+  end
+
 end
diff --git a/test/streaming_test.rb b/test/streaming_test.rb
new file mode 100644
index 0000000..20bc41c
--- /dev/null
+++ b/test/streaming_test.rb
@@ -0,0 +1,123 @@
+require File.expand_path('../helper', __FILE__)
+
+class StreamingTest < Test::Unit::TestCase
+  Stream = Sinatra::Helpers::Stream
+
+  it 'returns the concatinated body' do
+    mock_app do
+      get '/' do
+        stream do |out|
+          out << "Hello" << " "
+          out << "World!"
+        end
+      end
+    end
+
+    get('/')
+    assert_body "Hello World!"
+  end
+
+  it 'always yields strings' do
+    stream = Stream.new { |out| out << :foo }
+    stream.each { |str| assert_equal 'foo', str }
+  end
+
+  it 'postpones body generation' do
+    step = 0
+
+    stream = Stream.new do |out|
+      10.times do
+        out << step
+        step += 1
+      end
+    end
+
+    stream.each do |s|
+      assert_equal s, step.to_s
+      step += 1
+    end
+  end
+
+  it 'calls the callback after it is done' do
+    step   = 0
+    final  = 0
+    stream = Stream.new { |o| 10.times { step += 1 }}
+    stream.callback { final = step }
+    stream.each { |str| }
+    assert_equal 10, final
+  end
+
+  it 'does not trigger the callback if close is set to :keep_open' do
+    step   = 0
+    final  = 0
+    stream = Stream.new(Stream, :keep_open) { |o| 10.times { step += 1 } }
+    stream.callback { final = step }
+    stream.each { |str| }
+    assert_equal 0, final
+  end
+
+  it 'allows adding more than one callback' do
+    a = b = false
+    stream = Stream.new { }
+    stream.callback { a = true }
+    stream.callback { b = true }
+    stream.each { |str| }
+    assert a, 'should trigger first callback'
+    assert b, 'should trigger second callback'
+  end
+
+  class MockScheduler
+    def initialize(*)     @schedule, @defer = [], []                end
+    def schedule(&block)  @schedule << block                        end
+    def defer(&block)     @defer    << block                        end
+    def schedule!(*)      @schedule.pop.call until @schedule.empty? end
+    def defer!(*)         @defer.pop.call    until @defer.empty?    end
+  end
+
+  it 'allows dropping in another scheduler' do
+    scheduler  = MockScheduler.new
+    processing = sending = done = false
+
+    stream = Stream.new(scheduler) do |out|
+      processing = true
+      out << :foo
+    end
+
+    stream.each { sending = true}
+    stream.callback { done = true }
+
+    scheduler.schedule!
+    assert !processing
+    assert !sending
+    assert !done
+
+    scheduler.defer!
+    assert processing
+    assert !sending
+    assert !done
+
+    scheduler.schedule!
+    assert sending
+    assert done
+  end
+
+  it 'schedules exceptions to be raised on the main thread/event loop/...' do
+    scheduler = MockScheduler.new
+    Stream.new(scheduler) { fail 'should be caught' }.each { }
+    scheduler.defer!
+    assert_raise(RuntimeError) { scheduler.schedule! }
+  end
+
+  it 'does not trigger an infinite loop if you call close in a callback' do
+    stream = Stream.new { |out| out.callback { out.close }}
+    stream.each { |str| }
+  end
+
+  it 'gives access to route specific params' do
+    mock_app do
+      get('/:name') { stream { |o| o << params[:name] }}
+    end
+    get '/foo'
+    assert_body 'foo'
+  end
+end
diff --git a/test/templates_test.rb b/test/templates_test.rb
index d5eb0d6..a692f64 100644
--- a/test/templates_test.rb
+++ b/test/templates_test.rb
@@ -1,5 +1,5 @@
 # encoding: UTF-8
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 File.delete(File.dirname(__FILE__) + '/views/layout.test') rescue nil
 
 class TestTemplate < Tilt::Template
@@ -144,7 +144,7 @@ class TemplatesTest < Test::Unit::TestCase
   end
 
   it 'loads templates from specified views directory' do
-    render_app { render :test, :hello, :views => options.views + '/foo' }
+    render_app { render :test, :hello, :views => settings.views + '/foo' }
 
     assert_equal "from another views directory\n", body
   end
diff --git a/test/textile_test.rb b/test/textile_test.rb
index f9535b4..a3ed6f5 100644
--- a/test/textile_test.rb
+++ b/test/textile_test.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/helper'
+require File.expand_path('../helper', __FILE__)
 
 begin
 require 'redcloth'
@@ -28,6 +28,36 @@ class TextileTest < Test::Unit::TestCase
     mock_app { get('/') { textile :no_such_template } }
     assert_raise(Errno::ENOENT) { get('/') }
   end
+
+  it "renders with inline layouts" do
+    mock_app do
+      layout { 'THIS. IS. #{yield.upcase}!' }
+      get('/') { textile 'Sparta', :layout_engine => :str }
+    end
+    get '/'
+    assert ok?
+    assert_like 'THIS. IS. <P>SPARTA</P>!', body
+  end
+
+  it "renders with file layouts" do
+    textile_app { textile 'Hello World', :layout => :layout2, :layout_engine => :erb }
+    assert ok?
+    assert_body "ERB Layout!\n<p>Hello World</p>"
+  end
+
+  it "can be used in a nested fashion for partials and whatnot" do
+    mock_app do
+      template(:inner) { "hi" }
+      template(:outer) { "<outer><%= textile :inner %></outer>" }
+      get '/' do
+        erb :outer
+      end
+    end
+
+    get '/'
+    assert ok?
+    assert_like '<outer><p>hi</p></outer>', body
+  end
 end
 
 rescue LoadError
diff --git a/test/views/error.erubis b/test/views/error.erubis
deleted file mode 100644
index b48d1f0..0000000
--- a/test/views/error.erubis
+++ /dev/null
@@ -1,3 +0,0 @@
-Hello <%= 'World' %>
-<% raise 'Goodbye' unless defined?(french) && french %>
-<% raise 'Au revoir' if defined?(french) && french %>
diff --git a/test/views/hello.creole b/test/views/hello.creole
new file mode 100644
index 0000000..50db5f8
--- /dev/null
+++ b/test/views/hello.creole
@@ -0,0 +1 @@
+= Hello From Creole
diff --git a/test/views/hello.erubis b/test/views/hello.erubis
deleted file mode 100644
index bcbbc92..0000000
--- a/test/views/hello.erubis
+++ /dev/null
@@ -1 +0,0 @@
-Hello <%= 'World' %>
diff --git a/test/views/layout2.erubis b/test/views/layout2.erubis
deleted file mode 100644
index b61db6d..0000000
--- a/test/views/layout2.erubis
+++ /dev/null
@@ -1,2 +0,0 @@
-ERubis Layout!
-<%= yield %>

-- 
ruby-sinatra.git



More information about the Pkg-ruby-extras-commits mailing list