Sinatra: ku jatuh cinta lagi

This page is also available in Chinese, French, German, Hungarian, Portuguese (Brazilian), Portuguese (European), Russian, Spanish and Japanese.

Getting Started

  1. Routes
  1. Conditions
  2. Return Values
  3. Custom Route Matchers
  • Static Files
  • Views / Templates
    1. Haml Templates
    2. Erb Templates
    3. Erubis Templates
    4. Builder Templates
    5. Nokogiri Templates
    6. Sass Templates
    7. Scss Templates
    8. Less Templates
    9. Liquid Templates
    10. Markdown Templates
    11. Textile Templates
    12. RDoc Templates
    13. Radius Templates
    14. Markaby Templates
    15. Slim Templates
    16. CoffeeScript Templates
    17. Embedded Templates
    18. Accessing Variables in Templates
    19. Inline Templates
    20. Named Templates
    21. Associating File Extensions
    22. Adding Your Own Template Engine
  • Filters
  • Helpers
    1. Using Sessions
    2. Halting
    3. Passing
    4. Triggering Another Route
    5. Setting Body, Status Code and Headers
    6. Mime Types
    7. Generating URLs
    8. Browser Redirect
    9. Cache Control
    10. Sending Files
    11. Accessing the Request Object
    12. Attachments
    13. Looking Up Template Files
  • Configuration
    1. Available Settings
  • Error Handling
    1. Not Found
    2. Error
  • Rack Middleware
  • Testing
  • Sinatra::Base – Middleware, Libraries, and Modular Apps
    1. Modular vs. Classic Style
    2. Serving a Modular Application
    3. Using a Classic Style Application with a config.ru
    4. When to use a config.ru?
    5. Using Sinatra as Middleware
    6. Dynamic Application Creation
  • Scopes and Binding
    1. Application/Class Scope
    2. Request/Instance Scope
    3. Delegation Scope
  • Command Line
  • Requirements
  • The Bleeding Edge
    1. With Bundler
    2. Roll Your Own
    3. Install Globally
  • Versioning
  • Further Reading
  • Sinatra is a DSL for quickly creating web applications in Ruby with minimaleffort:

    # myapp.rb  require 'sinatra'    get '/' do    'Hello world!'  end

    Install the gem and run with:

    gem install sinatra  ruby -rubygems myapp.rb

    View at: localhost:4567

    It is recommended to also run gem install thin, which Sinatra willpick 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    .. update something ..  end  delete '/' do    .. annihilate something ..  end    options '/' do    .. appease something ..  end

    Routes are matched in the order they are defined. The first route thatmatches the request is invoked.

    Route patterns may include named parameters, accessible via theparams 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, accessiblevia 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

    Conditions

    Routes may include a variety of matching conditions, such as the useragent:

    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

    Return Values

    The return value of a route block determines at least the response bodypassed on to the HTTP client, or at least the next middleware in the Rackstack. Most commonly, this is a string, as in the above examples. But othervalues are also accepted.

    You can return any object that would either be a valid Rack response, Rackbody 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 (respondsto #each)]

    • An object that responds to #each and passes nothing but strings tothe 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 }

    Custom Route Matchers

    As shown above, Sinatra ships with built-in support for using Stringpatterns and regular expressions as route matches. However, it does notstop 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 beexpressed 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 canspecify a different location by setting the :public option:

    set :public, 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 example.com/css/style.css.

    Views / Templates

    Templates are assumed to be located directly under the ./viewsdirectory. To use a different views directory:

    set :views, File.dirname(__FILE__) + '/templates'

    One important thing to remember is that you always have to referencetemplates with symbols, even if they’re in a subdirectory (in thiscase, use :'subdir/template'). You must use a symbol becauseotherwise rendering methods will render any strings passed to themdirectly.

    Haml Templates

    The haml gem/library is required to render HAML templates:

    # You'll need to require haml in your app  require 'haml'  get '/' do    haml :index  end

    Renders ./views/index.haml.

    Haml’soptions can be set globally through Sinatra’s configurations, seeOptions andConfigurations, and overridden on an individual basis.

    set :haml, :format => :html5 # default Haml format is :xhtml  get '/' do    haml :index, :format => :html4 # overridden  end

    Erb Templates

    # You'll need to require erb in your app  require 'erb'  get '/' do    erb :index  end

    Renders ./views/index.erb.

    Erubis Templates

    The erubis gem/library is required to render Erubis templates:

    # You'll need to require erubis in your app  require 'erubis'  get '/' do    erubis :index  end

    Renders ./views/index.erubis.

    It is also possible to replace Erb with Erubis:

    require 'erubis'  Tilt.register :erb, Tilt[:erubis]    get '/' do    erb :index  end

    Renders ./views/index.erb with Erubis.

    Builder Templates

    The builder gem/library is required to render builder templates:

    # You'll need to require builder in your app  require 'builder'  get '/' do    builder :index  end

    Renders ./views/index.builder.

    Nokogiri Templates

    The nokogiri gem/library is required to render nokogiri templates:

    # You'll need to require nokogiri in your app  require 'nokogiri'  get '/' do    nokogiri :index  end

    Renders ./views/index.nokogiri.

    Sass Templates

    The haml or sass gem/library is required to render Sasstemplates:

    # You'll need to require haml or sass in your app  require 'sass'  get '/stylesheet.css' do    sass :stylesheet  end

    Renders ./views/stylesheet.sass.

    Sass’soptions can be set globally through Sinatra’s configurations, seeOptions andConfigurations, and overridden on an individual basis.

    set :sass, :style => :compact # default Sass style is :nested  get '/stylesheet.css' do    sass :stylesheet, :style => :expanded # overridden  end

    Scss Templates

    The haml or sass gem/library is required to render Scsstemplates:

    # You'll need to require haml or sass in your app  require 'sass'  get '/stylesheet.css' do    scss :stylesheet  end

    Renders ./views/stylesheet.scss.

    Scss’soptions can be set globally through Sinatra’s configurations, seeOptions andConfigurations, and overridden on an individual basis.

    set :scss, :style => :compact # default Scss style is :nested  get '/stylesheet.css' do    scss :stylesheet, :style => :expanded # overridden  end

    Less Templates

    The less gem/library is required to render Less templates:

    # You'll need to require less in your app  require 'less'  get '/stylesheet.css' do    less :stylesheet  end

    Renders ./views/stylesheet.less.

    Liquid Templates

    The liquid gem/library is required to render Liquid templates:

    # You'll need to require liquid in your app  require 'liquid'  get '/' do    liquid :index  end

    Renders ./views/index.liquid.

    Since you cannot call Ruby methods (except for yield) from aLiquid template, you almost always want to pass locals to it:

    liquid :index, :locals => { :key => 'value' }

    Markdown Templates

    The rdiscount gem/library is required to render Markdowntemplates:

    # You'll need to require rdiscount in your app  require "rdiscount"    get '/' do    markdown :index  end

    Renders ./views/index.markdown (md and mkd arealso valid file extensions).

    It is not possible to call methods from markdown, nor to pass locals to it.You therefore will usually use it in combination with another renderingengine:

    erb :overview, :locals => { :text => markdown(:introduction) }

    Note that you may also call the markdown method from within othertemplates:

    %h1 Hello From Haml!  %p= markdown(:greetings)

    Since you cannot call Ruby from Markdown, you cannot use layouts written inMarkdown. However, it is possible to use another rendering engine for thetemplate than for the layout by passing the :layout_engine option:

    get '/' do    markdown :index, :layout_engine => :erb  end

    This will render ./views/index.md with ./views/layout.erbas layout.

    Remember that you can set such rendering options globally:

    set :markdown, :layout_engine => :haml, :layout => :post  get '/' do    markdown :index  end

    This will render ./views/index.md (and any other Markdowntemplate) with ./views/post.haml as layout.

    It is also possible to parse Markdown with BlueCloth rather than RDiscount:

    require 'bluecloth'    Tilt.register 'markdown', BlueClothTemplate  Tilt.register 'mkd',      BlueClothTemplate  Tilt.register 'md',       BlueClothTemplate    get '/' do    markdown :index  end

    Renders ./views/index.md with BlueCloth.

    Textile Templates

    The RedCloth gem/library is required to render Textile templates:

    # You'll need to require redcloth in your app  require "redcloth"  get '/' do    textile :index  end

    Renders ./views/index.textile.

    It is not possible to call methods from textile, nor to pass locals to it.You therefore will usually use it in combination with another renderingengine:

    erb :overview, :locals => { :text => textile(:introduction) }

    Note that you may also call the textile method from within othertemplates:

    %h1 Hello From Haml!  %p= textile(:greetings)

    Since you cannot call Ruby from Textile, you cannot use layouts written inTextile. However, it is possible to use another rendering engine for thetemplate than for the layout by passing the :layout_engine option:

    get '/' do    textile :index, :layout_engine => :erb  end

    This will render ./views/index.textile with./views/layout.erb as layout.

    Remember that you can set such rendering options globally:

    set :textile, :layout_engine => :haml, :layout => :post  get '/' do    textile :index  end

    This will render ./views/index.textile (and any other Textiletemplate) with ./views/post.haml as layout.

    RDoc Templates

    The rdoc gem/library is required to render RDoc templates:

    # You'll need to require rdoc/markup/to_html in your app  require "rdoc/markup/to_html"  get '/' do    rdoc :index  end

    Renders ./views/index.rdoc.

    It is not possible to call methods from rdoc, nor to pass locals to it. Youtherefore 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 othertemplates:

    %h1 Hello From Haml!  %p= rdoc(:greetings)

    Since you cannot call Ruby from RDoc, you cannot use layouts written inRDoc. However, it is possible to use another rendering engine for thetemplate than for the layout by passing the :layout_engine option:

    get '/' do    rdoc :index, :layout_engine => :erb  end

    This will render ./views/index.rdoc with./views/layout.erb as layout.

    Remember that you can set such rendering options globally:

    set :rdoc, :layout_engine => :haml, :layout => :post  get '/' do    rdoc :index  end

    This will render ./views/index.rdoc (and any other RDoc template)with ./views/post.haml as layout.

    Radius Templates

    The radius gem/library is required to render Radius templates:

    # You'll need to require radius in your app  require 'radius'  get '/' do    radius :index  end

    Renders ./views/index.radius.

    Since you cannot call Ruby methods (except for yield) from aRadius template, you almost always want to pass locals to it:

    radius :index, :locals => { :key => 'value' }

    Markaby Templates

    The markaby gem/library is required to render Markaby templates:

    # You'll need to require markaby in your app  require 'markaby'  get '/' do    markaby :index  end

    Renders ./views/index.mab.

    You may also use inline Markaby:

    get '/' do    markaby { h1 "Welcome!" }  end

    Slim Templates

    The slim gem/library is required to render Slim templates:

    # You'll need to require slim in your app  require 'slim'  get '/' do    slim :index  end

    Renders ./views/index.slim.

    CoffeeScript Templates

    The coffee-script gem/library and at least one of thefollowing options to execute JavaScript:

    • node (from Node.js) in your path

    • you must be running on OSX

    • therubyracer gem/library

    See github.com/josh/ruby-coffee-scriptfor an updated list of options.

    Now you can render CoffeeScript templates:

    # You'll need to require coffee-script in your app  require 'coffee-script'  get '/application.js' do    coffee :application  end

    Renders ./views/application.coffee.

    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. Instancevariables 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 withinother 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 areautomatically loaded. Call enable :inline_templates explicitly ifyou have inline templates in other source files.

    Named Templates

    Templates may also be defined using the top-level template method:

    template :layout do    "%htmln  =yieldn"  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 timea 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, useTilt.register. For instance, if you like to use the file extensiontt 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 github.com/rtomayko/tilt tolearn more about Tilt.

    Filters

    Before filters are evaluated before each request within the same context asthe routes will be and can modify the request and response. Instancevariables 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 andcan also modify the request and response. Instance variables set in beforefilters and routes are accessible by after filters:

    after do    puts response.status  end

    Note: Unless you use the body method rather than just returning aString from the routes, the body will not yet be available in the afterfilter, since it is generated later on.

    Filters optionally take a pattern, causing them to be evaluated only if therequest 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 usein route handlers and templates:

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

    Using Sessions

    A session is used to keep state during requests. If activated, you have onesession 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 increaseyour traffic, for instance). You can use any Rack session middleware: inorder to do so, do not call enable :sessions, but insteadpull in your middleware of choice how 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 asession secret. A random secret is generate for you by Sinatra. However,since this secret will change with every start of your application, youmight want to set the secret yourself, so all your application instancesshare it:

    set :session_secret, 'super secret'

    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 nextmatching 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 getthe result of calling another route. Simply use call to achievethis:

    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 increaseperformance by simply moving "bar" into a helper used byboth /foo and /bar.

    If you want the request to be sent to the same application instance ratherthan a duplicate, use call! instead of call.

    Check out the Rack specification if you want to learn more aboutcall.

    Setting Body, Status Code and Headers

    It is possible and recommended to set the status code and response bodywith the return value of the route block. However, in some scenarios youmight want to set the body at an arbitrary point in the execution flow. Youcan do so with the body helper method. If you do so, you can usethat 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 beexecuted 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 argumentscan be used to access their current values.

    Mime Types

    When using send_file or static files you may have mime typesSinatra doesn’t understand. Use mime_type to register themby 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, forinstance, 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 helpermethod:

    get '/foo' do    redirect to('/bar')  end

    Any additional parameters are handled like arguments passed tohalt:

    redirect to('/bar'), 303  redirect 'http://google.com', 'wrong place, buddy'

    You can also easily redirect back to the page the user came from withredirect 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 :session    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 with 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 correspondingheader, 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 andlast_modified. It is recommended to call those helpersbefore doing heavy lifting, as they will immediately flush aresponse 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 weakETag:

    etag @article.sha1, :weak

    These helpers will not do any caching for you, but rather feed thenecessary information to your cache. If you are looking for a quick cachingsolutions, try rack-cache:

    require "rack/cache"  require "sinatra"    use Rack::Cache    get '/' do    cache_control :public, :max_age => 36000    sleep 5    "hello"  end

    Sending Files

    For sending files, you can use the send_file helper method:

    get '/' do    send_file 'foo.png'  end

    It also takes a couple of 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.

    If supported by the Rack handler, other means than streaming from the Rubyprocess will be used. If you use this helper method, Sinatra willautomatically 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    request.body              # request body sent by the client (see below)    request.scheme            # "http"    request.script_name       # "/example"    request.path_info         # "/foo"    request.port              # 80    request.request_method    # "GET"    request.query_string      # ""    request.content_length    # length of request.body    request.media_type        # media type of request.body    request.host              # "example.com"    request.get?              # true (similar methods for other verbs)    request.form_data?        # false    request["SOME_HEADER"]    # value of SOME_HEADER header    request.referrer          # the referrer of the client or '/'    request.user_agent        # user agent (used by :agent condition)    request.cookies           # hash of browser cookies    request.xhr?              # is this an ajax request?    request.url               # "http://example.com/example/foo"    request.path              # "/example/foo"    request.ip                # client IP address    request.secure?           # false (would be true over ssl)    request.forwarded?        # true (if running behind a reverse proxy)    request.env               # raw env hash handed in by Rack  end

    Some options, like script_name or path_info, can also bewritten:

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

    Looking Up Template Files

    The find_template helper is used to find template files forrendering:

    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 overridethis method to hook in your own lookup mechanism. For instance, if you wantto 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 existsbut rather calls the given block for all possible paths. This is not aperformance issue, since render will use break as soon asa file is found. Also, template locations (and content) will be cached ifyou are not running in development mode. You should keep that in mind ifyou 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

    Available Settings

    absolute_redirects

    If disabled, Sinatra will allow relative redirects, however, Sinatra willno longer conform with RFC 2616 (HTTP 1.1), which only allows absoluteredirects.

    Enable if your app is running behind a reverse proxy that has not been setup properly. Note that the url helper will still produce absoluteURLs, unless you pass in false as second parameter.

    Disabled per default.

    add_charsets

    mime types the content_type helper will automatically add thecharset info to.

    You should add to it rather than overriding this option:

    settings.add_charsets << "application/foobar"
    app_file

    main application file, used to detect project root, views and public folderand 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 perRuby 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 thatdon’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 noabsolute path is given. That way redirect '/foo' would behave likeredirect to('/foo'). Disabled per default.

    public

    folder public files are served from

    reload_templates

    whether or not to reload templates between requests. Enabled in developmentmode and on Ruby 1.8.6 (to compensate a bug in Ruby causing a memory leak).

    root

    project root folder.

    raise_errors

    raise exceptions (will stop application).

    run

    if enabled, Sinatra will handle starting the web server, do not enable ifusing 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’], orderindicates priority.

    sessions

    enable cookie based sessions.

    show_exceptions

    show a stack trace in the browser.

    static

    Whether Sinatra should handle serving static files. Disable when using aServer able to do this on its own. Disabling will boost performance.Enabled per default.

    views

    views folder.

    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 theresponse’s status code is 404, the not_found handler isinvoked:

    not_found do    'This is nowhere to be found.'  end

    Error

    The error handler is invoked any time an exception is raised froma route block or a filter. The exception object can be obtained from thesinatra.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 handlerswhen running under the development environment.

    Rack Middleware

    Sinatra rides on Rack, a minimalstandard interface for Ruby web frameworks. One of Rack’s mostinteresting capabilities for application developers is support for“middleware” — components that sit between the server andyour application monitoring and/or manipulating the HTTP request/responseto provide various types of common functionality.

    Sinatra makes building Rack middleware pipelines a cinch via a top-leveluse 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::BuilderDSL (most frequently used from rackup files). For example, the usemethod 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 usesmany of these components automatically based on configuration so youtypically don’t have to use them explicitly.

    You can find useful middleware in rack, rack-contrib, withCodeRack or in the Rack wiki.

    Testing

    Sinatra tests can be written using any Rack-based testing library orframework. Rack::Testis 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

    Sinatra::Base – Middleware, Libraries, and Modular Apps

    Defining your app at the top-level works well for micro-apps but hasconsiderable drawbacks when building reusable components such as Rackmiddleware, Rails metal, simple libraries with a server component, or evenSinatra extensions. The top-level DSL pollutes the Object namespace andassumes a micro-app style configuration (e.g., a single application file,./public and ./views directories, logging, exceptiondetail page, etc.). That’s where Sinatra::Base comes intoplay:

    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 asthose available via the top-level DSL. Most top-level apps can be convertedto 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 mainnamespace.

    • Put your app’s routes, error handlers, filters, and options in asubclass of Sinatra::Base.

    Sinatra::Base is a blank slate. Most options are disabled bydefault, including the built-in server. See Options andConfiguration for details on available options and their behavior.

    Modular vs. Classic Style

    Contrary to common belief, there is nothing wrong with classic style. If itsuits your application, you do not have to switch to a modular application.

    There are only two downsides compared with modular style:

    • You may only have one Sinatra application per Ruby process. If you plan touse more, switch to modular style.

    • Classic style pollutes Object with delegator methods. If you plan to shipyour application in a library/gem, switch to modular style.

    There is no reason you cannot mix modular and classic style.

    If switching from one style to the other, you should be aware of slightlydifferent default settings:

    Setting             Classic                 Modular  app_file            file loading sinatra    nil  run                 $0 == app_file          false  logging             true                    false  method_override     true                    false  inline_templates    true                    false

    Serving a Modular Application

    There are two common options for starting a modular app, actively startingwith 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, which allows using any Rack handler:

    # config.ru  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?

    Good signs you probably want to use a config.ru:

    • 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, but not as endpoint.

    There is no need to switch to a config.ru only because youswitched to modular style, and you don’t have to use modular stylefor running with a config.ru.

    Using Sinatra as Middleware

    Not only is Sinatra able to use other Rack middleware, any Sinatraapplication can in turn be added in front of any Rack endpoint asmiddleware itself. This endpoint could be another Sinatra application, orany 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 toassign 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 optional argument:

    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 Sinatrain 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 areavailable.

    Application/Class Scope

    Every Sinatra application corresponds to a subclass ofSinatra::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 youhave methods like get or before, but you cannot accessthe request object or the session, as there only is asingle 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 request scope

    Request/Instance Scope

    For every incoming request, a new instance of your application class iscreated and all handler blocks run in that scope. From within this scopeyou can access the request and session object or callrendering methods like erb or haml. You can access theapplication scope from within the request scope via the settingshelper:

    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, itdoes not behave 100% like the class scope, as you do not have the classbinding. Only methods explicitly marked for delegation are available andyou do not share variables/state with the class scope (read: you have adifferent self). You can explicitly add method delegations bycalling 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::Delegatormixin being includedinto the main namespace.

    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)

    Requirements

    It is recommended to install Sinatra on Ruby 1.8.7, 1.9.2, JRuby orRubinius.

    The following Ruby versions are officially supported:

    Ruby 1.8.6

    It is not recommended to use 1.8.6 for Sinatra. However, it will beofficially supported until Sinatra 1.3.0 is released. RDoc and CoffeeScripttemplates are not supported by this Ruby version. 1.8.6 includes a majormemory leak in its Hash implementation, which is triggered by Sinatraversions prior to 1.1.1. The current version explicitly prevents this leakat the cost of performance. You will have to downgrade Rack to 1.1.x, asRack >= 1.2 no longer supports 1.8.6.

    Ruby 1.8.7

    1.8.7 is fully supported, however, if nothing is keeping you from it, werecommend upgrading to 1.9.2 or switching to JRuby or Rubinius.

    Ruby 1.9.2

    1.9.2 is supported and recommended. Note that Radius and Markaby arecurrently not 1.9 compatible. Do not use 1.9.2p0, it is known to causesegmentation faults when using Sinatra.

    Rubinius

    Rubinius is officially supported (Rubinius >= 1.2.3), everything, includingall template languages, works.

    JRuby

    JRuby is officially supported (JRuby >= 1.6.0). No issues with third partytemplate libraries are known, however, if you choose to use JRuby, pleaselook into JRuby rack handlers, as the Thin web server is not fullysupported on JRuby. JRuby’s support for C extensions is stillexperimental, which only affects RDiscount at the moment.

    We also keep an eye on upcoming Ruby versions.

    The following Ruby implementations are not officially supported but stillare known to run Sinatra:

    • Older versions of JRuby and Rubinius

    • MacRuby, Maglev, IronRuby

    • Ruby 1.9.0 and 1.9.1

    Not being officially supported means if things only break there and not ona supported platform, we assume it’s not our issue but theirs.

    We also run our CI against ruby-head (the upcoming 1.9.3), but wecan’t guarantee anything, since it is constantly moving. Expect1.9.3p0 to be supported.

    Sinatra should work on any operating system supported by the chosen Rubyimplementation.

    The Bleeding Edge

    If you would like to use Sinatra’s latest bleeding code, feel free torun 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 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 applications dependencies inthere. 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/libdirectory 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, bothSemVer and SemVerTag.

    Further Reading

    Leave a Reply

    Your email address will not be published. Required fields are marked *