Classy web-development dressed in a DSL (official / canonical repo)
Go to file
Markus Prinz b73450982a Merge commit 'bmizerany/master' 2008-05-19 09:20:44 +02:00
images * Default error messages 2007-11-29 18:35:06 -08:00
lib Fixes problem in development where reloading overrides custom errors 2008-05-18 16:00:23 -07:00
test Rack pipelines leading to app; 'use' DSL method. 2008-04-28 01:02:55 -04:00
vendor Freezing Rack into package to take care of upload issue 2008-04-15 19:41:42 -07:00
.gitignore Merge commit 'bmizerany/master' 2008-05-19 09:20:44 +02:00
.gitmodules Freezing Rack into package to take care of upload issue 2008-04-15 19:41:42 -07:00
CHANGELOG Sinatra 0.2.1 Release 2008-04-15 19:41:42 -07:00
LICENSE added MIT for release 2008-04-13 23:54:08 -07:00
Manifest getting ready for 0.2.3 2008-05-18 15:58:57 -07:00
README.rdoc quick doc fix 2008-05-18 15:58:57 -07:00
Rakefile Freezing Rack into package to take care of upload issue 2008-04-15 19:41:42 -07:00

README.rdoc

= Sinatra

Sinatra a DSL for quickly creating web-applications in Ruby with minimal effort.

= Sample app:

  # myapp.rb

  require 'rubygems'
  require 'sinatra'

  get '/' do
    'Hello world!'
  end

Ruby this as <tt>ruby myapp.rb</tt> and view at <tt>http://localhost:4567</tt>

= RESTful

  get '/' do
    .. show things ..
  end
  
  post '/' do
    .. create something ..
  end
  
  put '/' do
    .. update something ..
  end
  
  delete '/' do
    .. annihilate something ..
  end
  
  head '/' do
  
  end

NOTE:  <tt>put</tt> and <tt>delete</tt> are triggered when a <tt>_method</tt> param is set to PUT or DELETE and the HTTP_REQUEST_METHOD is a POST

= Routes

NOTE: Routes are looked up in order of declaration

Simple

  get '/hi' do
    ...
  end
  
With params

  get '/:name' do
    # matches /sinatra and the like and sets params[:name]
  end
  
Splat'n

  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
  
Get an 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

= Static files

Put all of your static content in the ./public directory

  root
    \ public
    
If a file exists that maps to the REQUEST_PATH then it is served and the request end; Sinatra will look for and event that matches the path otherwise

= Views (if you need MVC)

All file-based views are looked up in:

  root
    | - views/
    

== Templates
  
=== Haml

  get '/' do
    haml :index
  end
  
This will render <tt>./views/index.haml</tt>

=== Sass
  get '/stylesheet.css' do
    content_type 'text/css', :charset => 'utf-8'
    sass :stylesheet
  end
  
This will render <tt>./views/stylesheet.sass</tt>

=== Inline

  get '/' do
    haml '%div.title Hello World'
  end
  
This will render the inlined template string

=== Accessing Variables

Templates are rendered in the context the current Sinatra::EventContext.  This means you get all instance/class variables and methods it has access to.

  get '/:id' do
    @foo = Foo.find(params[:id])
    haml '%h1== @foo.name'
  end

Send local objects like:

  get '/:id' do
    localvar = Foo.find(params[:id])
    haml '%h1== localvar.name', :locals => { :localvar => localvar }
  end
  
This is more ideal for rendering templates as partials from within templates

== In file templates

This one is cool:

  get '/' do
    haml :index
  end
  
  use_in_file_templates!
  
  __END__
  
  @@ layout
  X
  = yield
  X
  
  @@ index
  %div.title Hello world!!!!!
  
Try it!

= You can do this too but it's not as cool

  template :layout do
    "X\n=yield\nX"
  end

  template :index do
    '%div.title Hello World!'
  end

  get '/' do
    haml :index
  end

=== Erb

This works like Haml except you use <tt>erb</tt> instead of <tt>haml</tt>

=== Sass

This works like Haml except you use <tt>sass</tt> instead of <tt>haml</tt>. It's also a good idea to add <tt>content_type 'text/css', :charset => 'utf-8'</tt> before your call to <tt>sass</tt> so Sinatra returns the proper content type header with the file.

=== Builder

See Sinatra::Builder

= Helpers

It is ill-advised to create helpers on (main).  Use the handy <tt>helpers</tt> to install helper methods on Sinatra::EventContext for use inside events and templates.

Example:

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

= Before filters

These are run in Sinatra::EventContext

  before do
    .. this code will run before each event ..
  end
  
= Halt!

To immediately stop a request during a before filter or event use:
  
  throw :halt
  
=== Variations

Set the body to the result of a helper method

  throw :halt, :helper_method
  
Set the body to the result of a helper method after sending it parameters from the local scope
  
  throw :halt, [:helper_method, foo, bar]
    
Set the body to a simple string

  throw :halt, 'this will be the body'
  
Set status then the body

  throw :halt, [401, 'go away!']
  
Set the status then call a helper method with params from local scope

  throw :halt, [401, [:helper_method, foo, bar]]

Run a proc inside the Sinatra::EventContext instance and set the body to the result

  throw :halt, lambda { puts 'In a proc!'; 'I just wrote to $stdout!' }

Create you own to_result

  class MyResultObject
    def to_result(event_context, *args)
      event_context.body = 'This will be the body!
    end
  end
  
  get '/' do
    throw :halt, MyResultObject.new
  end

Get the gist?  If you want more fun with this then checkout <tt>to_result</tt> on Array, Symbol, Fixnum, NilClass.

= Configuration & Re-loading

Sinatra supports multiple environments and re-loading.  Re-loading happens on every request when in :development.  Wrap your configurations in <tt>configure</tt> (i.e. Database connections, Constants, etc.) to protect them from re-loading and to only work in certain environments.

All environments:

  configure do
  
  end
  
Production

  configure :production do
  
  end
  
Two at a time:

  configure :production, :test do
  
  end
  
This is also really nifty for error handling.

= Error handling

=== Not Found

Remember:  These are run inside the Sinatra::EventContext which means you get all the goodies is has to offer (i.e. haml, erb, :halt, etc.)

Whenever NotFound is raised this will be called

  not_found do
    'This is nowhere to be found'
  end
  
=== Error

By default +error+ will catch Sinatra::ServerError

Sinatra will pass you the error via the 'sinatra.error' in request.env

  error do
    'Sorry there was a nasty error - ' + request.env['sinatra.error'].name
  end

Custom error mapping:

  error MyCustomError do
    'So what happened was...' + request.env['sinatra.error'].message
  end

then if this happens:

  get '/' do
    raise MyCustomError, 'something bad'
  end

you gets this:

  So what happened was... something bad
  
one guess what this does ;)

  not_found do
    'I have no clue what you're looking for'
  end

Try it!

  
Because Sinatra give you a default <tt>not_found</tt> and <tt>error</tt> do :production that are secure.  If you want to customize only for :production but want to keep the friendly helper screens for :development then do this:

  configure :production do

    not_found do
      "We're so sorry, but we don't what this is"
    end
    
    error do
      "Something really nasty happened.  We're on it!"
    end
  
  end
  
= Mime types

When using send_file or static files you may have mime types Sinatra doesn't understand.  Use +mime+ in those cases.

  mime :foo, 'text/foo'

= Testing

=== Methods

  get_it path, params
  get_it path, params.merge(:env => { 'HTTP_HOST' => 'www.sinatrarb.com' }) or
  get_it path, params.merge(:env => { :host => 'www.sinatrarb.com' })
  
RESTful:

  post_it '/foo', '<myxml></myxml>', 'HTTP_ACCEPT' => 'application/xml'

also works with:

  get_it, post_it, put_it, delete_it, head_it

=== Test/Unit

  require 'my_sinatra_app'
  require 'sinatra/test/unit'
  
  class MyAppTest < Test::Unit::TestCase
    
    def test_my_default
      get_it '/'
      assert_equal 'My Default Page!', @response.body
    end
    
    def test_with_agent
      get_it '/', :agent => 'Songbird'
      assert_equal 'You're in Songbird!', @response.body
    end
    
    ...
    
  end
  
=== Test/Spec

  require 'my_sinatra_app'
  require 'sinatra/test/spec'

  context 'My app'
  
    should "show a default page" do
      get_it '/'
      should.be.ok
      body.should.equal 'My Default Page!'
    end
    ...
  
  end
  
== Test helpers

See Sinatra::Test::Methods

= Irb

This will be back in soon

= Command line

Run your sinatra file like:
  
  ruby myapp.rb [options]

Options are:

  -h # help
  -p # set the port (default is 4567)
  -e # set the environment (default is development)
  -x # turn on the mutex lock (default is off)

= Contribute

  cd where/you/keep/your/projects
  git clone git://github.com/bmizerany/sinatra.git
  cd your_project
  ln -s ../sinatra/

at the top of your sinatra.rb file

  $:.unshift File.dirname(__FILE__) + '/sinatra/lib'
  require 'sinatra'

  get '/about' do
    "I'm running on Version " + Sinatra::Version.combined
  end