From 459369eb66224836f72e21bbece58c007f3422fa Mon Sep 17 00:00:00 2001 From: Kashyap Date: Sun, 20 Jan 2013 12:17:56 +0100 Subject: [PATCH] Changed the README from .rdoc to .md Did this manually so I think I got most of the conversion right. Fixes #607 Squashed commit of the following: commit e0cbf22b35e08aba053d19674ed6295bd6b71ced Author: Kashyap Date: Sun Jan 20 06:16:26 2013 +0530 Removed the
  • tag for the markdown template list commit f999620eac07b0b970b7b2ea42d4386f1dc83097 Merge: 95ab367 eed4f71 Author: Kashyap Date: Sun Jan 20 06:06:39 2013 +0530 Merge branch 'master' of git://github.com/sinatra/sinatra commit 95ab3673ece8996991fef505e3a3556f8c06e49f Author: Kashyap Date: Fri Jan 11 07:53:31 2013 +0530 Fixed a typo commit 90a335049b711ee7fc1915616cf13bd926539ee6 Author: Kashyap Date: Fri Jan 11 07:44:22 2013 +0530
     tags break the whole layout. Replacing them with  tags
    
    commit e47cfff420c8e07b8b04c1ac4cab85ba9b78ca53
    Author: Kashyap 
    Date:   Fri Jan 11 07:18:10 2013 +0530
    
        
     tags break the whole layout. Testing out  tags
    
    commit 40f59da1f4d52fda5018bf91a9a317f52113a116
    Author: Kashyap 
    Date:   Fri Jan 11 06:59:02 2013 +0530
    
        Added the html definition lists
    
    commit 63a18c478c61d3c88ecd67a42fcca001deff8ea2
    Author: Kashyap 
    Date:   Fri Jan 11 03:39:57 2013 +0530
    
        Testing out some HTML in Markdown
    
    commit 8d9442be525e909acd1f5b5c1bd4972c8bf810b2
    Author: Kashyap 
    Date:   Thu Jan 10 01:14:16 2013 +0530
    
        Trying out code blocks instead of Rdoc tables
    
    commit 766c80f1af854e03161192fadec3edf6e5093eaa
    Author: Kashyap 
    Date:   Thu Jan 10 00:40:48 2013 +0530
    
        Fixed some more errors.
    
    commit 2e9dc82a5a246007a4976db95b4608212c50841b
    Author: Kashyap 
    Date:   Thu Jan 10 00:39:25 2013 +0530
    
        Fixed some more errors.
    
    commit f12333bcd3515f63b3c998da98cf1e8b68bc01f9
    Author: Kashyap 
    Date:   Thu Jan 10 00:33:47 2013 +0530
    
        Fixed a typo and finished the conversion.
    
    commit 72830645c0dc235f62ccf1f1ffc8d8deaf39519d
    Author: Kashyap 
    Date:   Thu Jan 10 00:31:52 2013 +0530
    
        First walkthrough done.
    
    commit a43f41fab945ac9dece1f7ee95a29526c6d7c25d
    Author: Kashyap 
    Date:   Wed Jan 9 22:32:34 2013 +0530
    
        First walkthrough done.
    
    commit eb73fe095f191dc7c7bae2acdbfce436ba21f1e3
    Author: Kashyap 
    Date:   Wed Jan 9 22:24:59 2013 +0530
    
        testing blockquote
    
    commit 3cb91c48286f308de144b763e51aebdb02f034ad
    Author: Kashyap 
    Date:   Wed Jan 9 22:19:21 2013 +0530
    
        testing blockquote
    
    commit ae9119393e7d463c2916074ff47e5af0b316ec82
    Author: Kashyap 
    Date:   Wed Jan 9 21:49:19 2013 +0530
    
        testing blockquote
    
    commit 64a659c731036474435eb57349afe17bfde7a801
    Author: Kashyap 
    Date:   Wed Jan 9 21:48:02 2013 +0530
    
        testing blockquote
    
    commit 8d718ef1aa1b558cf052768b0ced13cf102d0639
    Author: Kashyap 
    Date:   Wed Jan 9 21:42:17 2013 +0530
    
        testing blockquote
    
    commit 0b16fc50d868f77f0ecaeeed9177dcf412d7d5d3
    Author: Kashyap 
    Date:   Wed Jan 9 21:37:55 2013 +0530
    
        rdoc to md conversion in progress
    
    Signed-off-by: Konstantin Haase 
    ---
     .gitignore  |    5 +-
     README.md   | 2640 +++++++++++++++++++++++++++++++++++++++++++++++++++
     README.rdoc | 2049 ---------------------------------------
     3 files changed, 2644 insertions(+), 2050 deletions(-)
     create mode 100644 README.md
     delete mode 100644 README.rdoc
    
    diff --git a/.gitignore b/.gitignore
    index a3bb89a2..2b6c41c9 100644
    --- a/.gitignore
    +++ b/.gitignore
    @@ -1,6 +1,9 @@
     # please add general patterns to your global ignore list
     # see https://github.com/github/gitignore#readme
    -
    +.DS_STORE
    +*.swp
    +*.rbc
    +*.sass-cache
     /pkg
     /doc/api
     /Gemfile.lock
    diff --git a/README.md b/README.md
    new file mode 100644
    index 00000000..fbb71ddf
    --- /dev/null
    +++ b/README.md
    @@ -0,0 +1,2640 @@
    +# Sinatra
    +
    +Sinatra is a [DSL](http://en.wikipedia.org/wiki/Domain-specific_language) for
    +quickly creating web applications in Ruby with minimal effort:
    +
    +```ruby
    +    # myapp.rb
    +    require 'sinatra'
    +
    +    get '/' do
    +      'Hello world!'
    +    end
    +```
    +
    +Install the gem and run with:
    +
    +```ruby
    +    gem install sinatra
    +    ruby -rubygems myapp.rb
    +```
    +
    +View at: http://localhost:4567
    +
    +It is recommended to also run `gem install thin`, which Sinatra will
    +pick up if available.
    +
    +## Routes
    +
    +In Sinatra, a route is an HTTP method paired with a URL-matching pattern.
    +Each route is associated with a block:
    +
    +```ruby
    +    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
    +```
    +
    +Routes are matched in the order they are defined. The first route that
    +matches the request is invoked.
    +
    +Route patterns may include named parameters, accessible via the
    +`params` hash:
    +
    +```ruby
    +    get '/hello/:name' do
    +      # matches "GET /hello/foo" and "GET /hello/bar"
    +      # params[:name] is 'foo' or 'bar'
    +      "Hello #{params[:name]}!"
    +    end
    +```
    +
    +You can also access named parameters via block parameters:
    +
    +```ruby
    +    get '/hello/:name' do |n|
    +      "Hello #{n}!"
    +    end
    +```
    +
    +Route patterns may also include splat (or wildcard) parameters, accessible
    +via the `params[:splat]` array:
    +
    +```ruby
    +    get '/say/*/to/*' do
    +      # matches /say/hello/to/world
    +      params[:splat] # => ["hello", "world"]
    +    end
    +
    +    get '/download/*.*' do
    +      # matches /download/path/to/file.xml
    +      params[:splat] # => ["path/to/file", "xml"]
    +    end
    +```
    +
    +Or with block parameters:
    +
    +```ruby
    +    get '/download/*.*' do |path, ext|
    +      [path, ext] # => ["path/to/file", "xml"]
    +    end
    +```
    +
    +Route matching with Regular Expressions:
    +
    +```ruby
    +    get %r{/hello/([\w]+)} do
    +      "Hello, #{params[:captures].first}!"
    +    end
    +```
    +
    +Or with a block parameter:
    +
    +```ruby
    +    get %r{/hello/([\w]+)} do |c|
    +      "Hello, #{c}!"
    +    end
    +```
    +
    +Route patterns may have optional parameters:
    +
    +```ruby
    +    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:
    +
    +```ruby
    +    get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
    +      "You're using Songbird version #{params[:agent][0]}"
    +    end
    +
    +    get '/foo' do
    +      # Matches non-songbird browsers
    +    end
    +```
    +
    +Other available conditions are `host_name` and `provides`:
    +
    +```ruby
    +    get '/', :host_name => /^admin\./ do
    +      "Admin Area, Access denied!"
    +    end
    +
    +    get '/', :provides => 'html' do
    +      haml :index
    +    end
    +
    +    get '/', :provides => ['rss', 'atom', 'xml'] do
    +      builder :feed
    +    end
    +```
    +
    +You can easily define your own conditions:
    +
    +```ruby
    +    set(:probability) { |value| condition { rand <= value } }
    +
    +    get '/win_a_car', :probability => 0.1 do
    +      "You won!"
    +    end
    +
    +    get '/win_a_car' do
    +      "Sorry, you lost."
    +    end
    +```
    +
    +For a condition that takes multiple values use a splat:
    +
    +```ruby
    +    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
    +on to the HTTP client, or at least the next middleware in the Rack stack.
    +Most commonly, this is a string, as in the above examples. But other values are
    +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: `[status (Fixnum), headers (Hash), response
    +  body (responds to #each)]`
    +* An Array with two elements: `[status (Fixnum), response body (responds to
    +  #each)]`
    +* An object that responds to `#each` 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:
    +
    +```ruby
    +    class Stream
    +      def each
    +        100.times { |i| yield "#{i}\n" }
    +      end
    +    end
    +
    +    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
    +and regular expressions as route matches. However, it does not stop there. You
    +can easily define your own matchers:
    +
    +```ruby
    +    class AllButPattern
    +      Match = Struct.new(:captures)
    +
    +      def initialize(except)
    +        @except   = except
    +        @captures = Match.new([])
    +      end
    +
    +      def match(str)
    +        @captures unless @except === str
    +      end
    +    end
    +
    +    def all_but(pattern)
    +      AllButPattern.new(pattern)
    +    end
    +
    +    get all_but("/index") do
    +      # ...
    +    end
    +```
    +
    +Note that the above example might be over-engineered, as it can also be
    +expressed as:
    +
    +```ruby
    +    get // do
    +      pass if request.path_info == "/index"
    +      # ...
    +    end
    +```
    +
    +Or, using negative look ahead:
    +
    +```ruby
    +    get %r{^(?!/index$)} do
    +      # ...
    +    end
    +```
    +
    +### Static Files
    +
    +Static files are served from the `./public` directory. You can specify
    +a different location by setting the `:public_folder` option:
    +
    +```ruby
    +    set :public_folder, File.dirname(__FILE__) + '/static'
    +```
    +
    +Note that the public directory name is not included in the URL. A file
    +`./public/css/style.css` is made available as
    +`http://example.com/css/style.css`.
    +
    +Use the `:static_cache_control` setting (see below) to add
    +`Cache-Control` header info.
    +
    +### Views / Templates
    +
    +Each template language is exposed via its own rendering method. These
    +methods simply return a string:
    +
    +```ruby
    +    get '/' do
    +      erb :index
    +    end
    +```
    +
    +This renders `views/index.erb`.
    +
    +Instead of a template name, you can also just pass in the template content
    +directly:
    +
    +```ruby
    +    get '/' do
    +      code = "<%= Time.now %>"
    +      erb code
    +    end
    +```
    +
    +Templates take a second argument, the options hash:
    +
    +```ruby
    +    get '/' do
    +      erb :index, :layout => :post
    +    end
    +```
    +
    +This will render `views/index.erb` embedded in the
    +`views/post.erb` (default is `views/layout.erb`, if it exists).
    +
    +Any options not understood by Sinatra will be passed on to the template
    +engine:
    +
    +```ruby
    +    get '/' do
    +      haml :index, :format => :html5
    +    end
    +```
    +
    +You can also set options per template language in general:
    +
    +```ruby
    +    set :haml, :format => :html5
    +
    +    get '/' do
    +      haml :index
    +    end
    +```
    +
    +Options passed to the render method override options set via `set`.
    +
    +Available Options:
    +
    +
    +
    locals
    +
    + List of locals passed to the document. Handy with partials. + Example: erb "<%= foo %>", :locals => {:foo => "bar"} +
    + +
    default_encoding
    +
    + String encoding to use if uncertain. Defaults to + settings.default_encoding. +
    + +
    views
    +
    + Views folder to load templates from. Defaults to settings.views. +
    + +
    layout
    +
    + Whether to use a layout (true or false), if it's a Symbol, specifies + what template to use. Example: erb :index, :layout => !request.xhr? +
    + +
    content_type
    +
    + Content-Type the template produces, default depends on template language. +
    + +
    scope
    +
    + Scope to render template under. Defaults to the application instance. If you + change this, instance variables and helper methods will not be available. +
    + +
    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: set :rdoc, :layout_engine => :erb +
    + +
    + Templates are assumed to be located directly under the `./views` + directory. To use a different views directory: + set :views, settings.root + '/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: 'subdir/template'). You must use a symbol because + otherwise rendering methods will render any strings passed to them + directly. +
    +
    + +### Available Template Languages + +Some languages have multiple implementations. To specify what implementation +to use (and to be thread-safe), you should simply require it first: + +```ruby + require 'rdiscount' # or require 'bluecloth' + get('/') { markdown :index } +``` + +#### Haml Templates + + + + + + + + + + + + + + +
    Dependencyhaml
    File Extension.haml
    Examplehaml :index, :format => :html5
    + +#### Erb Templates + + + + + + + + + + + + + + +
    Dependency + erubis + or erb (included in Ruby) +
    File Extensions.erb, .rhtml or .erubis (Erubis only)
    Exampleerb :index
    + +#### Builder Templates + + + + + + + + + + + + + + +
    Dependency + builder +
    File Extension.builder
    Examplebuilder { |xml| xml.em "hi" }
    + +It also takes a block for inline templates (see example). + +#### Nokogiri Templates + + + + + + + + + + + + + + +
    Dependencynokogiri
    File Extension.nokogiri
    Examplenokogiri { |xml| xml.em "hi" }
    + +It also takes a block for inline templates (see example). + +#### Sass Templates + + + + + + + + + + + + + + +
    Dependencysass
    File Extension.sass
    Examplesass :stylesheet, :style => :expanded
    + +#### SCSS Templates + + + + + + + + + + + + + + +
    Dependencysass
    File Extension.scss
    Examplescss :stylesheet, :style => :expanded
    + +#### Less Templates + + + + + + + + + + + + + + +
    Dependencyless
    File Extension.less
    Exampleless :stylesheet
    + +#### Liquid Templates + + + + + + + + + + + + + + +
    Dependencyliquid
    File Extension.liquid
    Exampleliquid :index, :locals => { :key => 'value' }
    + +Since you cannot call Ruby methods (except for `yield`) from a Liquid +template, you almost always want to pass locals to it. + +#### Markdown Templates + + + + + + + + + + + + + + +
    Dependency + Anyone of: + RDiscount, + RedCarpet, + BlueCloth, + kramdown, + maruku +
    File Extensions.markdown, .mkd and .md
    Examplemarkdown :index, :layout_engine => :erb
    + +It is not possible to call methods from markdown, nor to pass locals to it. +You therefore will usually use it in combination with another rendering +engine: + +```ruby + erb :overview, :locals => { :text => markdown(:introduction) } +``` + +Note that you may also call the `markdown` method from within other templates: + +```ruby + %h1 Hello From Haml! + %p= markdown(:greetings) +``` + +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 `:layout_engine` option. + +#### Textile Templates + + + + + + + + + + + + + + +
    DependencyRedCloth
    File Extension.textile
    Exampletextile :index, :layout_engine => :erb
    + + +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: + +```ruby + erb :overview, :locals => { :text => textile(:introduction) } +``` + +Note that you may also call the `textile` method from within other templates: + +```ruby + %h1 Hello From Haml! + %p= textile(:greetings) +``` + +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 `:layout_engine` option. + +#### RDoc Templates + + + + + + + + + + + + + + +
    DependencyRDoc
    File Extension.rdoc
    Examplerdoc :README, :layout_engine => :erb
    + +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: + +```ruby + erb :overview, :locals => { :text => rdoc(:introduction) } +``` + +Note that you may also call the `rdoc` method from within other templates: + +```ruby + %h1 Hello From Haml! + %p= rdoc(:greetings) +``` + +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 `:layout_engine` option. + +#### Radius Templates + + + + + + + + + + + + + + +
    DependencyRadius
    File Extension.radius
    Exampleradius :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 + + + + + + + + + + + + + + +
    DependencyMarkaby
    File Extension.mab
    Examplemarkaby { h1 "Welcome!" }
    + +It also takes a block for inline templates (see example). + +#### RABL Templates + + + + + + + + + + + + + + +
    DependencyRabl
    File Extension.rabl
    Examplerabl :index
    + +#### Slim Templates + + + + + + + + + + + + + + +
    DependencySlim Lang
    File Extension.slim
    Exampleslim :index
    + +#### Creole Templates + + + + + + + + + + + + + + +
    DependencyCreole
    File Extension.creole
    Examplecreole :wiki, :layout_engine => :erb
    + +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: + +```ruby + erb :overview, :locals => { :text => creole(:introduction) } +``` + +Note that you may also call the `creole` method from within other templates: + +```ruby + %h1 Hello From Haml! + %p= creole(:greetings) +``` + +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 `:layout_engine` option. + +#### CoffeeScript Templates + + + + + + + + + + + + + + +
    Dependency + + CoffeeScript + and a + + way to execute javascript + +
    File Extension.coffee
    Examplecoffee :index
    + +#### Yajl Templates + + + + + + + + + + + + + + +
    Dependencyyajl-ruby
    File Extension.yajl
    Example + + yajl :index, + :locals => { :key => 'qux' }, + :callback => 'present', + :variable => 'resource' + +
    + + +The template source is evaluated as a Ruby string, and the +resulting json variable is converted using `#to_json`. + +```ruby + json = { :foo => 'bar' } + json[:baz] = key +``` + +The `:callback` and `:variable` options can be used to decorate the rendered object. + +```ruby + var resource = {"foo":"bar","baz":"qux"}; present(resource); +``` + +#### WLang Templates + + + + + + + + + + + + + + +
    Dependencywlang
    File Extension.wlang
    Examplewlang :index, :locals => { :key => 'value' }
    + +Since calling ruby methods is not idiomatic in wlang, you almost always want to pass locals +to it. Layouts written in wlang and `yield` are supported, though. + +#### Embedded Templates + +```ruby + get '/' do + haml '%div.title Hello World' + end +``` + +Renders the embedded template string. + +### Accessing Variables in Templates + +Templates are evaluated within the same context as route handlers. Instance +variables set in route handlers are directly accessible by templates: + +```ruby + get '/:id' do + @foo = Foo.find(params[:id]) + haml '%h1= @foo.name' + end +``` + +Or, specify an explicit Hash of local variables: + +```ruby + get '/:id' do + foo = Foo.find(params[:id]) + haml '%h1= bar.name', :locals => { :bar => foo } + end +``` + +This is typically used when rendering templates as partials from within +other templates. + +### Inline Templates + +Templates may be defined at the end of the source file: + +```ruby + require 'sinatra' + + get '/' do + haml :index + end + + __END__ + + @@ layout + %html + = yield + + @@ index + %div.title Hello world. +``` + +NOTE: Inline templates defined in the source file that requires sinatra are +automatically loaded. Call `enable :inline_templates` explicitly if you +have inline templates in other source files. + +### Named Templates + +Templates may also be defined using the top-level `template` method: + +```ruby + template :layout do + "%html\n =yield\n" + end + + template :index do + '%div.title Hello World!' + end + + get '/' do + haml :index + end +``` + +If a template named "layout" exists, it will be used each time a template +is rendered. You can individually disable layouts by passing +`:layout => false` or disable them by default via +`set :haml, :layout => false`: + +```ruby + get '/' do + haml :index, :layout => !request.xhr? + end +``` + +### Associating File Extensions + +To associate a file extension with a template engine, use +`Tilt.register`. For instance, if you like to use the file extension +`tt` for Textile templates, you can do the following: + +```ruby + Tilt.register :tt, Tilt[:textile] +``` + +### Adding Your Own Template Engine + +First, register your engine with Tilt, then create a rendering method: + +```ruby + Tilt.register :myat, MyAwesomeTemplateEngine + + helpers do + def myat(*args) render(:myat, *args) end + end + + get '/' do + myat :index + end +``` + +Renders `./views/index.myat`. See https://github.com/rtomayko/tilt to +learn more about Tilt. + +## Filters + +Before filters are evaluated before each request within the same +context as the routes will be and can modify the request and response. Instance +variables set in filters are accessible by routes and templates: + +```ruby + before do + @note = 'Hi!' + request.path_info = '/foo/bar/baz' + end + + get '/foo/*' do + @note #=> 'Hi!' + params[:splat] #=> 'bar/baz' + end +``` + +After filters are evaluated after each request within the same context and can +also modify the request and response. Instance variables set in before filters +and routes are accessible by after filters: + +```ruby + after do + puts response.status + end +``` + +Note: Unless you use the `body` method rather than just returning a String from +the routes, the body will not yet be available in the after filter, since it is +generated later on. + +Filters optionally take a pattern, causing them to be evaluated only if the +request path matches that pattern: + +```ruby + before '/protected/*' do + authenticate! + end + + after '/create/:slug' do |slug| + session[:last_slug] = slug + end +``` + +Like routes, filters also take conditions: + +```ruby + before :agent => /Songbird/ do + # ... + end + + after '/blog/*', :host_name => 'example.com' do + # ... + end +``` + +## Helpers + +Use the top-level `helpers` method to define helper methods for use in +route handlers and templates: + +```ruby + helpers do + def bar(name) + "#{name}bar" + end + end + + get '/:name' do + bar(params[:name]) + end +``` + +Alternatively, helper methods can be separately defined in a module: + +```ruby + module FooUtils + def foo(name) "#{name}foo" end + end + + module BarUtils + def bar(name) "#{name}bar" end + end + + helpers FooUtils, BarUtils +``` + +The effect is the same as including the modules in the application class. + +### Using Sessions + +A session is used to keep state during requests. If activated, you have one +session hash per user session: + +```ruby + enable :sessions + + get '/' do + "value = " << session[:value].inspect + end + + get '/:value' do + session[:value] = params[:value] + end +``` + +Note that `enable :sessions` 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 +do so, do **not** call `enable :sessions`, but instead pull in your +middleware of choice as you would any other middleware: + +```ruby + use Rack::Session::Pool, :expire_after => 2592000 + + get '/' do + "value = " << session[:value].inspect + end + + get '/:value' do + session[:value] = params[:value] + end +``` + +To improve security, the session data in the cookie is signed with a session +secret. A random secret is generated for you by Sinatra. However, since this +secret will change with every start of your application, you might want to +set the secret yourself, so all your application instances share it: + +```ruby + set :session_secret, 'super secret' +``` + +If you want to configure it further, you may also store a hash with options in +the `sessions` setting: + +```ruby + set :sessions, :domain => 'foo.com' +``` + +### Halting + +To immediately stop a request within a filter or route use: + +```ruby + halt +``` + +You can also specify the status when halting: + +```ruby + halt 410 +``` + +Or the body: + +```ruby + halt 'this will be the body' +``` + +Or both: + +```ruby + halt 401, 'go away!' +``` + +With headers: + +```ruby + halt 402, {'Content-Type' => 'text/plain'}, 'revenge' +``` + +It is of course possible to combine a template with `halt`: + +```ruby + halt erb(:error) +``` + +### Passing + +A route can punt processing to the next matching route using `pass`: + +```ruby + get '/guess/:who' do + pass unless params[:who] == 'Frank' + 'You got me!' + end + + get '/guess/*' do + 'You missed!' + end +``` + +The route block is immediately exited and control continues with the next +matching route. If no matching route is found, a 404 is returned. + +### Triggering Another Route + +Sometimes `pass` is not what you want, instead you would like to get the result +of calling another route. Simply use `call` to achieve this: + +```ruby + get '/foo' do + status, headers, body = call env.merge("PATH_INFO" => '/bar') + [status, headers, body.map(&:upcase)] + end + + get '/bar' do + "bar" + end +``` + +Note that in the example above, you would ease testing and increase performance +by simply moving `"bar"` into a helper used by both `/foo` +and `/bar`. + +If you want the request to be sent to the same application instance rather than +a duplicate, use `call!` instead of `call`. + +Check out the Rack specification if you want to learn more about `call`. + +### Setting Body, Status Code and Headers + +It is possible and recommended to set the status code and response body with the +return value of the route block. However, in some scenarios you might want to +set the body at an arbitrary point in the execution flow. You can do so with the +`body` helper method. If you do so, you can use that method from there on to +access the body: + +```ruby + get '/foo' do + body "bar" + end + + after do + 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"). + +Similar to the body, you can also set the status code and headers: + +```ruby + 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 +``` + +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: + +```ruby + 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 +the 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 requests, +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` finishes 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: + +```ruby + # long polling + + set :server, :thin + connections = [] + + get '/subscribe' do + # register a client's interest in server events + stream(:keep_open) { |out| connections << out } + + # purge dead connections + connections.reject!(&:closed?) + + # acknowledge + "subscribed" + end + + post '/message' do + connections.each do |out| + # notify client that a new message has arrived + out << message << "\n" + + # indicate client to connect again + out.close + end + + # acknowledge + "message received" + end +``` + +### Logging + +In the request scope, the `logger` helper exposes a `Logger` instance: + +```ruby + 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 `Sinatra::Application` by +default, so if you inherit from `Sinatra::Base`, you probably want to +enable it yourself: + +```ruby + 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 `env['rack.logger']`. + +### Mime Types + +When using `send_file` or static files you may have mime types Sinatra +doesn't understand. Use `mime_type` to register them by file extension: + +```ruby + configure do + mime_type :foo, 'text/foo' + end +``` + +You can also use it with the `content_type` helper: + +```ruby + get '/' do + content_type :foo + "foo foo foo" + end +``` + +### Generating URLs + +For generating URLs you should use the `url` helper method, for instance, in +Haml: + +```ruby + %a{:href => url('/foo')} foo +``` + +It takes reverse proxies and Rack routers into account, if present. + +This method is also aliased to `to` (see below for an example). + +### Browser Redirect + +You can trigger a browser redirect with the `redirect` helper method: + +```ruby + get '/foo' do + redirect to('/bar') + end +``` + +Any additional parameters are handled like arguments passed to `halt`: + +```ruby + redirect to('/bar'), 303 + redirect 'http://google.com', 'wrong place, buddy' +``` + +You can also easily redirect back to the page the user came from with +`redirect back`: + +```ruby + get '/foo' do + "do something" + end + + get '/bar' do + do_something + redirect back + end +``` + +To pass arguments with a redirect, either add them to the query: + +```ruby + redirect to('/bar?sum=42') +``` + +Or use a session: + +```ruby + enable :sessions + + get '/foo' do + session[:secret] = 'foo' + redirect to('/bar') + end + + get '/bar' do + session[:secret] + end +``` + +### Cache Control + +Setting your headers correctly is the foundation for proper HTTP caching. + +You can easily set the Cache-Control header like this: + +```ruby + get '/' do + cache_control :public + "cache it!" + end +``` + +Pro tip: Set up caching in a before filter: + +```ruby + before do + cache_control :public, :must_revalidate, :max_age => 60 + end +``` + +If you are using the `expires` helper to set the corresponding header, +`Cache-Control` will be set automatically for you: + +```ruby + before do + expires 500, :public, :must_revalidate + end +``` + +To properly use caches, you should consider using `etag` or `last_modified`. +It is recommended to call those helpers *before* doing any heavy lifting, as they +will immediately flush a response if the client already has the current +version in its cache: + +```ruby + get '/article/:id' do + @article = Article.find params[:id] + last_modified @article.updated_at + etag @article.sha1 + erb :article + end +``` + +It is also possible to use a +[weak ETag](http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation): + +```ruby + 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 reverse-proxy caching +solution, try [rack-cache](https://github.com/rtomayko/rack-cache): + +```ruby + require "rack/cache" + require "sinatra" + + use Rack::Cache + + get '/' do + cache_control :public, :max_age => 36000 + sleep 5 + "hello" + end +``` + +Use the `:static_cache_control` setting (see below) to add +`Cache-Control` 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 `*` 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 `:new_resource` option: + +```ruby + 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 `:kind` option: + +```ruby + etag '', :new_resource => true, :kind => :weak +``` + +### Sending Files + +For sending files, you can use the `send_file` helper method: + +```ruby + get '/' do + send_file 'foo.png' + end +``` + +It also takes options: + +```ruby + send_file 'foo.png', :type => :jpg +``` + +The options are: + +
    +
    filename
    +
    file name, in response, defaults to the real file name.
    + +
    last_modified
    +
    value for Last-Modified header, defaults to the file's mtime.
    + +
    type
    +
    content type to use, guessed from the file extension if missing.
    + + disposition +
    + used for Content-Disposition, possible value: nil (default), + :attachment and :inline +
    + +
    length
    +
    Content-Length header, defaults to file size.
    + +
    status
    +
    + Status code to be send. Useful when sending a static file as an error page. + + If supported by the Rack handler, other means than streaming from the Ruby + process will be used. If you use this helper method, Sinatra will automatically + handle range requests. +
    +
    + +### Accessing the Request Object + +The incoming request object can be accessed from request level (filter, routes, +error handlers) through the `request` method: + +```ruby + # app running on http://example.com/example + get '/foo' do + 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_param"] # value of some_param parameter. [] is a shortcut to the params hash. + 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 `script_name` or `path_info`, can also be +written: + +```ruby + before { request.path_info = "/" } + + get "/" do + "all requests end up here" + end +``` + +The `request.body` is an IO or StringIO object: + +```ruby + post "/api" do + request.body.rewind # in case someone already read it + data = JSON.parse request.body.read + "Hello #{data['name']}!" + end +``` + +### Attachments + +You can use the `attachment` helper to tell the browser the response should be +stored on disk rather than displayed in the browser: + +```ruby + get '/' do + attachment + "store it!" + end +``` + +You can also pass it a file name: + +```ruby + get '/' do + attachment "info.txt" + "store it!" + end +``` + +### Dealing with Date and Time + +Sinatra offers a `time_for` helper method that generates a Time object +from the given value. It is also able to convert `DateTime`, `Date` and +similar classes: + +```ruby + 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: + +```ruby + 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 `find_template` helper is used to find template files for rendering: + +```ruby + find_template settings.views, 'foo', Tilt[:haml] do |file| + puts "could be #{file}" + end +``` + +This is not really useful. But it is useful that you can actually override this +method to hook in your own lookup mechanism. For instance, if you want to be +able to use more than one view directory: + +```ruby + set :views, ['views', 'templates'] + + helpers do + def find_template(views, name, engine, &block) + Array(views).each { |v| super(v, name, engine, &block) } + end + end +``` + +Another example would be using different directories for different engines: + +```ruby + 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 +``` + +You can also easily wrap this up in an extension and share with others! + +Note that `find_template` does not check if the file really exists but +rather calls the given block for all possible paths. This is not a performance +issue, since `render` will use `break` as soon as a file is found. Also, +template locations (and content) will be cached if you are not running in +development mode. You should keep that in mind if you write a really crazy +method. + +## Configuration + +Run once, at startup, in any environment: + +```ruby + configure do + # setting one option + set :option, 'value' + + # setting multiple options + set :a => 1, :b => 2 + + # same as `set :option, true` + enable :option + + # same as `set :option, false` + disable :option + + # you can also have dynamic settings with blocks + set(:css_dir) { File.join(views, 'css') } + end +``` + +Run only when the environment (`RACK_ENV` environment variable) is set to +`:production`: + +```ruby + configure :production do + ... + end +``` + +Run when the environment is set to either `:production` or +`:test`: + +```ruby + configure :production, :test do + ... + end +``` + +You can access those options via `settings`: + +```ruby + configure do + set :foo, 'bar' + end + + get '/' do + settings.foo? # => true + settings.foo # => 'bar' + ... + end +``` + +### Configuring attack protection + +Sinatra is using +[Rack::Protection](https://github.com/rkh/rack-protection#readme) to defend +your application against common, opportunistic attacks. You can easily disable +this behavior (which will open up your application to tons of common +vulnerabilities): + +```ruby + disable :protection +``` + +To skip a single defense layer, set `protection` to an options hash: + +```ruby + set :protection, :except => :path_traversal +``` +You can also hand in an array in order to disable a list of protections: + +```ruby + 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 the second parameter. +
    +
    Disabled per default.
    + +
    add_charsets
    +
    + mime types the content_type 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 "utf-8").
    + +
    dump_errors
    +
    display errors in the log.
    + +
    environment
    +
    + current environment, defaults to ENV['RACK_ENV'], or "development" if + not available. +
    + +
    logging
    +
    use the logger.
    + +
    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.
    + +
    method_override
    +
    + use _method magic to allow put/delete forms in browsers that + don't support it. +
    + +
    port
    +
    Port to listen on. Only used for built-in server.
    + +
    prefixed_redirects
    +
    + Whether or not to insert request.script_name into redirects if no + absolute path is given. That way redirect '/foo' would behave like + redirect to('/foo'). Disabled per default. +
    + +
    protection
    +
    Whether or not to enable web attack protections. See protection section above.
    + +
    public_dir
    +
    Alias for public_folder. See below.
    + +
    public_folder
    +
    + Path to the folder public files are served from. Only used if static + file serving is enabled (see static setting below). Inferred from + app_file setting if not set. +
    + +
    reload_templates
    +
    + Whether or not to reload templates between requests. Enabled in development mode. +
    + +
    root
    +
    + Path to project root folder. Inferred from app_file setting if not set. +
    + +
    raise_errors
    +
    + raise exceptions (will stop application). Enabled by default when + environment is set to "test", disabled otherwise. +
    + +
    run
    +
    + if enabled, Sinatra will handle starting the web server, do not + enable if using rackup or other means. +
    + +
    running
    +
    is the built-in server running now? do not change this setting!
    + +
    server
    +
    + server or list of servers to use for built-in server. defaults to + ['thin', 'mongrel', 'webrick'], order indicates priority. +
    + +
    sessions
    +
    + Enable cookie-based sessions support using Rack::Session::Cookie. + See 'Using Sessions' section for more information. +
    + +
    show_exceptions
    +
    + Show a stack trace in the browser when an exception + happens. Enabled by default when environment + is set to "development", disabled otherwise. +
    +
    + Can also be set to :after_handler to trigger + app-specified error handling before showing a stack + trace in the browser. +
    + +
    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. +
    + +
    static_cache_control
    +
    + When Sinatra is serving static files, set this to add + Cache-Control headers to the responses. Uses the + cache_control helper. Disabled by default. +
    +
    + Use an explicit array when setting multiple values: + set :static_cache_control, [:public, :max_age => 300] +
    + +
    threaded
    +
    + If set to true, will tell Thin to use EventMachine.defer + for processing the request. +
    + +
    views
    +
    + Path to the views folder. Inferred from app_file setting if + not set. +
    +
    + +## Environments + +There are three predefined `environments`: `"development"`, +`"production"` and `"test"`. Environments can be set +through the `RACK_ENV` environment variable. The default value is +`"development"`. In the `"development"` environment all templates are reloaded between +requests, and special `not_found` and `error` handlers +display stack traces in your browser. +In the `"production"` and `"test"` environments, templates are cached by default. + +To run different environments use the `-e` option: + +```ruby + ruby my_app.rb -e [ENVIRONMENT] +``` + +You can use predefined methods: `development?`, `test?` and `production?` to +check the current environment setting. + +## Error Handling + +Error handlers run within the same context as routes and before filters, which +means you get all the goodies it has to offer, like `haml`, +`erb`, `halt`, etc. + +### Not Found + +When a `Sinatra::NotFound` exception is raised, or the response's status +code is 404, the `not_found` handler is invoked: + +```ruby + not_found do + 'This is nowhere to be found.' + end +``` + +### Error + +The `error` handler is invoked any time an exception is raised from a route +block or a filter. The exception object can be obtained from the +`sinatra.error` Rack variable: + +```ruby + error do + 'Sorry there was a nasty error - ' + env['sinatra.error'].name + end +``` + +Custom errors: + +```ruby + error MyCustomError do + 'So what happened was...' + env['sinatra.error'].message + end +``` + +Then, if this happens: + +```ruby + get '/' do + raise MyCustomError, 'something bad' + end +``` + +You get this: + +```ruby + So what happened was... something bad +``` + +Alternatively, you can install an error handler for a status code: + +```ruby + error 403 do + 'Access forbidden' + end + + get '/secret' do + 403 + end +``` + +Or a range: + +```ruby + error 400..510 do + 'Boom' + end +``` + +Sinatra installs special `not_found` and `error` handlers when +running under the development environment. + +## Rack Middleware + +Sinatra rides on [Rack](http://rack.rubyforge.org/), a minimal standard +interface for Ruby web frameworks. One of Rack's most interesting capabilities +for application developers is support for "middleware" -- components that sit +between the server and your application monitoring and/or manipulating the +HTTP request/response to provide various types of common functionality. + +Sinatra makes building Rack middleware pipelines a cinch via a top-level +`use` method: + +```ruby + require 'sinatra' + require 'my_custom_middleware' + + use Rack::Lint + use MyCustomMiddleware + + get '/hello' do + 'Hello World' + end +``` + +The semantics of `use` are identical to those defined for the +[Rack::Builder](http://rack.rubyforge.org/doc/classes/Rack/Builder.html) DSL +(most frequently used from rackup files). For example, the `use` method +accepts multiple/variable args as well as blocks: + +```ruby + use Rack::Auth::Basic do |username, password| + username == 'admin' && password == 'secret' + end +``` + +Rack is distributed with a variety of standard middleware for logging, +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#readm), +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://rdoc.info/github/brynary/rack-test/master/frames) +is recommended: + +```ruby + require 'my_sinatra_app' + require 'test/unit' + require 'rack/test' + + class MyAppTest < Test::Unit::TestCase + include Rack::Test::Methods + + def app + Sinatra::Application + end + + def test_my_default + get '/' + assert_equal 'Hello World!', last_response.body + end + + def test_with_params + get '/meet', :name => 'Frank' + assert_equal 'Hello Frank!', last_response.body + end + + def test_with_rack_env + get '/', {}, 'HTTP_USER_AGENT' => 'Songbird' + assert_equal "You're using Songbird!", last_response.body + end + end +``` + +Note: If you are using Sinatra in the modular style, replace `Sinatra::Application` +above with the class name of your app. + +## Sinatra::Base - Middleware, Libraries, and Modular Apps + +Defining your app at the top-level works well for micro-apps but has +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 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: + +```ruby + require 'sinatra/base' + + class MyApp < Sinatra::Base + set :sessions, true + set :foo, 'bar' + + get '/' do + 'Hello world!' + end + end +``` + +The methods available to `Sinatra::Base` subclasses are exactly the same as those +available via the top-level DSL. Most top-level apps can be converted to +`Sinatra::Base` components with two modifications: + +* Your file should require `sinatra/base` 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`. + +`Sinatra::Base` 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) +for details on available options and their behavior. + +### Modular vs. Classic Style + +Contrary to common belief, there is nothing wrong with the classic style. If it +suits your application, you do not have to switch to a modular application. + +The main disadvantage of using the classic style rather than the modular style is that +you will only have one Sinatra application per Ruby process. If you plan to use +more than one, switch to the modular style. There is no reason you cannot mix +the modular and the classic styles. + +If switching from one style to the other, you should be aware of slightly +different default settings: + +``` + Setting Classic Modular + + 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 + +There are two common options for starting a modular app, actively starting with +`run!`: + +```ruby + # my_app.rb + require 'sinatra/base' + + class MyApp < Sinatra::Base + # ... app code here ... + + # start the server if ruby file executed directly + run! if app_file == $0 + end +``` + +Start with: + +```ruby + ruby my_app.rb +``` + +Or with a `config.ru` file, which allows using any Rack handler: + +```ruby + # config.ru (run with rackup) + require './my_app' + run MyApp +``` + +Run: + +```ruby + rackup -p 4567 +``` + +### Using a Classic Style Application with a config.ru + +Write your app file: + +```ruby + # app.rb + require 'sinatra' + + get '/' do + 'Hello world!' + end +``` + +And a corresponding `config.ru`: + +```ruby + require './app' + run Sinatra::Application +``` + +### When to use a config.ru? + +A `config.ru` file is recommended if: + +* You want to deploy with a different Rack handler (Passenger, Unicorn, + Heroku, ...). +* You want to use more than one subclass of `Sinatra::Base`. +* You want to use Sinatra only for middleware, and not as an endpoint. + +**There is no need to switch to a `config.ru` simply because you +switched to the modular style, and you don't have to use the modular style for running +with a `config.ru`.** + +### Using Sinatra as Middleware + +Not only is Sinatra able to use other Rack middleware, any Sinatra application +can in turn be added in front of any Rack endpoint as middleware itself. This +endpoint could be another Sinatra application, or any other Rack-based +application (Rails/Ramaze/Camping/...): + +```ruby + require 'sinatra/base' + + class LoginScreen < Sinatra::Base + enable :sessions + + get('/login') { haml :login } + + post('/login') do + if params[:name] == 'admin' && params[:password] == 'admin' + session['user_name'] = params[:name] + else + redirect '/login' + end + end + end + + class MyApp < Sinatra::Base + # middleware will run before filters + use LoginScreen + + before do + unless session['user_name'] + halt "Access denied, please login." + end + end + + get('/') { "Hello #{session['user_name']}." } + end +``` + +### Dynamic Application Creation + +Sometimes you want to create new applications at runtime without having to +assign them to a constant, you can do this with `Sinatra.new`: + +```ruby + require 'sinatra/base' + my_app = Sinatra.new { get('/') { "hi" } } + my_app.run! +``` + +It takes the application to inherit from as an optional argument: + +```ruby + # config.ru (run with rackup) + 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 +``` + +This is especially useful for testing Sinatra extensions or using Sinatra in +your own library. + +This also makes using Sinatra as middleware extremely easy: + +```ruby + require 'sinatra/base' + + use Sinatra do + get('/') { ... } + end + + run RailsProject::Application +``` + +## Scopes and Binding + +The scope you are currently in determines what methods and variables are +available. + +### Application/Class Scope + +Every Sinatra application corresponds to a subclass of `Sinatra::Base`. +If you are using the top-level DSL (`require 'sinatra'`), 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` or `session` objects, as there is only a +single application class for all requests. + +Options created via `set` are methods at class level: + +```ruby + class MyApp < Sinatra::Base + # Hey, I'm in the application scope! + set :foo, 42 + foo # => 42 + + get '/foo' do + # Hey, I'm no longer in the application scope! + end + end +``` + +You have the application scope binding inside: + +* Your application class body +* Methods defined by extensions +* The block passed to `helpers` +* Procs/blocks used as value for `set` +* The block passed to `Sinatra.new` + +You can reach the scope object (the class) like this: + +* Via the object passed to configure blocks (`configure { |c| ... }`) +* `settings` from within the request scope + +### Request/Instance Scope + +For every incoming request, a new instance of your application class is +created and all handler blocks run in that scope. From within this scope you +can access the `request` and `session` objects or call rendering methods like +`erb` or `haml`. You can access the application scope from within the request +scope via the `settings` helper: + +```ruby + class MyApp < Sinatra::Base + # Hey, I'm in the application scope! + get '/define_route/:name' do + # Request scope for '/define_route/:name' + @value = 42 + + settings.get("/#{params[:name]}") do + # Request scope for "/#{params[:name]}" + @value # => nil (not the same request) + end + + "Route defined!" + end + end +``` + +You have the request scope binding inside: + +* get/head/post/put/delete/options blocks +* before/after filters +* helper methods +* templates/views + +### Delegation Scope + +The delegation scope just forwards methods to the class scope. However, it +does not behave exactly like the class scope, as you do not have the class +binding. Only methods explicitly marked for delegation are available, and you +do not share variables/state with the class scope (read: you have a different +`self`). You can explicitly add method delegations by calling +`Sinatra::Delegator.delegate :method_name`. + +You have the delegate scope binding inside: + +* The top level binding, if you did `require "sinatra"` +* An object extended with the `Sinatra::Delegator` mixin + +Have a look at the code for yourself: here's the +[Sinatra::Delegator mixin](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633) +being [extending the main object](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30). + +## Command Line + +Sinatra applications can be run directly: + +```ruby + ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER] +``` + +Options are: + +``` + -h # help + -p # set the port (default is 4567) + -o # set the host (default is 0.0.0.0) + -e # set the environment (default is development) + -s # specify rack server/handler (default is thin) + -x # turn on the mutex lock (default is off) +``` + +## Requirement + +The following Ruby versions are officially supported: +
    +
    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. Support for + 1.8.7 will not be dropped before Sinatra 2.0 and Ruby 2.0 except maybe in + the unlikely event of 1.8.8 being released. Even then, we might continue + supporting it. Ruby 1.8.6 is no longer supported. 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 fully supported and recommended. Do not use 1.9.2p0, as 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 and recommended. Please note that switching to 1.9.3 + from an earlier version will invalidate all sessions. +
    + +
    Rubinius
    +
    + Rubinius is officially supported (Rubinius >= 1.2.4), everything works, including + all template languages. The upcoming 2.0 release is supported as + well, including 1.9 mode. +
    + +
    JRuby
    +
    + JRuby is officially supported (JRuby >= 1.6.7). 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, Redcarpet, RedCloth and Yajl templates as well as Thin + and Mongrel at the moment. +
    +
    +We also keep an eye on upcoming Ruby versions. + +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 (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 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. + +Sinatra currently doesn't run on Cardinal, SmallRuby, BlueRuby or any +Ruby version prior to 1.8.7. + +## The Bleeding Edge + +If you would like to use Sinatra's latest bleeding-edge code, feel free to run your +application against the master branch, it should be rather stable. + +We also push out prerelease gems from time to time, so you can do a + +```ruby + gem install sinatra --pre +``` + +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. + +First, install bundler, if you haven't: + +```ruby + gem install bundler +``` + +Then, in your project directory, create a `Gemfile`: + +```ruby + source :rubygems + gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git" + + # other dependencies + gem 'haml' # for instance, if you use haml + gem 'activerecord', '~> 3.0' # maybe you also need ActiveRecord 3.x +``` + +Note that you will have to list all your application's dependencies in the `Gemfile`. +Sinatra's direct dependencies (Rack and Tilt) will, however, be automatically +fetched and added by Bundler. + +Now you can run your app like this: + +```ruby + bundle exec ruby myapp.rb +``` + +### Roll Your Own + +Create a local clone and run your app with the `sinatra/lib` directory +on the `$LOAD_PATH`: + +```ruby + cd myapp + git clone git://github.com/sinatra/sinatra.git + ruby -I sinatra/lib myapp.rb +``` + +To update the Sinatra sources in the future: + +```ruby + cd myapp/sinatra + git pull +``` +### Install Globally + +You can build the gem on your own: + +```ruby + git clone git://github.com/sinatra/sinatra.git + cd sinatra + rake sinatra.gemspec + rake install +``` + +If you install gems as root, the last step should be + +```ruby + sudo rake install +``` + +## Versioning + +Sinatra follows [Semantic Versioning](http://semver.org/), both SemVer and +SemVerTag. + +## Further Reading + +* [Project Website](http://www.sinatrarb.com/) - Additional documentation, + news, and links to other resources. +* [Contributing](http://www.sinatrarb.com/contributing) - Find a bug? Need + help? Have a patch? +* [Issue tracker](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) on http://freenode.net +* [Sinatra Book](http://sinatra-book.gittr.com) Cookbook Tutorial +* [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 +* [CI server](http://travis-ci.org/sinatra/sinatra) diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index 4c2ad53c..00000000 --- a/README.rdoc +++ /dev/null @@ -1,2049 +0,0 @@ -= Sinatra - -Sinatra is a {DSL}[http://en.wikipedia.org/wiki/Domain-specific_language] for -quickly creating web applications in Ruby with minimal effort: - - # myapp.rb - require 'sinatra' - - get '/' do - 'Hello world!' - end - -Install the gem and run with: - - gem install sinatra - ruby -rubygems myapp.rb - -View at: http://localhost:4567 - -It is recommended to also run gem install thin, which Sinatra will -pick up if available. - -== Routes - -In Sinatra, a route is an HTTP method paired with a URL-matching pattern. -Each route is associated with a block: - - 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 - -Routes are matched in the order they are defined. The first route that -matches the request is invoked. - -Route patterns may include named parameters, accessible via the -params hash: - - get '/hello/:name' do - # matches "GET /hello/foo" and "GET /hello/bar" - # params[:name] is 'foo' or 'bar' - "Hello #{params[:name]}!" - end - -You can also access named parameters via block parameters: - - get '/hello/:name' do |n| - "Hello #{n}!" - end - -Route patterns may also include splat (or wildcard) parameters, accessible -via the params[:splat] array: - - get '/say/*/to/*' do - # matches /say/hello/to/world - params[:splat] # => ["hello", "world"] - end - - get '/download/*.*' do - # matches /download/path/to/file.xml - 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 - "Hello, #{params[:captures].first}!" - end - -Or with a block parameter: - - get %r{/hello/([\w]+)} do |c| - "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: - - get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do - "You're using Songbird version #{params[:agent][0]}" - end - - get '/foo' do - # Matches non-songbird browsers - end - -Other available conditions are +host_name+ and +provides+: - - get '/', :host_name => /^admin\./ do - "Admin Area, Access denied!" - end - - get '/', :provides => 'html' do - haml :index - end - - get '/', :provides => ['rss', 'atom', 'xml'] do - builder :feed - end - -You can easily define your own conditions: - - set(:probability) { |value| condition { rand <= value } } - - get '/win_a_car', :probability => 0.1 do - "You won!" - end - - 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 -on to the HTTP client, or at least the next middleware in the Rack stack. -Most commonly, this is a string, as in the above examples. But other values are -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: [status (Fixnum), headers (Hash), response - body (responds to #each)] -* An Array with two elements: [status (Fixnum), response body (responds to - #each)] -* An object that responds to #each 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: - - class Stream - def each - 100.times { |i| yield "#{i}\n" } - end - end - - 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 -and regular expressions as route matches. However, it does not stop there. You -can easily define your own matchers: - - class AllButPattern - Match = Struct.new(:captures) - - def initialize(except) - @except = except - @captures = Match.new([]) - end - - def match(str) - @captures unless @except === str - end - end - - def all_but(pattern) - AllButPattern.new(pattern) - end - - get all_but("/index") do - # ... - end - -Note that the above example might be over-engineered, as it can also be -expressed as: - - get // do - pass if request.path_info == "/index" - # ... - end - -Or, using negative look ahead: - - get %r{^(?!/index$)} do - # ... - end - -== Static Files - -Static files are served from the ./public directory. You can specify -a different location by setting the :public_folder option: - - set :public_folder, File.dirname(__FILE__) + '/static' - -Note that the public directory name is not included in the URL. A file -./public/css/style.css is made available as -http://example.com/css/style.css. - -Use the :static_cache_control setting (see below) to add -Cache-Control header info. - -== Views / Templates - -Each template language is exposed via its own rendering method. These -methods simply return a string: - - get '/' do - erb :index - end - -This renders views/index.erb. - -Instead of a template name, you can also just pass in the template content -directly: - - get '/' do - code = "<%= Time.now %>" - erb code - end - -Templates take a second argument, the options hash: - - get '/' do - erb :index, :layout => :post - end - -This will render views/index.erb embedded in the -views/post.erb (default is views/layout.erb, if it exists). - -Any options not understood by Sinatra will be passed on to the template -engine: - - get '/' do - haml :index, :format => :html5 - end - -You can also set options per template language in general: - - set :haml, :format => :html5 - - get '/' do - haml :index - end - -Options passed to the render method override options set via +set+. - -Available Options: - -[locals] - List of locals passed to the document. Handy with partials. - Example: erb "<%= foo %>", :locals => {:foo => "bar"} - -[default_encoding] - String encoding to use if uncertain. Defaults to - settings.default_encoding. - -[views] - Views folder to load templates from. Defaults to settings.views. - -[layout] - Whether to use a layout (+true+ or +false+), if it's a Symbol, specifies - what template to use. Example: erb :index, :layout => !request.xhr? - -[content_type] - Content-Type the template produces, default depends on template language. - -[scope] - Scope to render template under. Defaults to the application instance. If you - change this, instance variables and helper methods will not be available. - -[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: set :rdoc, :layout_engine => :erb - -Templates are assumed to be located directly under the ./views -directory. To use a different views directory: - - set :views, settings.root + '/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 :'subdir/template'). You must use a symbol because -otherwise rendering methods will render any strings passed to them -directly. - -=== Available Template Languages - -Some languages have multiple implementations. To specify what implementation -to use (and to be thread-safe), you should simply require it first: - - require 'rdiscount' # or require 'bluecloth' - get('/') { markdown :index } - -=== Haml Templates - -Dependency:: {haml}[http://haml.info/] -File Extension:: .haml -Example:: haml :index, :format => :html5 - -=== Erb Templates - -Dependency:: {erubis}[http://www.kuwata-lab.com/erubis/] or - erb (included in Ruby) -File Extensions:: .erb, .rhtml or .erubis (Erubis - only) -Example:: erb :index - -=== Builder Templates - -Dependency:: {builder}[http://builder.rubyforge.org/] -File Extension:: .builder -Example:: builder { |xml| xml.em "hi" } - -It also takes a block for inline templates (see example). - -=== Nokogiri Templates - -Dependency:: {nokogiri}[http://nokogiri.org/] -File Extension:: .nokogiri -Example:: nokogiri { |xml| xml.em "hi" } - -It also takes a block for inline templates (see example). - -=== Sass Templates - -Dependency:: {sass}[http://sass-lang.com/] -File Extension:: .sass -Example:: sass :stylesheet, :style => :expanded - -=== SCSS Templates - -Dependency:: {sass}[http://sass-lang.com/] -File Extension:: .scss -Example:: scss :stylesheet, :style => :expanded - -=== Less Templates - -Dependency:: {less}[http://www.lesscss.org/] -File Extension:: .less -Example:: less :stylesheet - -=== Liquid Templates - -Dependency:: {liquid}[http://www.liquidmarkup.org/] -File Extension:: .liquid -Example:: liquid :index, :locals => { :key => 'value' } - -Since you cannot call Ruby methods (except for +yield+) from a Liquid -template, you almost always want to pass locals to it. - -=== Markdown Templates - -Dependency:: {rdiscount}[https://github.com/rtomayko/rdiscount], - {redcarpet}[https://github.com/vmg/redcarpet], - {bluecloth}[http://deveiate.org/projects/BlueCloth], - {kramdown}[http://kramdown.rubyforge.org/] *or* - {maruku}[http://maruku.rubyforge.org/] -File Extensions:: .markdown, .mkd and .md -Example:: markdown :index, :layout_engine => :erb - -It is not possible to call methods from markdown, nor to pass locals to it. -You therefore will usually use it in combination with another rendering -engine: - - erb :overview, :locals => { :text => markdown(:introduction) } - -Note that you may also call the +markdown+ method from within other templates: - - %h1 Hello From Haml! - %p= markdown(:greetings) - -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 :layout_engine option. - -=== Textile Templates - -Dependency:: {RedCloth}[http://redcloth.org/] -File Extension:: .textile -Example:: textile :index, :layout_engine => :erb - -It is not possible to call methods from textile, nor to pass locals to it. You -therefore will usually use it in combination with another rendering engine: - - erb :overview, :locals => { :text => textile(:introduction) } - -Note that you may also call the +textile+ method from within other templates: - - %h1 Hello From Haml! - %p= textile(:greetings) - -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 :layout_engine option. - -=== RDoc Templates - -Dependency:: {rdoc}[http://rdoc.rubyforge.org/] -File Extension:: .rdoc -Example:: rdoc :README, :layout_engine => :erb - -It is not possible to call methods from rdoc, nor to pass locals to it. You -therefore will usually use it in combination with another rendering engine: - - erb :overview, :locals => { :text => rdoc(:introduction) } - -Note that you may also call the +rdoc+ method from within other templates: - - %h1 Hello From Haml! - %p= rdoc(:greetings) - -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 :layout_engine option. - -=== Radius Templates - -Dependency:: {radius}[http://radius.rubyforge.org/] -File Extension:: .radius -Example:: 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 - -Dependency:: {markaby}[http://markaby.github.com/] -File Extension:: .mab -Example:: markaby { h1 "Welcome!" } - -It also takes a block for inline templates (see example). - -=== RABL Templates - -Dependency:: {rabl}[https://github.com/nesquena/rabl] -File Extension:: .rabl -Example:: rabl :index - -=== Slim Templates - -Dependency:: {slim}[http://slim-lang.com/] -File Extension:: .slim -Example:: slim :index - -=== Creole Templates - -Dependency:: {creole}[https://github.com/minad/creole] -File Extension:: .creole -Example:: creole :wiki, :layout_engine => :erb - -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: - - erb :overview, :locals => { :text => creole(:introduction) } - -Note that you may also call the +creole+ method from within other templates: - - %h1 Hello From Haml! - %p= creole(:greetings) - -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 :layout_engine option. - -=== CoffeeScript Templates - -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 Extension:: .coffee -Example:: coffee :index - -=== Yajl Templates - -Dependency:: {yajl-ruby}[https://github.com/brianmario/yajl-ruby] -File Extension:: .yajl -Example:: yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource' - -The template source is evaluated as a Ruby string, and the resulting json variable is converted #to_json. - - json = { :foo => 'bar' } - json[:baz] = key - -The :callback and :variable options can be used to decorate the rendered object. - - var resource = {"foo":"bar","baz":"qux"}; present(resource); - -=== WLang Templates - -Dependency:: {wlang}[https://github.com/blambeau/wlang/] -File Extension:: .wlang -Example:: wlang :index, :locals => { :key => 'value' } - -Since calling ruby methods is not idiomatic in wlang, you almost always want to pass locals -to it. Layouts written in wlang and +yield+ are supported, though. - -=== Embedded Templates - - get '/' do - haml '%div.title Hello World' - end - -Renders the embedded template string. - -=== Accessing Variables in Templates - -Templates are evaluated within the same context as route handlers. Instance -variables set in route handlers are directly accessible by templates: - - get '/:id' do - @foo = Foo.find(params[:id]) - haml '%h1= @foo.name' - end - -Or, specify an explicit Hash of local variables: - - get '/:id' do - foo = Foo.find(params[:id]) - haml '%h1= bar.name', :locals => { :bar => foo } - end - -This is typically used when rendering templates as partials from within -other templates. - -=== Inline Templates - -Templates may be defined at the end of the source file: - - require 'sinatra' - - get '/' do - haml :index - end - - __END__ - - @@ layout - %html - = yield - - @@ index - %div.title Hello world. - -NOTE: Inline templates defined in the source file that requires sinatra are -automatically loaded. Call enable :inline_templates explicitly if you -have inline templates in other source files. - -=== Named Templates - -Templates may also be defined using the top-level template method: - - template :layout do - "%html\n =yield\n" - end - - template :index do - '%div.title Hello World!' - end - - get '/' do - haml :index - end - -If a template named "layout" exists, it will be used each time a template -is rendered. You can individually disable layouts by passing -:layout => false or disable them by default via -set :haml, :layout => false: - - get '/' do - haml :index, :layout => !request.xhr? - end - -=== Associating File Extensions - -To associate a file extension with a template engine, use -Tilt.register. For instance, if you like to use the file extension -+tt+ for Textile templates, you can do the following: - - Tilt.register :tt, Tilt[:textile] - -=== Adding Your Own Template Engine - -First, register your engine with Tilt, then create a rendering method: - - Tilt.register :myat, MyAwesomeTemplateEngine - - helpers do - def myat(*args) render(:myat, *args) end - end - - get '/' do - myat :index - end - -Renders ./views/index.myat. See https://github.com/rtomayko/tilt to -learn more about Tilt. - -== Filters - -Before filters are evaluated before each request within the same -context as the routes will be and can modify the request and response. Instance -variables set in filters are accessible by routes and templates: - - before do - @note = 'Hi!' - request.path_info = '/foo/bar/baz' - end - - get '/foo/*' do - @note #=> 'Hi!' - params[:splat] #=> 'bar/baz' - end - -After filters are evaluated after each request within the same context and can -also modify the request and response. Instance variables set in before filters -and routes are accessible by after filters: - - after do - puts response.status - end - -Note: Unless you use the +body+ method rather than just returning a String from -the routes, the body will not yet be available in the after filter, since it is -generated later on. - -Filters optionally take a pattern, causing them to be evaluated only if the -request path matches that pattern: - - before '/protected/*' do - authenticate! - end - - after '/create/:slug' do |slug| - session[:last_slug] = slug - end - -Like routes, filters also take conditions: - - before :agent => /Songbird/ do - # ... - end - - after '/blog/*', :host_name => 'example.com' do - # ... - end - -== Helpers - -Use the top-level helpers method to define helper methods for use in -route handlers and templates: - - helpers do - def bar(name) - "#{name}bar" - end - end - - get '/:name' do - bar(params[:name]) - end - -Alternatively, helper methods can be separately defined in a module: - - module FooUtils - def foo(name) "#{name}foo" end - end - - module BarUtils - def bar(name) "#{name}bar" end - end - - helpers FooUtils, BarUtils - -The effect is the same as including the modules in the application class. - -=== Using Sessions - -A session is used to keep state during requests. If activated, you have one -session hash per user session: - - enable :sessions - - get '/' do - "value = " << session[:value].inspect - end - - get '/:value' do - session[:value] = params[:value] - end - -Note that enable :sessions 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 -do so, do *not* call enable :sessions, but instead pull in your -middleware of choice as you would any other middleware: - - use Rack::Session::Pool, :expire_after => 2592000 - - get '/' do - "value = " << session[:value].inspect - end - - get '/:value' do - session[:value] = params[:value] - end - -To improve security, the session data in the cookie is signed with a session -secret. A random secret is generated for you by Sinatra. However, since this -secret will change with every start of your application, you might want to -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: - - halt - -You can also specify the status when halting: - - halt 410 - -Or the body: - - halt 'this will be the body' - -Or both: - - halt 401, 'go away!' - -With headers: - - halt 402, {'Content-Type' => 'text/plain'}, 'revenge' - -It is of course possible to combine a template with +halt+: - - halt erb(:error) - -=== Passing - -A route can punt processing to the next matching route using pass: - - get '/guess/:who' do - pass unless params[:who] == 'Frank' - 'You got me!' - end - - get '/guess/*' do - 'You missed!' - end - -The route block is immediately exited and control continues with the next -matching route. If no matching route is found, a 404 is returned. - -=== Triggering Another Route - -Sometimes +pass+ is not what you want, instead you would like to get the result -of calling another route. Simply use +call+ to achieve this: - - get '/foo' do - status, headers, body = call env.merge("PATH_INFO" => '/bar') - [status, headers, body.map(&:upcase)] - end - - get '/bar' do - "bar" - end - -Note that in the example above, you would ease testing and increase performance -by simply moving "bar" into a helper used by both /foo -and /bar. - -If you want the request to be sent to the same application instance rather than -a duplicate, use call! instead of call. - -Check out the Rack specification if you want to learn more about call. - -=== Setting Body, Status Code and Headers - -It is possible and recommended to set the status code and response body with the -return value of the route block. However, in some scenarios you might want to -set the body at an arbitrary point in the execution flow. You can do so with the -+body+ helper method. If you do so, you can use that method from there on to -access the body: - - get '/foo' do - body "bar" - end - - after do - 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"). - -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", - "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt" - body "I'm a tea pot!" - end - -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 -the 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 requests, -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+ finishes 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: - - # long polling - - set :server, :thin - connections = [] - - get '/subscribe' do - # register a client's interest in server events - stream(:keep_open) { |out| connections << out } - - # purge dead connections - connections.reject!(&:closed?) - - # acknowledge - "subscribed" - end - - post '/message' do - connections.each do |out| - # notify client that a new message has arrived - out << message << "\n" - - # indicate client to connect again - out.close - end - - # acknowledge - "message received" - 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 Sinatra::Application by -default, so if you inherit from Sinatra::Base, 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 env['rack.logger']. - -=== Mime Types - -When using send_file or static files you may have mime types Sinatra -doesn't understand. Use +mime_type+ to register them by file extension: - - configure do - mime_type :foo, 'text/foo' - end - -You can also use it with the +content_type+ helper: - - get '/' do - content_type :foo - "foo foo foo" - end - -=== Generating URLs - -For generating URLs you should use the +url+ helper method, for instance, in -Haml: - - %a{:href => url('/foo')} foo - -It takes reverse proxies and Rack routers into account, if present. - -This method is also aliased to +to+ (see below for an example). - -=== Browser Redirect - -You can trigger a browser redirect with the +redirect+ helper method: - - get '/foo' do - redirect to('/bar') - end - -Any additional parameters are handled like arguments passed to +halt+: - - redirect to('/bar'), 303 - redirect 'http://google.com', 'wrong place, buddy' - -You can also easily redirect back to the page the user came from with -redirect back: - - get '/foo' do - "do something" - end - - get '/bar' do - do_something - redirect back - end - -To pass arguments with a redirect, either add them to the query: - - redirect to('/bar?sum=42') - -Or use a session: - - enable :sessions - - get '/foo' do - session[:secret] = 'foo' - redirect to('/bar') - end - - get '/bar' do - session[:secret] - end - -=== Cache Control - -Setting your headers correctly is the foundation for proper HTTP caching. - -You can easily set the Cache-Control header like this: - - get '/' do - cache_control :public - "cache it!" - end - -Pro tip: Set up caching in a before filter: - - before do - cache_control :public, :must_revalidate, :max_age => 60 - end - -If you are using the +expires+ helper to set the corresponding header, -Cache-Control will be set automatically for you: - - before do - expires 500, :public, :must_revalidate - end - -To properly use caches, you should consider using +etag+ or +last_modified+. -It is recommended to call those helpers *before* doing any heavy lifting, as they -will immediately flush a response if the client already has the current -version in its cache: - - get '/article/:id' do - @article = Article.find params[:id] - last_modified @article.updated_at - etag @article.sha1 - erb :article - end - -It is also possible to use a -{weak ETag}[http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation]: - - 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 reverse-proxy caching -solution, try {rack-cache}[https://github.com/rtomayko/rack-cache]: - - require "rack/cache" - require "sinatra" - - use Rack::Cache - - get '/' do - cache_control :public, :max_age => 36000 - sleep 5 - "hello" - end - -Use the :static_cache_control setting (see below) to add -Cache-Control 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 * 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 :new_resource 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 :kind option: - - etag '', :new_resource => true, :kind => :weak - -=== Sending Files - -For sending files, you can use the send_file helper method: - - get '/' do - send_file 'foo.png' - end - -It also takes options: - - send_file 'foo.png', :type => :jpg - -The options are: - -[filename] - file name, in response, defaults to the real file name. - -[last_modified] - value for Last-Modified header, defaults to the file's mtime. - -[type] - content type to use, guessed from the file extension if missing. - -[disposition] - used for Content-Disposition, possible values: +nil+ (default), - :attachment and :inline - -[length] - Content-Length header, defaults to file size. - -[status] - Status code to be send. Useful when sending a static file as an error page. - -If supported by the Rack handler, other means than streaming from the Ruby -process will be used. If you use this helper method, Sinatra will automatically -handle range requests. - -=== Accessing the Request Object - -The incoming request object can be accessed from request level (filter, routes, -error handlers) through the request method: - - # app running on http://example.com/example - get '/foo' do - 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_param"] # value of some_param parameter. [] is a shortcut to the params hash. - 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 script_name or path_info, can also be -written: - - before { request.path_info = "/" } - - get "/" do - "all requests end up here" - end - -The request.body is an IO or StringIO object: - - post "/api" do - request.body.rewind # in case someone already read it - data = JSON.parse request.body.read - "Hello #{data['name']}!" - end - -=== Attachments - -You can use the +attachment+ helper to tell the browser the response should be -stored on disk rather than displayed in the browser: - - get '/' do - attachment - "store it!" - end - -You can also pass it a file name: - - get '/' do - attachment "info.txt" - "store it!" - end - -=== Dealing with Date and Time - -Sinatra offers a +time_for+ helper method that generates a Time object -from the given value. 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 find_template helper is used to find template files for rendering: - - find_template settings.views, 'foo', Tilt[:haml] do |file| - puts "could be #{file}" - end - -This is not really useful. But it is useful that you can actually override this -method to hook in your own lookup mechanism. For instance, if you want to be -able to use more than one view directory: - - set :views, ['views', 'templates'] - - helpers do - def find_template(views, name, engine, &block) - Array(views).each { |v| super(v, name, engine, &block) } - end - end - -Another example would be using different directories for different engines: - - 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 - -You can also easily wrap this up in an extension and share with others! - -Note that find_template does not check if the file really exists but -rather calls the given block for all possible paths. This is not a performance -issue, since +render+ will use +break+ as soon as a file is found. Also, -template locations (and content) will be cached if you are not running in -development mode. You should keep that in mind if you write a really crazy -method. - -== Configuration - -Run once, at startup, in any environment: - - configure do - # setting one option - set :option, 'value' - - # setting multiple options - set :a => 1, :b => 2 - - # same as `set :option, true` - enable :option - - # same as `set :option, false` - disable :option - - # you can also have dynamic settings with blocks - set(:css_dir) { File.join(views, 'css') } - end - -Run only when the environment (RACK_ENV environment variable) is set to -:production: - - configure :production do - ... - end - -Run when the environment is set to either :production or -:test: - - configure :production, :test do - ... - end - -You can access those options via settings: - - configure do - set :foo, 'bar' - end - - get '/' do - settings.foo? # => true - settings.foo # => 'bar' - ... - end - -=== Configuring attack protection - -Sinatra is using -{Rack::Protection}[https://github.com/rkh/rack-protection#readme] to defend -your application against common, opportunistic attacks. You can easily disable -this behavior (which will open up your application to tons of common -vulnerabilities): - - 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 the second parameter. - - Disabled per default. - -[add_charsets] mime types the content_type 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 "utf-8"). - -[dump_errors] display errors in the log. - -[environment] current environment, defaults to ENV['RACK_ENV'], - or "development" if not available. - -[logging] use the logger. - -[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. - -[method_override] use _method magic to allow put/delete forms in - browsers that don't support it. - -[port] Port to listen on. Only used for built-in server. - -[prefixed_redirects] Whether or not to insert request.script_name - into redirects if no absolute path is given. That way - redirect '/foo' would behave like - redirect to('/foo'). Disabled per default. - -[protection] Whether or not to enable web attack protections. See - protection section above. - -[public_dir] Alias for public_folder. See below. - -[public_folder] Path to the folder public files are served from. Only - used if static file serving is enabled (see - static setting below). Inferred from - app_file setting if not set. - -[reload_templates] whether or not to reload templates between requests. - Enabled in development mode. - -[root] Path to project root folder. Inferred from +app_file+ - setting if not set. - -[raise_errors] raise exceptions (will stop application). Enabled - by default when environment is set to - "test", disabled otherwise. - -[run] if enabled, Sinatra will handle starting the web server, - do not enable if using rackup or other means. - -[running] is the built-in server running now? - do not change this setting! - -[server] server or list of servers to use for built-in server. - defaults to ['thin', 'mongrel', 'webrick'], order - indicates priority. - -[sessions] enable cookie-based sessions support using - Rack::Session::Cookie. See 'Using Sessions' - section for more information. - -[show_exceptions] show a stack trace in the browser when an exception - happens. Enabled by default when environment - is set to "development", disabled otherwise. - Can also be set to :after_handler to trigger - app-specified error handling before showing a stack - trace in the browser. - -[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. - -[static_cache_control] When Sinatra is serving static files, set this to add - Cache-Control headers to the responses. Uses the - +cache_control+ helper. Disabled by default. - Use an explicit array when setting multiple values: - set :static_cache_control, [:public, :max_age => 300] - -[threaded] If set to +true+, will tell Thin to use - EventMachine.defer for processing the request. - -[views] Path to the views folder. Inferred from app_file - setting if not set. - -== Environments - -There are three predefined +environments+: "development", -"production" and "test". Environments can be set -through the +RACK_ENV+ environment variable. The default value is -"development". In the "development" environment all templates are reloaded between -requests, and special not_found and error handlers -display stack traces in your browser. -In the "production" and "test" environments, templates are cached by default. - -To run different environments use the -e option: - - ruby my_app.rb -e [ENVIRONMENT] - -You can use predefined methods: +development?+, +test?+ and +production?+ to -check the current environment setting. - -== Error Handling - -Error handlers run within the same context as routes and before filters, which -means you get all the goodies it has to offer, like haml, -erb, halt, etc. - -=== Not Found - -When a Sinatra::NotFound exception is raised, or the response's status -code is 404, the not_found handler is invoked: - - not_found do - 'This is nowhere to be found.' - end - -=== Error - -The +error+ handler is invoked any time an exception is raised from a route -block or a filter. The exception object can be obtained from the -sinatra.error Rack variable: - - error do - 'Sorry there was a nasty error - ' + env['sinatra.error'].name - end - -Custom errors: - - error MyCustomError do - 'So what happened was...' + env['sinatra.error'].message - end - -Then, if this happens: - - get '/' do - raise MyCustomError, 'something bad' - end - -You get this: - - So what happened was... something bad - -Alternatively, you can install an error handler for a status code: - - error 403 do - 'Access forbidden' - end - - get '/secret' do - 403 - end - -Or a range: - - error 400..510 do - 'Boom' - end - -Sinatra installs special not_found and error handlers when -running under the development environment. - -== Rack Middleware - -Sinatra rides on Rack[http://rack.rubyforge.org/], a minimal standard -interface for Ruby web frameworks. One of Rack's most interesting capabilities -for application developers is support for "middleware" -- components that sit -between the server and your application monitoring and/or manipulating the -HTTP request/response to provide various types of common functionality. - -Sinatra makes building Rack middleware pipelines a cinch via a top-level -+use+ method: - - require 'sinatra' - require 'my_custom_middleware' - - use Rack::Lint - use MyCustomMiddleware - - get '/hello' do - 'Hello World' - end - -The semantics of +use+ are identical to those defined for the -Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSL -(most frequently used from rackup files). For example, the +use+ method -accepts multiple/variable args as well as blocks: - - use Rack::Auth::Basic do |username, password| - username == 'admin' && password == 'secret' - end - -Rack is distributed with a variety of standard middleware for logging, -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://rdoc.info/github/brynary/rack-test/master/frames] -is recommended: - - require 'my_sinatra_app' - require 'test/unit' - require 'rack/test' - - class MyAppTest < Test::Unit::TestCase - include Rack::Test::Methods - - def app - Sinatra::Application - end - - def test_my_default - get '/' - assert_equal 'Hello World!', last_response.body - end - - def test_with_params - get '/meet', :name => 'Frank' - assert_equal 'Hello Frank!', last_response.body - end - - def test_with_rack_env - get '/', {}, 'HTTP_USER_AGENT' => 'Songbird' - assert_equal "You're using Songbird!", last_response.body - end - end - -Note: If you are using Sinatra in the modular style, replace Sinatra::Application above with the class name of your app. - -== Sinatra::Base - Middleware, Libraries, and Modular Apps - -Defining your app at the top-level works well for micro-apps but has -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 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: - - require 'sinatra/base' - - class MyApp < Sinatra::Base - set :sessions, true - set :foo, 'bar' - - get '/' do - 'Hello world!' - end - end - -The methods available to Sinatra::Base subclasses are exactly the same as those -available via the top-level DSL. Most top-level apps can be converted to -Sinatra::Base components with two modifications: - -* Your file should require sinatra/base 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. - -Sinatra::Base 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] -for details on available options and their behavior. - -=== Modular vs. Classic Style - -Contrary to common belief, there is nothing wrong with the classic style. If it -suits your application, you do not have to switch to a modular application. - -The main disadvantage of using the classic style rather than the modular style is that -you will only have one Sinatra application per Ruby process. If you plan to use -more than one, switch to the modular style. There is no reason you cannot mix -the modular and the classic styles. - -If switching from one style to the other, you should be aware of slightly -different default settings: - - Setting Classic Modular - - 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 - -There are two common options for starting a modular app, actively starting with -run!: - - # my_app.rb - require 'sinatra/base' - - class MyApp < Sinatra::Base - # ... app code here ... - - # start the server if ruby file executed directly - run! if app_file == $0 - end - -Start with: - - ruby my_app.rb - -Or with a config.ru file, which allows using any Rack handler: - - # config.ru (run with rackup) - require './my_app' - run MyApp - -Run: - - rackup -p 4567 - -=== Using a Classic Style Application with a config.ru - -Write your app file: - - # app.rb - require 'sinatra' - - get '/' do - 'Hello world!' - end - -And a corresponding config.ru: - - require './app' - run Sinatra::Application - -=== When to use a config.ru? - -A config.ru file is recommended if: - -* You want to deploy with a different Rack handler (Passenger, Unicorn, - Heroku, ...). -* You want to use more than one subclass of Sinatra::Base. -* You want to use Sinatra only for middleware, and not as an endpoint. - -There is no need to switch to a config.ru simply because you -switched to the modular style, and you don't have to use the modular style for running -with a config.ru. - -=== Using Sinatra as Middleware - -Not only is Sinatra able to use other Rack middleware, any Sinatra application -can in turn be added in front of any Rack endpoint as middleware itself. This -endpoint could be another Sinatra application, or any other Rack-based -application (Rails/Ramaze/Camping/...): - - require 'sinatra/base' - - class LoginScreen < Sinatra::Base - enable :sessions - - get('/login') { haml :login } - - post('/login') do - if params[:name] == 'admin' && params[:password] == 'admin' - session['user_name'] = params[:name] - else - redirect '/login' - end - end - end - - class MyApp < Sinatra::Base - # middleware will run before filters - use LoginScreen - - before do - unless session['user_name'] - halt "Access denied, please login." - end - end - - get('/') { "Hello #{session['user_name']}." } - end - -=== Dynamic Application Creation - -Sometimes you want to create new applications at runtime without having to -assign them to a constant, you can do this with Sinatra.new: - - require 'sinatra/base' - my_app = Sinatra.new { get('/') { "hi" } } - my_app.run! - -It takes the application to inherit from as an optional argument: - - # config.ru (run with rackup) - 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 - -This is especially useful for testing Sinatra extensions or using Sinatra in -your own library. - -This also makes using Sinatra as middleware extremely easy: - - require 'sinatra/base' - - use Sinatra do - get('/') { ... } - end - - run RailsProject::Application - -== Scopes and Binding - -The scope you are currently in determines what methods and variables are -available. - -=== Application/Class Scope - -Every Sinatra application corresponds to a subclass of Sinatra::Base. -If you are using the top-level DSL (require 'sinatra'), 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+ or +session+ objects, as there is only a -single application class for all requests. - -Options created via +set+ are methods at class level: - - class MyApp < Sinatra::Base - # Hey, I'm in the application scope! - set :foo, 42 - foo # => 42 - - get '/foo' do - # Hey, I'm no longer in the application scope! - end - end - -You have the application scope binding inside: - -* Your application class body -* Methods defined by extensions -* The block passed to +helpers+ -* Procs/blocks used as value for +set+ -* The block passed to Sinatra.new - -You can reach the scope object (the class) like this: - -* Via the object passed to configure blocks (configure { |c| ... }) -* +settings+ from within the request scope - -=== Request/Instance Scope - -For every incoming request, a new instance of your application class is -created and all handler blocks run in that scope. From within this scope you -can access the +request+ and +session+ objects or call rendering methods like -+erb+ or +haml+. You can access the application scope from within the request -scope via the +settings+ helper: - - class MyApp < Sinatra::Base - # Hey, I'm in the application scope! - get '/define_route/:name' do - # Request scope for '/define_route/:name' - @value = 42 - - settings.get("/#{params[:name]}") do - # Request scope for "/#{params[:name]}" - @value # => nil (not the same request) - end - - "Route defined!" - end - end - -You have the request scope binding inside: - -* get/head/post/put/delete/options blocks -* before/after filters -* helper methods -* templates/views - -=== Delegation Scope - -The delegation scope just forwards methods to the class scope. However, it -does not behave exactly like the class scope, as you do not have the class -binding. Only methods explicitly marked for delegation are available, and you -do not share variables/state with the class scope (read: you have a different -+self+). You can explicitly add method delegations by calling -Sinatra::Delegator.delegate :method_name. - -You have the delegate scope binding inside: - -* The top level binding, if you did require "sinatra" -* An object extended with the Sinatra::Delegator mixin - -Have a look at the code for yourself: here's the -{Sinatra::Delegator mixin}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633] -being {extending the main object}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30]. - -== Command Line - -Sinatra applications can be run directly: - - ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER] - -Options are: - - -h # help - -p # set the port (default is 4567) - -o # set the host (default is 0.0.0.0) - -e # set the environment (default is development) - -s # specify rack server/handler (default is thin) - -x # turn on the mutex lock (default is off) - -== Requirement - -The following Ruby versions are officially supported: - -[ 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. Support for - 1.8.7 will not be dropped before Sinatra 2.0 and Ruby 2.0 except maybe in - the unlikely event of 1.8.8 being released. Even then, we might continue - supporting it. Ruby 1.8.6 is no longer supported. 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 fully supported and recommended. Do not use 1.9.2p0, as 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 and recommended. Please note that switching to 1.9.3 - from an earlier version will invalidate all sessions. - -[ Rubinius ] - Rubinius is officially supported (Rubinius >= 1.2.4), everything works, including - all template languages. The upcoming 2.0 release is supported as - well, including 1.9 mode. - -[ JRuby ] - JRuby is officially supported (JRuby >= 1.6.7). 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, Redcarpet, RedCloth and Yajl templates as well as Thin - and Mongrel at the moment. - -We also keep an eye on upcoming Ruby versions. - -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 (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 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. - -Sinatra currently doesn't run on Cardinal, SmallRuby, BlueRuby or any -Ruby version prior to 1.8.7. - -== The Bleeding Edge - -If you would like to use Sinatra's latest bleeding-edge code, feel free to run your -application against the master branch, it should be rather stable. - -We also push out prerelease gems from time to time, so you can do a - - gem install sinatra --pre - -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. - -First, install bundler, if you haven't: - - gem install bundler - -Then, in your project directory, create a +Gemfile+: - - source :rubygems - gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git" - - # other dependencies - gem 'haml' # for instance, if you use haml - gem 'activerecord', '~> 3.0' # maybe you also need ActiveRecord 3.x - -Note that you will have to list all your application's dependencies in the +Gemfile+. -Sinatra's direct dependencies (Rack and Tilt) will, however, be automatically -fetched and added by Bundler. - -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 sinatra/lib directory -on the $LOAD_PATH: - - cd myapp - git clone git://github.com/sinatra/sinatra.git - ruby -Isinatra/lib myapp.rb - -To update the Sinatra sources in the future: - - cd myapp/sinatra - git pull - -=== Install Globally - -You can build the gem on your own: - - git clone git://github.com/sinatra/sinatra.git - cd sinatra - rake sinatra.gemspec - rake install - -If you install gems as root, the last step should be - - sudo rake install - -== Versioning - -Sinatra follows {Semantic Versioning}[http://semver.org/], both SemVer and -SemVerTag. - -== Further Reading - -* {Project Website}[http://www.sinatrarb.com/] - Additional documentation, - news, and links to other resources. -* {Contributing}[http://www.sinatrarb.com/contributing] - Find a bug? Need - help? Have a patch? -* {Issue tracker}[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] on http://freenode.net -* {Sinatra Book}[http://sinatra-book.gittr.com] Cookbook Tutorial -* {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 -* {CI server}[http://travis-ci.org/sinatra/sinatra]