Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

init

  • Loading branch information...
commit 9e53efde58a3176b747cc782c720909f66bee67a 0 parents
@ainame authored
6 .gitignore
@@ -0,0 +1,6 @@
+# please add general patterns to your global ignore list
+# see https://github.com/github/gitignore#readme
+
+/pkg
+/doc/api
+/Gemfile.lock
23 .travis.yml
@@ -0,0 +1,23 @@
+rvm:
+ - 1.8.7
+ - 1.9.2
+ - 1.9.3
+ - rbx-18mode
+ - rbx-19mode
+ - jruby-18mode
+ - jruby-19mode
+ - ruby-head
+env:
+ - "rack=1.3.6"
+ - "rack=1.4.1"
+ - "rack=master"
+ - "tilt=1.3.3"
+ - "tilt=master"
+matrix:
+ allow_failures:
+ - rvm: ruby-head
+ - rvm: rbx-19mode
+ - rvm: jruby-19mode
+notifications:
+ recipients:
+ - k.haase@finn.de
4 .yardopts
@@ -0,0 +1,4 @@
+--readme README.rdoc
+--title 'Sinatra API Documentation'
+--charset utf-8
+'lib/**/*.rb' - '*.rdoc'
58 AUTHORS
@@ -0,0 +1,58 @@
+Sinatra was designed and developed by Blake Mizerany (bmizerany) in
+California. Continued development would not be possible without the ongoing
+financial support provided by [Heroku](http://heroku.com) and the emotional
+support provided by Adam Wiggins (adamwiggins) of Heroku, Chris Wanstrath (defunkt),
+PJ Hyett (pjhyett), and the rest of the GitHub crew.
+
+Special thanks to the following extraordinary individuals, who-out which
+Sinatra would not be possible:
+
+* Ryan Tomayko (rtomayko) for constantly fixing whitespace errors 60d5006
+* Ezra Zygmuntowicz (ezmobius) for initial help and letting Blake steal
+ some of merbs internal code.
+* Ari Lerner (http://xnot.org/) for his evangelism, spirit, and gumption
+ that got Sinatra recognized from Day 1.
+* Christopher Schneid (cschneid) for The Book, the blog (gittr.com),
+ irclogger.com, and a bunch of useful patches.
+* Markus Prinz (cypher) for patches over the years, caring about
+ the README, and hanging in there when times were rough.
+* Simon Rozet (sr) for a ton of doc patches, HAML options, and all that
+ advocacy stuff he's going to do for 1.0.
+* Erik Kastner (kastner) for fixing `MIME_TYPES` under Rack 0.5.
+* Ben Bleything (bleything) for caring about HTTP status codes and doc fixes.
+* Igal Koshevoy (igal) for root path detection under Thin/Passenger.
+* Jon Crosby (jcrosby) for coffee breaks, doc fixes, and just because, man.
+* Karel Minarik (karmi) for screaming until the website came back up.
+* Jeremy Evans (jeremyevans) for unbreaking optional path params (twice!)
+* The GitHub guys for stealing Blake's table.
+* Nickolas Means (nmeans) for Sass template support.
+* Victor Hugo Borja (vic) for splat'n routes specs and doco.
+* Avdi Grimm (avdi) for basic RSpec support.
+* Jack Danger Canty for a more accurate root directory and for making me
+ watch [this](http://www.youtube.com/watch?v=ueaHLHgskkw) just now.
+* Mathew Walker for making escaped paths work with static files.
+* Millions of Us for having the problem that led to Sinatra's conception.
+* Songbird for the problems that helped Sinatra's future become realized.
+* Rick Olson (technoweenie) for the killer plug at RailsConf '08.
+* Steven Garcia for the amazing custom artwork you see on 404's and 500's
+* Pat Nakajima (nakajima) for fixing non-nested params in nested params Hash's.
+* Konstantin Haase for his hard work and ongoing commitment to improving
+ Sinatra, for 1.1.0, 1.2.0 and beyond..
+* Zachary Scott for adding Konstantin to the AUTHORS file. He also did help
+ writing the book, but mainly for adding Konstantin.
+* Gabriel Andretta for having people wonder whether our documentation is
+ actually in English or in Spanish.
+* Vasily Polovnyov, Nickolay Schwarz, Luciano Sousa, Wu Jiang, Mickael Riga,
+ Bernhard Essl, Janos Hardi, Kouhei Yanagita and "burningTyger" for willingly
+ translating whatever ends up in the README.
+* [Wordy](http://www.wordy.com/) for proofreading our README. 73e137d
+* cactus for digging through code and specs, multiple times.
+* Nicolás Sanguinetti (foca) for strong demand of karma and shaping
+ helpers/register.
+
+and last but not least:
+
+* Frank Sinatra (chairman of the board) for having so much class he
+ deserves a web-framework named after him.
+
+For a complete list of all contributors to Sinatra itself, run `rake authors`.
1,039 CHANGES
@@ -0,0 +1,1039 @@
+= 1.4.0 / Not Yet Released
+
+ * Add support for Yajl templates. (Jamie Hodge)
+
+ * No longer include Sinatra::Delegator in Object, instead extend the main
+ object only. (Konstantin Haase)
+
+ * Add :status option support to send_file. (Konstantin Haase)
+
+ * The `provides` condition now respects an earlier set content type.
+ (Konstantin Haase)
+
+ * Exception#code is only used when :use_code is enabled. Moreover, it will
+ be ignored if the value is not between 400 and 599. You should use
+ Exception#http_status instead. (Konstantin Haase)
+
+ * Status, headers and body will be set correctly in an after filter when using
+ halt in a before filter or route. (Konstantin Haase)
+
+ * Sinatra::Base.new now returns a Sinatra::Wrapper instance, exposing
+ #settings and #helpers, yet going through the middleware stack on #call.
+ It also implements a nice #inspect, so it plays nice with Rails' `rake
+ routes`. (Konstantin Haase)
+
+= 1.3.3 / Not Yet Released
+
+ * When keeping a stream open, set up callback/errback correctly to deal with
+ clients closing the connection. (Konstantin Haase)
+
+= 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.
+ (Konstantin Haase)
+
+= 1.2.5 / 2011-04-30
+
+ * Restore compatibility with Ruby 1.8.6. (Konstantin Haase)
+
+= 1.2.4 / 2011-04-30
+
+ * Sinatra::Application (classic style) does not use a session secret in
+ development mode, so sessions are not invalidated after every request when
+ using Shotgun. (Konstantin Haase)
+
+ * The request object was shared between multiple Sinatra instances in the
+ same middleware chain. This caused issues if any non-sinatra routing
+ happend in-between two of those instances, or running a request twice
+ against an application (described in the README). The caching was reverted.
+ See GH#239 and GH#256 for more infos. (Konstantin Haase)
+
+ * Fixes issues where the top level DSL was interfering with method_missing
+ proxies. This issue surfaced when Rails 3 was used with older Sass versions
+ and Sinatra >= 1.2.0. (Konstantin Haase)
+
+ * Sinatra::Delegator.delegate is now able to delegate any method names, even
+ those containing special characters. This allows better integration into
+ other programming languages on Rubinius (probably on the JVM, too), like
+ Fancy. (Konstantin Haase)
+
+ * Remove HEAD request logic and let Rack::Head handle it instead. (Paolo
+ "Nusco" Perrotta)
+
+= 1.2.3 / 2011-04-13
+
+ * This release is compatible with Tilt 1.3, it will still work with Tilt 1.2.2,
+ however, if you want to use a newer Tilt version, you have to upgrade to at
+ least this version of Sinatra. (Konstantin Haase)
+
+ * Helpers dealing with time, like `expires`, handle objects that pretend to be
+ numbers, like `ActiveSupport::Duration`, better. (Konstantin Haase)
+
+= 1.2.2 / 2011-04-08
+
+ * The `:provides => :js` condition now matches both `application/javascript`
+ and `text/javascript`. The `:provides => :xml` condition now matches both
+ `application/xml` and `text/xml`. The `Content-Type` header is set
+ accordingly. If the client accepts both, the `application/*` version is
+ preferred, since the `text/*` versions are deprecated. (Konstantin Haase)
+
+ * The `provides` condition now handles wildcards in `Accept` headers correctly.
+ Thus `:provides => :html` matches `text/html`, `text/*` and `*/*`.
+ (Konstantin Haase)
+
+ * When parsing `Accept` headers, `Content-Type` preferences are honored
+ according to RFC 2616 section 14.1. (Konstantin Haase)
+
+ * URIs passed to the `url` helper or `redirect` may now use any schema to be
+ identified as absolute URIs, not only `http` or `https`. (Konstantin Haase)
+
+ * Handles `Content-Type` strings that already contain parameters correctly in
+ `content_type` (example: `content_type "text/plain; charset=utf-16"`).
+ (Konstantin Haase)
+
+ * If a route with an empty pattern is defined (`get("") { ... }`) requests with
+ an empty path info match this route instead of "/". (Konstantin Haase)
+
+ * In development environment, when running under a nested path, the image URIs
+ on the error pages are set properly. (Konstantin Haase)
+
+= 1.2.1 / 2011-03-17
+
+ * Use a generated session secret when using `enable :sessions`. (Konstantin
+ Haase)
+
+ * Fixed a bug where the wrong content type was used if no content type was set
+ and a template engine was used with a different engine for the layout with
+ different default content types, say Less embedded in Slim. (Konstantin
+ Haase)
+
+ * README translations improved (Gabriel Andretta, burningTyger, Sylvain Desvé,
+ Gregor Schmidt)
+
+= 1.2.0 / 2011-03-03
+
+ * Added `slim` rendering method for rendering Slim templates. (Steve
+ Hodgkiss)
+
+ * The `markaby` rendering method now allows passing a block, making inline
+ usage possible. Requires Tilt 1.2 or newer. (Konstantin Haase)
+
+ * All render methods now take a `:layout_engine` option, allowing to use a
+ layout in a different template language. Even more useful than using this
+ directly (`erb :index, :layout_engine => :haml`) is setting this globally for
+ a template engine that otherwise does not support layouts, like Markdown or
+ Textile (`set :markdown, :layout_engine => :erb`). (Konstantin Haase)
+
+ * Before and after filters now support conditions, both with and without
+ patterns (`before '/api/*', :agent => /Songbird/`). (Konstantin Haase)
+
+ * Added a `url` helper method which constructs absolute URLs. Copes with
+ reverse proxies and Rack handlers correctly. Aliased to `to`, so you can
+ write `redirect to('/foo')`. (Konstantin Haase)
+
+ * If running on 1.9, patterns for routes and filters now support named
+ captures: `get(%r{/hi/(?<name>[^/?#]+)}) { "Hi #{params['name']}" }`.
+ (Steve Price)
+
+ * All rendering methods now take a `:scope` option, which renders them in
+ another context. Note that helpers and instance variables will be
+ unavailable if you use this feature. (Paul Walker)
+
+ * The behavior of `redirect` can now be configured with `absolute_redirects`
+ and `prefixed_redirects`. (Konstantin Haase)
+
+ * `send_file` now allows overriding the Last-Modified header, which defaults
+ to the file's mtime, by passing a `:last_modified` option. (Konstantin Haase)
+
+ * You can use your own template lookup method by defining `find_template`.
+ This allows, among other things, using more than one views folder.
+ (Konstantin Haase)
+
+ * Largely improved documentation. (burningTyger, Vasily Polovnyov, Gabriel
+ Andretta, Konstantin Haase)
+
+ * Improved error handling. (cactus, Konstantin Haase)
+
+ * Skip missing template engines in tests correctly. (cactus)
+
+ * Sinatra now ships with a Gemfile for development dependencies, since it eases
+ supporting different platforms, like JRuby. (Konstantin Haase)
+
+= 1.1.4 (backports release) / 2011-04-13
+
+ * Compatible with Tilt 1.3. (Konstantin Haase)
+
+= 1.1.3 / 2011-02-20
+
+ * Fixed issues with `user_agent` condition if the user agent header is missing.
+ (Konstantin Haase)
+
+ * Fix some routing tests that have been skipped by accident (Ross A. Baker)
+
+ * Fix rendering issues with Builder and Nokogiri (Konstantin Haase)
+
+ * Replace last_modified helper with better implementation. (cactus,
+ Konstantin Haase)
+
+ * Fix issue with charset not being set when using `provides` condition.
+ (Konstantin Haase)
+
+ * Fix issue with `render` not picking up all alternative file extensions for
+ a rendering engine - it was not possible to register ".html.erb" without
+ tricks. (Konstantin Haase)
+
+= 1.1.2 / 2010-10-25
+
+Like 1.1.1, but with proper CHANGES file.
+
+= 1.1.1 / 2010-10-25
+
+ * README has been translated to Russian (Nickolay Schwarz, Vasily Polovnyov)
+ and Portuguese (Luciano Sousa).
+
+ * Nested templates without a `:layout` option can now be used from the layout
+ template without causing an infinite loop. (Konstantin Haase)
+
+ * Inline templates are now encoding aware and can therefore be used with
+ unicode characters on Ruby 1.9. Magic comments at the beginning of the file
+ will be honored. (Konstantin Haase)
+
+ * Default `app_file` is set correctly when running with bundler. Using
+ bundler caused Sinatra not to find the `app_file` and therefore not to find
+ the `views` folder on it's own. (Konstantin Haase)
+
+ * Better handling of Content-Type when using `send_file`: If file extension
+ is unknown, fall back to `application/octet-stream` and do not override
+ content type if it has already been set, except if `:type` is passed
+ explicitly (Konstantin Haase)
+
+ * Path is no longer cached if changed between handlers that do pattern
+ matching. This means you can change `request.path_info` in a pattern
+ matching before filter. (Konstantin Haase)
+
+ * Headers set by cache_control now always set max_age as an Integer, making
+ sure it is compatible with RFC2616. (Konstantin Haase)
+
+ * Further improved handling of string encodings on Ruby 1.9, templates now
+ honor default_encoding and URLs support unicode characters. (Konstantin
+ Haase)
+
+= 1.1.0 / 2010-10-24
+
+ * Before and after filters now support pattern matching, including the
+ ability to use captures: "before('/user/:name') { |name| ... }". This
+ avoids manual path checking. No performance loss if patterns are avoided.
+ (Konstantin Haase)
+
+ * It is now possible to render SCSS files with the `scss` method, which
+ behaves exactly like `sass` except for the different file extension and
+ assuming the SCSS syntax. (Pedro Menezes, Konstantin Haase)
+
+ * Added `liquid`, `markdown`, `nokogiri`, `textile`, `rdoc`, `radius`,
+ `markaby`, and `coffee` rendering methods for rendering Liquid, Markdown,
+ Nokogiri, Textile, RDoc, Radius, Markaby and CoffeeScript templates.
+ (Konstantin Haase)
+
+ * Now supports byte-range requests (the HTTP_RANGE header) for static files.
+ Multi-range requests are not supported, however. (Jens Alfke)
+
+ * You can now use #settings method from class and top level for convenience.
+ (Konstantin Haase)
+
+ * Setting multiple values now no longer relies on #to_hash and therefore
+ accepts any Enumerable as parameter. (Simon Rozet)
+
+ * Nested templates default the `layout` option to `false` rather than `true`.
+ This eases the use of partials. If you wanted to render one haml template
+ embedded in another, you had to call `haml :partial, {}, :layout => false`.
+ As you almost never want the partial to be wrapped in the standard layout
+ in this situation, you now only have to call `haml :partial`. Passing in
+ `layout` explicitly is still possible. (Konstantin Haase)
+
+ * If a the return value of one of the render functions is used as a response
+ body and the content type has not been set explicitly, Sinatra chooses a
+ content type corresponding to the rendering engine rather than just using
+ "text/html". (Konstantin Haase)
+
+ * README is now available in Chinese (Wu Jiang), French (Mickael Riga),
+ German (Bernhard Essl, Konstantin Haase, burningTyger), Hungarian (Janos
+ Hardi) and Spanish (Gabriel Andretta). The extremely outdated Japanese
+ README has been updated (Kouhei Yanagita).
+
+ * It is now possible to access Sinatra's template_cache from the outside.
+ (Nick Sutterer)
+
+ * The `last_modified` method now also accepts DateTime instances and makes
+ sure the header will always be set to a string. (Konstantin Haase)
+
+ * 599 now is a legal status code. (Steve Shreeve)
+
+ * This release is compatible with Ruby 1.9.2. Sinatra was trying to read
+ non existent files Ruby added to the call stack. (Shota Fukumori,
+ Konstantin Haase)
+
+ * Prevents a memory leak on 1.8.6 in production mode. Note, however, that
+ this is due to a bug in 1.8.6 and request will have the additional overhead
+ of parsing templates again on that version. It is recommended to use at
+ least Ruby 1.8.7. (Konstantin Haase)
+
+ * Compares last modified date correctly. `last_modified` was halting only
+ when the 'If-Modified-Since' header date was equal to the time specified.
+ Now, it halts when is equal or later than the time specified (Gabriel
+ Andretta).
+
+ * Sinatra is now usable in combination with Rails 3. When mounting a Sinatra
+ application under a subpath in Rails 3, the PATH_INFO is not prefixed with
+ a slash and no routes did match. (José Valim)
+
+ * Better handling of encodings in 1.9, defaults params encoding to UTF-8.
+ (Konstantin Haase)
+
+ * `show_exeptions` handling is now triggered after custom error handlers, if
+ it is set to `:after_handlers`, thus not disabling those handler in
+ development mode. (pangel, Konstantin Haase)
+
+ * Added ability to handle weighted HTTP_ACCEPT headers. (Davide D'Agostino)
+
+ * `send_file` now always respects the `:type` option if set. Previously it
+ was discarded if no matching mime type was found, which made it impossible
+ to directly pass a mime type. (Konstantin Haase)
+
+ * `redirect` always redirects to an absolute URI, even if a relative URI was
+ passed. Ensures compatibility with RFC 2616 section 14.30. (Jean-Philippe
+ Garcia Ballester, Anthony Williams)
+
+ * Broken examples for using Erubis, Haml and Test::Unit in README have been
+ fixed. (Nick Sutterer, Doug Ireton, Jason Stewart, Eric Marden)
+
+ * Sinatra now handles SIGTERM correctly. (Patrick Collison)
+
+ * Fixes an issue with inline templates in modular applications that manually
+ call `run!`. (Konstantin Haase)
+
+ * Spaces after inline template names are now ignored (Konstantin Haase)
+
+ * It's now possible to use Sinatra with different package management
+ systems defining a custom require. (Konstantin Haase)
+
+ * Lighthouse has been dropped in favor of GitHub issues.
+
+ * Tilt is now a dependency and therefore no longer ships bundled with
+ Sinatra. (Ryan Tomayko, Konstantin Haase)
+
+ * Sinatra now depends on Rack 1.1 or higher. Rack 1.0 is no longer supported.
+ (Konstantin Haase)
+
+= 1.0 / 2010-03-23
+
+ * It's now possible to register blocks to run after each request using
+ after filters. After filters run at the end of each request, after
+ routes and error handlers. (Jimmy Schementi)
+
+ * Sinatra now uses Tilt <http://github.com/rtomayko/tilt> for rendering
+ templates. This adds support for template caching, consistent
+ template backtraces, and support for new template engines, like
+ mustache and liquid. (Ryan Tomayko)
+
+ * ERB, Erubis, and Haml templates are now compiled the first time
+ they're rendered instead of being string eval'd on each invocation.
+ Benchmarks show a 5x-10x improvement in render time. This also
+ reduces the number of objects created, decreasing pressure on Ruby's
+ GC. (Ryan Tomayko)
+
+ * New 'settings' method gives access to options in both class and request
+ scopes. This replaces the 'options' method. (Chris Wanstrath)
+
+ * New boolean 'reload_templates' setting controls whether template files
+ are reread from disk and recompiled on each request. Template read/compile
+ is cached by default in all environments except development. (Ryan Tomayko)
+
+ * New 'erubis' helper method for rendering ERB template with Erubis. The
+ erubis gem is required. (Dylan Egan)
+
+ * New 'cache_control' helper method provides a convenient way of
+ setting the Cache-Control response header. Takes a variable number
+ of boolean directives followed by a hash of value directives, like
+ this: cache_control :public, :must_revalidate, :max_age => 60
+ (Ryan Tomayko)
+
+ * New 'expires' helper method is like cache_control but takes an
+ integer number of seconds or Time object:
+ expires 300, :public, :must_revalidate
+ (Ryan Tomayko)
+
+ * New request.secure? method for checking for an SSL connection.
+ (Adam Wiggins)
+
+ * Sinatra apps can now be run with a `-o <addr>` argument to specify
+ the address to bind to. (Ryan Tomayko)
+
+ * Rack::Session::Cookie is now added to the middleware pipeline when
+ running in test environments if the :sessions option is set.
+ (Simon Rozet)
+
+ * Route handlers, before filters, templates, error mappings, and
+ middleware are now resolved dynamically up the inheritance hierarchy
+ when needed instead of duplicating the superclass's version when
+ a new Sinatra::Base subclass is created. This should fix a variety
+ of issues with extensions that need to add any of these things
+ to the base class. (Ryan Tomayko)
+
+ * Exception error handlers always override the raise_errors option now.
+ Previously, all exceptions would be raised outside of the application
+ when the raise_errors option was enabled, even if an error handler was
+ defined for that exception. The raise_errors option now controls
+ whether unhandled exceptions are raised (enabled) or if a generic 500
+ error is returned (disabled). (Ryan Tomayko)
+
+ * The X-Cascade response header is set to 'pass' when no matching route
+ is found or all routes pass. (Josh Peek)
+
+ * Filters do not run when serving static files anymore. (Ryan Tomayko)
+
+ * pass takes an optional block to be used as the route handler if no
+ subsequent route matches the request. (Blake Mizerany)
+
+The following Sinatra features have been obsoleted (removed entirely) in
+the 1.0 release:
+
+ * The `sinatra/test` library is obsolete. This includes the `Sinatra::Test`
+ module, the `Sinatra::TestHarness` class, and the `get_it`, `post_it`,
+ `put_it`, `delete_it`, and `head_it` helper methods. The
+ [`Rack::Test` library](http://gitrdoc.com/brynary/rack-test) should
+ be used instead.
+
+ * Test framework specific libraries (`sinatra/test/spec`,
+ `sinatra/test/bacon`,`sinatra/test/rspec`, etc.) are obsolete. See
+ http://www.sinatrarb.com/testing.html for instructions on setting up a
+ testing environment under each of these frameworks.
+
+ * `Sinatra::Default` is obsolete; use `Sinatra::Base` instead.
+ `Sinatra::Base` acts more like `Sinatra::Default` in development mode.
+ For example, static file serving and sexy development error pages are
+ enabled by default.
+
+ * Auto-requiring template libraries in the `erb`, `builder`, `haml`,
+ and `sass` methods is obsolete due to thread-safety issues. You must
+ require the template libraries explicitly in your app.
+
+ * The `:views_directory` option to rendering methods is obsolete; use
+ `:views` instead.
+
+ * The `:haml` and `:sass` options to rendering methods are obsolete.
+ Template engine options should be passed in the second Hash argument
+ instead.
+
+ * The `use_in_file_templates` method is obsolete. Use
+ `enable :inline_templates` or `set :inline_templates, 'path/to/file'`
+
+ * The 'media_type' helper method is obsolete. Use 'mime_type' instead.
+
+ * The 'mime' main and class method is obsolete. Use 'mime_type' instead.
+
+ * The request-level `send_data` method is no longer supported.
+
+ * The `Sinatra::Event` and `Sinatra::EventContext` classes are no longer
+ supported. This may effect extensions written for versions prior to 0.9.2.
+ See [Writing Sinatra Extensions](http://www.sinatrarb.com/extensions.html)
+ for the officially supported extensions API.
+
+ * The `set_option` and `set_options` methods are obsolete; use `set`
+ instead.
+
+ * The `:env` setting (`settings.env`) is obsolete; use `:environment`
+ instead.
+
+ * The request level `stop` method is obsolete; use `halt` instead.
+
+ * The request level `entity_tag` method is obsolete; use `etag`
+ instead.
+
+ * The request level `headers` method (HTTP response headers) is obsolete;
+ use `response['Header-Name']` instead.
+
+ * `Sinatra.application` is obsolete; use `Sinatra::Application` instead.
+
+ * Using `Sinatra.application = nil` to reset an application is obsolete.
+ This should no longer be necessary.
+
+ * Using `Sinatra.default_options` to set base configuration items is
+ obsolete; use `Sinatra::Base.set(key, value)` instead.
+
+ * The `Sinatra::ServerError` exception is obsolete. All exceptions raised
+ within a request are now treated as internal server errors and result in
+ a 500 response status.
+
+ * The `:methodoverride' option to enable/disable the POST _method hack is
+ obsolete; use `:method_override` instead.
+
+= 0.9.2 / 2009-05-18
+
+ * This version is compatible with Rack 1.0. [Rein Henrichs]
+
+ * The development-mode unhandled exception / error page has been
+ greatly enhanced, functionally and aesthetically. The error
+ page is used when the :show_exceptions option is enabled and an
+ exception propagates outside of a route handler or before filter.
+ [Simon Rozet / Matte Noble / Ryan Tomayko]
+
+ * Backtraces that move through templates now include filenames and
+ line numbers where possible. [#51 / S. Brent Faulkner]
+
+ * All templates now have an app-level option for setting default
+ template options (:haml, :sass, :erb, :builder). The app-level
+ option value must be a Hash if set and is merged with the
+ template options specified to the render method (Base#haml,
+ Base#erb, Base#builder). [S. Brent Faulkner, Ryan Tomayko]
+
+ * The method signature for all template rendering methods has
+ been unified: "def engine(template, options={}, locals={})".
+ The options Hash now takes the generic :views, :layout, and
+ :locals options but also any template-specific options. The
+ generic options are removed before calling the template specific
+ render method. Locals may be specified using either the
+ :locals key in the options hash or a second Hash option to the
+ rendering method. [#191 / Ryan Tomayko]
+
+ * The receiver is now passed to "configure" blocks. This
+ allows for the following idiom in top-level apps:
+ configure { |app| set :foo, app.root + '/foo' }
+ [TJ Holowaychuck / Ryan Tomayko]
+
+ * The "sinatra/test" lib is deprecated and will be removed in
+ Sinatra 1.0. This includes the Sinatra::Test module and
+ Sinatra::TestHarness class in addition to all the framework
+ test helpers that were deprecated in 0.9.1. The Rack::Test
+ lib should be used instead: http://gitrdoc.com/brynary/rack-test
+ [#176 / Simon Rozet]
+
+ * Development mode source file reloading has been removed. The
+ "shotgun" (http://rtomayko.github.com/shotgun/) program can be
+ used to achieve the same basic functionality in most situations.
+ Passenger users should use the "tmp/always_restart.txt"
+ file (http://tinyurl.com/c67o4h). [#166 / Ryan Tomayko]
+
+ * Auto-requiring template libs in the erb, builder, haml, and
+ sass methods is deprecated due to thread-safety issues. You must
+ require the template libs explicitly in your app file. [Simon Rozet]
+
+ * A new Sinatra::Base#route_missing method was added. route_missing
+ is sent when no route matches the request or all route handlers
+ pass. The default implementation forwards the request to the
+ downstream app when running as middleware (i.e., "@app" is
+ non-nil), or raises a NotFound exception when no downstream app
+ is defined. Subclasses can override this method to perform custom
+ route miss logic. [Jon Crosby]
+
+ * A new Sinatra::Base#route_eval method was added. The method
+ yields to the block and throws :halt with the result. Subclasses
+ can override this method to tap into the route execution logic.
+ [TJ Holowaychuck]
+
+ * Fix the "-x" (enable request mutex / locking) command line
+ argument. Passing -x now properly sets the :lock option.
+ [S. Brent Faulkner, Ryan Tomayko]
+
+ * Fix writer ("foo=") and predicate ("foo?") methods in extension
+ modules not being added to the registering class.
+ [#172 / Pat Nakajima]
+
+ * Fix in-file templates when running alongside activesupport and
+ fatal errors when requiring activesupport before sinatra
+ [#178 / Brian Candler]
+
+ * Fix various issues running on Google AppEngine.
+ [Samuel Goebert, Simon Rozet]
+
+ * Fix in-file templates __END__ detection when __END__ exists with
+ other stuff on a line [Yoji Shidara]
+
+= 0.9.1.1 / 2009-03-09
+
+ * Fix directory traversal vulnerability in default static files
+ route. See [#177] for more info.
+
+= 0.9.1 / 2009-03-01
+
+ * Sinatra now runs under Ruby 1.9.1 [#61]
+
+ * Route patterns (splats, :named, or Regexp captures) are now
+ passed as arguments to the block. [#140]
+
+ * The "helpers" method now takes a variable number of modules
+ along with the normal block syntax. [#133]
+
+ * New request-level #forward method for middleware components: passes
+ the env to the downstream app and merges the response status, headers,
+ and body into the current context. [#126]
+
+ * Requests are now automatically forwarded to the downstream app when
+ running as middleware and no matching route is found or all routes
+ pass.
+
+ * New simple API for extensions/plugins to add DSL-level and
+ request-level methods. Use Sinatra.register(mixin) to extend
+ the DSL with all public methods defined in the mixin module;
+ use Sinatra.helpers(mixin) to make all public methods defined
+ in the mixin module available at the request level. [#138]
+ See http://www.sinatrarb.com/extensions.html for details.
+
+ * Named parameters in routes now capture the "." character. This makes
+ routes like "/:path/:filename" match against requests like
+ "/foo/bar.txt"; in this case, "params[:filename]" is "bar.txt".
+ Previously, the route would not match at all.
+
+ * Added request-level "redirect back" to redirect to the referring
+ URL.
+
+ * Added a new "clean_trace" option that causes backtraces dumped
+ to rack.errors and displayed on the development error page to
+ omit framework and core library backtrace lines. The option is
+ enabled by default. [#77]
+
+ * The ERB output buffer is now available to helpers via the @_out_buf
+ instance variable.
+
+ * It's now much easier to test sessions in unit tests by passing a
+ ":session" option to any of the mock request methods. e.g.,
+ get '/', {}, :session => { 'foo' => 'bar' }
+
+ * The testing framework specific files ('sinatra/test/spec',
+ 'sinatra/test/bacon', 'sinatra/test/rspec', etc.) have been deprecated.
+ See http://sinatrarb.com/testing.html for instructions on setting up
+ a testing environment with these frameworks.
+
+ * The request-level #send_data method from Sinatra 0.3.3 has been added
+ for compatibility but is deprecated.
+
+ * Fix :provides causing crash on any request when request has no
+ Accept header [#139]
+
+ * Fix that ERB templates were evaluated twice per "erb" call.
+
+ * Fix app-level middleware not being run when the Sinatra application is
+ run as middleware.
+
+ * Fixed some issues with running under Rack's CGI handler caused by
+ writing informational stuff to stdout.
+
+ * Fixed that reloading was sometimes enabled when starting from a
+ rackup file [#110]
+
+ * Fixed that "." in route patterns erroneously matched any character
+ instead of a literal ".". [#124]
+
+= 0.9.0.4 / 2009-01-25
+
+ * Using halt with more than 1 args causes ArgumentError [#131]
+ * using halt in a before filter doesn't modify response [#127]
+ * Add deprecated Sinatra::EventContext to unbreak plugins [#130]
+ * Give access to GET/POST params in filters [#129]
+ * Preserve non-nested params in nested params hash [#117]
+ * Fix backtrace dump with Rack::Lint [#116]
+
+= 0.9.0.3 / 2009-01-21
+
+ * Fall back on mongrel then webrick when thin not found. [#75]
+ * Use :environment instead of :env in test helpers to
+ fix deprecation warnings coming from framework.
+ * Make sinatra/test/rspec work again [#113]
+ * Fix app_file detection on windows [#118]
+ * Fix static files with Rack::Lint in pipeline [#121]
+
+= 0.9.0.2 / 2009-01-18
+
+ * Halting a before block should stop processing of routes [#85]
+ * Fix redirect/halt in before filters [#85]
+
+= 0.9.0 / 2009-01-18
+
+ * Works with and requires Rack >= 0.9.1
+
+ * Multiple Sinatra applications can now co-exist peacefully within a
+ single process. The new "Sinatra::Base" class can be subclassed to
+ establish a blank-slate Rack application or middleware component.
+ Documentation on using these features is forth-coming; the following
+ provides the basic gist: http://gist.github.com/38605
+
+ * Parameters with subscripts are now parsed into a nested/recursive
+ Hash structure. e.g., "post[title]=Hello&post[body]=World" yields
+ params: {'post' => {'title' => 'Hello', 'body' => 'World'}}.
+
+ * Regular expressions may now be used in route pattens; captures are
+ available at "params[:captures]".
+
+ * New ":provides" route condition takes an array of mime types and
+ matches only when an Accept request header is present with a
+ corresponding type. [cypher]
+
+ * New request-level "pass" method; immediately exits the current block
+ and passes control to the next matching route.
+
+ * The request-level "body" method now takes a block; evaluation is
+ deferred until an attempt is made to read the body. The block must
+ return a String or Array.
+
+ * New "route conditions" system for attaching rules for when a route
+ matches. The :agent and :host route options now use this system.
+
+ * New "dump_errors" option controls whether the backtrace is dumped to
+ rack.errors when an exception is raised from a route. The option is
+ enabled by default for top-level apps.
+
+ * Better default "app_file", "root", "public", and "views" location
+ detection; changes to "root" and "app_file" automatically cascade to
+ other options that depend on them.
+
+ * Error mappings are now split into two distinct layers: exception
+ mappings and custom error pages. Exception mappings are registered
+ with "error(Exception)" and are run only when the app raises an
+ exception. Custom error pages are registered with "error(status_code)",
+ where "status_code" is an integer, and are run any time the response
+ has the status code specified. It's also possible to register an error
+ page for a range of status codes: "error(500..599)".
+
+ * In-file templates are now automatically imported from the file that
+ requires 'sinatra'. The use_in_file_templates! method is still available
+ for loading templates from other files.
+
+ * Sinatra's testing support is no longer dependent on Test::Unit. Requiring
+ 'sinatra/test' adds the Sinatra::Test module and Sinatra::TestHarness
+ class, which can be used with any test framework. The 'sinatra/test/unit',
+ 'sinatra/test/spec', 'sinatra/test/rspec', or 'sinatra/test/bacon' files
+ can be required to setup a framework-specific testing environment. See the
+ README for more information.
+
+ * Added support for Bacon (test framework). The 'sinatra/test/bacon' file
+ can be required to setup Sinatra test helpers on Bacon::Context.
+
+ * Deprecated "set_option" and "set_options"; use "set" instead.
+
+ * Deprecated the "env" option ("options.env"); use "environment" instead.
+
+ * Deprecated the request level "stop" method; use "halt" instead.
+
+ * Deprecated the request level "entity_tag" method; use "etag" instead.
+ Both "entity_tag" and "etag" were previously supported.
+
+ * Deprecated the request level "headers" method (HTTP response headers);
+ use "response['Header-Name']" instead.
+
+ * Deprecated "Sinatra.application"; use "Sinatra::Application" instead.
+
+ * Deprecated setting Sinatra.application = nil to reset an application.
+ This should no longer be necessary.
+
+ * Deprecated "Sinatra.default_options"; use
+ "Sinatra::Default.set(key, value)" instead.
+
+ * Deprecated the "ServerError" exception. All Exceptions are now
+ treated as internal server errors and result in a 500 response
+ status.
+
+ * Deprecated the "get_it", "post_it", "put_it", "delete_it", and "head_it"
+ test helper methods. Use "get", "post", "put", "delete", and "head",
+ respectively, instead.
+
+ * Removed Event and EventContext classes. Applications are defined in a
+ subclass of Sinatra::Base; each request is processed within an
+ instance.
+
+= 0.3.3 / 2009-01-06
+
+ * Pin to Rack 0.4.0 (this is the last release on Rack 0.4)
+
+ * Log unhandled exception backtraces to rack.errors.
+
+ * Use RACK_ENV environment variable to establish Sinatra
+ environment when given. Thin sets this when started with
+ the -e argument.
+
+ * BUG: raising Sinatra::NotFound resulted in a 500 response
+ code instead of 404.
+
+ * BUG: use_in_file_templates! fails with CR/LF (#45)
+
+ * BUG: Sinatra detects the app file and root path when run under
+ thin/passenger.
+
+= 0.3.2
+
+ * BUG: Static and send_file read entire file into String before
+ sending. Updated to stream with 8K chunks instead.
+
+ * Rake tasks and assets for building basic documentation website.
+ See http://sinatra.rubyforge.org
+
+ * Various minor doc fixes.
+
+= 0.3.1
+
+ * Unbreak optional path parameters [jeremyevans]
+
+= 0.3.0
+
+ * Add sinatra.gemspec w/ support for github gem builds. Forks can now
+ enable the build gem option in github to get free username-sinatra.gem
+ builds: gem install username-sinatra.gem --source=http://gems.github.com/
+
+ * Require rack-0.4 gem; removes frozen rack dir.
+
+ * Basic RSpec support; require 'sinatra/test/rspec' instead of
+ 'sinatra/test/spec' to use. [avdi]
+
+ * before filters can modify request environment vars used for
+ routing (e.g., PATH_INFO, REQUEST_METHOD, etc.) for URL rewriting
+ type functionality.
+
+ * In-file templates now uses @@ instead of ## as template separator.
+
+ * Top-level environment test predicates: development?, test?, production?
+
+ * Top-level "set", "enable", and "disable" methods for tweaking
+ app options. [rtomayko]
+
+ * Top-level "use" method for building Rack middleware pipelines
+ leading to app. See README for usage. [rtomayko]
+
+ * New "reload" option - set false to disable reloading in development.
+
+ * New "host" option - host/ip to bind to [cschneid]
+
+ * New "app_file" option - override the file to reload in development
+ mode [cschneid]
+
+ * Development error/not_found page cleanup [sr, adamwiggins]
+
+ * Remove a bunch of core extensions (String#to_param, String#from_param,
+ Hash#from_params, Hash#to_params, Hash#symbolize_keys, Hash#pass)
+
+ * Various grammar and formatting fixes to README; additions on
+ community and contributing [cypher]
+
+ * Build RDoc using Hanna template: http://sinatrarb.rubyforge.org/api
+
+ * Specs, documentation and fixes for splat'n routes [vic]
+
+ * Fix whitespace errors across all source files. [rtomayko]
+
+ * Fix streaming issues with Mongrel (body not closed). [bmizerany]
+
+ * Fix various issues with environment not being set properly (configure
+ blocks not running, error pages not registering, etc.) [cypher]
+
+ * Fix to allow locals to be passed to ERB templates [cschneid]
+
+ * Fix locking issues causing random errors during reload in development.
+
+ * Fix for escaped paths not resolving static files [Matthew Walker]
+
+= 0.2.1
+
+ * File upload fix and minor tweaks.
+
+= 0.2.0
+
+ * Initial gem release of 0.2 codebase.
86 Gemfile
@@ -0,0 +1,86 @@
+# Why use bundler?
+# 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`.
+#
+# If you have issues with a gem: `bundle install --without-coffee-script`.
+
+RUBY_ENGINE = 'ruby' unless defined? RUBY_ENGINE
+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.
+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'
+gem 'sass' if RUBY_VERSION < "2.0"
+gem 'builder'
+gem 'erubis'
+gem 'liquid' unless RUBY_ENGINE == 'rbx' and RUBY_VERSION > '1.9'
+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'
+ gem 'trinidad'
+else
+ gem 'yajl-ruby'
+ gem 'nokogiri'
+ gem 'thin'
+end
+
+if RUBY_ENGINE == "ruby" and RUBY_VERSION > '1.9'
+ gem 'less', '~> 2.0'
+else
+ gem 'less', '~> 1.0'
+end
+
+if RUBY_ENGINE != 'jruby' or not ENV['TRAVIS']
+ # C extensions
+ gem 'rdiscount'
+ platforms(:ruby_18) do
+ gem 'redcarpet'
+ gem 'mongrel'
+ end
+ gem 'RedCloth' unless RUBY_ENGINE == "macruby"
+ gem 'puma'
+
+ ## bluecloth is broken
+ #gem 'bluecloth'
+end
+
+gem 'net-http-server'
+
+platforms :ruby_18, :jruby do
+ gem 'json' unless RUBY_VERSION > '1.9' # is there a jruby but 1.8 only selector?
+end
+
+platforms :mri_18 do
+ # bundler platforms are broken
+ next if RUBY_ENGINE != 'ruby' or RUBY_VERSION > "1.8"
+ gem 'rcov'
+end
22 LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2007, 2008, 2009, 2010, 2011 Blake Mizerany
+
+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.
182 Rakefile
@@ -0,0 +1,182 @@
+require 'rake/clean'
+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
+
+CLEAN.include "**/*.rbc"
+
+def source_version
+ @source_version ||= begin
+ load './lib/sinatra/version.rb'
+ Sinatra::VERSION
+ end
+end
+
+def prev_feature
+ source_version.gsub(/^(\d\.)(\d+)\..*$/) { $1 + ($2.to_i - 1).to_s }
+end
+
+def prev_version
+ return prev_feature + '.0' if source_version.end_with? '.0'
+ source_version.gsub(/\d+$/) { |s| s.to_i - 1 }
+end
+
+# SPECS ===============================================================
+
+task :test do
+ ENV['LANG'] = 'C'
+ ENV.delete 'LC_CTYPE'
+end
+
+Rake::TestTask.new(:test) do |t|
+ t.test_files = FileList['test/*_test.rb']
+ t.ruby_opts = ['-rubygems'] if defined? Gem
+ 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
+ desc 'Measures test coverage'
+ task :coverage do
+ rm_f "coverage"
+ sh "rcov -Ilib test/*_test.rb"
+ end
+end
+
+# Website =============================================================
+
+desc 'Generate RDoc under doc/api'
+task 'doc' => ['doc:api']
+task('doc:api') { sh "yardoc -o doc/api" }
+CLEAN.include 'doc/api'
+
+# README ===============================================================
+
+task :add_template, [:name] do |t, args|
+ Dir.glob('README.*') do |file|
+ code = File.read(file)
+ if code =~ /^===.*#{args.name.capitalize}/
+ puts "Already covered in #{file}"
+ else
+ template = code[/===[^\n]*Liquid.*index\.liquid<\/tt>[^\n]*/m]
+ if !template
+ puts "Liquid not found in #{file}"
+ else
+ puts "Adding section to #{file}"
+ 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
+ end
+ end
+end
+
+# Thanks in announcement ===============================================
+
+team = ["Ryan Tomayko", "Blake Mizerany", "Simon Rozet", "Konstantin Haase"]
+desc "list of contributors"
+task :thanks, [:release,:backports] do |t, a|
+ a.with_defaults :release => "#{prev_version}..HEAD",
+ :backports => "#{prev_feature}.0..#{prev_feature}.x"
+ included = `git log --format=format:"%aN\t%s" #{a.release}`.lines.to_a
+ excluded = `git log --format=format:"%aN\t%s" #{a.backports}`.lines.to_a
+ commits = (included - excluded).group_by { |c| c[/^[^\t]+/] }
+ authors = commits.keys.sort_by { |n| - commits[n].size } - team
+ puts authors[0..-2].join(', ') << " and " << authors.last,
+ "(based on commits included in #{a.release}, but not in #{a.backports})"
+end
+
+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@gmail.com" => blake, "bmizerany" => blake,
+ "a_user@mac.com" => blake, "ichverstehe" => "Harry Vangberg",
+ "Wu Jiang (nouse)" => "Wu Jiang" }
+ `git shortlog -s #{a.commit_range}`.lines.map do |line|
+ line = line.force_encoding 'binary' if line.respond_to? :force_encoding
+ 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
+
+# PACKAGING ============================================================
+
+if defined?(Gem)
+ # Load the gemspec using the same limitations as github
+ def spec
+ require 'rubygems' unless defined? Gem::Specification
+ @spec ||= eval(File.read('sinatra.gemspec'))
+ end
+
+ def package(ext='')
+ "pkg/sinatra-#{spec.version}" + ext
+ end
+
+ desc 'Build packages'
+ task :package => %w[.gem .tar.gz].map {|e| package(e)}
+
+ desc 'Build and install as local gem'
+ task :install => package('.gem') do
+ sh "gem install #{package('.gem')}"
+ end
+
+ directory 'pkg/'
+ CLOBBER.include('pkg')
+
+ file package('.gem') => %w[pkg/ sinatra.gemspec] + spec.files do |f|
+ sh "gem build sinatra.gemspec"
+ mv File.basename(f.name), f.name
+ end
+
+ file package('.tar.gz') => %w[pkg/] + spec.files do |f|
+ sh <<-SH
+ git archive \
+ --prefix=sinatra-#{source_version}/ \
+ --format=tar \
+ HEAD | gzip > #{f.name}
+ SH
+ 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')} &&
+ git commit --allow-empty -a -m '#{source_version} release' &&
+ git tag -s v#{source_version} -m '#{source_version} release' &&
+ git tag -s #{source_version} -m '#{source_version} release' &&
+ git push && (git push sinatra || true) &&
+ git push --tags && (git push sinatra --tags || true)
+ SH
+ end
+end
4 lib/sinatra.rb
@@ -0,0 +1,4 @@
+require 'sinatra/base'
+require 'sinatra/main'
+
+enable :inline_templates
1,774 lib/sinatra/base.rb
@@ -0,0 +1,1774 @@
+# external dependencies
+require 'rack'
+require 'tilt'
+require "rack/protection"
+
+# stdlib dependencies
+require 'thread'
+require 'time'
+require 'uri'
+
+# other files we need
+require 'sinatra/showexceptions'
+require 'sinatra/version'
+
+module Sinatra
+ # The request object. See Rack::Request for more info:
+ # http://rack.rubyforge.org/doc/classes/Rack/Request.html
+ class Request < Rack::Request
+ # Returns an array of acceptable media types for the response
+ def accept
+ @env['sinatra.accept'] ||= begin
+ entries = @env['HTTP_ACCEPT'].to_s.split(',')
+ entries.map { |e| accept_entry(e) }.sort_by(&:last).map(&:first)
+ end
+ end
+
+ def preferred_type(*types)
+ return accept.first if types.empty?
+ types.flatten!
+ accept.detect do |pattern|
+ type = types.detect { |t| File.fnmatch(pattern, t) }
+ return type if type
+ end
+ end
+
+ alias accept? preferred_type
+ alias secure? ssl?
+
+ def forwarded?
+ @env.include? "HTTP_X_FORWARDED_HOST"
+ end
+
+ def safe?
+ get? or head? or options? or trace?
+ end
+
+ def idempotent?
+ safe? or put? or delete?
+ end
+
+ private
+
+ def accept_entry(entry)
+ 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
+
+ # The response object. See Rack::Response and Rack::ResponseHelpers for
+ # more info:
+ # 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
+ 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)
+ # if some other code has already set Content-Length, don't muck with it
+ # currently, this would be the static file-handler
+ 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
+
+ # Some Rack handlers (Thin, Rainbows!) implement an extended body object protocol, however,
+ # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.
+ # This middleware will detect an extended body object and will make sure it reaches the
+ # handler directly. We do this here, so our middleware and middleware set up by the app will
+ # still be able to run.
+ class ExtendedRack < Struct.new(:app)
+ def call(env)
+ result, callback = app.call(env), env['async.callback']
+ return result unless callback and async?(*result)
+ after_response { callback.call result }
+ setup_close(env, *result)
+ throw :async
+ end
+
+ private
+
+ def setup_close(env, status, header, body)
+ return unless body.respond_to? :close and env.include? 'async.close'
+ env['async.close'].callback { body.close }
+ env['async.close'].errback { body.close }
+ end
+
+ def after_response(&block)
+ raise NotImplementedError, "only supports EventMachine at the moment" unless defined? EventMachine
+ EventMachine.next_tick(&block)
+ end
+
+ def async?(status, headers, body)
+ return true if status == -1
+ body.respond_to? :callback and body.respond_to? :errback
+ end
+ end
+
+ # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing,
+ # if another CommonLogger is already in the middleware chane.
+ class CommonLogger < Rack::CommonLogger
+ def call(env)
+ env['sinatra.commonlogger'] ? @app.call(env) : super
+ end
+
+ superclass.class_eval do
+ alias call_without_check call unless method_defined? :call_without_check
+ def call(env)
+ env['sinatra.commonlogger'] = true
+ call_without_check(env)
+ end
+ end
+ end
+
+ class NotFound < NameError #:nodoc:
+ def http_status; 404 end
+ end
+
+ # Methods available to routes, before/after filters, and views.
+ module Helpers
+ # Set or retrieve the response status code.
+ def status(value=nil)
+ response.status = value if value
+ response.status
+ end
+
+ # Set or retrieve the response body. When a block is given,
+ # evaluation is deferred until the body is read with #each.
+ def body(value=nil, &block)
+ if block_given?
+ def block.each; yield(call) end
+ response.body = block
+ elsif value
+ response.body = value
+ else
+ response.body
+ end
+ end
+
+ # Halt processing and redirect to the URI provided.
+ def redirect(uri, *args)
+ 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"
+ response['Location'] = uri(uri, settings.absolute_redirects?, settings.prefixed_redirects?)
+ halt(*args)
+ end
+
+ # Generates the absolute URI for a given path in the app.
+ # Takes Rack routers and reverse proxies into account.
+ def uri(addr = nil, absolute = true, add_script_name = true)
+ return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
+ uri = [host = ""]
+ if absolute
+ host << "http#{'s' if request.secure?}://"
+ if request.forwarded? or request.port != (request.secure? ? 443 : 80)
+ host << request.host_with_port
+ else
+ host << request.host
+ end
+ end
+ uri << request.script_name.to_s if add_script_name
+ uri << (addr ? addr : request.path_info).to_s
+ File.join uri
+ end
+
+ alias url uri
+ alias to uri
+
+ # Halt processing and return the error status provided.
+ def error(code, body=nil)
+ code, body = 500, code.to_str if code.respond_to? :to_str
+ response.body = body unless body.nil?
+ halt code
+ end
+
+ # Halt processing and return a 404 Not Found.
+ def not_found(body=nil)
+ error 404, body
+ end
+
+ # Set multiple response headers with Hash.
+ def headers(hash=nil)
+ response.headers.merge! hash if hash
+ response.headers
+ end
+
+ # Access the underlying Rack session.
+ def session
+ 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)
+ end
+
+ # Set the Content-Type of the response body given a media type or file
+ # extension.
+ def content_type(type = nil, params={})
+ return response['Content-Type'] unless type
+ default = params.delete :default
+ mime_type = mime_type(type) || default
+ fail "Unknown media type: %p" % type if mime_type.nil?
+ mime_type = mime_type.dup
+ unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type }
+ params[:charset] = params.delete('charset') || settings.default_encoding
+ end
+ params.delete :charset if mime_type.include? 'charset'
+ unless params.empty?
+ mime_type << (mime_type.include?(';') ? ', ' : ';')
+ mime_type << params.map { |kv| kv.join('=') }.join(', ')
+ end
+ response['Content-Type'] = mime_type
+ end
+
+ # Set the Content-Disposition to "attachment" with the specified filename,
+ # instructing the user agents to prompt to save.
+ def attachment(filename=nil)
+ response['Content-Disposition'] = 'attachment'
+ 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={})
+ if opts[:type] or not response['Content-Type']
+ content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
+ end
+
+ if opts[:disposition] == 'attachment' || opts[:filename]
+ attachment opts[:filename] || path
+ elsif opts[:disposition] == 'inline'
+ response['Content-Disposition'] = 'inline'
+ 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 }
+ headers['Content-Length'] = result[1]['Content-Length']
+ halt opts[:status] || result[0], result[2]
+ rescue Errno::ENOENT
+ not_found
+ 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
+ close unless @keep_open
+ end
+ end
+
+ def <<(data)
+ @scheduler.schedule { @front.call(data.to_s) }
+ self
+ end
+
+ def callback(&block)
+ return yield if @closed
+ @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
+ body Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } }
+ end
+
+ # Specify response freshness policy for HTTP caches (Cache-Control header).
+ # Any number of non-value directives (:public, :private, :no_cache,
+ # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
+ # a Hash of value directives (:max_age, :min_stale, :s_max_age).
+ #
+ # cache_control :public, :must_revalidate, :max_age => 60
+ # => Cache-Control: public, must-revalidate, max-age=60
+ #
+ # See RFC 2616 / 14.9 for more on standard cache control directives:
+ # http://tools.ietf.org/html/rfc2616#section-14.9.1
+ def cache_control(*values)
+ if values.last.kind_of?(Hash)
+ hash = values.pop
+ hash.reject! { |k,v| v == false }
+ hash.reject! { |k,v| values << k if v == true }
+ else
+ hash = {}
+ end
+
+ 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"
+ values << [key, value].join('=')
+ end
+
+ response['Cache-Control'] = values.join(', ') if values.any?
+ end
+
+ # Set the Expires header and Cache-Control/max-age directive. Amount
+ # can be an integer number of seconds in the future or a Time object
+ # indicating when the response should be considered "stale". The remaining
+ # "values" arguments are passed to the #cache_control helper:
+ #
+ # expires 500, :public, :must_revalidate
+ # => Cache-Control: public, must-revalidate, max-age=60
+ # => Expires: Mon, 08 Jun 2009 08:50:17 GMT
+ #
+ def expires(amount, *values)
+ values << {} unless values.last.kind_of?(Hash)
+
+ if amount.is_a? Integer
+ time = Time.now + amount.to_i
+ max_age = amount
+ else
+ time = time_for amount
+ max_age = time - Time.now
+ end
+
+ values.last.merge!(:max_age => max_age)
+ cache_control(*values)
+
+ response['Expires'] = time.httpdate
+ end
+
+ # Set the last modified time of the resource (HTTP 'Last-Modified' header)
+ # and halt if conditional GET matches. The +time+ argument is a Time,
+ # DateTime, or other object that responds to +to_time+.
+ #
+ # When the current request includes an 'If-Modified-Since' header that is
+ # equal or later than the time specified, execution is immediately halted
+ # with a '304 Not Modified' response.
+ def last_modified(time)
+ return unless time
+ time = time_for time
+ response['Last-Modified'] = time.httpdate
+ 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
+
+ # Set the response entity tag (HTTP 'ETag' header) and halt if conditional
+ # GET matches. The +value+ argument is an identifier that uniquely
+ # identifies the current version of the resource. The +kind+ argument
+ # indicates whether the etag should be used as a :strong (default) or :weak
+ # cache validator.
+ #
+ # 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, 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
+
+ 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
+
+ # Sugar for redirect (example: redirect back)
+ def back
+ request.referer
+ end
+
+ # 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
+
+ # 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
+ elsif value.is_a? Time
+ value
+ elsif value.respond_to? :new_offset
+ # DateTime#to_time does the same on 1.9
+ d = value.new_offset 0
+ t = Time.utc d.year, d.mon, d.mday, d.hour, d.min, d.sec + d.sec_fraction
+ t.getlocal
+ elsif value.respond_to? :mday
+ # Date#to_time does the same on 1.9
+ Time.local(value.year, value.mon, value.mday)
+ elsif value.is_a? Numeric
+ Time.at value
+ else
+ Time.parse value.to_s
+ end
+ rescue ArgumentError => boom
+ raise boom
+ 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
+
+ def with_params(temp_params)
+ original, @params = @params, temp_params
+ yield
+ ensure
+ @params = original if original
+ 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.
+ #
+ # `template` is either the name or path of the template as symbol
+ # (Use `:'subdir/myview'` for views in subdirectories), or a string
+ # that will be rendered.
+ #
+ # Possible options are:
+ # :content_type The content type to use, same arguments as content_type.
+ # :layout If set to false, no layout is rendered, otherwise
+ # the specified layout is used (Ignored for `sass` and `less`)
+ # :layout_engine Engine to use for rendering the layout.
+ # :locals A hash with local variables that should be available
+ # in the template
+ # :scope If set, template is evaluate with the binding of the given
+ # object rather than the application instance.
+ # :views Views directory to use.
+ module Templates
+ module ContentTyped
+ 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
+
+ def haml(template, options={}, locals={})
+ render :haml, template, options, locals
+ end
+
+ def sass(template, options={}, locals={})
+ options.merge! :layout => false, :default_content_type => :css
+ render :sass, template, options, locals
+ end
+
+ def scss(template, options={}, locals={})
+ options.merge! :layout => false, :default_content_type => :css
+ render :scss, template, options, locals
+ end
+
+ def less(template, options={}, locals={})
+ options.merge! :layout => false, :default_content_type => :css
+ render :less, template, options, locals
+ end
+
+ def builder(template=nil, options={}, locals={}, &block)
+ options[:default_content_type] = :xml
+ render_ruby(:builder, template, options, locals, &block)
+ end
+
+ def liquid(template, options={}, locals={})
+ render :liquid, template, options, locals
+ end
+
+ def markdown(template, options={}, locals={})
+ render :markdown, template, options, locals
+ end
+
+ def textile(template, options={}, locals={})
+ render :textile, template, options, locals
+ end
+
+ def rdoc(template, options={}, locals={})
+ render :rdoc, template, options, locals
+ end
+
+ def radius(template, options={}, locals={})
+ render :radius, template, options, locals
+ end
+
+ def markaby(template=nil, options={}, locals={}, &block)
+ render_ruby(:mab, template, options, locals, &block)
+ end
+
+ def coffee(template, options={}, locals={})
+ options.merge! :layout => false, :default_content_type => :js
+ render :coffee, template, options, locals
+ end
+
+ def nokogiri(template=nil, options={}, locals={}, &block)
+ options[:default_content_type] = :xml
+ render_ruby(:nokogiri, template, options, locals, &block)
+ end
+
+ def slim(template, options={}, locals={})
+ render :slim, template, options, locals
+ end
+
+ def creole(template, options={}, locals={})
+ render :creole, template, options, locals
+ end
+
+ def yajl(template, options={}, locals={})
+ options[:default_content_type] = :json
+ render :yajl, 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 engines.include? engine
+ yield ::File.join(views, "#{name}.#{ext}")
+ end
+ end
+
+ private
+ # logic shared between builder and nokogiri
+ def render_ruby(engine, template, options={}, locals={}, &block)
+ options, template = template, nil if template.is_a?(Hash)
+ template = Proc.new { block } if template.nil?
+ render engine, template, options, locals
+ end
+
+ def render(engine, data, options={}, locals={}, &block)
+ # merge app-level options
+ options = settings.send(engine).merge(options) if settings.respond_to?(engine)
+ options[:outvar] ||= '@_out_buf'
+ options[:default_encoding] ||= settings.default_encoding
+
+ # extract generic options
+ locals = options.delete(:locals) || locals || {}
+ views = options.delete(:views) || settings.views || "./views"
+ layout = options.delete(:layout)
+ eat_errors = layout.nil?
+ layout = @default_layout if layout.nil? or layout == true
+ content_type = options.delete(:content_type) || options.delete(:default_content_type)
+ layout_engine = options.delete(:layout_engine) || engine
+ scope = options.delete(:scope) || self
+
+ # compile and render template
+ 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
+ options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope)
+ catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } }
+ end
+
+ output.extend(ContentTyped).content_type = content_type if content_type
+ output
+ end
+
+ def compile_template(engine, data, options, views)
+ eat_errors = options.delete :eat_errors
+ template_cache.fetch engine, data, options do
+ template = Tilt[engine]
+ raise "Template engine not found: #{engine}" if template.nil?
+
+ case data
+ when Symbol
+ body, path, line = settings.templates[data]
+ if body
+ body = body.call if body.respond_to?(:call)
+ template.new(path, line.to_i, options) { body }
+ else
+ found = false
+ @preferred_extension = engine.to_s
+ find_template(views, data, template) do |file|
+ path ||= file # keep the initial path rather than the last one
+ if found = File.exists?(file)
+ path = file
+ break
+ end
+ end
+ throw :layout_missing if eat_errors and not found
+ template.new(path, 1, options)
+ end
+ 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, "Sorry, don't know how to render #{data.inspect}."
+ end
+ end
+ end
+ end
+
+ # Base class for all Sinatra applications and middleware.
+ class Base
+ include Rack::Utils
+ include Helpers
+ include Templates
+
+ attr_accessor :app
+ attr_reader :template_cache
+
+ def initialize(app=nil)
+ super()
+ @app = app
+ @template_cache = Tilt::Cache.new
+ yield self if block_given?
+ end
+
+ # Rack call interface.
+ def call(env)
+ dup.call!(env)
+ end
+
+ attr_accessor :env, :request, :response, :params
+
+ def call!(env) # :nodoc:
+ @env = env
+ @request = Request.new(env)
+ @response = Response.new
+ @params = indifferent_params(@request.params)
+ template_cache.clear if settings.reload_templates
+ force_encoding(@params)
+
+ @response['Content-Type'] = nil
+ invoke { dispatch! }
+ invoke { error_block!(response.status) }
+
+ unless @response['Content-Type']
+ if Array === body and body[0].respond_to? :content_type
+ content_type body[0].content_type
+ else
+ content_type :html
+ end
+ end
+
+ @response.finish
+ end
+
+ # Access settings defined with Base.set.
+ def self.settings
+ self
+ end
+
+ # Access settings defined with Base.set.
+ def settings
+ self.class.settings
+ end
+
+ 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
+ # of the request, and returns the specified response.
+ def halt(*response)
+ response = response.first if response.length == 1
+ throw :halt, response
+ end
+
+ # Pass control to the next matching route.
+ # If there are no more matching routes, Sinatra will
+ # return a 404 response.
+ def pass(&block)
+ throw :pass, block
+ end
+
+ # Forward the request to the downstream app -- middleware only.
+ def forward
+ fail "downstream app not set" unless @app.respond_to? :call
+ status, headers, body = @app.call env
+ @response.status = status
+ @response.body = body
+ @response.headers.merge! headers
+ nil
+ end
+
+ private
+ # 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 { |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 |*args|
+ route_eval { block[*args] }
+ end
+ end
+ end
+
+ # Run routes defined in superclass.
+ if base.superclass.respond_to?(:routes)
+ return route!(base.superclass, pass_block)
+ end
+
+ route_eval(&pass_block) if pass_block
+ route_missing
+ end
+
+ # Run a route block and throw :halt with the result.
+ def route_eval
+ throw :halt, yield
+ end
+
+ # If the current request matches pattern and conditions, fill params
+ # with keys and call the given block.
+ # Revert params afterwards.
+ #
+ # Returns pass block.
+ def process_route(pattern, keys, conditions, block = nil, values = [])
+ route = @request.path_info
+ route = '/' if route.empty? and not settings.empty_path_info?
+ return unless match = pattern.match(route)
+ values += match.captures.to_a.map { |v| force_encoding URI.decode_www_form_component(v) if v }
+
+ if values.any?
+ original, @params = params, params.merge('splat' => [], 'captures' => values)
+ keys.zip(values) { |k,v| Array === @params[k] ? @params[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 if original
+ end
+
+ # No matching route was found or all routes passed. The default
+ # implementation is to forward the request downstream when running
+ # as middleware (@app is non-nil); when no downstream app is set, raise
+ # a NotFound exception. Subclasses can override this method to perform
+ # custom route miss logic.
+ def route_missing
+ if @app
+ forward
+ else
+ raise NotFound
+ end
+ end
+
+ # 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_folder).nil?
+ public_dir = File.expand_path(public_dir)
+
+ path = File.expand_path(public_dir + unescape(request.path_info))
+ 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
+
+ # Enable string or symbol key access to the nested params hash.
+ def indifferent_params(object)
+ case object
+ when Hash
+ new_hash = indifferent_hash
+ object.each { |key, value| new_hash[key] = indifferent_params(value) }
+ new_hash
+ when Array
+ object.map { |item| indifferent_params(item) }
+ else
+ object
+ end
+ end
+
+ # Creates a Hash with indifferent access.
+ def indifferent_hash
+ Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
+ end
+
+ # Run the block with 'throw :halt' support and apply result to the response.
+ 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
+ nil # avoid double setting the same response tuple twice
+ end
+
+ # Dispatch a request with error handling.
+ def dispatch!
+ invoke do
+ static! if settings.static? && (request.get? || request.head?)
+ filter! :before
+ route!
+ end
+ rescue ::Exception => boom
+ invoke { handle_exception!(boom) }
+ ensure
+ filter! :after unless env['sinatra.static_file']
+ end
+
+ # Error handling during requests.
+ def handle_exception!(boom)
+ @env['sinatra.error'] = boom
+
+ if boom.respond_to? :http_status
+ status(boom.http_status)
+ elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
+ status(boom.code)
+ else
+ status(500)
+ end
+
+ status(500) unless status.between? 400, 599
+
+ if server_error?
+ dump_errors! boom if settings.dump_errors?
+ raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
+ end
+
+ 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!(key, *block_params)
+ base = settings
+ while base.respond_to?(:errors)
+ next base = base.superclass unless args_array = base.errors[key]
+ args_array.reverse_each do |args|
+ first = args == args_array.first
+ args += [block_params]
+ resp = process_route(*args)
+ return resp unless resp.nil? && !first
+ end
+ end
+ 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\t")
+ @env['rack.errors'].puts(msg)
+ end
+
+ class << self
+ attr_reader :routes, :filters, :templates, :errors
+
+ # Removes all routes, filters, middleware and extension hooks from the
+ # current class (not routes/filters/... defined by its superclass).
+ def reset!
+ @conditions = []
+ @routes = {}
+ @filters = {:before => [], :after => []}
+ @errors = {}
+ @middleware = []
+ @prototype = nil
+ @extensions = []
+
+ if superclass.respond_to?(:templates)
+ @templates = Hash.new { |hash,key| superclass.templates[key] }
+ else
+ @templates = {}
+ end
+ end
+
+ # Extension modules registered on this class and all superclasses.
+ def extensions
+ if superclass.respond_to?(:extensions)
+ (@extensions + superclass.extensions).uniq
+ else
+ @extensions
+ end
+ end
+
+ # Middleware used in this class and all superclasses.
+ def middleware
+ if superclass.respond_to?(:middleware)
+ superclass.middleware + @middleware
+ else
+ @middleware
+ end
+ end
+
+ # 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), ignore_setter = false, &block)
+ raise ArgumentError if block and !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) }
+ 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
+ getter = value.inspect
+ when Hash
+ setter = proc do |val|