2013-01-20 06:17:56 -05:00
|
|
|
|
# Sinatra
|
|
|
|
|
|
2017-01-20 19:59:37 -05:00
|
|
|
|
[![Build Status](https://secure.travis-ci.org/sinatra/sinatra.svg)](http://travis-ci.org/sinatra/sinatra)
|
2016-07-25 00:23:05 -04:00
|
|
|
|
|
2016-01-20 14:23:19 -05:00
|
|
|
|
Sinatra is a [DSL](https://en.wikipedia.org/wiki/Domain-specific_language) for
|
2013-01-20 06:17:56 -05:00
|
|
|
|
quickly creating web applications in Ruby with minimal effort:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# myapp.rb
|
|
|
|
|
require 'sinatra'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
'Hello world!'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-16 11:09:06 -04:00
|
|
|
|
Install the gem:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```shell
|
2013-02-26 03:22:49 -05:00
|
|
|
|
gem install sinatra
|
2013-03-16 11:09:06 -04:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
And run with:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```shell
|
2013-02-26 03:22:49 -05:00
|
|
|
|
ruby myapp.rb
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-01-21 20:09:12 -05:00
|
|
|
|
View at: [http://localhost:4567](http://localhost:4567)
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
It is recommended to also run `gem install thin`, which Sinatra will
|
|
|
|
|
pick up if available.
|
|
|
|
|
|
2013-02-26 06:25:50 -05:00
|
|
|
|
## Table of Contents
|
|
|
|
|
|
|
|
|
|
* [Sinatra](#sinatra)
|
|
|
|
|
* [Table of Contents](#table-of-contents)
|
|
|
|
|
* [Routes](#routes)
|
|
|
|
|
* [Conditions](#conditions)
|
2013-03-10 05:26:48 -04:00
|
|
|
|
* [Return Values](#return-values)
|
|
|
|
|
* [Custom Route Matchers](#custom-route-matchers)
|
|
|
|
|
* [Static Files](#static-files)
|
|
|
|
|
* [Views / Templates](#views--templates)
|
|
|
|
|
* [Literal Templates](#literal-templates)
|
2013-02-26 06:25:50 -05:00
|
|
|
|
* [Available Template Languages](#available-template-languages)
|
|
|
|
|
* [Haml Templates](#haml-templates)
|
|
|
|
|
* [Erb Templates](#erb-templates)
|
|
|
|
|
* [Builder Templates](#builder-templates)
|
|
|
|
|
* [Nokogiri Templates](#nokogiri-templates)
|
|
|
|
|
* [Sass Templates](#sass-templates)
|
|
|
|
|
* [SCSS Templates](#scss-templates)
|
|
|
|
|
* [Less Templates](#less-templates)
|
|
|
|
|
* [Liquid Templates](#liquid-templates)
|
|
|
|
|
* [Markdown Templates](#markdown-templates)
|
|
|
|
|
* [Textile Templates](#textile-templates)
|
|
|
|
|
* [RDoc Templates](#rdoc-templates)
|
2013-09-27 13:41:23 -04:00
|
|
|
|
* [AsciiDoc Templates](#asciidoc-templates)
|
2013-02-26 06:25:50 -05:00
|
|
|
|
* [Radius Templates](#radius-templates)
|
|
|
|
|
* [Markaby Templates](#markaby-templates)
|
|
|
|
|
* [RABL Templates](#rabl-templates)
|
|
|
|
|
* [Slim Templates](#slim-templates)
|
|
|
|
|
* [Creole Templates](#creole-templates)
|
2013-09-27 17:56:42 -04:00
|
|
|
|
* [MediaWiki Templates](#mediawiki-templates)
|
2013-02-26 06:25:50 -05:00
|
|
|
|
* [CoffeeScript Templates](#coffeescript-templates)
|
|
|
|
|
* [Stylus Templates](#stylus-templates)
|
|
|
|
|
* [Yajl Templates](#yajl-templates)
|
|
|
|
|
* [WLang Templates](#wlang-templates)
|
|
|
|
|
* [Accessing Variables in Templates](#accessing-variables-in-templates)
|
|
|
|
|
* [Templates with `yield` and nested layouts](#templates-with-yield-and-nested-layouts)
|
|
|
|
|
* [Inline Templates](#inline-templates)
|
|
|
|
|
* [Named Templates](#named-templates)
|
|
|
|
|
* [Associating File Extensions](#associating-file-extensions)
|
|
|
|
|
* [Adding Your Own Template Engine](#adding-your-own-template-engine)
|
2014-06-11 17:46:10 -04:00
|
|
|
|
* [Using Custom Logic for Template Lookup](#using-custom-logic-for-template-lookup)
|
2013-02-26 06:25:50 -05:00
|
|
|
|
* [Filters](#filters)
|
|
|
|
|
* [Helpers](#helpers)
|
|
|
|
|
* [Using Sessions](#using-sessions)
|
2016-12-14 15:47:26 -05:00
|
|
|
|
* [Session Secret Security](#session-secret-security)
|
|
|
|
|
* [Session Config](#session-config)
|
|
|
|
|
* [Choosing Your Own Session Middleware](#choosing-your-own-session-middleware)
|
2013-02-26 06:25:50 -05:00
|
|
|
|
* [Halting](#halting)
|
|
|
|
|
* [Passing](#passing)
|
|
|
|
|
* [Triggering Another Route](#triggering-another-route)
|
|
|
|
|
* [Setting Body, Status Code and Headers](#setting-body-status-code-and-headers)
|
|
|
|
|
* [Streaming Responses](#streaming-responses)
|
|
|
|
|
* [Logging](#logging)
|
|
|
|
|
* [Mime Types](#mime-types)
|
|
|
|
|
* [Generating URLs](#generating-urls)
|
|
|
|
|
* [Browser Redirect](#browser-redirect)
|
|
|
|
|
* [Cache Control](#cache-control)
|
|
|
|
|
* [Sending Files](#sending-files)
|
|
|
|
|
* [Accessing the Request Object](#accessing-the-request-object)
|
|
|
|
|
* [Attachments](#attachments)
|
|
|
|
|
* [Dealing with Date and Time](#dealing-with-date-and-time)
|
|
|
|
|
* [Looking Up Template Files](#looking-up-template-files)
|
|
|
|
|
* [Configuration](#configuration)
|
|
|
|
|
* [Configuring attack protection](#configuring-attack-protection)
|
|
|
|
|
* [Available Settings](#available-settings)
|
|
|
|
|
* [Environments](#environments)
|
|
|
|
|
* [Error Handling](#error-handling)
|
|
|
|
|
* [Not Found](#not-found)
|
|
|
|
|
* [Error](#error)
|
|
|
|
|
* [Rack Middleware](#rack-middleware)
|
|
|
|
|
* [Testing](#testing)
|
|
|
|
|
* [Sinatra::Base - Middleware, Libraries, and Modular Apps](#sinatrabase---middleware-libraries-and-modular-apps)
|
|
|
|
|
* [Modular vs. Classic Style](#modular-vs-classic-style)
|
|
|
|
|
* [Serving a Modular Application](#serving-a-modular-application)
|
|
|
|
|
* [Using a Classic Style Application with a config.ru](#using-a-classic-style-application-with-a-configru)
|
|
|
|
|
* [When to use a config.ru?](#when-to-use-a-configru)
|
|
|
|
|
* [Using Sinatra as Middleware](#using-sinatra-as-middleware)
|
|
|
|
|
* [Dynamic Application Creation](#dynamic-application-creation)
|
|
|
|
|
* [Scopes and Binding](#scopes-and-binding)
|
|
|
|
|
* [Application/Class Scope](#applicationclass-scope)
|
|
|
|
|
* [Request/Instance Scope](#requestinstance-scope)
|
|
|
|
|
* [Delegation Scope](#delegation-scope)
|
|
|
|
|
* [Command Line](#command-line)
|
2015-04-09 08:40:38 -04:00
|
|
|
|
* [Multi-threading](#multi-threading)
|
2013-02-26 06:25:50 -05:00
|
|
|
|
* [Requirement](#requirement)
|
|
|
|
|
* [The Bleeding Edge](#the-bleeding-edge)
|
|
|
|
|
* [With Bundler](#with-bundler)
|
|
|
|
|
* [Versioning](#versioning)
|
|
|
|
|
* [Further Reading](#further-reading)
|
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
## Routes
|
|
|
|
|
|
|
|
|
|
In Sinatra, a route is an HTTP method paired with a URL-matching pattern.
|
|
|
|
|
Each route is associated with a block:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
.. show something ..
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
post '/' do
|
|
|
|
|
.. create something ..
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
put '/' do
|
|
|
|
|
.. replace something ..
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
patch '/' do
|
|
|
|
|
.. modify something ..
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
delete '/' do
|
|
|
|
|
.. annihilate something ..
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
options '/' do
|
|
|
|
|
.. appease something ..
|
|
|
|
|
end
|
2013-03-09 12:09:56 -05:00
|
|
|
|
|
|
|
|
|
link '/' do
|
|
|
|
|
.. affiliate something ..
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
unlink '/' do
|
|
|
|
|
.. separate something ..
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Routes are matched in the order they are defined. The first route that
|
|
|
|
|
matches the request is invoked.
|
|
|
|
|
|
2016-05-09 10:07:14 -04:00
|
|
|
|
Routes with trailing slashes are different from the ones without:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2016-08-14 13:49:17 -04:00
|
|
|
|
get '/foo' do
|
|
|
|
|
# Does not match "GET /foo/"
|
|
|
|
|
end
|
2016-05-09 10:07:14 -04:00
|
|
|
|
```
|
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
Route patterns may include named parameters, accessible via the
|
|
|
|
|
`params` hash:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/hello/:name' do
|
|
|
|
|
# matches "GET /hello/foo" and "GET /hello/bar"
|
2014-07-21 10:49:18 -04:00
|
|
|
|
# params['name'] is 'foo' or 'bar'
|
|
|
|
|
"Hello #{params['name']}!"
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can also access named parameters via block parameters:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/hello/:name' do |n|
|
2013-03-16 11:09:06 -04:00
|
|
|
|
# matches "GET /hello/foo" and "GET /hello/bar"
|
2014-07-21 10:49:18 -04:00
|
|
|
|
# params['name'] is 'foo' or 'bar'
|
|
|
|
|
# n stores params['name']
|
2013-02-26 03:22:49 -05:00
|
|
|
|
"Hello #{n}!"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Route patterns may also include splat (or wildcard) parameters, accessible
|
2014-07-21 10:49:18 -04:00
|
|
|
|
via the `params['splat']` array:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/say/*/to/*' do
|
|
|
|
|
# matches /say/hello/to/world
|
2014-07-21 10:49:18 -04:00
|
|
|
|
params['splat'] # => ["hello", "world"]
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/download/*.*' do
|
|
|
|
|
# matches /download/path/to/file.xml
|
2014-07-21 10:49:18 -04:00
|
|
|
|
params['splat'] # => ["path/to/file", "xml"]
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Or with block parameters:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/download/*.*' do |path, ext|
|
|
|
|
|
[path, ext] # => ["path/to/file", "xml"]
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Route matching with Regular Expressions:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2016-07-25 02:05:59 -04:00
|
|
|
|
get /\/hello\/([\w]+)/ do
|
2014-07-21 10:49:18 -04:00
|
|
|
|
"Hello, #{params['captures'].first}!"
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Or with a block parameter:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get %r{/hello/([\w]+)} do |c|
|
2014-12-07 14:10:29 -05:00
|
|
|
|
# Matches "GET /meta/hello/world", "GET /hello/world/1234" etc.
|
2013-02-26 03:22:49 -05:00
|
|
|
|
"Hello, #{c}!"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Route patterns may have optional parameters:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2016-01-24 06:51:18 -05:00
|
|
|
|
get '/posts/:format?' do
|
|
|
|
|
# matches "GET /posts/" and any extension "GET /posts/json", "GET /posts/xml" etc
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2014-03-20 19:28:40 -04:00
|
|
|
|
Routes may also utilize query parameters:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2014-03-20 19:28:40 -04:00
|
|
|
|
get '/posts' do
|
|
|
|
|
# matches "GET /posts?title=foo&author=bar"
|
2014-07-21 10:49:18 -04:00
|
|
|
|
title = params['title']
|
|
|
|
|
author = params['author']
|
2014-03-20 19:28:40 -04:00
|
|
|
|
# uses title and author variables; query is optional to the /posts route
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
By the way, unless you disable the path traversal attack protection (see
|
|
|
|
|
below), the request path might be modified before matching against your
|
|
|
|
|
routes.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2017-01-13 17:38:04 -05:00
|
|
|
|
You may customize the Mustermann options used for a given route by passing in a
|
|
|
|
|
`:mustermann_opts` hash:
|
|
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
|
get '\A/posts\z', :mustermann_opts => { :type => :regexp, :check_anchors => false } do
|
|
|
|
|
# matches /posts exactly, with explicit anchoring
|
|
|
|
|
"If you match an anchored pattern clap your hands!"
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It looks like a [condition](#conditions), but it isn't one! These options will
|
|
|
|
|
be merged into the global `:mustermann_opts` hash described
|
|
|
|
|
[below](#available-settings).
|
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
## Conditions
|
|
|
|
|
|
|
|
|
|
Routes may include a variety of matching conditions, such as the user agent:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
|
2014-07-21 10:49:18 -04:00
|
|
|
|
"You're using Songbird version #{params['agent'][0]}"
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
# Matches non-songbird browsers
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Other available conditions are `host_name` and `provides`:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/', :host_name => /^admin\./ do
|
|
|
|
|
"Admin Area, Access denied!"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/', :provides => 'html' do
|
|
|
|
|
haml :index
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/', :provides => ['rss', 'atom', 'xml'] do
|
|
|
|
|
builder :feed
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
2014-08-25 00:06:05 -04:00
|
|
|
|
`provides` searches the request's Accept header.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
You can easily define your own conditions:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
set(:probability) { |value| condition { rand <= value } }
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/win_a_car', :probability => 0.1 do
|
|
|
|
|
"You won!"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/win_a_car' do
|
|
|
|
|
"Sorry, you lost."
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For a condition that takes multiple values use a splat:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
set(:auth) do |*roles| # <- notice the splat here
|
|
|
|
|
condition do
|
|
|
|
|
unless logged_in? && roles.any? {|role| current_user.in_role? role }
|
|
|
|
|
redirect "/login/", 303
|
2013-01-20 06:17:56 -05:00
|
|
|
|
end
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get "/my/account/", :auth => [:user, :admin] do
|
|
|
|
|
"Your Account Details"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get "/only/admin/", :auth => :admin do
|
|
|
|
|
"Only admins are allowed here!"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-10 05:26:48 -04:00
|
|
|
|
## Return Values
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
class Stream
|
|
|
|
|
def each
|
|
|
|
|
100.times { |i| yield "#{i}\n" }
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get('/') { Stream.new }
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
You can also use the `stream` helper method (described below) to reduce
|
|
|
|
|
boiler plate and embed the streaming logic in the route.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-03-10 05:26:48 -04:00
|
|
|
|
## Custom Route Matchers
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
class AllButPattern
|
|
|
|
|
Match = Struct.new(:captures)
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
def initialize(except)
|
|
|
|
|
@except = except
|
|
|
|
|
@captures = Match.new([])
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
def match(str)
|
|
|
|
|
@captures unless @except === str
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
def all_but(pattern)
|
|
|
|
|
AllButPattern.new(pattern)
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get all_but("/index") do
|
|
|
|
|
# ...
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Note that the above example might be over-engineered, as it can also be
|
|
|
|
|
expressed as:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get // do
|
|
|
|
|
pass if request.path_info == "/index"
|
|
|
|
|
# ...
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Or, using negative look ahead:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2016-07-25 02:05:59 -04:00
|
|
|
|
get %r{(?!/index)} do
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# ...
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-10 05:26:48 -04:00
|
|
|
|
## Static Files
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
Static files are served from the `./public` directory. You can specify
|
|
|
|
|
a different location by setting the `:public_folder` option:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
set :public_folder, File.dirname(__FILE__) + '/static'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2013-03-10 05:26:48 -04:00
|
|
|
|
## Views / Templates
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
Each template language is exposed via its own rendering method. These
|
|
|
|
|
methods simply return a string:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
erb :index
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This renders `views/index.erb`.
|
|
|
|
|
|
|
|
|
|
Instead of a template name, you can also just pass in the template content
|
|
|
|
|
directly:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
code = "<%= Time.now %>"
|
|
|
|
|
erb code
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Templates take a second argument, the options hash:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
erb :index, :layout => :post
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
haml :index, :format => :html5
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can also set options per template language in general:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
set :haml, :format => :html5
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
haml :index
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Options passed to the render method override options set via `set`.
|
|
|
|
|
|
|
|
|
|
Available Options:
|
|
|
|
|
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>locals</dt>
|
|
|
|
|
<dd>
|
|
|
|
|
List of locals passed to the document. Handy with partials.
|
|
|
|
|
Example: <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>
|
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
|
|
<dt>default_encoding</dt>
|
|
|
|
|
<dd>
|
|
|
|
|
String encoding to use if uncertain. Defaults to
|
|
|
|
|
<tt>settings.default_encoding</tt>.
|
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
|
|
<dt>views</dt>
|
|
|
|
|
<dd>
|
|
|
|
|
Views folder to load templates from. Defaults to <tt>settings.views</tt>.
|
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
|
|
<dt>layout</dt>
|
|
|
|
|
<dd>
|
2014-09-13 17:47:03 -04:00
|
|
|
|
Whether to use a layout (<tt>true</tt> or <tt>false</tt>). If it's a
|
|
|
|
|
Symbol, specifies what template to use. Example:
|
|
|
|
|
<tt>erb :index, :layout => !request.xhr?</tt>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
|
|
<dt>content_type</dt>
|
|
|
|
|
<dd>
|
2013-11-27 16:35:39 -05:00
|
|
|
|
Content-Type the template produces. Default depends on template language.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
|
|
<dt>scope</dt>
|
|
|
|
|
<dd>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
Scope to render template under. Defaults to the application
|
|
|
|
|
instance. If you change this, instance variables and helper methods
|
|
|
|
|
will not be available.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
|
|
<dt>layout_engine</dt>
|
|
|
|
|
<dd>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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: <tt>set :rdoc, :layout_engine
|
|
|
|
|
=> :erb</tt>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</dd>
|
|
|
|
|
|
2013-02-26 01:13:02 -05:00
|
|
|
|
<dt>layout_options</dt>
|
|
|
|
|
<dd>
|
|
|
|
|
Special options only used for rendering the layout. Example:
|
|
|
|
|
<tt>set :rdoc, :layout_options => { :views => 'views/layouts' }</tt>
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</dl>
|
2013-09-16 09:02:43 -04:00
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
Templates are assumed to be located directly under the `./views`
|
|
|
|
|
directory. To use a different views directory:
|
2013-10-16 12:53:45 -04:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-10-16 12:53:45 -04:00
|
|
|
|
set :views, settings.root + '/templates'
|
|
|
|
|
```
|
|
|
|
|
|
2013-03-09 17:46:33 -05:00
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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'` or `'subdir/template'.to_sym`). You must use a
|
|
|
|
|
symbol because otherwise rendering methods will render any strings
|
|
|
|
|
passed to them directly.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-03-10 05:26:48 -04:00
|
|
|
|
### Literal Templates
|
2013-02-25 08:21:01 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
haml '%div.title Hello World'
|
|
|
|
|
end
|
2013-02-25 08:21:01 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
Renders the template string. You can optionally specify `:path` and
|
|
|
|
|
`:line` for a clearer backtrace if there is a filesystem path or line
|
|
|
|
|
associated with that string:
|
2013-10-21 09:05:43 -04:00
|
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
|
get '/' do
|
|
|
|
|
haml '%div.title Hello World', :path => 'examples/file.haml', :line => 3
|
|
|
|
|
end
|
|
|
|
|
```
|
2013-02-25 08:21:01 -05:00
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
### 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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
require 'rdiscount' # or require 'bluecloth'
|
|
|
|
|
get('/') { markdown :index }
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### Haml Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td><a href="http://haml.info/" title="haml">haml</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.haml</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>haml :index, :format => :html5</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
#### Erb Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td>
|
|
|
|
|
<a href="http://www.kuwata-lab.com/erubis/" title="erubis">erubis</a>
|
|
|
|
|
or erb (included in Ruby)
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extensions</td>
|
|
|
|
|
<td><tt>.erb</tt>, <tt>.rhtml</tt> or <tt>.erubis</tt> (Erubis only)</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>erb :index</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
#### Builder Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td>
|
2014-09-19 10:24:03 -04:00
|
|
|
|
<a href="https://github.com/jimweirich/builder" title="builder">builder</a>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.builder</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>builder { |xml| xml.em "hi" }</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
It also takes a block for inline templates (see example).
|
|
|
|
|
|
|
|
|
|
#### Nokogiri Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
2016-01-20 14:23:19 -05:00
|
|
|
|
<td><a href="http://www.nokogiri.org/" title="nokogiri">nokogiri</a></td>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.nokogiri</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>nokogiri { |xml| xml.em "hi" }</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
It also takes a block for inline templates (see example).
|
|
|
|
|
|
|
|
|
|
#### Sass Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td><a href="http://sass-lang.com/" title="sass">sass</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.sass</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>sass :stylesheet, :style => :expanded</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
#### SCSS Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td><a href="http://sass-lang.com/" title="sass">sass</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.scss</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>scss :stylesheet, :style => :expanded</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
#### Less Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
2016-01-20 14:23:19 -05:00
|
|
|
|
<td><a href="http://lesscss.org/" title="less">less</a></td>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.less</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>less :stylesheet</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
#### Liquid Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
2016-01-20 14:23:19 -05:00
|
|
|
|
<td><a href="http://liquidmarkup.org/" title="liquid">liquid</a></td>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.liquid</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>liquid :index, :locals => { :key => 'value' }</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
Since you cannot call Ruby methods (except for `yield`) from a Liquid
|
|
|
|
|
template, you almost always want to pass locals to it.
|
|
|
|
|
|
|
|
|
|
#### Markdown Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td>
|
|
|
|
|
Anyone of:
|
2016-01-20 14:23:19 -05:00
|
|
|
|
<a href="https://github.com/davidfstr/rdiscount" title="RDiscount">RDiscount</a>,
|
2013-01-26 21:10:48 -05:00
|
|
|
|
<a href="https://github.com/vmg/redcarpet" title="RedCarpet">RedCarpet</a>,
|
|
|
|
|
<a href="http://deveiate.org/projects/BlueCloth" title="BlueCloth">BlueCloth</a>,
|
2014-09-19 10:24:03 -04:00
|
|
|
|
<a href="http://kramdown.gettalong.org/" title="kramdown">kramdown</a>,
|
|
|
|
|
<a href="https://github.com/bhollis/maruku" title="maruku">maruku</a>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extensions</td>
|
|
|
|
|
<td><tt>.markdown</tt>, <tt>.mkd</tt> and <tt>.md</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>markdown :index, :layout_engine => :erb</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2016-01-31 03:48:56 -05:00
|
|
|
|
It is not possible to call methods from Markdown, nor to pass locals to it.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
You therefore will usually use it in combination with another rendering
|
|
|
|
|
engine:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
erb :overview, :locals => { :text => markdown(:introduction) }
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
Note that you may also call the `markdown` method from within other
|
|
|
|
|
templates:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
%h1 Hello From Haml!
|
|
|
|
|
%p= markdown(:greetings)
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td><a href="http://redcloth.org/" title="RedCloth">RedCloth</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.textile</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>textile :index, :layout_engine => :erb</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
erb :overview, :locals => { :text => textile(:introduction) }
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Note that you may also call the `textile` method from within other templates:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
%h1 Hello From Haml!
|
|
|
|
|
%p= textile(:greetings)
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
2014-09-19 10:24:03 -04:00
|
|
|
|
<td><a href="http://rdoc.sourceforge.net/" title="RDoc">RDoc</a></td>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.rdoc</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>rdoc :README, :layout_engine => :erb</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2016-01-31 03:48:56 -05:00
|
|
|
|
It is not possible to call methods from RDoc, nor to pass locals to it. You
|
2013-01-20 06:17:56 -05:00
|
|
|
|
therefore will usually use it in combination with another rendering engine:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
erb :overview, :locals => { :text => rdoc(:introduction) }
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Note that you may also call the `rdoc` method from within other templates:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
%h1 Hello From Haml!
|
|
|
|
|
%p= rdoc(:greetings)
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
2013-09-27 13:41:23 -04:00
|
|
|
|
#### AsciiDoc Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td><a href="http://asciidoctor.org/" title="Asciidoctor">Asciidoctor</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.asciidoc</tt>, <tt>.adoc</tt> and <tt>.ad</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>asciidoc :README, :layout_engine => :erb</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2014-09-13 17:47:03 -04:00
|
|
|
|
Since you cannot call Ruby methods directly from an AsciiDoc template, you
|
|
|
|
|
almost always want to pass locals to it.
|
2013-09-27 13:41:23 -04:00
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
#### Radius Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
2014-09-19 10:24:03 -04:00
|
|
|
|
<td><a href="https://github.com/jlong/radius" title="Radius">Radius</a></td>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.radius</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>radius :index, :locals => { :key => 'value' }</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
Since you cannot call Ruby methods directly from a Radius template, you
|
|
|
|
|
almost always want to pass locals to it.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
#### Markaby Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
2016-01-20 14:23:19 -05:00
|
|
|
|
<td><a href="http://markaby.github.io/" title="Markaby">Markaby</a></td>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.mab</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>markaby { h1 "Welcome!" }</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
It also takes a block for inline templates (see example).
|
|
|
|
|
|
|
|
|
|
#### RABL Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td><a href="https://github.com/nesquena/rabl" title="Rabl">Rabl</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.rabl</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>rabl :index</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
#### Slim Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td><a href="http://slim-lang.com/" title="Slim Lang">Slim Lang</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.slim</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>slim :index</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
#### Creole Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td><a href="https://github.com/minad/creole" title="Creole">Creole</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.creole</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>creole :wiki, :layout_engine => :erb</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2016-01-31 03:48:56 -05:00
|
|
|
|
It is not possible to call methods from Creole, nor to pass locals to it. You
|
2013-01-20 06:17:56 -05:00
|
|
|
|
therefore will usually use it in combination with another rendering engine:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
erb :overview, :locals => { :text => creole(:introduction) }
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Note that you may also call the `creole` method from within other templates:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
%h1 Hello From Haml!
|
|
|
|
|
%p= creole(:greetings)
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
2013-09-27 17:56:42 -04:00
|
|
|
|
template than for the layout by passing the `:layout_engine` option.
|
|
|
|
|
|
|
|
|
|
#### MediaWiki Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td><a href="https://github.com/nricciar/wikicloth" title="WikiCloth">WikiCloth</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.mediawiki</tt> and <tt>.mw</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>mediawiki :wiki, :layout_engine => :erb</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
It is not possible to call methods from MediaWiki markup, nor to pass
|
|
|
|
|
locals to it. You therefore will usually use it in combination with
|
|
|
|
|
another rendering engine:
|
2013-09-27 17:56:42 -04:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-09-27 17:56:42 -04:00
|
|
|
|
erb :overview, :locals => { :text => mediawiki(:introduction) }
|
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
Note that you may also call the `mediawiki` method from within other
|
|
|
|
|
templates:
|
2013-09-27 17:56:42 -04:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-09-27 17:56:42 -04:00
|
|
|
|
%h1 Hello From Haml!
|
|
|
|
|
%p= mediawiki(:greetings)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Since you cannot call Ruby from MediaWiki, you cannot use layouts written in
|
|
|
|
|
MediaWiki. However, it is possible to use another rendering engine for the
|
2013-01-20 06:17:56 -05:00
|
|
|
|
template than for the layout by passing the `:layout_engine` option.
|
|
|
|
|
|
|
|
|
|
#### CoffeeScript Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td>
|
|
|
|
|
<a href="https://github.com/josh/ruby-coffee-script" title="Ruby CoffeeScript">
|
|
|
|
|
CoffeeScript
|
2013-02-26 01:38:01 -05:00
|
|
|
|
</a> and a
|
2013-01-20 06:17:56 -05:00
|
|
|
|
<a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme" title="ExecJS">
|
|
|
|
|
way to execute javascript
|
|
|
|
|
</a>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.coffee</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>coffee :index</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2013-02-26 04:15:34 -05:00
|
|
|
|
#### Stylus Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td>
|
2016-01-20 14:23:19 -05:00
|
|
|
|
<a href="https://github.com/forgecrafted/ruby-stylus" title="Ruby Stylus">
|
2013-02-26 04:15:34 -05:00
|
|
|
|
Stylus
|
|
|
|
|
</a> and a
|
|
|
|
|
<a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme" title="ExecJS">
|
|
|
|
|
way to execute javascript
|
2013-02-26 04:36:53 -05:00
|
|
|
|
</a>
|
2013-02-26 04:15:34 -05:00
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.styl</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>stylus :index</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2013-02-26 04:42:39 -05:00
|
|
|
|
Before being able to use Stylus templates, you need to load `stylus` and
|
|
|
|
|
`stylus/tilt` first:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 04:42:39 -05:00
|
|
|
|
require 'sinatra'
|
|
|
|
|
require 'stylus'
|
|
|
|
|
require 'stylus/tilt'
|
|
|
|
|
|
|
|
|
|
get '/' do
|
|
|
|
|
stylus :example
|
|
|
|
|
end
|
|
|
|
|
```
|
2013-02-26 04:38:02 -05:00
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
#### Yajl Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
|
|
|
|
<td><a href="https://github.com/brianmario/yajl-ruby" title="yajl-ruby">yajl-ruby</a></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.yajl</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td>
|
|
|
|
|
<tt>
|
2013-02-26 01:38:01 -05:00
|
|
|
|
yajl :index,
|
|
|
|
|
:locals => { :key => 'qux' },
|
|
|
|
|
:callback => 'present',
|
2013-01-20 06:17:56 -05:00
|
|
|
|
:variable => 'resource'
|
|
|
|
|
</tt>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
|
2013-02-26 01:38:01 -05:00
|
|
|
|
The template source is evaluated as a Ruby string, and the
|
2013-03-09 17:46:33 -05:00
|
|
|
|
resulting json variable is converted using `#to_json`:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
json = { :foo => 'bar' }
|
|
|
|
|
json[:baz] = key
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-03-09 17:46:33 -05:00
|
|
|
|
The `:callback` and `:variable` options can be used to decorate the rendered
|
|
|
|
|
object:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```javascript
|
2014-01-21 17:46:45 -05:00
|
|
|
|
var resource = {"foo":"bar","baz":"qux"};
|
|
|
|
|
present(resource);
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### WLang Templates
|
|
|
|
|
|
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Dependency</td>
|
2014-01-21 17:49:51 -05:00
|
|
|
|
<td><a href="https://github.com/blambeau/wlang/" title="WLang">WLang</a></td>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>File Extension</td>
|
|
|
|
|
<td><tt>.wlang</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td>Example</td>
|
|
|
|
|
<td><tt>wlang :index, :locals => { :key => 'value' }</tt></td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
### 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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/:id' do
|
2014-07-21 10:49:18 -04:00
|
|
|
|
@foo = Foo.find(params['id'])
|
2013-02-26 03:22:49 -05:00
|
|
|
|
haml '%h1= @foo.name'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Or, specify an explicit Hash of local variables:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/:id' do
|
2014-07-21 10:49:18 -04:00
|
|
|
|
foo = Foo.find(params['id'])
|
2013-02-26 03:22:49 -05:00
|
|
|
|
haml '%h1= bar.name', :locals => { :bar => foo }
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This is typically used when rendering templates as partials from within
|
|
|
|
|
other templates.
|
|
|
|
|
|
2013-02-25 09:42:52 -05:00
|
|
|
|
### Templates with `yield` and nested layouts
|
|
|
|
|
|
|
|
|
|
A layout is usually just a template that calls `yield`.
|
2013-03-16 11:09:06 -04:00
|
|
|
|
Such a template can be used either through the `:template` option as
|
2013-02-25 09:42:52 -05:00
|
|
|
|
described above, or it can be rendered with a block as follows:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
erb :post, :layout => false do
|
|
|
|
|
erb :index
|
|
|
|
|
end
|
2013-02-25 09:42:52 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This code is mostly equivalent to `erb :index, :layout => :post`.
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
Passing blocks to rendering methods is most useful for creating nested
|
|
|
|
|
layouts:
|
2013-02-25 09:42:52 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
erb :main_layout, :layout => false do
|
|
|
|
|
erb :admin_layout do
|
|
|
|
|
erb :user
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-02-25 09:42:52 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This can also be done in fewer lines of code with:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
erb :admin_layout, :layout => :main_layout do
|
|
|
|
|
erb :user
|
|
|
|
|
end
|
2013-02-25 09:42:52 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-11-27 16:35:39 -05:00
|
|
|
|
Currently, the following rendering methods accept a block: `erb`, `haml`,
|
2014-09-13 17:47:03 -04:00
|
|
|
|
`liquid`, `slim `, `wlang`. Also the general `render` method accepts a block.
|
2013-02-25 09:42:52 -05:00
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
### Inline Templates
|
|
|
|
|
|
|
|
|
|
Templates may be defined at the end of the source file:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
require 'sinatra'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
haml :index
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
__END__
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
@@ layout
|
|
|
|
|
%html
|
|
|
|
|
= yield
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
@@ index
|
|
|
|
|
%div.title Hello world.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
template :layout do
|
|
|
|
|
"%html\n =yield\n"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
template :index do
|
|
|
|
|
'%div.title Hello World!'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
haml :index
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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`:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
haml :index, :layout => !request.xhr?
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
Tilt.register :tt, Tilt[:textile]
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Adding Your Own Template Engine
|
|
|
|
|
|
|
|
|
|
First, register your engine with Tilt, then create a rendering method:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
Tilt.register :myat, MyAwesomeTemplateEngine
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
helpers do
|
|
|
|
|
def myat(*args) render(:myat, *args) end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
myat :index
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Renders `./views/index.myat`. See https://github.com/rtomayko/tilt to
|
|
|
|
|
learn more about Tilt.
|
|
|
|
|
|
2014-05-30 10:38:15 -04:00
|
|
|
|
### Using Custom Logic for Template Lookup
|
|
|
|
|
|
|
|
|
|
To implement your own template lookup mechanism you can write your
|
|
|
|
|
own `#find_template` method:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2014-05-30 10:38:15 -04:00
|
|
|
|
configure do
|
|
|
|
|
set :views [ './views/a', './views/b' ]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def find_template(views, name, engine, &block)
|
|
|
|
|
Array(views).each do |v|
|
|
|
|
|
super(v, name, engine, &block)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
```
|
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
## Filters
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
Before filters are evaluated before each request within the same context
|
|
|
|
|
as the routes will be and can modify the request and response. Instance
|
2013-01-20 06:17:56 -05:00
|
|
|
|
variables set in filters are accessible by routes and templates:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
before do
|
|
|
|
|
@note = 'Hi!'
|
|
|
|
|
request.path_info = '/foo/bar/baz'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/foo/*' do
|
|
|
|
|
@note #=> 'Hi!'
|
2014-07-21 10:49:18 -04:00
|
|
|
|
params['splat'] #=> 'bar/baz'
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
After filters are evaluated after each request within the same context
|
|
|
|
|
as the routes will be and can also modify the request and response.
|
|
|
|
|
Instance variables set in before filters and routes are accessible by
|
|
|
|
|
after filters:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
after do
|
|
|
|
|
puts response.status
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
Filters optionally take a pattern, causing them to be evaluated only if the
|
|
|
|
|
request path matches that pattern:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
before '/protected/*' do
|
|
|
|
|
authenticate!
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
after '/create/:slug' do |slug|
|
|
|
|
|
session[:last_slug] = slug
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Like routes, filters also take conditions:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
before :agent => /Songbird/ do
|
|
|
|
|
# ...
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
after '/blog/*', :host_name => 'example.com' do
|
|
|
|
|
# ...
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Helpers
|
|
|
|
|
|
|
|
|
|
Use the top-level `helpers` method to define helper methods for use in
|
|
|
|
|
route handlers and templates:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
helpers do
|
|
|
|
|
def bar(name)
|
|
|
|
|
"#{name}bar"
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/:name' do
|
2014-07-21 10:49:18 -04:00
|
|
|
|
bar(params['name'])
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Alternatively, helper methods can be separately defined in a module:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
module FooUtils
|
|
|
|
|
def foo(name) "#{name}foo" end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
module BarUtils
|
|
|
|
|
def bar(name) "#{name}bar" end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
helpers FooUtils, BarUtils
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
enable :sessions
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
"value = " << session[:value].inspect
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/:value' do
|
2014-07-21 10:49:18 -04:00
|
|
|
|
session['value'] = params['value']
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-12-14 15:47:26 -05:00
|
|
|
|
#### Session Secret Security
|
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
To improve security, the session data in the cookie is signed with a session
|
2016-12-14 15:47:26 -05:00
|
|
|
|
secret using `HMAC-SHA1`. This session secret should optimally be a
|
2016-08-14 13:49:17 -04:00
|
|
|
|
cryptographically secure random value of an appropriate length which for
|
|
|
|
|
`HMAC-SHA1` is greater than or equal to 64 bytes (512 bits, 128 hex
|
|
|
|
|
characters). You would be advised not to use a secret that is less than 32
|
|
|
|
|
bytes of randomness (256 bits, 64 hex characters). It is therefore **very
|
|
|
|
|
important** that you don't just make the secret up, but instead use a secure
|
|
|
|
|
random number generator to create it. Humans are extremely bad at generating
|
|
|
|
|
random values.
|
|
|
|
|
|
|
|
|
|
By default, a 32 byte secure random session secret is generated for you by
|
|
|
|
|
Sinatra, but it will change with every restart of your application. If you
|
|
|
|
|
have multiple instances of your application, and you let Sinatra generate the
|
|
|
|
|
key, each instance would then have a different session key which is probably
|
|
|
|
|
not what you want.
|
|
|
|
|
|
|
|
|
|
For better security and usability it's
|
|
|
|
|
[recommended](https://12factor.net/config) that you generate a secure random
|
|
|
|
|
secret and store it in an environment variable on each host running your
|
|
|
|
|
application so that all of your application instances will share the same
|
|
|
|
|
secret. You should periodically rotate this session secret to a new value.
|
|
|
|
|
Here are some examples of how you might create a 64 byte secret and set it:
|
2016-12-14 15:47:26 -05:00
|
|
|
|
|
|
|
|
|
**Session Secret Generation**
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
$ ruby -e "require 'securerandom'; puts SecureRandom.hex(64)"
|
|
|
|
|
99ae8af...snip...ec0f262ac
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Session Secret Generation (Bonus Points)**
|
|
|
|
|
|
|
|
|
|
Use the [sysrandom gem](https://github.com/cryptosphere/sysrandom) to prefer
|
|
|
|
|
use of system RNG facilities to generate random values instead of
|
2016-08-14 13:49:17 -04:00
|
|
|
|
userspace `OpenSSL` which MRI Ruby currently defaults to:
|
2016-12-14 15:47:26 -05:00
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
$ gem install sysrandom
|
|
|
|
|
Building native extensions. This could take a while...
|
|
|
|
|
Successfully installed sysrandom-1.x
|
|
|
|
|
1 gem installed
|
|
|
|
|
|
|
|
|
|
$ ruby -e "require 'sysrandom/securerandom'; puts SecureRandom.hex(64)"
|
|
|
|
|
99ae8af...snip...ec0f262ac
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Session Secret Environment Variable**
|
|
|
|
|
|
|
|
|
|
Set a `SESSION_SECRET` environment variable for Sinatra to the value you
|
|
|
|
|
generated. Make this value persistent across reboots of your host. Since the
|
|
|
|
|
method for doing this will vary across systems this is for illustrative
|
2016-08-14 13:49:17 -04:00
|
|
|
|
purposes only:
|
2016-12-14 15:47:26 -05:00
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
```bash
|
|
|
|
|
# echo "export SESSION_SECRET=99ae8af...snip...ec0f262ac" >> ~/.bashrc
|
2016-12-14 15:47:26 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Session Secret App Config**
|
|
|
|
|
|
|
|
|
|
Setup your app config to fail-safe to a secure random secret
|
|
|
|
|
if the `SESSION_SECRET` environment variable is not available.
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
For bonus points use the [sysrandom
|
|
|
|
|
gem](https://github.com/cryptosphere/sysrandom) here as well:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2016-12-14 15:47:26 -05:00
|
|
|
|
require 'securerandom'
|
|
|
|
|
# -or- require 'sysrandom/securerandom'
|
|
|
|
|
set :session_secret, ENV.fetch('SESSION_SECRET') { SecureRandom.hex(64) }
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-12-14 15:47:26 -05:00
|
|
|
|
#### Session Config
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
If you want to configure it further, you may also store a hash with options
|
|
|
|
|
in the `sessions` setting:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
set :sessions, :domain => 'foo.com'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-06-25 08:10:03 -04:00
|
|
|
|
To share your session across other apps on subdomains of foo.com, prefix the
|
|
|
|
|
domain with a *.* like this instead:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-06-25 08:10:03 -04:00
|
|
|
|
set :sessions, :domain => '.foo.com'
|
|
|
|
|
```
|
|
|
|
|
|
2016-08-04 10:05:02 -04:00
|
|
|
|
#### Choosing Your Own Session Middleware
|
|
|
|
|
|
|
|
|
|
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
|
2016-08-14 13:49:17 -04:00
|
|
|
|
traffic, for instance). You can use any Rack session middleware in order to
|
2016-08-04 10:05:02 -04:00
|
|
|
|
do so, one of the following methods can be used:
|
|
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
|
enable :sessions
|
|
|
|
|
set :session_store, Rack::Session::Pool
|
|
|
|
|
```
|
|
|
|
|
|
2016-08-04 22:38:34 -04:00
|
|
|
|
Or to set up sessions with a hash of options:
|
2016-08-04 10:05:02 -04:00
|
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
|
set :sessions, :expire_after => 2592000
|
|
|
|
|
set :session_store, Rack::Session::Pool
|
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
Another option is to **not** call `enable :sessions`, but instead pull in
|
|
|
|
|
your middleware of choice as you would any other middleware.
|
2016-08-04 10:05:02 -04:00
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
It is important to note that when using this method, session based
|
|
|
|
|
protection **will not be enabled by default**.
|
2016-08-08 23:51:45 -04:00
|
|
|
|
|
|
|
|
|
The Rack middleware to do that will also need to be added:
|
2016-08-04 10:05:02 -04:00
|
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
|
use Rack::Session::Pool, :expire_after => 2592000
|
|
|
|
|
use Rack::Protection::RemoteToken
|
|
|
|
|
use Rack::Protection::SessionHijacking
|
|
|
|
|
```
|
|
|
|
|
|
2016-08-08 23:51:45 -04:00
|
|
|
|
See 'Configuring attack protection' for more information.
|
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
### Halting
|
|
|
|
|
|
|
|
|
|
To immediately stop a request within a filter or route use:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
halt
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can also specify the status when halting:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
halt 410
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Or the body:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
halt 'this will be the body'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Or both:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
halt 401, 'go away!'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
With headers:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It is of course possible to combine a template with `halt`:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
halt erb(:error)
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Passing
|
|
|
|
|
|
|
|
|
|
A route can punt processing to the next matching route using `pass`:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/guess/:who' do
|
2014-07-21 10:49:18 -04:00
|
|
|
|
pass unless params['who'] == 'Frank'
|
2013-02-26 03:22:49 -05:00
|
|
|
|
'You got me!'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/guess/*' do
|
|
|
|
|
'You missed!'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
status, headers, body = call env.merge("PATH_INFO" => '/bar')
|
|
|
|
|
[status, headers, body.map(&:upcase)]
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/bar' do
|
|
|
|
|
"bar"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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`.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
If you want the request to be sent to the same application instance rather
|
|
|
|
|
than a duplicate, use `call!` instead of `call`.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
Check out the Rack specification if you want to learn more about `call`.
|
|
|
|
|
|
|
|
|
|
### Setting Body, Status Code and Headers
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
body "bar"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
after do
|
|
|
|
|
puts body
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
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
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
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
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This allows you to implement streaming APIs,
|
2016-01-20 14:23:19 -05:00
|
|
|
|
[Server Sent Events](https://w3c.github.io/eventsource/), and can be used as
|
2016-08-14 13:49:17 -04:00
|
|
|
|
the basis for [WebSockets](https://en.wikipedia.org/wiki/WebSocket). It can
|
|
|
|
|
also be used to increase throughput if some but not all content depends on a
|
|
|
|
|
slow resource.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
Note that the streaming behavior, especially the number of concurrent
|
|
|
|
|
requests, highly depends on the web server used to serve the application.
|
|
|
|
|
Some servers 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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# long polling
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
set :server, :thin
|
|
|
|
|
connections = []
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/subscribe' do
|
|
|
|
|
# register a client's interest in server events
|
2014-11-15 12:42:37 -05:00
|
|
|
|
stream(:keep_open) do |out|
|
|
|
|
|
connections << out
|
|
|
|
|
# purge dead connections
|
|
|
|
|
connections.reject!(&:closed?)
|
|
|
|
|
end
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2014-07-21 10:49:18 -04:00
|
|
|
|
post '/:message' do
|
2013-02-26 03:22:49 -05:00
|
|
|
|
connections.each do |out|
|
|
|
|
|
# notify client that a new message has arrived
|
2014-07-21 10:49:18 -04:00
|
|
|
|
out << params['message'] << "\n"
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# indicate client to connect again
|
|
|
|
|
out.close
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# acknowledge
|
|
|
|
|
"message received"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
It's also possible for the client to close the connection when trying to
|
|
|
|
|
write to the socket. Because of this, it's recommended to check
|
|
|
|
|
`out.closed?` before trying to write.
|
2016-05-10 07:39:49 -04:00
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
### Logging
|
|
|
|
|
|
|
|
|
|
In the request scope, the `logger` helper exposes a `Logger` instance:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
logger.info "loading data"
|
|
|
|
|
# ...
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
2013-11-27 16:35:39 -05:00
|
|
|
|
you do not have to worry about it in your routes and filters.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
class MyApp < Sinatra::Base
|
|
|
|
|
configure :production, :development do
|
|
|
|
|
enable :logging
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
configure do
|
|
|
|
|
mime_type :foo, 'text/foo'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can also use it with the `content_type` helper:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
content_type :foo
|
|
|
|
|
"foo foo foo"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Generating URLs
|
|
|
|
|
|
|
|
|
|
For generating URLs you should use the `url` helper method, for instance, in
|
|
|
|
|
Haml:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
%a{:href => url('/foo')} foo
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
redirect to('/bar')
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Any additional parameters are handled like arguments passed to `halt`:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
redirect to('/bar'), 303
|
2016-01-20 14:23:19 -05:00
|
|
|
|
redirect 'http://www.google.com/', 'wrong place, buddy'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can also easily redirect back to the page the user came from with
|
|
|
|
|
`redirect back`:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
"<a href='/bar'>do something</a>"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/bar' do
|
|
|
|
|
do_something
|
|
|
|
|
redirect back
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
To pass arguments with a redirect, either add them to the query:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
redirect to('/bar?sum=42')
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Or use a session:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
enable :sessions
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/foo' do
|
|
|
|
|
session[:secret] = 'foo'
|
|
|
|
|
redirect to('/bar')
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/bar' do
|
|
|
|
|
session[:secret]
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Cache Control
|
|
|
|
|
|
|
|
|
|
Setting your headers correctly is the foundation for proper HTTP caching.
|
|
|
|
|
|
|
|
|
|
You can easily set the Cache-Control header like this:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
cache_control :public
|
|
|
|
|
"cache it!"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Pro tip: Set up caching in a before filter:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
before do
|
|
|
|
|
cache_control :public, :must_revalidate, :max_age => 60
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
If you are using the `expires` helper to set the corresponding header,
|
|
|
|
|
`Cache-Control` will be set automatically for you:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
before do
|
|
|
|
|
expires 500, :public, :must_revalidate
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
To properly use caches, you should consider using `etag` or `last_modified`.
|
2014-09-13 17:47:03 -04:00
|
|
|
|
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
|
2013-01-20 06:17:56 -05:00
|
|
|
|
version in its cache:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2014-07-21 10:49:18 -04:00
|
|
|
|
get "/article/:id" do
|
|
|
|
|
@article = Article.find params['id']
|
2013-02-26 03:22:49 -05:00
|
|
|
|
last_modified @article.updated_at
|
|
|
|
|
etag @article.sha1
|
|
|
|
|
erb :article
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It is also possible to use a
|
2016-01-20 14:23:19 -05:00
|
|
|
|
[weak ETag](https://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation):
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
etag @article.sha1, :weak
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
These helpers will not do any caching for you, but rather feed the necessary
|
2016-08-14 13:49:17 -04:00
|
|
|
|
information to your cache. If you are looking for a quick
|
|
|
|
|
reverse-proxy caching solution, try
|
|
|
|
|
[rack-cache](https://github.com/rtomayko/rack-cache):
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
require "rack/cache"
|
|
|
|
|
require "sinatra"
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
use Rack::Cache
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
cache_control :public, :max_age => 36000
|
|
|
|
|
sleep 5
|
|
|
|
|
"hello"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Use the `:static_cache_control` setting (see below) to add
|
|
|
|
|
`Cache-Control` header info to static files.
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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 post requests) are treated as new
|
|
|
|
|
resources. You can change this behavior by passing in a `:new_resource`
|
|
|
|
|
option:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/create' do
|
|
|
|
|
etag '', :new_resource => true
|
|
|
|
|
Article.create
|
|
|
|
|
erb :new_article
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
If you still want to use a weak ETag, pass in a `:kind` option:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
etag '', :new_resource => true, :kind => :weak
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Sending Files
|
|
|
|
|
|
2014-09-13 17:47:03 -04:00
|
|
|
|
To return the contents of a file as the response, you can use the `send_file`
|
|
|
|
|
helper method:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
send_file 'foo.png'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It also takes options:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
send_file 'foo.png', :type => :jpg
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The options are:
|
|
|
|
|
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>filename</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>File name to be used in the response,
|
|
|
|
|
defaults to the real file name.</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
<dt>last_modified</dt>
|
2014-08-25 00:06:05 -04:00
|
|
|
|
<dd>Value for Last-Modified header, defaults to the file's mtime.</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>type</dt>
|
2014-09-13 17:47:03 -04:00
|
|
|
|
<dd>Value for Content-Type header, guessed from the file extension if
|
|
|
|
|
missing.</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2014-04-13 09:54:29 -04:00
|
|
|
|
<dt>disposition</dt>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
<dd>
|
2014-09-13 17:47:03 -04:00
|
|
|
|
Value for Content-Disposition header, possible values: <tt>nil</tt>
|
|
|
|
|
(default), <tt>:attachment</tt> and <tt>:inline</tt>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
|
|
<dt>length</dt>
|
2014-08-25 00:06:05 -04:00
|
|
|
|
<dd>Value for Content-Length header, defaults to file size.</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>status</dt>
|
|
|
|
|
<dd>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
Status code to be sent. 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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</dd>
|
|
|
|
|
</dl>
|
|
|
|
|
|
|
|
|
|
### Accessing the Request Object
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
The incoming request object can be accessed from request level (filter,
|
|
|
|
|
routes, error handlers) through the `request` method:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# 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
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2014-09-13 17:47:03 -04:00
|
|
|
|
Some options, like `script_name` or `path_info`, can also be written:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
before { request.path_info = "/" }
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get "/" do
|
|
|
|
|
"all requests end up here"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The `request.body` is an IO or StringIO object:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
post "/api" do
|
|
|
|
|
request.body.rewind # in case someone already read it
|
|
|
|
|
data = JSON.parse request.body.read
|
|
|
|
|
"Hello #{data['name']}!"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Attachments
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
You can use the `attachment` helper to tell the browser the response should
|
|
|
|
|
be stored on disk rather than displayed in the browser:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
attachment
|
|
|
|
|
"store it!"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can also pass it a file name:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
attachment "info.txt"
|
|
|
|
|
"store it!"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Dealing with Date and Time
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
2016-08-14 13:49:17 -04:00
|
|
|
|
pass if Time.now > time_for('Dec 23, 2016')
|
2013-02-26 03:22:49 -05:00
|
|
|
|
"still time"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
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
|
2013-01-20 06:17:56 -05:00
|
|
|
|
end
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
last_modified :yesterday
|
|
|
|
|
expires :tomorrow
|
|
|
|
|
"hello"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Looking Up Template Files
|
|
|
|
|
|
|
|
|
|
The `find_template` helper is used to find template files for rendering:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
find_template settings.views, 'foo', Tilt[:haml] do |file|
|
|
|
|
|
puts "could be #{file}"
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
set :views, ['views', 'templates']
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
helpers do
|
|
|
|
|
def find_template(views, name, engine, &block)
|
|
|
|
|
Array(views).each { |v| super(v, name, engine, &block) }
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Another example would be using different directories for different engines:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
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
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
## Configuration
|
|
|
|
|
|
|
|
|
|
Run once, at startup, in any environment:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
configure do
|
|
|
|
|
# setting one option
|
|
|
|
|
set :option, 'value'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# setting multiple options
|
|
|
|
|
set :a => 1, :b => 2
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# same as `set :option, true`
|
|
|
|
|
enable :option
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# same as `set :option, false`
|
|
|
|
|
disable :option
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# you can also have dynamic settings with blocks
|
|
|
|
|
set(:css_dir) { File.join(views, 'css') }
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-08-01 07:14:48 -04:00
|
|
|
|
Run only when the environment (`APP_ENV` environment variable) is set to
|
2013-01-20 06:17:56 -05:00
|
|
|
|
`:production`:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
configure :production do
|
|
|
|
|
...
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-10-16 12:53:45 -04:00
|
|
|
|
Run when the environment is set to either `:production` or `:test`:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
configure :production, :test do
|
|
|
|
|
...
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can access those options via `settings`:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
configure do
|
|
|
|
|
set :foo, 'bar'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
settings.foo? # => true
|
|
|
|
|
settings.foo # => 'bar'
|
|
|
|
|
...
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Configuring attack protection
|
|
|
|
|
|
|
|
|
|
Sinatra is using
|
2016-08-14 13:49:17 -04:00
|
|
|
|
[Rack::Protection](https://github.com/sinatra/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):
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
disable :protection
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
To skip a single defense layer, set `protection` to an options hash:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
set :protection, :except => :path_traversal
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
You can also hand in an array in order to disable a list of protections:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
set :protection, :except => [:path_traversal, :session_hijacking]
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-02-26 01:25:19 -05:00
|
|
|
|
By default, Sinatra will only set up session based protection if `:sessions`
|
2016-08-08 11:16:56 -04:00
|
|
|
|
have been enabled. See 'Using Sessions'. Sometimes you may want to set up
|
|
|
|
|
sessions "outside" of the Sinatra app, such as in the config.ru or with a
|
2016-08-14 13:49:17 -04:00
|
|
|
|
separate `Rack::Builder` instance. In that case you can still set up session
|
2016-08-08 11:16:56 -04:00
|
|
|
|
based protection by passing the `:session` option:
|
|
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
|
set :protection, :session => true
|
|
|
|
|
```
|
2013-02-26 01:25:19 -05:00
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
### Available Settings
|
|
|
|
|
|
|
|
|
|
<dl>
|
|
|
|
|
<dt>absolute_redirects</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
If disabled, Sinatra will allow relative redirects, however, Sinatra
|
|
|
|
|
will no longer conform with RFC 2616 (HTTP 1.1), which only allows
|
|
|
|
|
absolute redirects.
|
|
|
|
|
</dd>
|
|
|
|
|
<dd>
|
|
|
|
|
Enable if your app is running behind a reverse proxy that has not been
|
|
|
|
|
set up properly. Note that the <tt>url</tt> helper will still produce
|
|
|
|
|
absolute URLs, unless you pass in <tt>false</tt> as the second
|
|
|
|
|
parameter.
|
|
|
|
|
</dd>
|
|
|
|
|
<dd>Disabled by default.</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2014-09-05 12:33:35 -04:00
|
|
|
|
<dt>add_charset</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Mime types the <tt>content_type</tt> helper will automatically add the
|
|
|
|
|
charset info to. You should add to it rather than overriding this
|
|
|
|
|
option: <tt>settings.add_charset << "application/foobar"</tt>
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>app_file</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Path to the main application file, used to detect project root, views
|
|
|
|
|
and public folder and inline templates.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>bind</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
IP address to bind to (default: <tt>0.0.0.0</tt> <em>or</em>
|
|
|
|
|
<tt>localhost</tt> if your `environment` is set to development). Only
|
|
|
|
|
used for built-in server.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>default_encoding</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>Encoding to assume if unknown (defaults to <tt>"utf-8"</tt>).</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>dump_errors</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>Display errors in the log.</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>environment</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Current environment. Defaults to <tt>ENV['APP_ENV']</tt>, or
|
|
|
|
|
<tt>"development"</tt> if not available.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>logging</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>Use the logger.</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>lock</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Places a lock around every request, only running processing on request
|
|
|
|
|
per Ruby process concurrently.
|
|
|
|
|
</dd>
|
|
|
|
|
<dd>Enabled if your app is not thread-safe. Disabled by default.</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>method_override</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Use <tt>_method</tt> magic to allow put/delete forms in browsers that
|
|
|
|
|
don't support it.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2017-01-13 17:38:04 -05:00
|
|
|
|
<dt>mustermann_opts</dt>
|
|
|
|
|
<dd>
|
|
|
|
|
A default hash of options to pass to Mustermann.new when compiling routing
|
|
|
|
|
paths.
|
|
|
|
|
</dd>
|
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
<dt>port</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>Port to listen on. Only used for built-in server.</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>prefixed_redirects</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Whether or not to insert <tt>request.script_name</tt> into redirects
|
|
|
|
|
if no absolute path is given. That way <tt>redirect '/foo'</tt> would
|
|
|
|
|
behave like <tt>redirect to('/foo')</tt>. Disabled by default.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>protection</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Whether or not to enable web attack protections. See protection section
|
|
|
|
|
above.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>public_dir</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>Alias for <tt>public_folder</tt>. See below.</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>public_folder</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Path to the folder public files are served from. Only used if static
|
|
|
|
|
file serving is enabled (see <tt>static</tt> setting below). Inferred
|
|
|
|
|
from <tt>app_file</tt> setting if not set.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2016-07-25 14:18:42 -04:00
|
|
|
|
<dt>quiet</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Disables logs generated by Sinatra's start and stop commands.
|
|
|
|
|
<tt>false</tt> by default.
|
|
|
|
|
</dd>
|
2016-07-25 14:18:42 -04:00
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
<dt>reload_templates</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Whether or not to reload templates between requests. Enabled in
|
|
|
|
|
development mode.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>root</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Path to project root folder. Inferred from <tt>app_file</tt> setting
|
|
|
|
|
if not set.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>raise_errors</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Raise exceptions (will stop application). Enabled by default when
|
|
|
|
|
<tt>environment</tt> is set to <tt>"test"</tt>, disabled otherwise.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>run</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
If enabled, Sinatra will handle starting the web server. Do not
|
|
|
|
|
enable if using rackup or other means.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>running</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>Is the built-in server running now? Do not change this setting!</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>server</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Server or list of servers to use for built-in server. Order indicates
|
|
|
|
|
priority, default depends on Ruby implementation.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>sessions</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Enable cookie-based sessions support using
|
|
|
|
|
<tt>Rack::Session::Cookie</tt>. See 'Using Sessions' section for more
|
|
|
|
|
information.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2016-08-04 10:05:02 -04:00
|
|
|
|
<dt>session_store</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
The Rack session middleware used. Defaults to
|
|
|
|
|
<tt>Rack::Session::Cookie</tt>. See 'Using Sessions' section for more
|
|
|
|
|
information.
|
|
|
|
|
</dd>
|
2016-08-04 10:05:02 -04:00
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
<dt>show_exceptions</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Show a stack trace in the browser when an exception happens. Enabled by
|
|
|
|
|
default when <tt>environment</tt> is set to <tt>"development"</tt>,
|
|
|
|
|
disabled otherwise.
|
|
|
|
|
</dd>
|
|
|
|
|
<dd>
|
|
|
|
|
Can also be set to <tt>:after_handler</tt> to trigger app-specified
|
|
|
|
|
error handling before showing a stack trace in the browser.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>static</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>Whether Sinatra should handle serving static files.</dd>
|
|
|
|
|
<dd>Disable when using a server able to do this on its own.</dd>
|
|
|
|
|
<dd>Disabling will boost performance.</dd>
|
|
|
|
|
<dd>
|
|
|
|
|
Enabled by default in classic style, disabled for modular apps.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>static_cache_control</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
When Sinatra is serving static files, set this to add
|
|
|
|
|
<tt>Cache-Control</tt> headers to the responses. Uses the
|
|
|
|
|
<tt>cache_control</tt> helper. Disabled by default.
|
|
|
|
|
</dd>
|
|
|
|
|
<dd>
|
|
|
|
|
Use an explicit array when setting multiple values:
|
|
|
|
|
<tt>set :static_cache_control, [:public, :max_age => 300]</tt>
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
<dt>threaded</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
If set to <tt>true</tt>, will tell Thin to use
|
|
|
|
|
<tt>EventMachine.defer</tt> for processing the request.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-09-27 11:21:53 -04:00
|
|
|
|
<dt>traps</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>Whether Sinatra should handle system signals.</dd>
|
2013-09-27 11:21:53 -04:00
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
<dt>views</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Path to the views folder. Inferred from <tt>app_file</tt> setting if
|
|
|
|
|
not set.
|
|
|
|
|
</dd>
|
2013-02-26 01:37:02 -05:00
|
|
|
|
|
|
|
|
|
<dt>x_cascade</dt>
|
2016-08-14 13:49:17 -04:00
|
|
|
|
<dd>
|
|
|
|
|
Whether or not to set the X-Cascade header if no route matches.
|
|
|
|
|
Defaults to <tt>true</tt>.
|
|
|
|
|
</dd>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</dl>
|
|
|
|
|
|
|
|
|
|
## Environments
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
There are three predefined `environments`: `"development"`,
|
|
|
|
|
`"production"` and `"test"`. Environments can be set through the
|
|
|
|
|
`APP_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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2016-08-01 07:14:48 -04:00
|
|
|
|
To run different environments, set the `APP_ENV` environment variable:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```shell
|
2016-08-01 07:14:48 -04:00
|
|
|
|
APP_ENV=production ruby my_app.rb
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You can use predefined methods: `development?`, `test?` and `production?` to
|
2013-02-26 03:22:49 -05:00
|
|
|
|
check the current environment setting:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
if settings.development?
|
|
|
|
|
"development!"
|
|
|
|
|
else
|
|
|
|
|
"not development!"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
```
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
## Error Handling
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
### Not Found
|
|
|
|
|
|
|
|
|
|
When a `Sinatra::NotFound` exception is raised, or the response's status
|
|
|
|
|
code is 404, the `not_found` handler is invoked:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
not_found do
|
|
|
|
|
'This is nowhere to be found.'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Error
|
|
|
|
|
|
|
|
|
|
The `error` handler is invoked any time an exception is raised from a route
|
2014-06-19 08:25:21 -04:00
|
|
|
|
block or a filter. But note in development it will only run if you set the
|
2014-09-13 17:47:03 -04:00
|
|
|
|
show exceptions option to `:after_handler`:
|
2014-06-19 08:25:21 -04:00
|
|
|
|
|
|
|
|
|
```ruby
|
|
|
|
|
set :show_exceptions, :after_handler
|
|
|
|
|
```
|
|
|
|
|
|
2014-09-13 17:47:03 -04:00
|
|
|
|
The exception object can be obtained from the `sinatra.error` Rack variable:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
error do
|
2014-08-14 04:48:02 -04:00
|
|
|
|
'Sorry there was a nasty error - ' + env['sinatra.error'].message
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Custom errors:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
error MyCustomError do
|
|
|
|
|
'So what happened was...' + env['sinatra.error'].message
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Then, if this happens:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
raise MyCustomError, 'something bad'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You get this:
|
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
```
|
|
|
|
|
So what happened was... something bad
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Alternatively, you can install an error handler for a status code:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
error 403 do
|
|
|
|
|
'Access forbidden'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/secret' do
|
|
|
|
|
403
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Or a range:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
error 400..510 do
|
|
|
|
|
'Boom'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Sinatra installs special `not_found` and `error` handlers when
|
2013-02-12 10:14:48 -05:00
|
|
|
|
running under the development environment to display nice stack traces
|
|
|
|
|
and additional debugging information in your browser.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
## Rack Middleware
|
|
|
|
|
|
2014-09-19 10:24:03 -04:00
|
|
|
|
Sinatra rides on [Rack](http://rack.github.io/), a minimal standard
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
Sinatra makes building Rack middleware pipelines a cinch via a top-level
|
|
|
|
|
`use` method:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
require 'sinatra'
|
|
|
|
|
require 'my_custom_middleware'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
use Rack::Lint
|
|
|
|
|
use MyCustomMiddleware
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/hello' do
|
|
|
|
|
'Hello World'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The semantics of `use` are identical to those defined for the
|
2016-01-20 14:23:19 -05:00
|
|
|
|
[Rack::Builder](http://www.rubydoc.info/github/rack/rack/master/Rack/Builder) DSL
|
2013-01-20 06:17:56 -05:00
|
|
|
|
(most frequently used from rackup files). For example, the `use` method
|
|
|
|
|
accepts multiple/variable args as well as blocks:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
use Rack::Auth::Basic do |username, password|
|
|
|
|
|
username == 'admin' && password == 'secret'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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),
|
2014-01-25 03:36:58 -05:00
|
|
|
|
or in the [Rack wiki](https://github.com/rack/rack/wiki/List-of-Middleware).
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
## Testing
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
Sinatra tests can be written using any Rack-based testing library or
|
|
|
|
|
framework.
|
2016-01-20 14:23:19 -05:00
|
|
|
|
[Rack::Test](http://www.rubydoc.info/github/brynary/rack-test/master/frames)
|
2013-01-20 06:17:56 -05:00
|
|
|
|
is recommended:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
require 'my_sinatra_app'
|
2015-01-10 14:30:47 -05:00
|
|
|
|
require 'minitest/autorun'
|
2013-02-26 03:22:49 -05:00
|
|
|
|
require 'rack/test'
|
|
|
|
|
|
2015-01-10 14:30:47 -05:00
|
|
|
|
class MyAppTest < Minitest::Test
|
2013-02-26 03:22:49 -05:00
|
|
|
|
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
|
|
|
|
|
|
2016-08-01 07:14:48 -04:00
|
|
|
|
def test_with_user_agent
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
|
|
|
|
|
assert_equal "You're using Songbird!", last_response.body
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2014-09-13 17:47:03 -04:00
|
|
|
|
Note: If you are using Sinatra in the modular style, replace
|
|
|
|
|
`Sinatra::Application` above with the class name of your app.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
## 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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
require 'sinatra/base'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
class MyApp < Sinatra::Base
|
|
|
|
|
set :sessions, true
|
|
|
|
|
set :foo, 'bar'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
'Hello world!'
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
* 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,
|
2016-08-14 13:49:17 -04:00
|
|
|
|
including the built-in server. See [Configuring
|
|
|
|
|
Settings](http://www.sinatrarb.com/configuration.html) for details on
|
|
|
|
|
available options and their behavior. If you want behavior more similar
|
|
|
|
|
to when you define your app at the top level (also known as Classic
|
|
|
|
|
style), you can subclass `Sinatra::Application`:
|
2014-03-30 15:38:53 -04:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2014-03-30 15:38:53 -04:00
|
|
|
|
require 'sinatra/base'
|
|
|
|
|
|
|
|
|
|
class MyApp < Sinatra::Application
|
|
|
|
|
get '/' do
|
|
|
|
|
'Hello world!'
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
```
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
### Modular vs. Classic Style
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
If switching from one style to the other, you should be aware of
|
|
|
|
|
slightly different default settings:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:49:10 -05:00
|
|
|
|
<table>
|
|
|
|
|
<tr>
|
|
|
|
|
<th>Setting</th>
|
|
|
|
|
<th>Classic</th>
|
|
|
|
|
<th>Modular</th>
|
2014-03-30 15:38:53 -04:00
|
|
|
|
<th>Modular</th>
|
2013-02-26 03:49:10 -05:00
|
|
|
|
</tr>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:49:10 -05:00
|
|
|
|
<tr>
|
|
|
|
|
<td>app_file</td>
|
|
|
|
|
<td>file loading sinatra</td>
|
|
|
|
|
<td>file subclassing Sinatra::Base</td>
|
2014-03-30 15:38:53 -04:00
|
|
|
|
<td>file subclassing Sinatra::Application</td>
|
2013-02-26 03:49:10 -05:00
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>run</td>
|
|
|
|
|
<td>$0 == app_file</td>
|
|
|
|
|
<td>false</td>
|
2014-03-30 15:38:53 -04:00
|
|
|
|
<td>false</td>
|
2013-02-26 03:49:10 -05:00
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>logging</td>
|
|
|
|
|
<td>true</td>
|
|
|
|
|
<td>false</td>
|
2014-03-30 15:38:53 -04:00
|
|
|
|
<td>true</td>
|
2013-02-26 03:49:10 -05:00
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>method_override</td>
|
|
|
|
|
<td>true</td>
|
|
|
|
|
<td>false</td>
|
2014-03-30 15:38:53 -04:00
|
|
|
|
<td>true</td>
|
2013-02-26 03:49:10 -05:00
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>inline_templates</td>
|
|
|
|
|
<td>true</td>
|
|
|
|
|
<td>false</td>
|
2014-03-30 15:38:53 -04:00
|
|
|
|
<td>true</td>
|
2013-02-26 03:49:10 -05:00
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
<tr>
|
|
|
|
|
<td>static</td>
|
|
|
|
|
<td>true</td>
|
2016-01-24 07:00:19 -05:00
|
|
|
|
<td>File.exist?(public_folder)</td>
|
2014-03-30 15:38:53 -04:00
|
|
|
|
<td>true</td>
|
2013-02-26 03:49:10 -05:00
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
### Serving a Modular Application
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
There are two common options for starting a modular app, actively
|
|
|
|
|
starting with `run!`:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# my_app.rb
|
|
|
|
|
require 'sinatra/base'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
class MyApp < Sinatra::Base
|
|
|
|
|
# ... app code here ...
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# start the server if ruby file executed directly
|
|
|
|
|
run! if app_file == $0
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Start with:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```shell
|
2013-02-26 03:22:49 -05:00
|
|
|
|
ruby my_app.rb
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Or with a `config.ru` file, which allows using any Rack handler:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# config.ru (run with rackup)
|
|
|
|
|
require './my_app'
|
|
|
|
|
run MyApp
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Run:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```shell
|
2013-02-26 03:22:49 -05:00
|
|
|
|
rackup -p 4567
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Using a Classic Style Application with a config.ru
|
|
|
|
|
|
|
|
|
|
Write your app file:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# app.rb
|
|
|
|
|
require 'sinatra'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get '/' do
|
|
|
|
|
'Hello world!'
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
And a corresponding `config.ru`:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
require './app'
|
|
|
|
|
run Sinatra::Application
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 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.
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
**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`.**
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
### Using Sinatra as Middleware
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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/Hanami/Roda/...):
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
require 'sinatra/base'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
class LoginScreen < Sinatra::Base
|
|
|
|
|
enable :sessions
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
get('/login') { haml :login }
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
post('/login') do
|
2014-07-21 10:49:18 -04:00
|
|
|
|
if params['name'] == 'admin' && params['password'] == 'admin'
|
|
|
|
|
session['user_name'] = params['name']
|
2013-02-26 03:22:49 -05:00
|
|
|
|
else
|
|
|
|
|
redirect '/login'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
end
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
class MyApp < Sinatra::Base
|
|
|
|
|
# middleware will run before filters
|
|
|
|
|
use LoginScreen
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
before do
|
|
|
|
|
unless session['user_name']
|
|
|
|
|
halt "Access denied, please <a href='/login'>login</a>."
|
2013-01-20 06:17:56 -05:00
|
|
|
|
end
|
2013-02-26 03:22:49 -05:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
get('/') { "Hello #{session['user_name']}." }
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Dynamic Application Creation
|
|
|
|
|
|
|
|
|
|
Sometimes you want to create new applications at runtime without having to
|
2013-11-27 16:35:39 -05:00
|
|
|
|
assign them to a constant. You can do this with `Sinatra.new`:
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
require 'sinatra/base'
|
|
|
|
|
my_app = Sinatra.new { get('/') { "hi" } }
|
|
|
|
|
my_app.run!
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It takes the application to inherit from as an optional argument:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# config.ru (run with rackup)
|
|
|
|
|
require 'sinatra/base'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
controller = Sinatra.new do
|
|
|
|
|
enable :logging
|
|
|
|
|
helpers MyHelpers
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
map('/a') do
|
|
|
|
|
run Sinatra.new(controller) { get('/') { 'a' } }
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
map('/b') do
|
|
|
|
|
run Sinatra.new(controller) { get('/') { 'b' } }
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This is especially useful for testing Sinatra extensions or using Sinatra in
|
|
|
|
|
your own library.
|
|
|
|
|
|
|
|
|
|
This also makes using Sinatra as middleware extremely easy:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
require 'sinatra/base'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
use Sinatra do
|
|
|
|
|
get('/') { ... }
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
run RailsProject::Application
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 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
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
Options created via `set` are methods at class level:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
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
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
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
|
2013-11-27 16:35:39 -05:00
|
|
|
|
created, and all handler blocks run in that scope. From within this scope you
|
2013-01-20 06:17:56 -05:00
|
|
|
|
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:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
class MyApp < Sinatra::Base
|
|
|
|
|
# Hey, I'm in the application scope!
|
|
|
|
|
get '/define_route/:name' do
|
|
|
|
|
# Request scope for '/define_route/:name'
|
|
|
|
|
@value = 42
|
|
|
|
|
|
2014-07-21 10:49:18 -04:00
|
|
|
|
settings.get("/#{params['name']}") do
|
|
|
|
|
# Request scope for "/#{params['name']}"
|
2013-02-26 03:22:49 -05:00
|
|
|
|
@value # => nil (not the same request)
|
2013-01-20 06:17:56 -05:00
|
|
|
|
end
|
2013-02-26 03:22:49 -05:00
|
|
|
|
|
|
|
|
|
"Route defined!"
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You have the request scope binding inside:
|
|
|
|
|
|
2016-01-19 04:03:17 -05:00
|
|
|
|
* get, head, post, put, delete, options, patch, link and unlink blocks
|
2013-03-10 05:43:03 -04:00
|
|
|
|
* before and after filters
|
2013-01-20 06:17:56 -05:00
|
|
|
|
* 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:
|
|
|
|
|
|
2015-04-06 01:54:05 -04:00
|
|
|
|
```shell
|
2016-07-25 14:18:42 -04:00
|
|
|
|
ruby myapp.rb [-h] [-x] [-q] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER]
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Options are:
|
|
|
|
|
|
|
|
|
|
```
|
2013-02-26 03:22:49 -05:00
|
|
|
|
-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)
|
2016-07-25 14:18:42 -04:00
|
|
|
|
-q # turn on quiet mode for server (default is off)
|
2013-02-26 03:22:49 -05:00
|
|
|
|
-x # turn on the mutex lock (default is off)
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2015-04-06 01:54:05 -04:00
|
|
|
|
### Multi-threading
|
|
|
|
|
|
|
|
|
|
_Paraphrasing from [this StackOverflow answer][so-answer] by Konstantin_
|
|
|
|
|
|
|
|
|
|
Sinatra doesn't impose any concurrency model, but leaves that to the
|
|
|
|
|
underlying Rack handler (server) like Thin, Puma or WEBrick. Sinatra
|
|
|
|
|
itself is thread-safe, so there won't be any problem if the Rack handler
|
|
|
|
|
uses a threaded model of concurrency. This would mean that when starting
|
|
|
|
|
the server, you'd have to specify the correct invocation method for the
|
|
|
|
|
specific Rack handler. The following example is a demonstration of how
|
|
|
|
|
to start a multi-threaded Thin server:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```ruby
|
2015-04-06 01:54:05 -04:00
|
|
|
|
# app.rb
|
|
|
|
|
|
|
|
|
|
require 'sinatra/base'
|
|
|
|
|
|
|
|
|
|
class App < Sinatra::Base
|
|
|
|
|
get '/' do
|
|
|
|
|
"Hello, World"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
App.run!
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
To start the server, the command would be:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```shell
|
2015-04-06 01:54:05 -04:00
|
|
|
|
thin --threaded start
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[so-answer]: http://stackoverflow.com/questions/6278817/is-sinatra-multi-threaded/6282999#6282999)
|
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
## Requirement
|
|
|
|
|
|
|
|
|
|
The following Ruby versions are officially supported:
|
|
|
|
|
<dl>
|
2015-09-05 16:46:17 -04:00
|
|
|
|
<dt>Ruby 2.2</dt>
|
2013-01-20 06:17:56 -05:00
|
|
|
|
<dd>
|
2015-09-05 16:46:17 -04:00
|
|
|
|
2.2 is fully supported and recommended. There are currently no plans to
|
2015-09-05 04:55:42 -04:00
|
|
|
|
drop official support for it.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
|
|
<dt>Rubinius</dt>
|
|
|
|
|
<dd>
|
2013-07-21 15:40:50 -04:00
|
|
|
|
Rubinius is officially supported (Rubinius >= 2.x). It is recommended to
|
2013-03-16 11:09:06 -04:00
|
|
|
|
<tt>gem install puma</tt>.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
|
|
<dt>JRuby</dt>
|
|
|
|
|
<dd>
|
2013-03-10 04:03:46 -04:00
|
|
|
|
The latest stable release of JRuby is officially supported. It is not
|
|
|
|
|
recommended to use C extensions with JRuby. It is recommended to
|
2013-03-16 11:09:06 -04:00
|
|
|
|
<tt>gem install trinidad</tt>.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
</dd>
|
|
|
|
|
</dl>
|
2013-03-10 04:03:46 -04:00
|
|
|
|
|
2015-09-05 04:55:42 -04:00
|
|
|
|
Versions of Ruby prior to 2.2.2 are no longer supported as of Sinatra 2.0.
|
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
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.
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
We also run our CI against ruby-head (future releases of MRI), but we
|
|
|
|
|
can't guarantee anything, since it is constantly moving. Expect upcoming
|
|
|
|
|
2.x releases to be fully supported.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
Sinatra should work on any operating system supported by the chosen Ruby
|
|
|
|
|
implementation.
|
|
|
|
|
|
2013-03-10 04:03:46 -04:00
|
|
|
|
If you run MacRuby, you should `gem install control_tower`.
|
|
|
|
|
|
2013-01-20 06:17:56 -05:00
|
|
|
|
Sinatra currently doesn't run on Cardinal, SmallRuby, BlueRuby or any
|
2015-09-05 16:46:17 -04:00
|
|
|
|
Ruby version prior to 2.2.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
## The Bleeding Edge
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
We also push out prerelease gems from time to time, so you can do a
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```shell
|
2013-02-26 03:22:49 -05:00
|
|
|
|
gem install sinatra --pre
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2013-11-27 16:35:39 -05:00
|
|
|
|
to get some of the latest features.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
### With Bundler
|
|
|
|
|
|
|
|
|
|
If you want to run your application with the latest Sinatra, using
|
2016-01-20 14:23:19 -05:00
|
|
|
|
[Bundler](http://bundler.io) is the recommended way.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
First, install bundler, if you haven't:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```shell
|
2013-02-26 03:22:49 -05:00
|
|
|
|
gem install bundler
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Then, in your project directory, create a `Gemfile`:
|
|
|
|
|
|
|
|
|
|
```ruby
|
2013-02-26 03:22:49 -05:00
|
|
|
|
source 'https://rubygems.org'
|
2015-09-05 16:46:17 -04:00
|
|
|
|
gem 'sinatra', :github => 'sinatra/sinatra'
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
2013-02-26 03:22:49 -05:00
|
|
|
|
# other dependencies
|
|
|
|
|
gem 'haml' # for instance, if you use haml
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
2016-08-14 13:49:17 -04:00
|
|
|
|
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.
|
2013-01-20 06:17:56 -05:00
|
|
|
|
|
|
|
|
|
Now you can run your app like this:
|
|
|
|
|
|
2015-09-20 12:50:57 -04:00
|
|
|
|
```shell
|
2013-02-26 03:22:49 -05:00
|
|
|
|
bundle exec ruby myapp.rb
|
2013-01-20 06:17:56 -05:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 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?
|
2016-01-20 14:23:19 -05:00
|
|
|
|
* [Issue tracker](https://github.com/sinatra/sinatra/issues)
|
|
|
|
|
* [Twitter](https://twitter.com/sinatra)
|
2013-01-20 06:17:56 -05:00
|
|
|
|
* [Mailing List](http://groups.google.com/group/sinatrarb/topics)
|
|
|
|
|
* IRC: [#sinatra](irc://chat.freenode.net/#sinatra) on http://freenode.net
|
2015-05-02 03:59:38 -04:00
|
|
|
|
* [Sinatra & Friends](https://sinatrarb.slack.com) on Slack and see
|
|
|
|
|
[here](https://sinatra-slack.herokuapp.com/) for an invite.
|
2014-08-25 00:06:05 -04:00
|
|
|
|
* [Sinatra Book](https://github.com/sinatra/sinatra-book/) Cookbook Tutorial
|
2013-01-20 06:17:56 -05:00
|
|
|
|
* [Sinatra Recipes](http://recipes.sinatrarb.com/) Community
|
|
|
|
|
contributed recipes
|
2016-01-20 14:23:19 -05:00
|
|
|
|
* API documentation for the [latest release](http://www.rubydoc.info/gems/sinatra)
|
|
|
|
|
or the [current HEAD](http://www.rubydoc.info/github/sinatra/sinatra) on
|
|
|
|
|
http://www.rubydoc.info/
|
|
|
|
|
* [CI server](https://travis-ci.org/sinatra/sinatra)
|