mirror of
https://github.com/sinatra/sinatra
synced 2023-03-27 23:18:01 -04:00
440 lines
8.2 KiB
Text
440 lines
8.2 KiB
Text
= 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
|