<commit_before># Sinatra

*Внимание: Этот документ является переводом английской версии и может быть
устаревшим*

Sinatra — это предметно-ориентированный каркас
([DSL](http://ru.wikipedia.org/wiki/Предметно-ориентированный_язык_программирования))
для быстрого создания функциональных веб-приложений на Ruby с минимумом усилий:

```ruby
# myapp.rb
require 'sinatra'

get '/' do
  'Hello world!'
end
```

Установите gem:

``` shell
gem install sinatra
```

и запустите приложение с помощью:

``` shell
ruby myapp.rb
```

Оцените результат: http://localhost:4567

Рекомендуется также установить Thin, сделать это можно командой: `gem install
thin`. Thin — это более производительный и функциональный сервер для
разработки приложений на Sinatra.

## Маршруты

В Sinatra маршрут — это пара: &lt;HTTP метод&gt; и &lt;шаблон URL&gt;. Каждый маршрут
связан с блоком кода:

```ruby
get '/' do
  # .. что-то показать ..
end

post '/' do
  # .. что-то создать ..
end

put '/' do
  # .. что-то заменить ..
end

patch '/' do
  # .. что-то изменить ..
end

delete '/' do
  # .. что-то удалить ..
end

options '/' do
  # .. что-то ответить ..
end

link '/' do
  .. что-то подключить ..
end

unlink '/' do
  .. что-то отключить ..
end
```

Маршруты сверяются с запросом в порядке очередности их записи в файле
приложения. Первый же совпавший с запросом маршрут и будет вызван.

Шаблоны маршрутов могут включать в себя именованные параметры, доступные в xэше
`params`:

```ruby
get '/hello/:name' do
  # соответствует "GET /hello/foo" и "GET /hello/bar",
  # где params['name'] 'foo' или 'bar'
  "Hello #{params['name']}!"
end
```

Также можно использовать именованные параметры в качестве переменных блока:

```ruby
get '/hello/:name' do |n|
  "Hello #{n}!"
end
```

Шаблоны маршрутов также могут включать в себя splat (или '*' маску,
обозначающую любой символ) параметры, доступные в массиве `params['splat']`:

```ruby
get '/say/*/to/*' do
  # соответствует /say/hello/to/world
  params['splat'] # => ["hello", "world"]
end

get '/download/*.*' do
  # соответствует /download/path/to/file.xml
  params['splat'] # => ["path/to/file", "xml"]
end
```

Или с параметрами блока:

```ruby
get '/download/*.*' do |path, ext|
  [path, ext] # => ["path/to/file", "xml"]
end
```

Регулярные выражения в качестве шаблонов маршрутов:

```ruby
get /\A\/hello\/([\w]+)\z/ do
  "Hello, #{params['captures'].first}!"
end
```

Или с параметром блока:

```ruby
# Находит "GET /meta/hello/world", "GET /hello/world/1234" и так далее
get %r{/hello/([\w]+)} do |c|
  "Hello, #{c}!"
end
```

Шаблоны маршрутов могут иметь необязательные параметры:

```ruby
get '/posts.?:format?' do
  # соответствует "GET /posts", "GET /posts.json", "GET /posts.xml" и т.д.
end
```

Кстати, если вы не отключите защиту от обратного пути в директориях (path
traversal, см. ниже), путь запроса может быть изменен до начала поиска
подходящего маршрута.

### Условия

Маршруты могут включать различные условия совпадений, например, клиентское
приложение (user agent):

```ruby
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
  "You're using Songbird version #{params['agent'][0]}"
end

get '/foo' do
  # соответствует не-songbird браузерам
end
```

Другими доступными условиями являются `host_name` и `provides`:

```ruby
get '/', :host_name => /^admin\./ do
  "Admin Area, Access denied!"
end

get '/', :provides => 'html' do
  haml :index
end

get '/', :provides => ['rss', 'atom', 'xml'] do
  builder :feed
end
```

Вы можете задать собственные условия:

```ruby
set(:probability) { |value| condition { rand <= value } }

get '/win_a_car', :probability => 0.1 do
  "You won!"
end

get '/win_a_car' do
  "Sorry, you lost."
end
```

Для условия, которое принимает несколько параметров, используйте звездочку:

```ruby
set(:auth) do |*roles|   # <- обратите внимание на звездочку
  condition do
    unless logged_in? && roles.any? {|role| current_user.in_role? role }
      redirect "/login/", 303
    end
  end
end

get "/my/account/", :auth => [:user, :admin] do
  "Your Account Details"
end

get "/only/admin/", :auth => :admin do
  "Only admins are allowed here!"
end
```

### Возвращаемые значения

Возвращаемое значение блока маршрута ограничивается телом ответа, которое
будет передано HTTP клиенту, или следующей "прослойкой" (middleware) в Rack
стеке. Чаще всего это строка, как в примерах выше. Но также приемлемы и
другие значения.

Вы можете вернуть любой объект, который будет либо корректным Rack ответом,
объектом Rack body, либо кодом состояния HTTP:

* массив с тремя переменными: `[код (Fixnum), заголовки (Hash), тело ответа
  (должно отвечать на #each)]`;
* массив с двумя переменными: `[код (Fixnum), тело ответа (должно отвечать
  на #each)]`;
* объект, отвечающий на `#each`, который передает только строковые типы
  данных в этот блок;
* Fixnum, представляющий код состояния HTTP.


Таким образом, легко можно реализовать, например, поточный пример:

```ruby
class Stream
  def each
    100.times { |i| yield "#{i}\n" }
  end
end

get('/') { Stream.new }
```

Вы также можете использовать метод `stream` (описываемый ниже), чтобы
уменьшить количество дублируемого кода и держать логику стриминга прямо в
маршруте.

### Собственные детекторы совпадений для маршрутов

Как показано выше, Sinatra поставляется со встроенной поддержкой строк и
регулярных выражений в качестве шаблонов URL. Но и это еще не все. Вы можете
легко определить свои собственные детекторы совпадений (matchers) для
маршрутов:

```ruby
class AllButPattern
  Match = Struct.new(:captures)

  def initialize(except)
    @except   = except
    @captures = Match.new([])
  end

  def match(str)
    @captures unless @except === str
  end
end

def all_but(pattern)
  AllButPattern.new(pattern)
end

get all_but("/index") do
  # ...
end
```

Заметьте, что предыдущий пример, возможно, чересчур усложнен, потому что он
может быть реализован так:

```ruby
get // do
  pass if request.path_info == "/index"
  # ...
end
```

Или с использованием негативного просмотра вперед:

```ruby
get %r{^(?!/index$)} do
  # ...
end
```

## Статические файлы

Статические файлы отдаются из `./public` директории. Вы можете указать другое
место, используя опцию `:public_folder`:

```ruby
set :public_folder, File.dirname(__FILE__) + '/static'
```

Учтите, что имя директории со статическими файлами не включено в URL.
Например, файл `./public/css/style.css` будет доступен как
`http://example.com/css/style.css`.

Используйте опцию `:static_cache_control` (см. ниже), чтобы добавить заголовок
`Cache-Control`.

## Представления / Шаблоны

Каждый шаблонизатор представлен своим собственным методом. Эти методы попросту
возвращают строку:

```ruby
get '/' do
  erb :index
end
```

Отобразит `views/index.erb`.

Вместо имени шаблона вы так же можете передавать непосредственно само
содержимое шаблона:

```ruby
get '/' do
  code = "<%= Time.now %>"
  erb code
end
```

Эти методы принимают второй аргумент, хеш с опциями:

```ruby
get '/' do
  erb :index, :layout => :post
end
```

Отобразит `views/index.erb`, вложенным в `views/post.erb` (по умолчанию:
`views/layout.erb`, если существует).

Любые опции, не понимаемые Sinatra, будут переданы в шаблонизатор:

```ruby
get '/' do
  haml :index, :format => :html5
end
```

Вы также можете задавать опции для шаблонизаторов в общем:

```ruby
set :haml, :format => :html5

get '/' do
  haml :index
end
```

Опции, переданные в метод, переопределяют опции, заданные с помощью `set`.

Доступные опции:

<dl>
  <dt>locals</dt>
  <dd>
    Список локальных переменных, передаваемых в документ.
    Например: <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>
  </dd>

  <dt>default_encoding</dt>
  <dd>
    Кодировка, которую следует использовать, если не удалось определить
    оригинальную. По умолчанию: <tt>settings.default_encoding</tt>.
  </dd>

  <dt>views</dt>
  <dd>
    Директория с шаблонами. По умолчанию: <tt>settings.views</tt>.
  </dd>

  <dt>layout</dt>
  <dd>
    Использовать или нет лэйаут (<tt>true</tt> или <tt>false</tt>). Если же значение Symbol,
    то указывает, какой шаблон использовать в качестве лэйаута. Например:
    <tt>erb :index, :layout => !request.xhr?</tt>
  </dd>

  <dt>content_type</dt>
  <dd>
    Content-Type отображенного шаблона. По умолчанию: задается шаблонизатором.
  </dd>

  <dt>scope</dt>
  <dd>
    Область видимости, в которой рендерятся шаблоны. По умолчанию: экземпляр
    приложения. Если вы измените эту опцию, то переменные экземпляра и
    методы-помощники станут недоступными в ваших шаблонах.
  </dd>

  <dt>layout_engine</dt>
  <dd>
    Шаблонизатор, который следует использовать для отображения лэйаута.
    Полезная опция для шаблонизаторов, в которых нет никакой поддержки
    лэйаутов. По умолчанию: тот же шаблонизатор, что используется и для самого
    шаблона. Пример: <tt>set :rdoc, :layout_engine => :erb</tt>
  </dd>
</dl>

По умолчанию считается, что шаблоны находятся в директории `./views`. Чтобы
использовать другую директорию с шаблонами:

```ruby
set :views, settings.root + '/templates'
```

Важное замечание: вы всегда должны ссылаться на шаблоны с помощью символов
(Symbol), даже когда они в поддиректории (в этом случае используйте
`:'subdir/template'`). Вы должны использовать символы, потому что иначе
шаблонизаторы попросту отображают любые строки, переданные им.

### Буквальные шаблоны

```ruby
get '/' do
  haml '%div.title Hello World'
end
```

Отобразит шаблон, переданный строкой.

### Доступные шаблонизаторы

Некоторые языки шаблонов имеют несколько реализаций. Чтобы указать, какую
реализацию использовать, вам следует просто подключить нужную библиотеку:

```ruby
require 'rdiscount' # или require 'bluecloth'
get('/') { markdown :index }
```

#### Haml шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://haml.info/" title="haml">haml</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.haml</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>haml :index, :format => :html5</tt></td>
  </tr>
</table>

#### Erb шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td>
      <a href="http://www.kuwata-lab.com/erubis/" title="erubis">erubis</a>
      или erb (включен в Ruby)
    </td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.erb</tt>, <tt>.rhtml</tt> or <tt>.erubis</tt> (только Erubis)</td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>erb :index</tt></td>
  </tr>
</table>

#### Builder шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td>
      <a href="https://github.com/jimweirich/builder" title="builder">builder</a>
    </td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.builder</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>builder { |xml| xml.em "hi" }</tt></td>
  </tr>
</table>

Блок также используется и для встроенных шаблонов (см. пример).

#### Nokogiri шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://nokogiri.org/" title="nokogiri">nokogiri</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.nokogiri</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>nokogiri { |xml| xml.em "hi" }</tt></td>
  </tr>
</table>

Блок также используется и для встроенных шаблонов (см. пример).

#### Sass шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://sass-lang.com/" title="sass">sass</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.sass</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>sass :stylesheet, :style => :expanded</tt></td>
  </tr>
</table>

#### SCSS шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://sass-lang.com/" title="sass">sass</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.scss</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>scss :stylesheet, :style => :expanded</tt></td>
  </tr>
</table>

#### Less шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://www.lesscss.org/" title="less">less</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.less</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>less :stylesheet</tt></td>
  </tr>
</table>

#### Liquid шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://www.liquidmarkup.org/" title="liquid">liquid</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.liquid</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>liquid :index, :locals => { :key => 'value' }</tt></td>
  </tr>
</table>

Так как в Liquid шаблонах невозможно вызывать методы из Ruby (кроме `yield`), то
вы почти всегда будете передавать в шаблон локальные переменные.

#### Markdown шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td>
      Любая из библиотек:
        <a href="https://github.com/rtomayko/rdiscount" title="RDiscount">RDiscount</a>,
        <a href="https://github.com/vmg/redcarpet" title="RedCarpet">RedCarpet</a>,
        <a href="http://deveiate.org/projects/BlueCloth" title="BlueCloth">BlueCloth</a>,
        <a href="http://kramdown.gettalong.org/" title="kramdown">kramdown</a>,
        <a href="https://github.com/bhollis/maruku" title="maruku">maruku</a>
    </td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.markdown</tt>, <tt>.mkd</tt> and <tt>.md</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>markdown :index, :layout_engine => :erb</tt></td>
  </tr>
</table>

В Markdown невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:

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

Заметьте, что вы можете вызывать метод `markdown` из других шаблонов:

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

Вы не можете вызывать Ruby из Markdown, соответственно, вы не можете
использовать лэйауты на Markdown. Тем не менее, есть возможность использовать
один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
опции `:layout_engine`.

#### Textile шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://redcloth.org/" title="RedCloth">RedCloth</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.textile</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>textile :index, :layout_engine => :erb</tt></td>
  </tr>
</table>

В Textile невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:

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

Заметьте, что вы можете вызывать метод `textile` из других шаблонов:

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

Вы не можете вызывать Ruby из Textile, соответственно, вы не можете
использовать лэйауты на Textile. Тем не менее, есть возможность использовать
один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
опции `:layout_engine`.

#### RDoc шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://rdoc.sourceforge.net/" title="RDoc">RDoc</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.rdoc</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>rdoc :README, :layout_engine => :erb</tt></td>
  </tr>
</table>

В RDoc невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:

```ruby
erb :overview, :locals => { :text => rdoc(:introduction) }
```

Заметьте, что вы можете вызывать метод `rdoc` из других шаблонов:

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

Вы не можете вызывать Ruby из RDoc, соответственно, вы не можете использовать
лэйауты на RDoc. Тем не менее, есть возможность использовать один шаблонизатор
для отображения шаблона, а другой для лэйаута с помощью опции
`:layout_engine`.

#### AsciiDoc шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://asciidoctor.org/" title="Asciidoctor">Asciidoctor</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.asciidoc</tt>, <tt>.adoc</tt> и <tt>.ad</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>asciidoc :README, :layout_engine => :erb</tt></td>
  </tr>
</table>

Так как в AsciiDoc шаблонах невозможно вызывать методы из Ruby напрямую, то вы
почти всегда будете передавать в шаблон локальные переменные.

#### Radius шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="https://github.com/jlong/radius" title="Radius">Radius</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.radius</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>radius :index, :locals => { :key => 'value' }</tt></td>
  </tr>
</table>

Так как в Radius шаблонах невозможно вызывать методы из Ruby напрямую, то вы
почти всегда будете передавать в шаблон локальные переменные.

#### Markaby шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://markaby.github.com/" title="Markaby">Markaby</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.mab</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>markaby { h1 "Welcome!" }</tt></td>
  </tr>
</table>

Блок также используется и для встроенных шаблонов (см. пример).

#### RABL шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="https://github.com/nesquena/rabl" title="Rabl">Rabl</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.rabl</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>rabl :index</tt></td>
  </tr>
</table>

#### Slim шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://slim-lang.com/" title="Slim Lang">Slim Lang</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.slim</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>slim :index</tt></td>
  </tr>
</table>

#### Creole шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="https://github.com/minad/creole" title="Creole">Creole</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.creole</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>creole :wiki, :layout_engine => :erb</tt></td>
  </tr>
</table>

В Creole невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:

```ruby
erb :overview, :locals => { :text => creole(:introduction) }
```

Заметьте, что вы можете вызывать метод `creole` из других шаблонов:

```ruby
%h1 Hello From Haml!
%p= creole(:greetings)
```

Вы не можете вызывать Ruby из Creole, соответственно, вы не можете
использовать лэйауты на Creole. Тем не менее, есть возможность использовать
один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
опции `:layout_engine`.

#### MediaWiki шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="https://github.com/nricciar/wikicloth" title="WikiCloth">WikiCloth</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.mediawiki</tt> и <tt>.mw</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>mediawiki :wiki, :layout_engine => :erb</tt></td>
  </tr>
</table>

В разметке MediaWiki невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:

```ruby
erb :overview, :locals => { :text => mediawiki(:introduction) }
```

Заметьте, что вы можете вызывать метод `mediawiki` из других шаблонов:

```ruby
%h1 Hello From Haml!
%p= mediawiki(:greetings)
```

Вы не можете вызывать Ruby из MediaWiki, соответственно, вы не можете
использовать лэйауты на MediaWiki. Тем не менее, есть возможность использовать
один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
опции `:layout_engine`.

#### CoffeeScript шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td>
      <a href="https://github.com/josh/ruby-coffee-script" title="Ruby CoffeeScript">
        CoffeeScript
      </a> и
      <a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme" title="ExecJS">
        способ запускать JavaScript
      </a>
    </td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.coffee</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>coffee :index</tt></td>
  </tr>
</table>

#### Stylus шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td>
      <a href="https://github.com/lucasmazza/ruby-stylus" title="Ruby Stylus">
        Stylus
      </a> и
      <a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme" title="ExecJS">
        способ запускать JavaScript
      </a>
    </td>
  </tr>
  <tr>
    <td>Расширение файла</td>
    <td><tt>.styl</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>stylus :index</tt></td>
  </tr>
</table>

Перед тем, как использовать шаблоны стилус, загрузите `stylus` и
`stylus/tilt`:

``` ruby
require 'sinatra'
require 'stylus'
require 'stylus/tilt'

get '/' do
  stylus :example
end
```

#### Yajl шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="https://github.com/brianmario/yajl-ruby" title="yajl-ruby">yajl-ruby</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.yajl</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td>
      <tt>
        yajl :index,
             :locals => { :key => 'qux' },
             :callback => 'present',
             :variable => 'resource'
      </tt>
    </td>
  </tr>
</table>

Содержимое шаблона интерпретируется как код на Ruby, а результирующая
переменная json затем конвертируется с помощью `#to_json`.

```ruby
json = { :foo => 'bar' }
json[:baz] = key
```

Опции `:callback` и `:variable` используются для "декорирования" итогового
объекта.

```ruby
var resource = {"foo":"bar","baz":"qux"}; present(resource);
```

#### WLang шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="https://github.com/blambeau/wlang/" title="wlang">wlang</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.wlang</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>wlang :index, :locals => { :key => 'value' }</tt></td>
  </tr>
</table>

Так как в WLang шаблонах невозможно вызывать методы из Ruby напрямую (за
исключением `yield`), то вы почти всегда будете передавать в шаблон локальные
переменные.

### Доступ к переменным в шаблонах

Шаблоны интерпретируются в том же контексте, что и обработчики маршрутов.
Переменные экземпляра, установленные в процессе обработки маршрутов, будут
доступны напрямую в шаблонах:

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

Либо установите их через хеш локальных переменных:

```ruby
get '/:id' do
  foo = Foo.find(params['id'])
  haml '%h1= bar.name', :locals => { :bar => foo }
end
```

Это обычный подход, когда шаблоны рендерятся как части других шаблонов.

### Шаблоны с `yield` и вложенные раскладки (layout)

Раскладка (layout) обычно представляет собой шаблон, который исполняет
`yield`.
Такой шаблон может быть либо использован с помощью опции `:template`,
как описано выше, либо он может быть дополнен блоком:

```ruby
    erb :post, :layout => false do
      erb :index
    end
```

Эти инструкции в основном эквивалентны `erb :index, :layout => :post`.

Передача блоков интерпретирующим шаблоны методам наиболее полезна для
создания вложенных раскладок:

```ruby
    erb :main_layout, :layout => false do
      erb :admin_layout do
        erb :user
      end
    end
```

Это же самое может быть сделано короче:

```ruby
    erb :admin_layout, :layout => :main_layout do
      erb :user
    end
```

В настоящее время, следующие интерпретирующие шаблоны методы
принимают блок:
`erb`, `haml`, `liquid`, `slim `, `wlang`.
Общий метод заполнения шаблонов `render` также принимает блок.

### Включённые шаблоны

Шаблоны также могут быть определены в конце исходного файла:

```ruby
require 'sinatra'

get '/' do
  haml :index
end

__END__

@@ layout
%html
  = yield

@@ index
%div.title Hello world.
```

Заметьте: включённые шаблоны, определенные в исходном файле, который подключил
Sinatra, будут загружены автоматически. Вызовите `enable :inline_templates`
напрямую, если используете включённые шаблоны в других файлах.

### Именованные шаблоны

Шаблоны также могут быть определены при помощи `template` метода:

```ruby
template :layout do
  "%html\n  =yield\n"
end

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

get '/' do
  haml :index
end
```

Если шаблон с именем "layout" существует, то он будет использоваться каждый
раз при рендеринге. Вы можете отключать лэйаут в каждом конкретном случае с
помощью `:layout => false` или отключить его для всего приложения: `set :haml,
:layout => false`:

```ruby
get '/' do
  haml :index, :layout => !request.xhr?
end
```

### Привязка файловых расширений

Чтобы связать расширение файла с движком рендеринга, используйте
`Tilt.register`. Например, если вы хотите использовать расширение `tt` для
шаблонов Textile:

```ruby
Tilt.register :tt, Tilt[:textile]
```

### Добавление собственного движка рендеринга

Сначала зарегистрируйте свой движок в Tilt, а затем создайте метод, отвечающий
за рендеринг:

```ruby
Tilt.register :myat, MyAwesomeTemplateEngine

helpers do
  def myat(*args) render(:myat, *args) end
end

get '/' do
  myat :index
end
```

Отобразит `./views/index.myat`. Чтобы узнать больше о Tilt, смотрите
https://github.com/rtomayko/tilt

## Фильтры

`before`-фильтры выполняются перед каждым запросом в том же контексте, что и
маршруты, и могут изменять как запрос, так и ответ на него. Переменные
экземпляра, установленные в фильтрах, доступны в маршрутах и шаблонах:

```ruby
before do
  @note = 'Hi!'
  request.path_info = '/foo/bar/baz'
end

get '/foo/*' do
  @note #=> 'Hi!'
  params['splat'] #=> 'bar/baz'
end
```

`after`-фильтры выполняются после каждого запроса в том же контексте
и могут изменять как запрос, так и ответ на него. Переменные
экземпляра, установленные в `before`-фильтрах и маршрутах, будут доступны в
`after`-фильтрах:

```ruby
after do
  puts response.status
end
```

Заметьте: если вы используете метод `body`, а не просто возвращаете строку из
маршрута, то тело ответа не будет доступно в `after`-фильтрах, так как оно
будет сгенерировано позднее.

Фильтры могут использовать шаблоны URL и будут интерпретированы, только если
путь запроса совпадет с этим шаблоном:

```ruby
before '/protected/*' do
  authenticate!
end

after '/create/:slug' do |slug|
  session['last_slug'] = slug
end
```

Как и маршруты, фильтры могут использовать условия:

```ruby
before :agent => /Songbird/ do
  # ...
end

after '/blog/*', :host_name => 'example.com' do
  # ...
end
```

## Методы-помощники

Используйте метод `helpers`, чтобы определить методы-помощники, которые в
дальнейшем можно будет использовать в обработчиках маршрутов и шаблонах:

```ruby
helpers do
  def bar(name)
    "#{name}bar"
  end
end

get '/:name' do
  bar(params['name'])
end
```

Также методы-помощники могут быть заданы в отдельных модулях:

```ruby
module FooUtils
  def foo(name) "#{name}foo" end
end

module BarUtils
  def bar(name) "#{name}bar" end
end

helpers FooUtils, BarUtils
```

Эффект равносилен включению модулей в класс приложения.

### Использование сессий

Сессия используется, чтобы сохранять состояние между запросами. Если эта опция
включена, то у вас будет один хеш сессии на одну пользовательскую сессию:

```ruby
enable :sessions

get '/' do
  "value = " << session['value'].inspect
end

get '/:value' do
  session['value'] = params['value']
end
```

Заметьте, что при использовании `enable :sessions` все данные сохраняются в
куках (cookies). Это может быть не совсем то, что вы хотите (например,
сохранение больших объемов данных увеличит ваш трафик). В таком случае вы
можете использовать альтернативную Rack "прослойку" (middleware), реализующую
механизм сессий. Для этого *не надо* вызывать `enable :sessions`, вместо этого
следует подключить ее так же, как и любую другую "прослойку":

```ruby
use Rack::Session::Pool, :expire_after => 2592000

get '/' do
  "value = " << session['value'].inspect
end

get '/:value' do
  session['value'] = params['value']
end
```

Для повышения безопасности данные сессии в куках подписываются секретным
ключом. Секретный ключ генерируется Sinatra. Тем не менее, так как этот ключ
будет меняться с каждым запуском приложения, вы, возможно, захотите установить
ключ вручную, чтобы у всех экземпляров вашего приложения был один и тот же
ключ:

```ruby
set :session_secret, 'super secret'
```

Если вы хотите больше настроек для сессий, вы можете задать их, передав хеш
опций в параметр `sessions`:

```ruby
set :sessions, :domain => 'foo.com'
```

Чтобы сделать сессию доступной другим приложениям, размещенным на поддоменах
foo.com, добавьте *.* перед доменом:

``` ruby
set :sessions, :domain => '.foo.com'
```

### Прерывание

Чтобы незамедлительно прервать обработку запроса внутри фильтра или маршрута,
используйте:

```ruby
halt
```

Можно также указать статус при прерывании:

```ruby
halt 410
```

Тело:

```ruby
halt 'this will be the body'
```

И то, и другое:

```ruby
halt 401, 'go away!'
```

Можно указать заголовки:

```ruby
halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
```

И, конечно, можно использовать шаблоны с `halt`:

```ruby
halt erb(:error)
```

### Передача

Маршрут может передать обработку запроса следующему совпадающему маршруту,
используя `pass`:

```ruby
get '/guess/:who' do
  pass unless params['who'] == 'Frank'
  'You got me!'
end

get '/guess/*' do
  'You missed!'
end
```

Блок маршрута сразу же прерывается, и контроль переходит к следующему
совпадающему маршруту. Если соответствующий маршрут не найден, то ответом на
запрос будет 404.

### Вызов другого маршрута

Иногда `pass` не подходит, например, если вы хотите получить результат вызова
другого обработчика маршрута. В таком случае просто используйте `call`:

```ruby
get '/foo' do
  status, headers, body = call env.merge("PATH_INFO" => '/bar')
  [status, headers, body.map(&:upcase)]
end

get '/bar' do
  "bar"
end
```

Заметьте, что в предыдущем примере можно облегчить тестирование и повысить
производительность, перенеся `"bar"` в метод-помощник, используемый и в
`/foo`, и в `/bar`.

Если вы хотите, чтобы запрос был отправлен в тот же экземпляр приложения, а не
в его копию, используйте `call!` вместо `call`.

Если хотите узнать больше о `call`, смотрите спецификацию Rack.

### Задание тела, кода и заголовков ответа

Хорошим тоном является установка кода состояния HTTP и тела ответа в
возвращаемом значении обработчика маршрута. Тем не менее, в некоторых
ситуациях вам, возможно, понадобится задать тело ответа в произвольной точке
потока исполнения. Вы можете сделать это с помощью метода-помощника `body`.
Если вы задействуете метод `body`, то вы можете использовать его и в
дальнейшем, чтобы получить доступ к телу ответа.

```ruby
get '/foo' do
  body "bar"
end

after do
  puts body
end
```

Также можно передать блок в метод `body`, который затем будет вызван
обработчиком Rack (такой подход может быть использован для реализации
поточного ответа, см. "Возвращаемые значения").

Аналогично вы можете установить код ответа и его заголовки:

```ruby
get '/foo' do
  status 418
  headers \
    "Allow"   => "BREW, POST, GET, PROPFIND, WHEN",
    "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
  body "I'm a tea pot!"
end
```

Как и `body`, методы `headers` и `status`, вызванные без аргументов,
возвращают свои текущие значения.

### Стриминг ответов

Иногда требуется начать отправлять данные клиенту прямо в процессе
генерирования частей этих данных. В особых случаях требуется постоянно
отправлять данные до тех пор, пока клиент не закроет соединение. Вы можете
использовать метод `stream` вместо написания собственных "оберток".

```ruby
get '/' do
  stream do |out|
    out << "It's gonna be legen -\n"
    sleep 0.5
    out << " (wait for it) \n"
    sleep 1
    out << "- dary!\n"
  end
end
```

Что позволяет вам реализовать стриминговые API,
[Server Sent Events](http://dev.w3.org/html5/eventsource/),
и может служить основой для [WebSockets](http://en.wikipedia.org/wiki/WebSocket).
Также такой подход можно использовать для увеличения производительности в случае,
когда какая-то часть контента зависит от медленного ресурса.

Заметьте, что возможности стриминга, особенно количество одновременно
обслуживаемых запросов, очень сильно зависят от используемого веб-сервера.
Некоторые серверы, например, WEBRick, могут и вовсе не поддерживать стриминг.
Если сервер не поддерживает стриминг, то все данные будут отправлены за один
раз сразу после того, как блок, переданный в `stream`, завершится. Стриминг
вообще не работает при использовании Shotgun.

Если метод используется с параметром `keep_open`, то он не будет вызывать
`close` у объекта потока, что позволит вам закрыть его позже в любом другом
месте. Это работает только с событийными серверами, например, с Thin и
Rainbows. Другие же серверы все равно будут закрывать поток:

```ruby
# long polling

set :server, :thin
connections = []

get '/subscribe' do
  # регистрация клиента
  stream(:keep_open) do |out|
    connections << out }
    # удаление "мертвых клиентов"
    connections.reject!(&:closed?)
  end
end

post '/message' do
  connections.each do |out|
    # уведомить клиента о новом сообщении
    out << params['message'] << "\n"

    # указать клиенту на необходимость снова соединиться
    out.close
  end

  # допуск
  "message received"
end
```

### Логирование

В области видимости запроса метод `logger` предоставляет доступ к экземпляру
`Logger`:

```ruby
get '/' do
  logger.info "loading data"
  # ...
end
```

Этот логер автоматически учитывает ваши настройки логирования в Rack. Если
логирование выключено, то этот метод вернет пустой (dummy) объект, поэтому вы
можете смело использовать его в маршрутах и фильтрах.

Заметьте, что логирование включено по умолчанию только для
`Sinatra::Application`, а если ваше приложение — подкласс `Sinatra::Base`, то
вы, наверное, захотите включить его вручную:

```ruby
class MyApp < Sinatra::Base
  configure :production, :development do
    enable :logging
  end
end
```

Чтобы избежать использования любой логирующей "прослойки", задайте опции
`logging` значение `nil`. Тем не менее, не забывайте, что в такой ситуации
`logger` вернет `nil`. Чаще всего так делают, когда задают свой собственный
логер. Sinatra будет использовать то, что находится в `env['rack.logger']`.

### Mime-типы

Когда вы используете `send_file` или статические файлы, у вас могут быть
mime-типы, которые Sinatra не понимает по умолчанию. Используйте `mime_type`
для их регистрации по расширению файла:

```ruby
configure do
  mime_type :foo, 'text/foo'
end
```

Вы также можете использовать это в `content_type` методе-помощнике:

```ruby
get '/' do
  content_type :foo
  "foo foo foo"
end
```

### Генерирование URL

Чтобы сформировать URL, вам следует использовать метод `url`, например, в Haml:

```ruby
%a{:href => url('/foo')} foo
```

Этот метод учитывает обратные прокси и маршрутизаторы Rack, если они
присутствуют.

Наряду с `url` вы можете использовать `to` (смотрите пример ниже).

### Перенаправление (редирект)

Вы можете перенаправить браузер пользователя с помощью метода `redirect`:

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

Любые дополнительные параметры используются по аналогии с аргументами метода
`halt`:

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

Вы также можете перенаправить пользователя обратно, на страницу, с которой он
пришел, с помощью `redirect back`:

```ruby
get '/foo' do
  "<a href='/bar'>do something</a>"
end

get '/bar' do
  do_something
  redirect back
end
```

Чтобы передать какие-либо параметры вместе с перенаправлением, либо добавьте
их в строку запроса:

```ruby
redirect to('/bar?sum=42')
```

либо используйте сессию:

```ruby
enable :sessions

get '/foo' do
  session['secret'] = 'foo'
  redirect to('/bar')
end

get '/bar' do
  session['secret']
end
```

### Управление кэшированием

Установка корректных заголовков — основа правильного HTTP кэширования.

Вы можете легко выставить заголовок Cache-Control таким образом:

```ruby
get '/' do
  cache_control :public
  "cache it!"
end
```

Совет: задавайте кэширование в `before`-фильтре:

```ruby
before do
  cache_control :public, :must_revalidate, :max_age => 60
end
```

Если вы используете метод `expires` для задания соответствующего заголовка, то
`Cache-Control` будет выставлен автоматически:

```ruby
before do
  expires 500, :public, :must_revalidate
end
```

Чтобы как следует использовать кэширование, вам следует подумать об
использовании `etag` или `last_modified`. Рекомендуется использовать эти
методы-помощники *до* выполнения ресурсоемких вычислений, так как они
немедленно отправят ответ клиенту, если текущая версия уже есть в их кэше:

```ruby
get '/article/:id' do
  @article = Article.find params['id']
  last_modified @article.updated_at
  etag @article.sha1
  erb :article
end
```

Также вы можете использовать
[weak ETag](http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation):

```ruby
etag @article.sha1, :weak
```

Эти методы-помощники не станут ничего кэшировать для вас, но они дадут
необходимую информацию для вашего кэша. Если вы ищете легкое решение для
кэширования, попробуйте [rack-cache](https://github.com/rtomayko/rack-cache):

```ruby
require 'rack/cache'
require 'sinatra'

use Rack::Cache

get '/' do
  cache_control :public, :max_age => 36000
  sleep 5
  "hello"
end
```

Используйте опцию `:static_cache_control` (см. ниже), чтобы добавить заголовок
`Cache-Control` к статическим файлам.

В соответствии с RFC 2616 ваше приложение должно вести себя по-разному, когда
заголовки If-Match или If-None-Match имеют значение `*`, в зависимости от
того, существует или нет запрашиваемый ресурс. Sinatra предполагает, что
ресурсы, к которым обращаются с помощью безопасных (GET) и идемпотентных (PUT)
методов, уже существуют, а остальные ресурсы (к которым обращаются, например,
с помощью POST) считает новыми. Вы можете изменить данное поведение с помощью
опции `:new_resource`:

```ruby
get '/create' do
  etag '', :new_resource => true
  Article.create
  erb :new_article
end
```

Если вы хотите использовать weak ETag, задайте опцию `:kind`:

```ruby
etag '', :new_resource => true, :kind => :weak
```

### Отправка файлов

Для отправки файлов пользователю вы можете использовать метод `send_file`:

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

Этот метод имеет несколько опций:

```ruby
send_file 'foo.png', :type => :jpg
```

Возможные опции:

<dl>
  <dt>filename</dt>
  <dd>имя файла, по умолчанию: реальное имя файла.</dd>

  <dt>last_modified</dt>
  <dd>значение для заголовка Last-Modified, по умолчанию: mtime (время
      изменения) файла.</dd>

  <dt>type</dt>
  <dd>тип файла, по умолчанию: определяется по расширению файла.</dd>

  <dt>disposition</dt>
  <dd>используется для заголовка Content-Disposition, возможные значения: <tt>nil</tt>
      (по умолчанию), <tt>:attachment</tt> и <tt>:inline</tt>.</dd>

  <dt>length</dt>
  <dd>значения для заголовка Content-Length, по умолчанию: размер файла.</dd>

  <dt>status</dt>
  <dd>Код ответа. Полезно, когда отдается статический файл в качестве страницы с
      сообщением об ошибке.</dd>
</dl>

Этот метод будет использовать возможности Rack сервера для отправки файлов,
если они доступны, в противном случае будет напрямую отдавать файл из Ruby
процесса. Метод `send_file` также обеспечивает автоматическую обработку
частичных (range) запросов с помощью Sinatra.

### Доступ к объекту запроса

Объект входящего запроса доступен на уровне обработки запроса (в фильтрах,
маршрутах, обработчиках ошибок) с помощью `request` метода:

```ruby
# приложение запущено на 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.scheme              # "http"
  request.script_name         # "/example"
  request.path_info           # "/foo"
  request.port                # 80
  request.request_method      # "GET"
  request.query_string        # ""
  request.content_length      # длина тела запроса
  request.media_type          # медиатип тела запроса
  request.host                # "example.com"
  request.get?                # true (есть аналоги для других методов HTTP)
  request.form_data?          # false
  request["some_param"]       # значение параметра some_param. Шорткат для хеша params
  request.referrer            # источник запроса клиента либо '/'
  request.user_agent          # user agent (используется для :agent условия)
  request.cookies             # хеш, содержащий cookies браузера
  request.xhr?                # является ли запрос ajax запросом?
  request.url                 # "http://example.com/example/foo"
  request.path                # "/example/foo"
  request.ip                  # IP-адрес клиента
  request.secure?             # false (true, если запрос сделан через SSL)
  request.forwarded?          # true (если сервер работает за обратным прокси)
  request.env                 # "сырой" env хеш, полученный Rack
end
```

Некоторые опции, такие как `script_name` или `path_info`, доступны для
изменения:

```ruby
before { request.path_info = "/" }

get "/" do
  "all requests end up here"
end
```

`request.body` является IO или StringIO объектом:

```ruby
post "/api" do
  request.body.rewind  # в случае, если кто-то уже прочитал тело запроса
  data = JSON.parse request.body.read
  "Hello #{data['name']}!"
end
```

### Вложения

Вы можете использовать метод `attachment`, чтобы сказать браузеру, что ответ
сервера должен быть сохранен на диск, а не отображен:

```ruby
get '/' do
  attachment
  "store it!"
end
```

Вы также можете указать имя файла:

```ruby
get '/' do
  attachment "info.txt"
  "store it!"
end
```

### Работа со временем и датами

Sinatra предлагает метод-помощник `time_for`, который из заданного значения
создает объект Time. Он также может конвертировать `DateTime`, `Date` и
подобные классы:

```ruby
get '/' do
  pass if Time.now > time_for('Dec 23, 2012')
  "still time"
end
```

Этот метод используется внутри Sinatra методами `expires`, `last_modified` и
им подобными. Поэтому вы легко можете расширить возможности этих методов,
переопределив `time_for` в своем приложении:

```ruby
helpers do
  def time_for(value)<commit_msg>wicked small fix of time_for description<commit_after># Sinatra

*Внимание: Этот документ является переводом английской версии и может быть
устаревшим*

Sinatra — это предметно-ориентированный каркас
([DSL](http://ru.wikipedia.org/wiki/Предметно-ориентированный_язык_программирования))
для быстрого создания функциональных веб-приложений на Ruby с минимумом усилий:

```ruby
# myapp.rb
require 'sinatra'

get '/' do
  'Hello world!'
end
```

Установите gem:

``` shell
gem install sinatra
```

и запустите приложение с помощью:

``` shell
ruby myapp.rb
```

Оцените результат: http://localhost:4567

Рекомендуется также установить Thin, сделать это можно командой: `gem install
thin`. Thin — это более производительный и функциональный сервер для
разработки приложений на Sinatra.

## Маршруты

В Sinatra маршрут — это пара: &lt;HTTP метод&gt; и &lt;шаблон URL&gt;. Каждый маршрут
связан с блоком кода:

```ruby
get '/' do
  # .. что-то показать ..
end

post '/' do
  # .. что-то создать ..
end

put '/' do
  # .. что-то заменить ..
end

patch '/' do
  # .. что-то изменить ..
end

delete '/' do
  # .. что-то удалить ..
end

options '/' do
  # .. что-то ответить ..
end

link '/' do
  .. что-то подключить ..
end

unlink '/' do
  .. что-то отключить ..
end
```

Маршруты сверяются с запросом в порядке очередности их записи в файле
приложения. Первый же совпавший с запросом маршрут и будет вызван.

Шаблоны маршрутов могут включать в себя именованные параметры, доступные в xэше
`params`:

```ruby
get '/hello/:name' do
  # соответствует "GET /hello/foo" и "GET /hello/bar",
  # где params['name'] 'foo' или 'bar'
  "Hello #{params['name']}!"
end
```

Также можно использовать именованные параметры в качестве переменных блока:

```ruby
get '/hello/:name' do |n|
  "Hello #{n}!"
end
```

Шаблоны маршрутов также могут включать в себя splat (или '*' маску,
обозначающую любой символ) параметры, доступные в массиве `params['splat']`:

```ruby
get '/say/*/to/*' do
  # соответствует /say/hello/to/world
  params['splat'] # => ["hello", "world"]
end

get '/download/*.*' do
  # соответствует /download/path/to/file.xml
  params['splat'] # => ["path/to/file", "xml"]
end
```

Или с параметрами блока:

```ruby
get '/download/*.*' do |path, ext|
  [path, ext] # => ["path/to/file", "xml"]
end
```

Регулярные выражения в качестве шаблонов маршрутов:

```ruby
get /\A\/hello\/([\w]+)\z/ do
  "Hello, #{params['captures'].first}!"
end
```

Или с параметром блока:

```ruby
# Находит "GET /meta/hello/world", "GET /hello/world/1234" и так далее
get %r{/hello/([\w]+)} do |c|
  "Hello, #{c}!"
end
```

Шаблоны маршрутов могут иметь необязательные параметры:

```ruby
get '/posts.?:format?' do
  # соответствует "GET /posts", "GET /posts.json", "GET /posts.xml" и т.д.
end
```

Кстати, если вы не отключите защиту от обратного пути в директориях (path
traversal, см. ниже), путь запроса может быть изменен до начала поиска
подходящего маршрута.

### Условия

Маршруты могут включать различные условия совпадений, например, клиентское
приложение (user agent):

```ruby
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
  "You're using Songbird version #{params['agent'][0]}"
end

get '/foo' do
  # соответствует не-songbird браузерам
end
```

Другими доступными условиями являются `host_name` и `provides`:

```ruby
get '/', :host_name => /^admin\./ do
  "Admin Area, Access denied!"
end

get '/', :provides => 'html' do
  haml :index
end

get '/', :provides => ['rss', 'atom', 'xml'] do
  builder :feed
end
```

Вы можете задать собственные условия:

```ruby
set(:probability) { |value| condition { rand <= value } }

get '/win_a_car', :probability => 0.1 do
  "You won!"
end

get '/win_a_car' do
  "Sorry, you lost."
end
```

Для условия, которое принимает несколько параметров, используйте звездочку:

```ruby
set(:auth) do |*roles|   # <- обратите внимание на звездочку
  condition do
    unless logged_in? && roles.any? {|role| current_user.in_role? role }
      redirect "/login/", 303
    end
  end
end

get "/my/account/", :auth => [:user, :admin] do
  "Your Account Details"
end

get "/only/admin/", :auth => :admin do
  "Only admins are allowed here!"
end
```

### Возвращаемые значения

Возвращаемое значение блока маршрута ограничивается телом ответа, которое
будет передано HTTP клиенту, или следующей "прослойкой" (middleware) в Rack
стеке. Чаще всего это строка, как в примерах выше. Но также приемлемы и
другие значения.

Вы можете вернуть любой объект, который будет либо корректным Rack ответом,
объектом Rack body, либо кодом состояния HTTP:

* массив с тремя переменными: `[код (Fixnum), заголовки (Hash), тело ответа
  (должно отвечать на #each)]`;
* массив с двумя переменными: `[код (Fixnum), тело ответа (должно отвечать
  на #each)]`;
* объект, отвечающий на `#each`, который передает только строковые типы
  данных в этот блок;
* Fixnum, представляющий код состояния HTTP.


Таким образом, легко можно реализовать, например, поточный пример:

```ruby
class Stream
  def each
    100.times { |i| yield "#{i}\n" }
  end
end

get('/') { Stream.new }
```

Вы также можете использовать метод `stream` (описываемый ниже), чтобы
уменьшить количество дублируемого кода и держать логику стриминга прямо в
маршруте.

### Собственные детекторы совпадений для маршрутов

Как показано выше, Sinatra поставляется со встроенной поддержкой строк и
регулярных выражений в качестве шаблонов URL. Но и это еще не все. Вы можете
легко определить свои собственные детекторы совпадений (matchers) для
маршрутов:

```ruby
class AllButPattern
  Match = Struct.new(:captures)

  def initialize(except)
    @except   = except
    @captures = Match.new([])
  end

  def match(str)
    @captures unless @except === str
  end
end

def all_but(pattern)
  AllButPattern.new(pattern)
end

get all_but("/index") do
  # ...
end
```

Заметьте, что предыдущий пример, возможно, чересчур усложнен, потому что он
может быть реализован так:

```ruby
get // do
  pass if request.path_info == "/index"
  # ...
end
```

Или с использованием негативного просмотра вперед:

```ruby
get %r{^(?!/index$)} do
  # ...
end
```

## Статические файлы

Статические файлы отдаются из `./public` директории. Вы можете указать другое
место, используя опцию `:public_folder`:

```ruby
set :public_folder, File.dirname(__FILE__) + '/static'
```

Учтите, что имя директории со статическими файлами не включено в URL.
Например, файл `./public/css/style.css` будет доступен как
`http://example.com/css/style.css`.

Используйте опцию `:static_cache_control` (см. ниже), чтобы добавить заголовок
`Cache-Control`.

## Представления / Шаблоны

Каждый шаблонизатор представлен своим собственным методом. Эти методы попросту
возвращают строку:

```ruby
get '/' do
  erb :index
end
```

Отобразит `views/index.erb`.

Вместо имени шаблона вы так же можете передавать непосредственно само
содержимое шаблона:

```ruby
get '/' do
  code = "<%= Time.now %>"
  erb code
end
```

Эти методы принимают второй аргумент, хеш с опциями:

```ruby
get '/' do
  erb :index, :layout => :post
end
```

Отобразит `views/index.erb`, вложенным в `views/post.erb` (по умолчанию:
`views/layout.erb`, если существует).

Любые опции, не понимаемые Sinatra, будут переданы в шаблонизатор:

```ruby
get '/' do
  haml :index, :format => :html5
end
```

Вы также можете задавать опции для шаблонизаторов в общем:

```ruby
set :haml, :format => :html5

get '/' do
  haml :index
end
```

Опции, переданные в метод, переопределяют опции, заданные с помощью `set`.

Доступные опции:

<dl>
  <dt>locals</dt>
  <dd>
    Список локальных переменных, передаваемых в документ.
    Например: <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>
  </dd>

  <dt>default_encoding</dt>
  <dd>
    Кодировка, которую следует использовать, если не удалось определить
    оригинальную. По умолчанию: <tt>settings.default_encoding</tt>.
  </dd>

  <dt>views</dt>
  <dd>
    Директория с шаблонами. По умолчанию: <tt>settings.views</tt>.
  </dd>

  <dt>layout</dt>
  <dd>
    Использовать или нет лэйаут (<tt>true</tt> или <tt>false</tt>). Если же значение Symbol,
    то указывает, какой шаблон использовать в качестве лэйаута. Например:
    <tt>erb :index, :layout => !request.xhr?</tt>
  </dd>

  <dt>content_type</dt>
  <dd>
    Content-Type отображенного шаблона. По умолчанию: задается шаблонизатором.
  </dd>

  <dt>scope</dt>
  <dd>
    Область видимости, в которой рендерятся шаблоны. По умолчанию: экземпляр
    приложения. Если вы измените эту опцию, то переменные экземпляра и
    методы-помощники станут недоступными в ваших шаблонах.
  </dd>

  <dt>layout_engine</dt>
  <dd>
    Шаблонизатор, который следует использовать для отображения лэйаута.
    Полезная опция для шаблонизаторов, в которых нет никакой поддержки
    лэйаутов. По умолчанию: тот же шаблонизатор, что используется и для самого
    шаблона. Пример: <tt>set :rdoc, :layout_engine => :erb</tt>
  </dd>
</dl>

По умолчанию считается, что шаблоны находятся в директории `./views`. Чтобы
использовать другую директорию с шаблонами:

```ruby
set :views, settings.root + '/templates'
```

Важное замечание: вы всегда должны ссылаться на шаблоны с помощью символов
(Symbol), даже когда они в поддиректории (в этом случае используйте
`:'subdir/template'`). Вы должны использовать символы, потому что иначе
шаблонизаторы попросту отображают любые строки, переданные им.

### Буквальные шаблоны

```ruby
get '/' do
  haml '%div.title Hello World'
end
```

Отобразит шаблон, переданный строкой.

### Доступные шаблонизаторы

Некоторые языки шаблонов имеют несколько реализаций. Чтобы указать, какую
реализацию использовать, вам следует просто подключить нужную библиотеку:

```ruby
require 'rdiscount' # или require 'bluecloth'
get('/') { markdown :index }
```

#### Haml шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://haml.info/" title="haml">haml</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.haml</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>haml :index, :format => :html5</tt></td>
  </tr>
</table>

#### Erb шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td>
      <a href="http://www.kuwata-lab.com/erubis/" title="erubis">erubis</a>
      или erb (включен в Ruby)
    </td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.erb</tt>, <tt>.rhtml</tt> or <tt>.erubis</tt> (только Erubis)</td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>erb :index</tt></td>
  </tr>
</table>

#### Builder шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td>
      <a href="https://github.com/jimweirich/builder" title="builder">builder</a>
    </td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.builder</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>builder { |xml| xml.em "hi" }</tt></td>
  </tr>
</table>

Блок также используется и для встроенных шаблонов (см. пример).

#### Nokogiri шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://nokogiri.org/" title="nokogiri">nokogiri</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.nokogiri</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>nokogiri { |xml| xml.em "hi" }</tt></td>
  </tr>
</table>

Блок также используется и для встроенных шаблонов (см. пример).

#### Sass шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://sass-lang.com/" title="sass">sass</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.sass</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>sass :stylesheet, :style => :expanded</tt></td>
  </tr>
</table>

#### SCSS шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://sass-lang.com/" title="sass">sass</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.scss</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>scss :stylesheet, :style => :expanded</tt></td>
  </tr>
</table>

#### Less шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://www.lesscss.org/" title="less">less</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.less</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>less :stylesheet</tt></td>
  </tr>
</table>

#### Liquid шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://www.liquidmarkup.org/" title="liquid">liquid</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.liquid</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>liquid :index, :locals => { :key => 'value' }</tt></td>
  </tr>
</table>

Так как в Liquid шаблонах невозможно вызывать методы из Ruby (кроме `yield`), то
вы почти всегда будете передавать в шаблон локальные переменные.

#### Markdown шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td>
      Любая из библиотек:
        <a href="https://github.com/rtomayko/rdiscount" title="RDiscount">RDiscount</a>,
        <a href="https://github.com/vmg/redcarpet" title="RedCarpet">RedCarpet</a>,
        <a href="http://deveiate.org/projects/BlueCloth" title="BlueCloth">BlueCloth</a>,
        <a href="http://kramdown.gettalong.org/" title="kramdown">kramdown</a>,
        <a href="https://github.com/bhollis/maruku" title="maruku">maruku</a>
    </td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.markdown</tt>, <tt>.mkd</tt> and <tt>.md</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>markdown :index, :layout_engine => :erb</tt></td>
  </tr>
</table>

В Markdown невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:

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

Заметьте, что вы можете вызывать метод `markdown` из других шаблонов:

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

Вы не можете вызывать Ruby из Markdown, соответственно, вы не можете
использовать лэйауты на Markdown. Тем не менее, есть возможность использовать
один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
опции `:layout_engine`.

#### Textile шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://redcloth.org/" title="RedCloth">RedCloth</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.textile</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>textile :index, :layout_engine => :erb</tt></td>
  </tr>
</table>

В Textile невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:

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

Заметьте, что вы можете вызывать метод `textile` из других шаблонов:

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

Вы не можете вызывать Ruby из Textile, соответственно, вы не можете
использовать лэйауты на Textile. Тем не менее, есть возможность использовать
один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
опции `:layout_engine`.

#### RDoc шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://rdoc.sourceforge.net/" title="RDoc">RDoc</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.rdoc</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>rdoc :README, :layout_engine => :erb</tt></td>
  </tr>
</table>

В RDoc невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:

```ruby
erb :overview, :locals => { :text => rdoc(:introduction) }
```

Заметьте, что вы можете вызывать метод `rdoc` из других шаблонов:

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

Вы не можете вызывать Ruby из RDoc, соответственно, вы не можете использовать
лэйауты на RDoc. Тем не менее, есть возможность использовать один шаблонизатор
для отображения шаблона, а другой для лэйаута с помощью опции
`:layout_engine`.

#### AsciiDoc шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://asciidoctor.org/" title="Asciidoctor">Asciidoctor</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.asciidoc</tt>, <tt>.adoc</tt> и <tt>.ad</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>asciidoc :README, :layout_engine => :erb</tt></td>
  </tr>
</table>

Так как в AsciiDoc шаблонах невозможно вызывать методы из Ruby напрямую, то вы
почти всегда будете передавать в шаблон локальные переменные.

#### Radius шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="https://github.com/jlong/radius" title="Radius">Radius</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.radius</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>radius :index, :locals => { :key => 'value' }</tt></td>
  </tr>
</table>

Так как в Radius шаблонах невозможно вызывать методы из Ruby напрямую, то вы
почти всегда будете передавать в шаблон локальные переменные.

#### Markaby шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://markaby.github.com/" title="Markaby">Markaby</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.mab</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>markaby { h1 "Welcome!" }</tt></td>
  </tr>
</table>

Блок также используется и для встроенных шаблонов (см. пример).

#### RABL шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="https://github.com/nesquena/rabl" title="Rabl">Rabl</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.rabl</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>rabl :index</tt></td>
  </tr>
</table>

#### Slim шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="http://slim-lang.com/" title="Slim Lang">Slim Lang</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.slim</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>slim :index</tt></td>
  </tr>
</table>

#### Creole шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="https://github.com/minad/creole" title="Creole">Creole</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.creole</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>creole :wiki, :layout_engine => :erb</tt></td>
  </tr>
</table>

В Creole невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:

```ruby
erb :overview, :locals => { :text => creole(:introduction) }
```

Заметьте, что вы можете вызывать метод `creole` из других шаблонов:

```ruby
%h1 Hello From Haml!
%p= creole(:greetings)
```

Вы не можете вызывать Ruby из Creole, соответственно, вы не можете
использовать лэйауты на Creole. Тем не менее, есть возможность использовать
один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
опции `:layout_engine`.

#### MediaWiki шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="https://github.com/nricciar/wikicloth" title="WikiCloth">WikiCloth</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.mediawiki</tt> и <tt>.mw</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>mediawiki :wiki, :layout_engine => :erb</tt></td>
  </tr>
</table>

В разметке MediaWiki невозможно вызывать методы или передавать локальные переменные.
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
с другим шаблонизатором:

```ruby
erb :overview, :locals => { :text => mediawiki(:introduction) }
```

Заметьте, что вы можете вызывать метод `mediawiki` из других шаблонов:

```ruby
%h1 Hello From Haml!
%p= mediawiki(:greetings)
```

Вы не можете вызывать Ruby из MediaWiki, соответственно, вы не можете
использовать лэйауты на MediaWiki. Тем не менее, есть возможность использовать
один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
опции `:layout_engine`.

#### CoffeeScript шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td>
      <a href="https://github.com/josh/ruby-coffee-script" title="Ruby CoffeeScript">
        CoffeeScript
      </a> и
      <a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme" title="ExecJS">
        способ запускать JavaScript
      </a>
    </td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.coffee</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>coffee :index</tt></td>
  </tr>
</table>

#### Stylus шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td>
      <a href="https://github.com/lucasmazza/ruby-stylus" title="Ruby Stylus">
        Stylus
      </a> и
      <a href="https://github.com/sstephenson/execjs/blob/master/README.md#readme" title="ExecJS">
        способ запускать JavaScript
      </a>
    </td>
  </tr>
  <tr>
    <td>Расширение файла</td>
    <td><tt>.styl</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>stylus :index</tt></td>
  </tr>
</table>

Перед тем, как использовать шаблоны стилус, загрузите `stylus` и
`stylus/tilt`:

``` ruby
require 'sinatra'
require 'stylus'
require 'stylus/tilt'

get '/' do
  stylus :example
end
```

#### Yajl шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="https://github.com/brianmario/yajl-ruby" title="yajl-ruby">yajl-ruby</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.yajl</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td>
      <tt>
        yajl :index,
             :locals => { :key => 'qux' },
             :callback => 'present',
             :variable => 'resource'
      </tt>
    </td>
  </tr>
</table>

Содержимое шаблона интерпретируется как код на Ruby, а результирующая
переменная json затем конвертируется с помощью `#to_json`.

```ruby
json = { :foo => 'bar' }
json[:baz] = key
```

Опции `:callback` и `:variable` используются для "декорирования" итогового
объекта.

```ruby
var resource = {"foo":"bar","baz":"qux"}; present(resource);
```

#### WLang шаблоны

<table>
  <tr>
    <td>Зависимости</td>
    <td><a href="https://github.com/blambeau/wlang/" title="wlang">wlang</a></td>
  </tr>
  <tr>
    <td>Расширения файлов</td>
    <td><tt>.wlang</tt></td>
  </tr>
  <tr>
    <td>Пример</td>
    <td><tt>wlang :index, :locals => { :key => 'value' }</tt></td>
  </tr>
</table>

Так как в WLang шаблонах невозможно вызывать методы из Ruby напрямую (за
исключением `yield`), то вы почти всегда будете передавать в шаблон локальные
переменные.

### Доступ к переменным в шаблонах

Шаблоны интерпретируются в том же контексте, что и обработчики маршрутов.
Переменные экземпляра, установленные в процессе обработки маршрутов, будут
доступны напрямую в шаблонах:

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

Либо установите их через хеш локальных переменных:

```ruby
get '/:id' do
  foo = Foo.find(params['id'])
  haml '%h1= bar.name', :locals => { :bar => foo }
end
```

Это обычный подход, когда шаблоны рендерятся как части других шаблонов.

### Шаблоны с `yield` и вложенные раскладки (layout)

Раскладка (layout) обычно представляет собой шаблон, который исполняет
`yield`.
Такой шаблон может быть либо использован с помощью опции `:template`,
как описано выше, либо он может быть дополнен блоком:

```ruby
    erb :post, :layout => false do
      erb :index
    end
```

Эти инструкции в основном эквивалентны `erb :index, :layout => :post`.

Передача блоков интерпретирующим шаблоны методам наиболее полезна для
создания вложенных раскладок:

```ruby
    erb :main_layout, :layout => false do
      erb :admin_layout do
        erb :user
      end
    end
```

Это же самое может быть сделано короче:

```ruby
    erb :admin_layout, :layout => :main_layout do
      erb :user
    end
```

В настоящее время, следующие интерпретирующие шаблоны методы
принимают блок:
`erb`, `haml`, `liquid`, `slim `, `wlang`.
Общий метод заполнения шаблонов `render` также принимает блок.

### Включённые шаблоны

Шаблоны также могут быть определены в конце исходного файла:

```ruby
require 'sinatra'

get '/' do
  haml :index
end

__END__

@@ layout
%html
  = yield

@@ index
%div.title Hello world.
```

Заметьте: включённые шаблоны, определенные в исходном файле, который подключил
Sinatra, будут загружены автоматически. Вызовите `enable :inline_templates`
напрямую, если используете включённые шаблоны в других файлах.

### Именованные шаблоны

Шаблоны также могут быть определены при помощи `template` метода:

```ruby
template :layout do
  "%html\n  =yield\n"
end

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

get '/' do
  haml :index
end
```

Если шаблон с именем "layout" существует, то он будет использоваться каждый
раз при рендеринге. Вы можете отключать лэйаут в каждом конкретном случае с
помощью `:layout => false` или отключить его для всего приложения: `set :haml,
:layout => false`:

```ruby
get '/' do
  haml :index, :layout => !request.xhr?
end
```

### Привязка файловых расширений

Чтобы связать расширение файла с движком рендеринга, используйте
`Tilt.register`. Например, если вы хотите использовать расширение `tt` для
шаблонов Textile:

```ruby
Tilt.register :tt, Tilt[:textile]
```

### Добавление собственного движка рендеринга

Сначала зарегистрируйте свой движок в Tilt, а затем создайте метод, отвечающий
за рендеринг:

```ruby
Tilt.register :myat, MyAwesomeTemplateEngine

helpers do
  def myat(*args) render(:myat, *args) end
end

get '/' do
  myat :index
end
```

Отобразит `./views/index.myat`. Чтобы узнать больше о Tilt, смотрите
https://github.com/rtomayko/tilt

## Фильтры

`before`-фильтры выполняются перед каждым запросом в том же контексте, что и
маршруты, и могут изменять как запрос, так и ответ на него. Переменные
экземпляра, установленные в фильтрах, доступны в маршрутах и шаблонах:

```ruby
before do
  @note = 'Hi!'
  request.path_info = '/foo/bar/baz'
end

get '/foo/*' do
  @note #=> 'Hi!'
  params['splat'] #=> 'bar/baz'
end
```

`after`-фильтры выполняются после каждого запроса в том же контексте
и могут изменять как запрос, так и ответ на него. Переменные
экземпляра, установленные в `before`-фильтрах и маршрутах, будут доступны в
`after`-фильтрах:

```ruby
after do
  puts response.status
end
```

Заметьте: если вы используете метод `body`, а не просто возвращаете строку из
маршрута, то тело ответа не будет доступно в `after`-фильтрах, так как оно
будет сгенерировано позднее.

Фильтры могут использовать шаблоны URL и будут интерпретированы, только если
путь запроса совпадет с этим шаблоном:

```ruby
before '/protected/*' do
  authenticate!
end

after '/create/:slug' do |slug|
  session['last_slug'] = slug
end
```

Как и маршруты, фильтры могут использовать условия:

```ruby
before :agent => /Songbird/ do
  # ...
end

after '/blog/*', :host_name => 'example.com' do
  # ...
end
```

## Методы-помощники

Используйте метод `helpers`, чтобы определить методы-помощники, которые в
дальнейшем можно будет использовать в обработчиках маршрутов и шаблонах:

```ruby
helpers do
  def bar(name)
    "#{name}bar"
  end
end

get '/:name' do
  bar(params['name'])
end
```

Также методы-помощники могут быть заданы в отдельных модулях:

```ruby
module FooUtils
  def foo(name) "#{name}foo" end
end

module BarUtils
  def bar(name) "#{name}bar" end
end

helpers FooUtils, BarUtils
```

Эффект равносилен включению модулей в класс приложения.

### Использование сессий

Сессия используется, чтобы сохранять состояние между запросами. Если эта опция
включена, то у вас будет один хеш сессии на одну пользовательскую сессию:

```ruby
enable :sessions

get '/' do
  "value = " << session['value'].inspect
end

get '/:value' do
  session['value'] = params['value']
end
```

Заметьте, что при использовании `enable :sessions` все данные сохраняются в
куках (cookies). Это может быть не совсем то, что вы хотите (например,
сохранение больших объемов данных увеличит ваш трафик). В таком случае вы
можете использовать альтернативную Rack "прослойку" (middleware), реализующую
механизм сессий. Для этого *не надо* вызывать `enable :sessions`, вместо этого
следует подключить ее так же, как и любую другую "прослойку":

```ruby
use Rack::Session::Pool, :expire_after => 2592000

get '/' do
  "value = " << session['value'].inspect
end

get '/:value' do
  session['value'] = params['value']
end
```

Для повышения безопасности данные сессии в куках подписываются секретным
ключом. Секретный ключ генерируется Sinatra. Тем не менее, так как этот ключ
будет меняться с каждым запуском приложения, вы, возможно, захотите установить
ключ вручную, чтобы у всех экземпляров вашего приложения был один и тот же
ключ:

```ruby
set :session_secret, 'super secret'
```

Если вы хотите больше настроек для сессий, вы можете задать их, передав хеш
опций в параметр `sessions`:

```ruby
set :sessions, :domain => 'foo.com'
```

Чтобы сделать сессию доступной другим приложениям, размещенным на поддоменах
foo.com, добавьте *.* перед доменом:

``` ruby
set :sessions, :domain => '.foo.com'
```

### Прерывание

Чтобы незамедлительно прервать обработку запроса внутри фильтра или маршрута,
используйте:

```ruby
halt
```

Можно также указать статус при прерывании:

```ruby
halt 410
```

Тело:

```ruby
halt 'this will be the body'
```

И то, и другое:

```ruby
halt 401, 'go away!'
```

Можно указать заголовки:

```ruby
halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
```

И, конечно, можно использовать шаблоны с `halt`:

```ruby
halt erb(:error)
```

### Передача

Маршрут может передать обработку запроса следующему совпадающему маршруту,
используя `pass`:

```ruby
get '/guess/:who' do
  pass unless params['who'] == 'Frank'
  'You got me!'
end

get '/guess/*' do
  'You missed!'
end
```

Блок маршрута сразу же прерывается, и контроль переходит к следующему
совпадающему маршруту. Если соответствующий маршрут не найден, то ответом на
запрос будет 404.

### Вызов другого маршрута

Иногда `pass` не подходит, например, если вы хотите получить результат вызова
другого обработчика маршрута. В таком случае просто используйте `call`:

```ruby
get '/foo' do
  status, headers, body = call env.merge("PATH_INFO" => '/bar')
  [status, headers, body.map(&:upcase)]
end

get '/bar' do
  "bar"
end
```

Заметьте, что в предыдущем примере можно облегчить тестирование и повысить
производительность, перенеся `"bar"` в метод-помощник, используемый и в
`/foo`, и в `/bar`.

Если вы хотите, чтобы запрос был отправлен в тот же экземпляр приложения, а не
в его копию, используйте `call!` вместо `call`.

Если хотите узнать больше о `call`, смотрите спецификацию Rack.

### Задание тела, кода и заголовков ответа

Хорошим тоном является установка кода состояния HTTP и тела ответа в
возвращаемом значении обработчика маршрута. Тем не менее, в некоторых
ситуациях вам, возможно, понадобится задать тело ответа в произвольной точке
потока исполнения. Вы можете сделать это с помощью метода-помощника `body`.
Если вы задействуете метод `body`, то вы можете использовать его и в
дальнейшем, чтобы получить доступ к телу ответа.

```ruby
get '/foo' do
  body "bar"
end

after do
  puts body
end
```

Также можно передать блок в метод `body`, который затем будет вызван
обработчиком Rack (такой подход может быть использован для реализации
поточного ответа, см. "Возвращаемые значения").

Аналогично вы можете установить код ответа и его заголовки:

```ruby
get '/foo' do
  status 418
  headers \
    "Allow"   => "BREW, POST, GET, PROPFIND, WHEN",
    "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
  body "I'm a tea pot!"
end
```

Как и `body`, методы `headers` и `status`, вызванные без аргументов,
возвращают свои текущие значения.

### Стриминг ответов

Иногда требуется начать отправлять данные клиенту прямо в процессе
генерирования частей этих данных. В особых случаях требуется постоянно
отправлять данные до тех пор, пока клиент не закроет соединение. Вы можете
использовать метод `stream` вместо написания собственных "оберток".

```ruby
get '/' do
  stream do |out|
    out << "It's gonna be legen -\n"
    sleep 0.5
    out << " (wait for it) \n"
    sleep 1
    out << "- dary!\n"
  end
end
```

Что позволяет вам реализовать стриминговые API,
[Server Sent Events](http://dev.w3.org/html5/eventsource/),
и может служить основой для [WebSockets](http://en.wikipedia.org/wiki/WebSocket).
Также такой подход можно использовать для увеличения производительности в случае,
когда какая-то часть контента зависит от медленного ресурса.

Заметьте, что возможности стриминга, особенно количество одновременно
обслуживаемых запросов, очень сильно зависят от используемого веб-сервера.
Некоторые серверы, например, WEBRick, могут и вовсе не поддерживать стриминг.
Если сервер не поддерживает стриминг, то все данные будут отправлены за один
раз сразу после того, как блок, переданный в `stream`, завершится. Стриминг
вообще не работает при использовании Shotgun.

Если метод используется с параметром `keep_open`, то он не будет вызывать
`close` у объекта потока, что позволит вам закрыть его позже в любом другом
месте. Это работает только с событийными серверами, например, с Thin и
Rainbows. Другие же серверы все равно будут закрывать поток:

```ruby
# long polling

set :server, :thin
connections = []

get '/subscribe' do
  # регистрация клиента
  stream(:keep_open) do |out|
    connections << out }
    # удаление "мертвых клиентов"
    connections.reject!(&:closed?)
  end
end

post '/message' do
  connections.each do |out|
    # уведомить клиента о новом сообщении
    out << params['message'] << "\n"

    # указать клиенту на необходимость снова соединиться
    out.close
  end

  # допуск
  "message received"
end
```

### Логирование

В области видимости запроса метод `logger` предоставляет доступ к экземпляру
`Logger`:

```ruby
get '/' do
  logger.info "loading data"
  # ...
end
```

Этот логер автоматически учитывает ваши настройки логирования в Rack. Если
логирование выключено, то этот метод вернет пустой (dummy) объект, поэтому вы
можете смело использовать его в маршрутах и фильтрах.

Заметьте, что логирование включено по умолчанию только для
`Sinatra::Application`, а если ваше приложение — подкласс `Sinatra::Base`, то
вы, наверное, захотите включить его вручную:

```ruby
class MyApp < Sinatra::Base
  configure :production, :development do
    enable :logging
  end
end
```

Чтобы избежать использования любой логирующей "прослойки", задайте опции
`logging` значение `nil`. Тем не менее, не забывайте, что в такой ситуации
`logger` вернет `nil`. Чаще всего так делают, когда задают свой собственный
логер. Sinatra будет использовать то, что находится в `env['rack.logger']`.

### Mime-типы

Когда вы используете `send_file` или статические файлы, у вас могут быть
mime-типы, которые Sinatra не понимает по умолчанию. Используйте `mime_type`
для их регистрации по расширению файла:

```ruby
configure do
  mime_type :foo, 'text/foo'
end
```

Вы также можете использовать это в `content_type` методе-помощнике:

```ruby
get '/' do
  content_type :foo
  "foo foo foo"
end
```

### Генерирование URL

Чтобы сформировать URL, вам следует использовать метод `url`, например, в Haml:

```ruby
%a{:href => url('/foo')} foo
```

Этот метод учитывает обратные прокси и маршрутизаторы Rack, если они
присутствуют.

Наряду с `url` вы можете использовать `to` (смотрите пример ниже).

### Перенаправление (редирект)

Вы можете перенаправить браузер пользователя с помощью метода `redirect`:

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

Любые дополнительные параметры используются по аналогии с аргументами метода
`halt`:

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

Вы также можете перенаправить пользователя обратно, на страницу, с которой он
пришел, с помощью `redirect back`:

```ruby
get '/foo' do
  "<a href='/bar'>do something</a>"
end

get '/bar' do
  do_something
  redirect back
end
```

Чтобы передать какие-либо параметры вместе с перенаправлением, либо добавьте
их в строку запроса:

```ruby
redirect to('/bar?sum=42')
```

либо используйте сессию:

```ruby
enable :sessions

get '/foo' do
  session['secret'] = 'foo'
  redirect to('/bar')
end

get '/bar' do
  session['secret']
end
```

### Управление кэшированием

Установка корректных заголовков — основа правильного HTTP кэширования.

Вы можете легко выставить заголовок Cache-Control таким образом:

```ruby
get '/' do
  cache_control :public
  "cache it!"
end
```

Совет: задавайте кэширование в `before`-фильтре:

```ruby
before do
  cache_control :public, :must_revalidate, :max_age => 60
end
```

Если вы используете метод `expires` для задания соответствующего заголовка, то
`Cache-Control` будет выставлен автоматически:

```ruby
before do
  expires 500, :public, :must_revalidate
end
```

Чтобы как следует использовать кэширование, вам следует подумать об
использовании `etag` или `last_modified`. Рекомендуется использовать эти
методы-помощники *до* выполнения ресурсоемких вычислений, так как они
немедленно отправят ответ клиенту, если текущая версия уже есть в их кэше:

```ruby
get '/article/:id' do
  @article = Article.find params['id']
  last_modified @article.updated_at
  etag @article.sha1
  erb :article
end
```

Также вы можете использовать
[weak ETag](http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation):

```ruby
etag @article.sha1, :weak
```

Эти методы-помощники не станут ничего кэшировать для вас, но они дадут
необходимую информацию для вашего кэша. Если вы ищете легкое решение для
кэширования, попробуйте [rack-cache](https://github.com/rtomayko/rack-cache):

```ruby
require 'rack/cache'
require 'sinatra'

use Rack::Cache

get '/' do
  cache_control :public, :max_age => 36000
  sleep 5
  "hello"
end
```

Используйте опцию `:static_cache_control` (см. ниже), чтобы добавить заголовок
`Cache-Control` к статическим файлам.

В соответствии с RFC 2616 ваше приложение должно вести себя по-разному, когда
заголовки If-Match или If-None-Match имеют значение `*`, в зависимости от
того, существует или нет запрашиваемый ресурс. Sinatra предполагает, что
ресурсы, к которым обращаются с помощью безопасных (GET) и идемпотентных (PUT)
методов, уже существуют, а остальные ресурсы (к которым обращаются, например,
с помощью POST) считает новыми. Вы можете изменить данное поведение с помощью
опции `:new_resource`:

```ruby
get '/create' do
  etag '', :new_resource => true
  Article.create
  erb :new_article
end
```

Если вы хотите использовать weak ETag, задайте опцию `:kind`:

```ruby
etag '', :new_resource => true, :kind => :weak
```

### Отправка файлов

Для отправки файлов пользователю вы можете использовать метод `send_file`:

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

Этот метод имеет несколько опций:

```ruby
send_file 'foo.png', :type => :jpg
```

Возможные опции:

<dl>
  <dt>filename</dt>
  <dd>имя файла, по умолчанию: реальное имя файла.</dd>

  <dt>last_modified</dt>
  <dd>значение для заголовка Last-Modified, по умолчанию: mtime (время
      изменения) файла.</dd>

  <dt>type</dt>
  <dd>тип файла, по умолчанию: определяется по расширению файла.</dd>

  <dt>disposition</dt>
  <dd>используется для заголовка Content-Disposition, возможные значения: <tt>nil</tt>
      (по умолчанию), <tt>:attachment</tt> и <tt>:inline</tt>.</dd>

  <dt>length</dt>
  <dd>значения для заголовка Content-Length, по умолчанию: размер файла.</dd>

  <dt>status</dt>
  <dd>Код ответа. Полезно, когда отдается статический файл в качестве страницы с
      сообщением об ошибке.</dd>
</dl>

Этот метод будет использовать возможности Rack сервера для отправки файлов,
если они доступны, в противном случае будет напрямую отдавать файл из Ruby
процесса. Метод `send_file` также обеспечивает автоматическую обработку
частичных (range) запросов с помощью Sinatra.

### Доступ к объекту запроса

Объект входящего запроса доступен на уровне обработки запроса (в фильтрах,
маршрутах, обработчиках ошибок) с помощью `request` метода:

```ruby
# приложение запущено на 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.scheme              # "http"
  request.script_name         # "/example"
  request.path_info           # "/foo"
  request.port                # 80
  request.request_method      # "GET"
  request.query_string        # ""
  request.content_length      # длина тела запроса
  request.media_type          # медиатип тела запроса
  request.host                # "example.com"
  request.get?                # true (есть аналоги для других методов HTTP)
  request.form_data?          # false
  request["some_param"]       # значение параметра some_param. Шорткат для хеша params
  request.referrer            # источник запроса клиента либо '/'
  request.user_agent          # user agent (используется для :agent условия)
  request.cookies             # хеш, содержащий cookies браузера
  request.xhr?                # является ли запрос ajax запросом?
  request.url                 # "http://example.com/example/foo"
  request.path                # "/example/foo"
  request.ip                  # IP-адрес клиента
  request.secure?             # false (true, если запрос сделан через SSL)
  request.forwarded?          # true (если сервер работает за обратным прокси)
  request.env                 # "сырой" env хеш, полученный Rack
end
```

Некоторые опции, такие как `script_name` или `path_info`, доступны для
изменения:

```ruby
before { request.path_info = "/" }

get "/" do
  "all requests end up here"
end
```

`request.body` является IO или StringIO объектом:

```ruby
post "/api" do
  request.body.rewind  # в случае, если кто-то уже прочитал тело запроса
  data = JSON.parse request.body.read
  "Hello #{data['name']}!"
end
```

### Вложения

Вы можете использовать метод `attachment`, чтобы сказать браузеру, что ответ
сервера должен быть сохранен на диск, а не отображен:

```ruby
get '/' do
  attachment
  "store it!"
end
```

Вы также можете указать имя файла:

```ruby
get '/' do
  attachment "info.txt"
  "store it!"
end
```

### Работа со временем и датами

Sinatra предлагает метод-помощник `time_for`, который из заданного значения
создает объект Time. Он также может конвертировать `DateTime`, `Date` и
подобные классы:

```ruby
get '/' do
  pass if Time.now > time_for('Dec 23, 2012')
  "still time"
end
```

Этот метод используется внутри Sinatra методами `expires`, `last_modified` и
им подобными. Поэтому вы легко можете изменить и дополнить поведение этих методов,
переопределив `time_for` в своем приложении:

```ruby
helpers do
  def time_for(value)
{'fraction_non_alphanumeric': 0.1370329489678495, 'fraction_numerical': 0.0021371034029261876, 'mean_word_length': 3.9559473929236497, 'pattern_counts': {'":': 6, '<': 1397, '<?xml version=': 0, '>': 1509, 'https://': 32, 'lorem ipsum': 0, 'www.': 8, 'xml': 20}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7265771', 'n_tokens_mistral': 36429, 'n_tokens_neox': 39026, 'n_words': 9296}