diff --git a/README.ru.md b/README.ru.md
new file mode 100644
index 00000000..8180381d
--- /dev/null
+++ b/README.ru.md
@@ -0,0 +1,2685 @@
+# Sinatra
+
+*Внимание: Этот документ является переводом английской версии и может быть
+устаревшим*
+
+Sinatra — это предметно-ориентированный каркас
+([DSL](http://ru.wikipedia.org/wiki/Предметно-ориентированный_язык_программирования))
+для быстрого создания функциональных веб-приложений на Ruby с минимумом усилий:
+
+```ruby
+# myapp.rb
+require 'sinatra'
+
+get '/' do
+ 'Hello world!'
+end
+```
+
+Установите gem:
+
+```
+gem install sinatra
+```
+
+и запустите приложение с помощью:
+
+```
+ruby myapp.rb
+```
+
+Оцените результат: http://localhost:4567
+
+Рекомендуется также установить Thin, сделать это можно командой: `gem install
+thin`. Thin — это более производительный и функциональный сервер для
+разработки приложений на Sinatra.
+
+## Маршруты
+
+В Sinatra маршрут — это пара: <HTTP метод> и <шаблон URL>. Каждый маршрут
+связан с блоком кода:
+
+```ruby
+get '/' do
+ # .. что-то показать ..
+end
+
+post '/' do
+ # .. что-то создать ..
+end
+
+put '/' do
+ # .. что-то заменить ..
+end
+
+patch '/' do
+ # .. что-то изменить ..
+end
+
+delete '/' do
+ # .. что-то удалить ..
+end
+
+options '/' 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 %r{/hello/([\w]+)} do
+ "Hello, #{params[:captures].first}!"
+end
+```
+
+Или с параметром блока:
+
+```ruby
+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`.
+
+Доступные опции:
+
+
+ - locals
+ -
+ Список локальных переменных, передаваемых в документ.
+ Например: erb "<%= foo %>", :locals => {:foo => "bar"}
+
+
+ - default_encoding
+ -
+ Кодировка, которую следует использовать, если не удалось определить
+ оригинальную. По умолчанию: settings.default_encoding.
+
+
+ - views
+ -
+ Директория с шаблонами. По умолчанию: settings.views.
+
+
+ - layout
+ -
+ Использовать или нет лэйаут (true или false). Если же значение Symbol,
+ то указывает, какой шаблон использовать в качестве лэйаута. Например:
+ erb :index, :layout => !request.xhr?
+
+
+ - content_type
+ -
+ Content-Type отображенного шаблона. По умолчанию: задается шаблонизатором.
+
+
+ - scope
+ -
+ Область видимости, в которой рендерятся шаблоны. По умолчанию: экземпляр
+ приложения. Если вы измените эту опцию, то переменные экземпляра и
+ методы-помощники станут недоступными в ваших шаблонах.
+
+
+ - layout_engine
+ -
+ Шаблонизатор, который следует использовать для отображения лэйаута.
+ Полезная опция для шаблонизаторов, в которых нет никакой поддержки
+ лэйаутов. По умолчанию: тот же шаблонизатор, что используется и для самого
+ шаблона. Пример: set :rdoc, :layout_engine => :erb
+
+
+
+По умолчанию считается, что шаблоны находятся в директории `./views`. Чтобы
+использовать другую директорию с шаблонами:
+
+```ruby
+set :views, settings.root + '/templates'
+```
+
+Важное замечание: вы всегда должны ссылаться на шаблоны с помощью символов
+(Symbol), даже когда они в поддиректории (в этом случае используйте
+`:'subdir/template'`). Вы должны использовать символы, потому что иначе
+шаблонизаторы попросту отображают любые строки, переданные им.
+
+### Доступные шаблонизаторы
+
+Некоторые языки шаблонов имеют несколько реализаций. Чтобы указать, какую
+реализацию использовать, вам следует просто подключить нужную библиотеку:
+
+```ruby
+require 'rdiscount' # или require 'bluecloth'
+get('/') { markdown :index }
+```
+
+### Haml шаблоны
+
+
+
+ Зависимости |
+ haml |
+
+
+ Расширения файлов |
+ .haml |
+
+
+ Пример |
+ haml :index, :format => :html5 |
+
+
+
+### Erb шаблоны
+
+
+
+ Зависимости |
+
+ erubis
+ или erb (включен в Ruby)
+ |
+
+
+ Расширения файлов |
+ .erb, .rhtml or .erubis (только Erubis) |
+
+
+ Пример |
+ erb :index |
+
+
+
+### Builder шаблоны
+
+
+
+ Зависимости |
+
+ builder
+ |
+
+
+ Расширения файлов |
+ .builder |
+
+
+ Пример |
+ builder { |xml| xml.em "hi" } |
+
+
+
+Блок также используется и для встроенных шаблонов (см. пример).
+
+### Nokogiri шаблоны
+
+
+
+ Зависимости |
+ nokogiri |
+
+
+ Расширения файлов |
+ .nokogiri |
+
+
+ Пример |
+ nokogiri { |xml| xml.em "hi" } |
+
+
+
+Блок также используется и для встроенных шаблонов (см. пример).
+
+### Sass шаблоны
+
+
+
+ Зависимости |
+ sass |
+
+
+ Расширения файлов |
+ .sass |
+
+
+ Пример |
+ sass :stylesheet, :style => :expanded |
+
+
+
+### SCSS шаблоны
+
+
+
+ Зависимости |
+ sass |
+
+
+ Расширения файлов |
+ .scss |
+
+
+ Пример |
+ scss :stylesheet, :style => :expanded |
+
+
+
+### Less шаблоны
+
+
+
+ Зависимости |
+ less |
+
+
+ Расширения файлов |
+ .less |
+
+
+ Пример |
+ less :stylesheet |
+
+
+
+### Liquid шаблоны
+
+
+
+ Зависимости |
+ liquid |
+
+
+ Расширения файлов |
+ .liquid |
+
+
+ Пример |
+ liquid :index, :locals => { :key => 'value' } |
+
+
+
+Так как в Liquid шаблонах невозможно вызывать методы из Ruby (кроме `yield`), то
+вы почти всегда будете передавать в шаблон локальные переменные.
+
+### Markdown шаблоны
+
+
+
+ Зависимости |
+
+ Любая из библиотек:
+ RDiscount,
+ RedCarpet,
+ BlueCloth,
+ kramdown,
+ maruku
+ |
+
+
+ Расширения файлов |
+ .markdown, .mkd and .md |
+
+
+ Пример |
+ markdown :index, :layout_engine => :erb |
+
+
+
+В Markdown невозможно вызывать методы или передавать локальные переменные.
+Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
+с другим шаблонизатором:
+
+```ruby
+erb :overview, :locals => { :text => markdown(:introduction) }
+```
+
+Заметьте, что вы можете вызывать метод `markdown` из других шаблонов:
+
+```ruby
+%h1 Hello From Haml!
+%p= markdown(:greetings)
+```
+
+Вы не можете вызывать Ruby из Markdown, соответственно, вы не можете
+использовать лэйауты на Markdown. Тем не менее, есть возможность использовать
+один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
+опции `:layout_engine`.
+
+### Textile шаблоны
+
+
+
+ Зависимости |
+ RedCloth |
+
+
+ Расширения файлов |
+ .textile |
+
+
+ Пример |
+ textile :index, :layout_engine => :erb |
+
+
+
+В Textile невозможно вызывать методы или передавать локальные переменные.
+Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
+с другим шаблонизатором:
+
+```ruby
+erb :overview, :locals => { :text => textile(:introduction) }
+```
+
+Заметьте, что вы можете вызывать метод `textile` из других шаблонов:
+
+```ruby
+%h1 Hello From Haml!
+%p= textile(:greetings)
+```
+
+Вы не можете вызывать Ruby из Textile, соответственно, вы не можете
+использовать лэйауты на Textile. Тем не менее, есть возможность использовать
+один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
+опции `:layout_engine`.
+
+### RDoc шаблоны
+
+
+
+ Зависимости |
+ RDoc |
+
+
+ Расширения файлов |
+ .rdoc |
+
+
+ Пример |
+ rdoc :README, :layout_engine => :erb |
+
+
+
+В RDoc невозможно вызывать методы или передавать локальные переменные.
+Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
+с другим шаблонизатором:
+
+```ruby
+erb :overview, :locals => { :text => rdoc(:introduction) }
+```
+
+Заметьте, что вы можете вызывать метод `rdoc` из других шаблонов:
+
+```ruby
+%h1 Hello From Haml!
+%p= rdoc(:greetings)
+```
+
+Вы не можете вызывать Ruby из RDoc, соответственно, вы не можете использовать
+лэйауты на RDoc. Тем не менее, есть возможность использовать один шаблонизатор
+для отображения шаблона, а другой для лэйаута с помощью опции
+`:layout_engine`.
+
+### Radius шаблоны
+
+
+
+ Зависимости |
+ Radius |
+
+
+ Расширения файлов |
+ .radius |
+
+
+ Пример |
+ radius :index, :locals => { :key => 'value' } |
+
+
+
+Так как в Radius шаблонах невозможно вызывать методы из Ruby напрямую, то вы
+почти всегда будете передавать в шаблон локальные переменные.
+
+### Markaby шаблоны
+
+
+
+ Зависимости |
+ Markaby |
+
+
+ Расширения файлов |
+ .mab |
+
+
+ Пример |
+ markaby { h1 "Welcome!" } |
+
+
+
+Блок также используется и для встроенных шаблонов (см. пример).
+
+### RABL шаблоны
+
+
+
+ Зависимости |
+ Rabl |
+
+
+ Расширения файлов |
+ .rabl |
+
+
+ Пример |
+ rabl :index |
+
+
+
+### Slim шаблоны
+
+
+
+ Зависимости |
+ Slim Lang |
+
+
+ Расширения файлов |
+ .slim |
+
+
+ Пример |
+ slim :index |
+
+
+
+### Creole шаблоны
+
+
+
+ Зависимости |
+ Creole |
+
+
+ Расширения файлов |
+ .creole |
+
+
+ Пример |
+ creole :wiki, :layout_engine => :erb |
+
+
+
+В Creole невозможно вызывать методы или передавать локальные переменные.
+Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
+с другим шаблонизатором:
+
+```ruby
+erb :overview, :locals => { :text => creole(:introduction) }
+```
+
+Заметьте, что вы можете вызывать метод `creole` из других шаблонов:
+
+```ruby
+%h1 Hello From Haml!
+%p= creole(:greetings)
+```
+
+Вы не можете вызывать Ruby из Creole, соответственно, вы не можете
+использовать лэйауты на Creole. Тем не менее, есть возможность использовать
+один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
+опции `:layout_engine`.
+
+### CoffeeScript шаблоны
+
+
+
+### Yajl шаблоны
+
+
+
+ Зависимости |
+ yajl-ruby |
+
+
+ Расширения файлов |
+ .yajl |
+
+
+ Пример |
+
+
+ yajl :index,
+ :locals => { :key => 'qux' },
+ :callback => 'present',
+ :variable => 'resource'
+
+ |
+
+
+
+Содержимое шаблона интерпретируется как код на Ruby, а результирующая
+переменная json затем конвертируется с помощью `#to_json`.
+
+```ruby
+json = { :foo => 'bar' }
+json[:baz] = key
+```
+
+Опции `:callback` и `:variable` используются для "декорирования" итогового
+объекта.
+
+```ruby
+var resource = {"foo":"bar","baz":"qux"}; present(resource);
+```
+
+### WLang шаблоны
+
+
+
+ Зависимости |
+ wlang |
+
+
+ Расширения файлов |
+ .wlang |
+
+
+ Пример |
+ wlang :index, :locals => { :key => 'value' } |
+
+
+
+Так как в WLang шаблонах невозможно вызывать методы из Ruby напрямую (за
+исключением `yield`), то вы почти всегда будете передавать в шаблон локальные
+переменные.
+
+### Встроенные шаблоны
+
+```ruby
+get '/' do
+ haml '%div.title Hello World'
+end
+```
+
+Отобразит встроенный шаблон, переданный строкой.
+
+### Доступ к переменным в шаблонах
+
+Шаблоны интерпретируются в том же контексте, что и обработчики маршрутов.
+Переменные экземпляра, установленные в процессе обработки маршрутов, будут
+доступны напрямую в шаблонах:
+
+```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
+```
+
+Это обычный подход, когда шаблоны рендерятся как части других шаблонов.
+
+### Вложенные шаблоны
+
+Шаблоны также могут быть определены в конце исходного файла:
+
+```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'
+```
+
+### Прерывание
+
+Чтобы незамедлительно прервать обработку запроса внутри фильтра или маршрута,
+используйте:
+
+```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) { |out| connections << out }
+
+ # удаление "мертвых клиентов"
+ connections.reject!(&:closed?)
+
+ # допуск
+ "subscribed"
+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
+ "do something"
+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
+```
+
+Возможные опции:
+
+
+ - filename
+ - имя файла, по умолчанию: реальное имя файла.
+
+ - last_modified
+ - значение для заголовка Last-Modified, по умолчанию: mtime (время
+ изменения) файла.
+
+ - type
+ - тип файла, по умолчанию: определяется по расширению файла.
+
+ - disposition
+ - используется для заголовка Content-Disposition, возможные значения: nil
+ (по умолчанию), :attachment и :inline.
+
+ - length
+ - значения для заголовка Content-Length, по умолчанию: размер файла.
+
+ - status
+ - Код ответа. Полезно, когда отдается статический файл в качестве страницы с
+ сообщением об ошибке.
+
+
+Этот метод будет использовать возможности 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)
+ case value
+ when :yesterday then Time.now - 24*60*60
+ when :tomorrow then Time.now + 24*60*60
+ else super
+ end
+ end
+end
+
+get '/' do
+ last_modified :yesterday
+ expires :tomorrow
+ "hello"
+end
+```
+
+### Поиск шаблонов
+
+Для поиска шаблонов и их последующего рендеринга используется метод
+`find_template`:
+
+```ruby
+find_template settings.views, 'foo', Tilt[:haml] do |file|
+ puts "could be #{file}"
+end
+```
+
+Это не слишком полезный пример. Зато полезен тот факт, что вы можете
+переопределить этот метод, чтобы использовать свой собственный механизм
+поиска. Например, если вы хотите, чтобы можно было использовать несколько
+директорий с шаблонами:
+
+```ruby
+set :views, ['views', 'templates']
+
+helpers do
+ def find_template(views, name, engine, &block)
+ Array(views).each { |v| super(v, name, engine, &block) }
+ end
+end
+```
+
+Другой пример, в котором используются разные директории для движков
+рендеринга:
+
+```ruby
+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
+```
+
+Вы можете легко вынести этот код в расширение и поделиться им с остальными!
+
+Заметьте, что `find_template` не проверяет, существует ли файл на самом деле,
+а вызывает заданный блок для всех возможных путей. Дело тут не в
+производительности, дело в том, что `render` вызовет `break`, как только файл
+не будет найден. Содержимое и местонахождение шаблонов будет закэшировано,
+если приложение запущено не в режиме разработки (`set :environment,
+:development`). Вы должны помнить об этих нюансах, если пишите по-настоящему
+"сумасшедший" метод.
+
+## Конфигурация
+
+Этот блок исполняется один раз при старте в любом окружении, режиме
+(environment):
+
+```ruby
+configure do
+ # задание одной опции
+ set :option, 'value'
+
+ # устанавливаем несколько опций
+ set :a => 1, :b => 2
+
+ # то же самое, что и `set :option, true`
+ enable :option
+
+ # то же самое, что и `set :option, false`
+ disable :option
+
+ # у вас могут быть "динамические" опции с блоками
+ set(:css_dir) { File.join(views, 'css') }
+end
+```
+
+Будет запущено, когда окружение (RACK_ENV переменная) `:production`:
+
+```ruby
+configure :production do
+ ...
+end
+```
+
+Будет запущено, когда окружение `:production` или `:test`:
+
+```ruby
+configure :production, :test do
+ ...
+end
+```
+
+Вы можете получить доступ к этим опциям с помощью `settings`:
+
+```ruby
+configure do
+ set :foo, 'bar'
+end
+
+get '/' do
+ settings.foo? # => true
+ settings.foo # => 'bar'
+ ...
+end
+```
+
+### Настройка защиты от атак
+
+Sinatra использует
+[Rack::Protection](https://github.com/rkh/rack-protection#readme) для защиты
+приложения от простых атак. Вы можете легко выключить эту защиту (что сделает
+ваше приложение чрезвычайно уязвимым):
+
+```ruby
+disable :protection
+```
+
+Чтобы пропустить какой-либо уровень защиты, передайте хеш опций в параметр
+`protection`:
+
+```ruby
+set :protection, :except => :path_traversal
+```
+
+Вы также можете отключить сразу несколько уровней защиты:
+
+```ruby
+set :protection, :except => [:path_traversal, :session_hijacking]
+```
+
+### Доступные настройки
+
+
+ - absolute_redirects
+ -
+ если отключено, то Sinatra будет позволять использование относительных
+ перенаправлений, но при этом перестанет соответствовать RFC 2616 (HTTP
+ 1.1), который разрешает только абсолютные перенаправления.
+
+ -
+ Включайте эту опцию, если ваше приложение работает за обратным прокси,
+ который настроен не совсем корректно. Обратите внимание, метод url все
+ равно будет генерировать абсолютные URL, если вы не передадите false
+ вторым аргументом.
+
+ - Отключено по умолчанию.
+
+ - add_charsets
+ -
+ mime-типы, к которым метод content_type будет автоматически добавлять
+ информацию о кодировке. Вам следует добавлять значения к этой опции
+ вместо ее переопределения: settings.add_charsets << "application/foobar"
+
+
+ - app_file
+ -
+ путь к главному файлу приложения, используется для нахождения корневой
+ директории проекта, директорий с шаблонами и статическими файлами,
+ вложенных шаблонов.
+
+
+ - bind
+ -
+ используемый IP-адрес (по умолчанию: 0.0.0.0). Используется только
+ встроенным сервером.
+
+
+ - default_encoding
+ - кодировка, если неизвестна (по умолчанию: "utf-8").
+
+ - dump_errors
+ - отображать ошибки в логе.
+
+ - environment
+ -
+ текущее окружение, по умолчанию, значение ENV['RACK_ENV'] или
+ "development", если ENV['RACK_ENV'] недоступна.
+
+
+ - logging
+ - использовать логер.
+
+ - lock
+ -
+ создает блокировку для каждого запроса, которая гарантирует обработку
+ только одного запроса в текущий момент времени в Ruby процессе.
+
+ -
+ Включайте, если ваше приложение не потоко-безопасно (thread-safe).
+ Отключено по умолчанию.
+
+ - method_override
+ -
+ использовать "магический" параметр _method, для поддержки
+ PUT/DELETE форм в браузерах, которые не поддерживают эти методы.
+
+
+ - port
+ -
+ порт, на котором будет работать сервер.
+ Используется только встроенным сервером.
+
+
+ - prefixed_redirects
+ -
+ добавлять или нет параметр request.script_name к редиректам, если не
+ задан абсолютный путь. Таким образом, redirect '/foo' будет вести себя
+ как redirect to('/foo'). Отключено по умолчанию.
+
+
+ - protection
+ - включена или нет защита от атак. Смотрите секцию выше.
+
+ - public_dir
+ - Алиас для public_folder.
+
+ - public_folder
+ -
+ путь к директории, откуда будут раздаваться статические файлы.
+ Используется, только если включена раздача статических файлов
+ (см. опцию static ниже).
+
+
+ - reload_templates
+ -
+ перезагружать или нет шаблоны на каждый запрос. Включено в режиме
+ разработки.
+
+
+ - root
+ - путь к корневой директории проекта.
+
+ - raise_errors
+ -
+ выбрасывать исключения (будет останавливать приложение).
+ По умолчанию включено только в окружении test.
+
+
+ - run
+ -
+ если включено, Sinatra будет самостоятельно запускать веб-сервер. Не
+ включайте, если используете rackup или аналогичные средства.
+
+
+ - running
+ - работает ли сейчас встроенный сервер? Не меняйте эту опцию!
+
+ - server
+ -
+ сервер или список серверов, которые следует использовать в качестве
+ встроенного сервера. По умолчанию: ['thin', 'mongrel', 'webrick'], порядок
+ задает приоритет.
+
+ - sessions
+ -
+ включить сессии на основе кук (cookie) на базе Rack::Session::Cookie.
+ Смотрите секцию "Использование сессий" выше.
+
+
+ - show_exceptions
+ -
+ показывать исключения/стек вызовов (stack trace) в браузере. По умолчанию
+ включено только в окружении development.
+
+ -
+ Может быть установлено в
+ :after_handler для запуска специфичной для приложения обработки ошибок,
+ перед показом трассировки стека в браузере.
+
+
+ - static
+ - должна ли Sinatra осуществлять раздачу статических файлов.
+ - Отключите, когда используете какой-либо веб-сервер для этой цели.
+ - Отключение значительно улучшит производительность приложения.
+ - По умолчанию включено в классических и отключено в модульных приложениях.
+
+ - static_cache_control
+ -
+ когда Sinatra отдает статические файлы, используйте эту опцию, чтобы
+ добавить им заголовок Cache-Control. Для этого используется
+ метод-помощник cache_control. По умолчанию отключено.
+
+ -
+ Используйте массив, когда надо задать несколько значений:
+ set :static_cache_control, [:public, :max_age => 300]
+
+
+ - threaded
+ -
+ если включено, то Thin будет использовать EventMachine.defer для
+ обработки запросов.
+
+
+ - views
+ - путь к директории с шаблонами.
+
+
+## Режим, окружение
+
+Есть 3 предопределенных режима, окружения: `"development"`, `"production"` и
+`"test"`. Режим может быть задан через переменную окружения `RACK_ENV`.
+Значение по умолчанию — `"development"`. В этом режиме работы все шаблоны
+перезагружаются между запросами. А также задаются специальные обработчики
+`not_found` и `error`, чтобы вы могли увидеть стек вызовов. В окружениях
+`"production"` и `"test"` шаблоны по умолчанию кэшируются.
+
+Для запуска приложения в определенном окружении используйте ключ `-e`
+
+```
+ruby my_app.rb -e [ENVIRONMENT]
+```
+
+Вы можете использовать предопределенные методы `development?`, `test?` и
++production?, чтобы определить текущее окружение.
+
+## Обработка ошибок
+
+Обработчики ошибок исполняются в том же контексте, что и маршруты, и
+`before`-фильтры, а это означает, что всякие прелести вроде `haml`, `erb`,
+`halt` и т.д. доступны и им.
+
+### Not Found
+
+Когда выброшено исключение `Sinatra::NotFound`, или кодом ответа является 404,
+то будет вызван `not_found` обработчик:
+
+```ruby
+not_found do
+ 'This is nowhere to be found.'
+end
+```
+
+### Ошибки
+
+Обработчик ошибок `error` будет вызван, когда исключение выброшено из блока
+маршрута, либо из фильтра. Объект-исключение доступен как переменная
+`sinatra.error` в Rack:
+
+```ruby
+error do
+ 'Sorry there was a nasty error - ' + env['sinatra.error'].name
+end
+```
+
+Конкретные ошибки:
+
+```ruby
+error MyCustomError do
+ 'So what happened was...' + env['sinatra.error'].message
+end
+```
+
+Тогда, если это произошло:
+
+```ruby
+get '/' do
+ raise MyCustomError, 'something bad'
+end
+```
+
+То вы получите:
+
+```
+So what happened was... something bad
+```
+
+Также вы можете установить обработчик ошибок для кода состояния HTTP:
+
+```ruby
+error 403 do
+ 'Access forbidden'
+end
+
+get '/secret' do
+ 403
+end
+```
+
+Либо набора кодов:
+
+```ruby
+error 400..510 do
+ 'Boom'
+end
+```
+
+Sinatra устанавливает специальные `not_found` и `error` обработчики, когда
+приложение запущено в режиме разработки (окружение `:development`).
+
+## Rack "прослойки"
+
+Sinatra использует [Rack](http://rack.rubyforge.org/), минимальный стандартный
+интерфейс для веб-фреймворков на Ruby. Одной из самых интересных для
+разработчиков возможностей Rack является поддержка "прослоек" ("middleware") —
+компонентов, находящихся "между" сервером и вашим приложением, которые
+отслеживают и/или манипулируют HTTP запросами/ответами для предоставления
+различной функциональности.
+
+В Sinatra очень просто использовать такие "прослойки" с помощью метода `use`:
+
+```ruby
+require 'sinatra'
+require 'my_custom_middleware'
+
+use Rack::Lint
+use MyCustomMiddleware
+
+get '/hello' do
+ 'Hello World'
+end
+```
+
+Семантика `use` идентична той, что определена для
+[Rack::Builder](http://rack.rubyforge.org/doc/classes/Rack/Builder.html) DSL
+(чаще всего используется в rackup файлах). Например, метод `use` принимает как
+множественные переменные, так и блоки:
+
+```ruby
+use Rack::Auth::Basic do |username, password|
+ username == 'admin' && password == 'secret'
+end
+```
+
+Rack распространяется с различными стандартными "прослойками" для логирования,
+отладки, маршрутизации URL, аутентификации, обработки сессий. Sinatra
+использует многие из этих компонентов автоматически, основываясь на
+конфигурации, чтобы вам не приходилось подключать (`use`) их вручную.
+
+Вы можете найти полезные прослойки в
+[rack](https://github.com/rack/rack/tree/master/lib/rack),
+[rack-contrib](https://github.com/rack/rack-contrib#readme),
+[CodeRack](http://coderack.org/) или в
+[Rack wiki](https://github.com/rack/rack/wiki/List-of-Middleware).
+
+## Тестирование
+
+Тесты для Sinatra приложений могут быть написаны с помощью библиотек,
+фреймворков, поддерживающих тестирование Rack.
+[Rack::Test](http://rdoc.info/github/brynary/rack-test/master/frames)
+рекомендован:
+
+```ruby
+require 'my_sinatra_app'
+require 'test/unit'
+require 'rack/test'
+
+class MyAppTest < Test::Unit::TestCase
+ include Rack::Test::Methods
+
+ def app
+ Sinatra::Application
+ end
+
+ def test_my_default
+ get '/'
+ assert_equal 'Hello World!', last_response.body
+ end
+
+ def test_with_params
+ get '/meet', :name => 'Frank'
+ assert_equal 'Hello Frank!', last_response.body
+ end
+
+ def test_with_rack_env
+ get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
+ assert_equal "You're using Songbird!", last_response.body
+ end
+end
+```
+
+## Sinatra::Base — "прослойки", библиотеки и модульные приложения
+
+Описание своего приложения самым простейшим способом (с помощью DSL верхнего
+уровня, классический стиль) отлично работает для крохотных приложений. В таких
+случаях используется конфигурация, рассчитанная на микро-приложения
+(единственный файл приложения, `./public` и `./views` директории, логирование,
+страница информации об исключении и т.д.). Тем не менее, такой метод имеет
+множество недостатков при создании компонентов, таких как Rack middleware
+("прослоек"), Rails metal, простых библиотек с серверными компонентами,
+расширений Sinatra. И тут на помощь приходит `Sinatra::Base`:
+
+```ruby
+require 'sinatra/base'
+
+class MyApp < Sinatra::Base
+ set :sessions, true
+ set :foo, 'bar'
+
+ get '/' do
+ 'Hello world!'
+ end
+end
+```
+
+Методы, доступные `Sinatra::Base` подклассам идентичны тем, что доступны
+приложениям в DSL верхнего уровня. Большинство таких приложений могут быть
+конвертированы в `Sinatra::Base` компоненты с помощью двух модификаций:
+
+* Вы должны подключать `sinatra/base` вместо `sinatra`, иначе все методы,
+ предоставляемые Sinatra, будут импортированы в глобальное пространство
+ имен.
+* Поместите все маршруты, обработчики ошибок, фильтры и опции в подкласс
+ `Sinatra::Base`.
+
+`Sinatra::Base` — это чистый лист. Большинство опций, включая встроенный
+сервер, по умолчанию отключены. Смотрите
+[Опции и конфигурация](http://www.sinatrarb.com/configuration.html)
+для детальной информации об опциях и их поведении.
+
+### Модульные приложения против классических
+
+Вопреки всеобщему убеждению, в классическом стиле (самом простом) нет ничего
+плохого. Если этот стиль подходит вашему приложению, вы не обязаны
+переписывать его в модульное приложение.
+
+Основным недостатком классического стиля является тот факт, что у вас может
+быть только одно приложение Sinatra на один процесс Ruby. Если вы планируете
+использовать больше, переключайтесь на модульный стиль. Вы можете смело
+смешивать модульный и классический стили.
+
+Переходя с одного стиля на другой, примите во внимание следующие изменения в
+настройках:
+
+ Опция Классический Модульный
+
+ app_file файл с приложением файл с подклассом Sinatra::Base
+ run $0 == app_file false
+ logging true false
+ method_override true false
+ inline_templates true false
+ static true false
+
+### Запуск модульных приложений
+
+Есть два общепринятых способа запускать модульные приложения: запуск напрямую
+с помощью `run!`:
+
+```ruby
+# my_app.rb
+require 'sinatra/base'
+
+class MyApp < Sinatra::Base
+ # ... здесь код приложения ...
+
+ # запускаем сервер, если исполняется текущий файл
+ run! if app_file == $0
+end
+```
+
+Затем:
+
+```
+ruby my_app.rb
+```
+
+Или с помощью конфигурационного файла `config.ru`, который позволяет
+использовать любой Rack-совместимый сервер приложений.
+
+```ruby
+# config.ru
+require './my_app'
+run MyApp
+```
+
+Запускаем:
+
+```
+rackup -p 4567
+```
+
+### Запуск классических приложений с config.ru
+
+Файл приложения:
+
+```ruby
+# app.rb
+require 'sinatra'
+
+get '/' do
+ 'Hello world!'
+end
+```
+
+И соответствующий `config.ru`:
+
+```ruby
+require './app'
+run Sinatra::Application
+```
+
+### Когда использовать config.ru?
+
+Вот несколько причин, по которым вы, возможно, захотите использовать
+`config.ru`:
+
+* вы хотите разворачивать свое приложение на различных Rack-совместимых
+ серверах (Passenger, Unicorn, Heroku, ...);
+* вы хотите использовать более одного подкласса `Sinatra::Base`;
+* вы хотите использовать Sinatra только в качестве "прослойки" Rack.
+
+**Совсем необязательно переходить на использование `config.ru` лишь потому,
+что вы стали использовать модульный стиль приложения. И необязательно
+использовать модульный стиль, чтобы запускать приложение с помощью
+`config.ru`.**
+
+### Использование Sinatra в качестве "прослойки"
+
+Не только сама Sinatra может использовать "прослойки" Rack, но и любое Sinatra
+приложение само может быть добавлено к любому Rack endpoint в качестве
+"прослойки". Этим endpoint (конечной точкой) может быть другое Sinatra
+приложение, или приложение, основанное на Rack (Rails/Ramaze/Camping/...):
+
+```ruby
+require 'sinatra/base'
+
+class LoginScreen < Sinatra::Base
+ enable :sessions
+
+ get('/login') { haml :login }
+
+ post('/login') do
+ if params[:name] == 'admin' && params[:password] == 'admin'
+ session['user_name'] = params[:name]
+ else
+ redirect '/login'
+ end
+ end
+end
+
+class MyApp < Sinatra::Base
+ # "прослойка" будет запущена перед фильтрами
+ use LoginScreen
+
+ before do
+ unless session['user_name']
+ halt "Access denied, please login."
+ end
+ end
+
+ get('/') { "Hello #{session['user_name']}." }
+end
+```
+
+### Создание приложений "на лету"
+
+Иногда требуется создавать Sinatra приложения "на лету" (например, из другого
+приложения). Это возможно с помощью `Sinatra.new`:
+
+```ruby
+require 'sinatra/base'
+my_app = Sinatra.new { get('/') { "hi" } }
+my_app.run!
+```
+
+Этот метод может принимать аргументом приложение, от которого следует
+наследоваться:
+
+```ruby
+# config.ru
+require 'sinatra/base'
+
+controller = Sinatra.new do
+ enable :logging
+ helpers MyHelpers
+end
+
+map('/a') do
+ run Sinatra.new(controller) { get('/') { 'a' } }
+end
+
+map('/b') do
+ run Sinatra.new(controller) { get('/') { 'b' } }
+end
+```
+
+Это особенно полезно для тестирования расширений Sinatra и при использовании
+Sinatra внутри вашей библиотеки.
+
+Благодаря этому, использовать Sinatra как "прослойку" очень просто:
+
+```ruby
+require 'sinatra/base'
+
+use Sinatra do
+ get('/') { ... }
+end
+
+run RailsProject::Application
+```
+
+## Области видимости и привязка
+
+Текущая область видимости определяет методы и переменные, доступные в данный
+момент.
+
+### Область видимости приложения / класса
+
+Любое Sinatra приложение соответствует подклассу `Sinatra::Base`. Если вы
+используете DSL верхнего уровня (`require 'sinatra'`), то этим классом будет
+`Sinatra::Application`, иначе это будет подкласс, который вы создали вручную.
+На уровне класса вам будут доступны такие методы, как `get` или `before`, но
+вы не сможете получить доступ к объектам `request` или `session`, так как
+существует только один класс приложения для всех запросов.
+
+Опции, созданные с помощью `set`, являются методами уровня класса:
+
+```ruby
+class MyApp < Sinatra::Base
+ # Я в области видимости приложения!
+ set :foo, 42
+ foo # => 42
+
+ get '/foo' do
+ # Я больше не в области видимости приложения!
+ end
+end
+```
+
+У вас будет область видимости приложения внутри:
+
+* тела вашего класса приложения;
+* методов, определенных расширениями;
+* блока, переданного в `helpers`;
+* блоков, использованных как значения для `set`;
+* блока, переданного в `Sinatra.new`.
+
+Вы можете получить доступ к объекту области видимости (классу приложения)
+следующими способами:
+
+* через объект, переданный блокам конфигурации (`configure { |c| ... }`);
+* `settings` внутри области видимости запроса.
+
+### Область видимости запроса/экземпляра
+
+Для каждого входящего запроса будет создан новый экземпляр вашего приложения,
+и все блоки обработчика будут запущены в этом контексте. В этой области
+видимости вам доступны `request` и `session` объекты, вызовы методов
+рендеринга, такие как `erb` или `haml`. Вы можете получить доступ к области
+видимости приложения из контекста запроса, используя метод-помощник
+`settings`:
+
+```ruby
+class MyApp < Sinatra::Base
+ # Я в области видимости приложения!
+ get '/define_route/:name' do
+ # Область видимости запроса '/define_route/:name'
+ @value = 42
+
+ settings.get("/#{params[:name]}") do
+ # Область видимости запроса "/#{params[:name]}"
+ @value # => nil (другой запрос)
+ end
+
+ "Route defined!"
+ end
+end
+```
+
+У вас будет область видимости запроса в:
+
+* get/head/post/put/delete/options блоках;
+* before/after фильтрах;
+* методах-помощниках;
+* шаблонах/отображениях.
+
+### Область видимости делегирования
+
+Область видимости делегирования просто перенаправляет методы в область
+видимости класса. Однако, она не полностью ведет себя как область видимости
+класса, так как у вас нет привязки к классу. Только методы, явно помеченные
+для делегирования, будут доступны, а переменных/состояний области видимости
+класса не будет (иначе говоря, у вас будет другой `self` объект). Вы можете
+непосредственно добавить методы делегирования, используя
+`Sinatra::Delegator.delegate :method_name`.
+
+У вас будет контекст делегирования внутри:
+
+* привязки верхнего уровня, если вы сделали `require 'sinatra'`;
+* объекта, расширенного с помощью `Sinatra::Delegator`.
+
+Посмотрите сами в код: вот
+[примесь Sinatra::Delegator](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633)
+[расширяет главный объект](https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30).
+
+## Командная строка
+
+Sinatra приложения могут быть запущены напрямую:
+
+```
+ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER]
+```
+
+Опции включают:
+
+```
+-h # раздел помощи
+-p # указание порта (по умолчанию 4567)
+-o # указание хоста (по умолчанию 0.0.0.0)
+-e # указание окружения, режима (по умолчанию development)
+-s # указание rack сервера/обработчика (по умолчанию thin)
+-x # включить мьютекс-блокировку (по умолчанию выключена)
+```
+
+## Системные требования
+
+Следующие версии Ruby официально поддерживаются:
+
+
+ - Ruby 1.8.7
+ - 1.8.7 полностью поддерживается, тем не менее, если вас ничто не держит на
+ этой версии, рекомендуем обновиться до 1.9.2 или перейти на JRuby или
+ Rubinius. Поддержка 1.8.7 не будет прекращена до выхода Sinatra 2.0 и Ruby
+ 2.0, разве что в случае релиза 1.8.8 (что маловероятно). Но даже тогда,
+ возможно, поддержка не будет прекращена. Ruby 1.8.6 больше не
+ поддерживается. Если вы хотите использовать 1.8.6, откатитесь до Sinatra
+ 1.2, которая будет получать все исправления ошибок до тех пор, пока не
+ будет выпущена Sinatra 1.4.0.
+
+ - Ruby 1.9.2
+ - 1.9.2 полностью поддерживается и рекомендована к использованию.
+ Не используйте 1.9.2p0,
+ известно, что эта версия очень нестабильна при использовании Sinatra. Эта
+ версия будет поддерживаться по крайней мере до выхода Ruby 1.9.4/2.0, а
+ поддержка последней версии 1.9 будет осуществляться до тех пор, пока она
+ поддерживается командой разработчиков Ruby.
+
+ - Ruby 1.9.3
+ - 1.9.3 полностью поддерживается. Заметьте, что переход на 1.9.3 с
+ ранних версий сделает недействительными все сессии.
+
+ - Rubinius
+ - Rubinius официально поддерживается (Rubinius >= 1.2.4), всё, включая все
+ языки шаблонов, работает. Предстоящий релиз 2.0 также поддерживается.
+
+ - JRuby
+ - JRuby официально поддерживается (JRuby >= 1.6.5). Нет никаких проблем с
+ использованием альтернативных шаблонов. Тем не менее, если вы выбираете
+ JRuby, то, пожалуйста, посмотрите на JRuby Rack-серверы, так как Thin не
+ поддерживается полностью на JRuby. Поддержка расширений на C в JRuby все
+ еще экспериментальная, что на данный момент затрагивает только RDiscount,
+ Redcarpet и RedCloth.
+
+
+Мы также следим за предстоящими к выходу версиями Ruby.
+
+Следующие реализации Ruby не поддерживаются официально, но известно, что на
+них запускается Sinatra:
+
+* старые версии JRuby и Rubinius;
+* Ruby Enterprise Edition;
+* MacRuby, Maglev, IronRuby;
+* Ruby 1.9.0 и 1.9.1 (настоятельно не рекомендуются к использованию).
+
+То, что версия официально не поддерживается, означает, что, если что-то не
+работает на этой версии, а на поддерживаемой работает — это не наша проблема,
+а их.
+
+Мы также запускаем наши CI-тесты на версии Ruby, находящейся в разработке
+(предстоящей 2.0.0), и на 1.9.4, но мы не можем ничего гарантировать, так как
+они находятся в разработке. Предполагается, что 1.9.4p0 и 2.0.0p0 будут
+поддерживаться.
+
+Sinatra должна работать на любой операционной системе, в которой есть одна из
+указанных выше версий Ruby.
+
+Пока невозможно запустить Sinatra на Cardinal, SmallRuby, BlueRuby и на любой
+версии Ruby до 1.8.7.
+
+## На острие
+
+Если вы хотите использовать самый последний код Sinatra, не бойтесь запускать
+свое приложение вместе с кодом из master ветки Sinatra, она весьма стабильна.
+
+Мы также время от времени выпускаем предварительные версии, так что вы можете
+делать так:
+
+```
+gem install sinatra --pre
+```
+
+Чтобы воспользоваться некоторыми самыми последними возможностями.
+
+### С помощью Bundler
+
+Если вы хотите запускать свое приложение с последней версией Sinatra, то
+рекомендуем использовать [Bundler](http://gembundler.com/).
+
+Сначала установите Bundler, если у вас его еще нет:
+
+```
+gem install bundler
+```
+
+Затем создайте файл `Gemfile` в директории вашего проекта:
+
+```ruby
+source :rubygems
+gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
+
+# другие зависимости
+gem 'haml' # например, если используете haml
+gem 'activerecord', '~> 3.0' # может быть, вам нужен и ActiveRecord 3.x
+```
+
+Обратите внимание, вам нужно будет указывать все зависимости вашего приложения
+в этом файле. Однако, непосредственные зависимости Sinatra (Rack и Tilt)
+Bundler автоматически скачает и добавит.
+
+Теперь вы можете запускать свое приложение так:
+
+```
+bundle exec ruby myapp.rb
+```
+
+### Вручную
+
+Создайте локальный клон репозитория и запускайте свое приложение с
+`sinatra/lib` директорией в `$LOAD_PATH`:
+
+```
+cd myapp
+git clone git://github.com/sinatra/sinatra.git
+ruby -Isinatra/lib myapp.rb
+```
+
+Чтобы обновить исходники Sinatra:
+
+```
+cd myapp/sinatra
+git pull
+```
+
+### Установка глобально
+
+Вы можете самостоятельно собрать gem:
+
+```
+git clone git://github.com/sinatra/sinatra.git
+cd sinatra
+rake sinatra.gemspec
+rake install
+```
+
+Если вы устанавливаете пакеты (gem) от пользователя root, то вашим последним
+шагом должна быть команда
+
+```
+sudo rake install
+```
+
+## Версии
+
+Sinatra использует [Semantic Versioning](http://semver.org/), SemVer и
+SemVerTag.
+
+## Дальнейшее чтение
+
+* [Веб-сайт проекта](http://www.sinatrarb.com/) — Дополнительная
+ документация, новости и ссылки на другие ресурсы.
+* [Участие в проекте](http://www.sinatrarb.com/contributing) — Обнаружили
+ баг? Нужна помощь? Написали патч?
+* [Слежение за проблемами/ошибками](http://github.com/sinatra/sinatra/issues)
+* [Twitter](http://twitter.com/sinatra)
+* [Группы рассылки](http://groups.google.com/group/sinatrarb/topics)
+* [#sinatra](irc://chat.freenode.net/#sinatra) на http://freenode.net
+* [Sinatra Book](http://sinatra-book.gittr.com) учебник и сборник рецептов
+* [Sinatra Recipes](http://recipes.sinatrarb.com/) сборник рецептов
+* API документация к [последнему релизу](http://rubydoc.info/gems/sinatra)
+ или [текущему HEAD](http://rubydoc.info/github/sinatra/sinatra) на
+ http://rubydoc.info
+* [Сервер непрерывной интеграции](http://travis-ci.org/sinatra/sinatra)
diff --git a/README.ru.rdoc b/README.ru.rdoc
deleted file mode 100644
index 52340387..00000000
--- a/README.ru.rdoc
+++ /dev/null
@@ -1,2033 +0,0 @@
-= Sinatra
-Внимание: Этот документ является переводом английской версии и может быть устаревшим
-
-Sinatra — это предметно-ориентированный каркас ({DSL}[http://ru.wikipedia.org/wiki/Предметно-ориентированный_язык_программирования]) для быстрого создания функциональных
-веб-приложений на Ruby с минимумом усилий:
-
- # myapp.rb
- require 'sinatra'
-
- get '/' do
- 'Hello world!'
- end
-
-Установите gem:
-
- gem install sinatra
-
-и запустите приложение с помощью:
-
- ruby myapp.rb
-
-Оцените результат: http://localhost:4567
-
-Рекомендуется также установить Thin, сделать это можно командой: gem install thin.
-Thin — это более производительный и функциональный сервер для разработки приложений на Sinatra.
-
-== Маршруты
-
-В Sinatra маршрут — это пара: и <шаблон URL>.
-Каждый маршрут связан с блоком кода:
-
- get '/' do
- .. что-то показать ..
- end
-
- post '/' do
- .. что-то создать ..
- end
-
- put '/' do
- .. что-то заменить ..
- end
-
- patch '/' do
- .. что-то изменить ..
- end
-
- delete '/' do
- .. что-то удалить ..
- end
-
- options '/' do
- .. что-то ответить ..
- end
-
-Маршруты сверяются с запросом в порядке очередности их записи в файле приложения.
-Первый же совпавший с запросом маршрут и будет вызван.
-
-Шаблоны маршрутов могут включать в себя именованные параметры доступные
-в xэше params:
-
- get '/hello/:name' do
- # соответствует "GET /hello/foo" и "GET /hello/bar",
- # где params[:name] 'foo' или 'bar'
- "Hello #{params[:name]}!"
- end
-
-Также можно использовать именованные параметры в качестве переменных
-блока:
-
- get '/hello/:name' do |n|
- "Hello #{n}!"
- end
-
-Шаблоны маршрутов также могут включать в себя splat (или '*'
-маску, обозначающую любой символ) параметры доступные в массиве
-params[:splat]:
-
- 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
-
-Или с параметрами блока:
-
- get '/download/*.*' do |path, ext|
- [path, ext] # => ["path/to/file", "xml"]
- end
-
-Регулярные выражения в качестве шаблонов маршрутов:
-
- get %r{/hello/([\w]+)} do
- "Hello, #{params[:captures].first}!"
- end
-
-Или с параметром блока:
-
- get %r{/hello/([\w]+)} do |c|
- "Hello, #{c}!"
- end
-
-Шаблоны маршрутов могут иметь необязательные параметры:
-
- get '/posts.?:format?' do
- # соответствует "GET /posts", "GET /posts.json", "GET /posts.xml" и т.д.
- end
-
-Кстати, если вы не отключите защиту от обратного пути в директориях
-(path traversal, см. ниже), путь запроса может быть изменен до начала
-поиска подходящего маршрута.
-
-=== Условия
-
-Маршруты могут включать различные условия совпадений, например,
-клиентское приложение (user agent):
-
- 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+:
-
- 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
-
-Вы можете задать собственные условия:
-
- 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
-
-Для условия, которое принимает несколько параметров, используйте
-звездочку:
-
- 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.
-
-Таким образом, легко можно реализовать, например, поточный пример:
-
- class Stream
- def each
- 100.times { |i| yield "#{i}\n" }
- end
- end
-
- get('/') { Stream.new }
-
-Вы также можете использовать метод +stream+ (описываемый ниже), чтобы
-уменьшить количество дублируемого кода и держать логику стриминга прямо в маршруте.
-
-=== Собственные детекторы совпадений для маршрутов
-
-Как показано выше, Sinatra поставляется со встроенной поддержкой строк и
-регулярных выражений в качестве шаблонов URL. Но и это еще не все. Вы можете
-легко определить свои собственные детекторы совпадений (matchers) для маршрутов:
-
- class AllButPattern
- Match = Struct.new(:captures)
-
- def initialize(except)
- @except = except
- @captures = Match.new([])
- end
-
- def match(str)
- @captures unless @except === str
- end
- end
-
- def all_but(pattern)
- AllButPattern.new(pattern)
- end
-
- get all_but("/index") do
- # ...
- end
-
-Заметьте, что предыдущий пример возможно чересчур усложнен, потому что
-он может быть реализован так:
-
- get // do
- pass if request.path_info == "/index"
- # ...
- end
-
-Или с использованием негативного просмотра вперед:
-
- get %r{^(?!/index$)} do
- # ...
- end
-
-== Статические файлы
-
-Статические файлы отдаются из ./public директории. Вы можете
-указать другое место, используя опцию :public_folder:
-
- set :public_folder, File.dirname(__FILE__) + '/static'
-
-Учтите, что имя директории со статическими файлами не включено в URL. Например, файл
-./public/css/style.css будет доступен как
-http://example.com/css/style.css.
-
-Используйте опцию :static_cache_control (см. ниже), чтобы
-добавить заголовок Cache-Control.
-
-== Представления / Шаблоны
-
-Каждый шаблонизатор представлен своим собственным методом. Эти методы
-попросту возвращают строку:
-
- get '/' do
- erb :index
- end
-
-Отобразит views/index.erb.
-
-Вместо имени шаблона вы так же можете передавать непосредственно само
-содержимое шаблона:
-
- get '/' do
- code = "<%= Time.now %>"
- erb code
- end
-
-Эти методы принимают второй аргумент, хеш с опциями:
-
- get '/' do
- erb :index, :layout => :post
- end
-
-Отобразит views/index.erb, вложенным в
-views/post.erb (по умолчанию: views/layout.erb, если существует).
-
-Любые опции, не понимаемые Sinatra, будут переданы в шаблонизатор:
-
- get '/' do
- haml :index, :format => :html5
- end
-
-Вы также можете задавать опции для шаблонизаторов в общем:
-
- set :haml, :format => :html5
-
- get '/' do
- haml :index
- end
-
-Опции, переданные в метод, переопределяют опции, заданные с помощью
-+set+.
-
-Доступные опции:
-
-[locals]
- Список локальных переменных, передаваемых в документ.
- Например: erb "<%= foo %>", :locals => {:foo => "bar"}
-
-[default_encoding]
- Кодировка, которую следует использовать, если не удалось определить
- оригинальную. По умолчанию: settings.default_encoding.
-
-[views]
- Директория с шаблонами. По умолчанию: settings.views.
-
-[layout]
- Использовать или нет лэйаут (+true+ или +false+). Если же значение Symbol,
- то указывает, какой шаблон использовать в качестве лэйаута. Например:
- erb :index, :layout => !request.xhr?
-
-[content_type]
- Content-Type отображенного шаблона. По умолчанию: задается шаблонизатором.
-
-[scope]
- Область видимости, в которой рендерятся шаблоны. По умолчанию: экземпляр
- приложения. Если вы измените эту опцию, то переменные экземпляра и
- методы-помощники станут недоступными в ваших шаблонах.
-
-[layout_engine]
- Шаблонизатор, который следует использовать для отображения лэйаута. Полезная
- опция для шаблонизаторов, в которых нет никакой поддержки лэйаутов. По
- умолчанию: тот же шаблонизатор, что используется и для самого шаблона.
- Пример: set :rdoc, :layout_engine => :erb
-
-По умолчанию считается, что шаблоны находятся в директории ./views.
-Чтобы использовать другую директорию с шаблонами:
-
- set :views, settings.root + '/templates'
-
-Важное замечание: вы всегда должны ссылаться на шаблоны с помощью символов
-(Symbol), даже когда они в поддиректории (в этом случае используйте
-:'subdir/template'). Вы должны использовать символы, потому что
-иначе шаблонизаторы попросту отображают любые строки, переданные им.
-
-=== Доступные шаблонизаторы
-
-Некоторые языки шаблонов имеют несколько реализаций. Чтобы указать, какую
-реализацию использовать, вам следует просто подключить нужную
-библиотеку:
-
- require 'rdiscount' # или require 'bluecloth'
- get('/') { markdown :index }
-
-=== Haml шаблоны
-
-Зависимости:: {haml}[http://haml.info/]
-Расширения файлов:: .haml
-Пример:: haml :index, :format => :html5
-
-=== Erb шаблоны
-
-Зависимости:: {erubis}[http://www.kuwata-lab.com/erubis/] или erb (включен в Ruby)
-Расширения файлов:: .erb, .rhtml или .erubis (только Erubis)
-Пример:: erb :index
-
-=== Builder шаблоны
-
-Зависимости:: {builder}[http://builder.rubyforge.org/]
-Расширения файлов:: .builder
-Пример:: builder { |xml| xml.em "hi" }
-
-Блок также используется и для встроенных шаблонов (см. пример).
-
-=== Nokogiri шаблоны
-
-Зависимости:: {nokogiri}[http://nokogiri.org/]
-Расширения файлов:: .nokogiri
-Пример:: nokogiri { |xml| xml.em "hi" }
-
-Блок также используется и для встроенных шаблонов (см. пример).
-
-=== Sass шаблоны
-
-Зависимости:: {sass}[http://sass-lang.com/]
-Расширения файлов:: .sass
-Пример:: sass :stylesheet, :style => :expanded
-
-=== SCSS шаблоны
-
-Зависимости:: {sass}[http://sass-lang.com/]
-Расширения файлов:: .scss
-Пример:: scss :stylesheet, :style => :expanded
-
-=== Less шаблоны
-
-Зависимости:: {less}[http://www.lesscss.org/]
-Расширения файлов:: .less
-Пример:: less :stylesheet
-
-=== Liquid шаблоны
-
-Зависимости:: {liquid}[http://www.liquidmarkup.org/]
-Расширения файлов:: .liquid
-Пример:: liquid :index, :locals => { :key => 'value' }
-
-Так как в Liquid шаблонах невозможно вызывать методы из Ruby (кроме yield), то
-вы почти всегда будете передавать в шаблон локальные переменные.
-
-=== Markdown шаблоны
-
-Зависимости:: {rdiscount}[https://github.com/rtomayko/rdiscount], {redcarpet}[https://github.com/vmg/redcarpet], {bluecloth}[http://deveiate.org/projects/BlueCloth], {kramdown}[http://kramdown.rubyforge.org/] или {maruku}[http://maruku.rubyforge.org/]
-Расширения файлов:: .markdown, .mkd and .md
-Пример:: markdown :index, :layout_engine => :erb
-
-В Markdown невозможно вызывать методы или передавать локальные переменные.
-Следовательно, вам, скорее всего, придется использовать этот шаблон совместно
-с другим шаблонизатором:
-
- erb :overview, :locals => { :text => markdown(:introduction) }
-
-Заметьте, что вы можете вызывать метод +markdown+ из других шаблонов:
-
- %h1 Hello From Haml!
- %p= markdown(:greetings)
-
-Вы не можете вызывать Ruby из Markdown, соответственно, вы не можете
-использовать лэйауты на Markdown. Тем не менее, есть возможность использовать
-один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
-опции :layout_engine.
-
-=== Textile шаблоны
-
-Зависимости:: {RedCloth}[http://redcloth.org/]
-Расширения файлов:: .textile
-Пример:: textile :index, :layout_engine => :erb
-
-В Textile невозможно вызывать методы или передавать локальные переменные.
-Следовательно, вам, скорее всего, придется использовать этот шаблон совместно с
-другим шаблонизатором:
-
- erb :overview, :locals => { :text => textile(:introduction) }
-
-Заметьте, что вы можете вызывать метод +textile+ из других шаблонов:
-
- %h1 Hello From Haml!
- %p= textile(:greetings)
-
-Вы не можете вызывать Ruby из Textile, соответственно, вы не можете
-использовать лэйауты на Textile. Тем не менее, есть возможность использовать
-один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
-опции :layout_engine.
-
-=== RDoc шаблоны
-
-Зависимости:: {rdoc}[http://rdoc.rubyforge.org/]
-Расширения файлов:: .rdoc
-Пример:: rdoc :README, :layout_engine => :erb
-
-В RDoc невозможно вызывать методы или передавать локальные переменные.
-Следовательно, вам, скорее всего, придется использовать этот шаблон совместно с
-другим шаблонизатором:
-
- erb :overview, :locals => { :text => rdoc(:introduction) }
-
-Заметьте, что вы можете вызывать метод +rdoc+ из других шаблонов:
-
- %h1 Hello From Haml!
- %p= rdoc(:greetings)
-
-Вы не можете вызывать Ruby из RDoc, соответственно, вы не можете
-использовать лэйауты на RDoc. Тем не менее, есть возможность использовать
-один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
-опции :layout_engine.
-
-=== Radius шаблоны
-
-Зависимости:: {radius}[http://radius.rubyforge.org/]
-Расширения файлов:: .radius
-Пример:: radius :index, :locals => { :key => 'value' }
-
-Так как в Radius шаблонах невозможно вызывать методы из Ruby напрямую, то
-вы почти всегда будете передавать в шаблон локальные переменные.
-
-=== Markaby шаблоны
-
-Зависимости:: {markaby}[http://markaby.github.com/]
-Расширения файлов:: .mab
-Пример:: markaby { h1 "Welcome!" }
-
-Блок также используется и для встроенных шаблонов (см. пример).
-
-=== RABL шаблоны
-
-Зависимости:: {rabl}[https://github.com/nesquena/rabl]
-Расширения файлов:: .rabl
-Пример:: rabl :index
-
-=== Slim шаблоны
-
-Зависимости:: {slim}[http://slim-lang.com/]
-Расширения файлов:: .slim
-Пример:: slim :index
-
-=== Creole шаблоны
-
-Зависимости:: {creole}[https://github.com/minad/creole]
-Расширения файлов:: .creole
-Пример:: creole :wiki, :layout_engine => :erb
-
-В Creole невозможно вызывать методы или передавать локальные переменные.
-Следовательно, вам, скорее всего, придется использовать этот шаблон совместно с
-другим шаблонизатором:
-
- erb :overview, :locals => { :text => creole(:introduction) }
-
-Заметьте, что вы можете вызывать метод +creole+ из других шаблонов:
-
- %h1 Hello From Haml!
- %p= creole(:greetings)
-
-Вы не можете вызывать Ruby из Creole, соответственно, вы не можете
-использовать лэйауты на Creole. Тем не менее, есть возможность использовать
-один шаблонизатор для отображения шаблона, а другой для лэйаута с помощью
-опции :layout_engine.
-
-=== CoffeeScript шаблоны
-
-Зависимости:: {coffee-script}[https://github.com/josh/ruby-coffee-script] и {способ запускать javascript}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
-Расширения файлов:: .coffee
-Пример:: coffee :index
-
-=== Yajl шаблоны
-
-Зависимости:: {yajl-ruby}[https://github.com/brianmario/yajl-ruby]
-Расширения файлов:: .yajl
-Пример:: yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource'
-
-Содержимое шаблона интерпретируется как код на Ruby, а результирующая переменная json затем
-конвертируется с помощью #to_json.
-
- json = { :foo => 'bar' }
- json[:baz] = key
-
-Опции :callback и :variable используются для "декорирования" итогового объекта.
-
- var resource = {"foo":"bar","baz":"qux"}; present(resource);
-
-=== WLang шаблоны
-
-Зависимости:: {wlang}[https://github.com/blambeau/wlang/]
-Расширения файлов:: .wlang
-Пример:: wlang :index, :locals => { :key => 'value' }
-
-Так как в WLang шаблонах невозможно вызывать методы из Ruby напрямую (за исключением +yield+), то
-вы почти всегда будете передавать в шаблон локальные переменные.
-
-=== Встроенные шаблоны
-
- get '/' do
- haml '%div.title Hello World'
- end
-
-Отобразит встроенный шаблон, переданный строкой.
-
-=== Доступ к переменным в шаблонах
-
-Шаблоны интерпретируются в том же контексте, что и обработчики маршрутов. Переменные экземпляра,
-установленные в процессе обработки маршрутов, будут доступны напрямую в шаблонах:
-
- get '/:id' do
- @foo = Foo.find(params[:id])
- haml '%h1= @foo.name'
- end
-
-Либо установите их через хеш локальных переменных:
-
- get '/:id' do
- foo = Foo.find(params[:id])
- haml '%h1= bar.name', :locals => { :bar => foo }
- end
-
-Это обычный подход, когда шаблоны рендерятся как части других шаблонов.
-
-=== Вложенные шаблоны
-
-Шаблоны также могут быть определены в конце исходного файла:
-
- require 'sinatra'
-
- get '/' do
- haml :index
- end
-
- __END__
-
- @@ layout
- %html
- = yield
-
- @@ index
- %div.title Hello world.
-
-Заметьте: вложенные шаблоны, определенные в исходном файле, который подключила Sinatra, будут
-загружены автоматически. Вызовите enable :inline_templates напрямую, если
-используете вложенные шаблоны в других файлах.
-
-=== Именованные шаблоны
-
-Шаблоны также могут быть определены при помощи template метода:
-
- 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:
-
- get '/' do
- haml :index, :layout => !request.xhr?
- end
-
-=== Привязка файловых расширений
-
-Чтобы связать расширение файла с движком рендеринга, используйте
-Tilt.register. Например, если вы хотите использовать расширение +tt+
-для шаблонов Textile:
-
- Tilt.register :tt, Tilt[:textile]
-
-=== Добавление собственного движка рендеринга
-
-Сначала зарегистрируйте свой движок в Tilt, а затем создайте метод,
-отвечающий за рендеринг:
-
- 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+-фильтры выполняются перед каждым запросом в том же контексте, что и маршруты. Фильтры могут изменять
-как запрос, так и ответ на него. Переменные экземпляра, установленные в фильтрах, доступны в маршрутах и шаблонах:
-
- before do
- @note = 'Hi!'
- request.path_info = '/foo/bar/baz'
- end
-
- get '/foo/*' do
- @note #=> 'Hi!'
- params[:splat] #=> 'bar/baz'
- end
-
-+after+-фильтры выполняются после каждого запроса в том же контексте, что и пути. Фильтры могут изменять
-как запрос, так и ответ на него. Переменные экземпляра, установленные в +before+-фильтрах и маршрутах,
-будут доступны в +after+-фильтрах:
-
- after do
- puts response.status
- end
-
-Заметьте: если вы используете метод +body+, а не просто возвращаете строку из
-маршрута, то тело ответа не будет доступно в +after+-фильтрах, так как оно будет сгенерировано позднее.
-
-Фильтры могут использовать шаблоны URL и будут интерпретированы только если путь запроса совпадет с этим шаблоном:
-
- before '/protected/*' do
- authenticate!
- end
-
- after '/create/:slug' do |slug|
- session[:last_slug] = slug
- end
-
-Как и маршруты, фильтры могут использовать условия:
-
- before :agent => /Songbird/ do
- # ...
- end
-
- after '/blog/*', :host_name => 'example.com' do
- # ...
- end
-
-== Методы-помощники
-
-Используйте метод helpers, чтобы определить методы-помощники, которые
-в дальнейшем можно будет использовать в обработчиках маршрутов и шаблонах:
-
- helpers do
- def bar(name)
- "#{name}bar"
- end
- end
-
- get '/:name' do
- bar(params[:name])
- end
-
-Также методы-помощники могут быть заданы в отдельных модулях:
-
- module FooUtils
- def foo(name) "#{name}foo" end
- end
-
- module BarUtils
- def bar(name) "#{name}bar" end
- end
-
- helpers FooUtils, BarUtils
-
-Эффект равносилен включению модулей в класс приложения.
-
-=== Использование сессий
-
-Сессия используется, чтобы сохранять состояние между запросами. Если эта опция
-включена, то у вас будет один хеш сессии на одну пользовательскую сессию:
-
- enable :sessions
-
- get '/' do
- "value = " << session[:value].inspect
- end
-
- get '/:value' do
- session[:value] = params[:value]
- end
-
-Заметьте, что при использовании enable :sessions все данные
-сохраняются в куках (cookies). Это может быть не совсем то, что вы хотите
-(например, сохранение больших объемов данных увеличит ваш трафик). В таком случае
-вы можете использовать альтернативную Rack "прослойку" (middleware), реализующую
-механизм сессий. Для этого *не надо* вызывать enable :sessions,
-вместо этого следует подключить ее так же, как и любую другую "прослойку":
-
- use Rack::Session::Pool, :expire_after => 2592000
-
- get '/' do
- "value = " << session[:value].inspect
- end
-
- get '/:value' do
- session[:value] = params[:value]
- end
-
-Для повышения безопасности данные сессии в куках подписываются секретным
-ключом. Секретный ключ генерируется Sinatra. Тем не менее, так как этот
-ключ будет меняться с каждым запуском приложения, вы, возможно, захотите
-установить ключ вручную, чтобы у всех экземпляров вашего приложения
-был один и тот же ключ:
-
- set :session_secret, 'super secret'
-
-Если вы хотите больше настроек для сессий, вы можете задать их, передав хеш опций в параметр +sessions+:
-
- set :sessions, :domain => 'foo.com'
-
-=== Прерывание
-
-Чтобы незамедлительно прервать обработку запроса внутри фильтра или маршрута, используйте:
-
- halt
-
-Можно также указать статус при прерывании:
-
- halt 410
-
-Тело:
-
- halt 'this will be the body'
-
-И то, и другое:
-
- halt 401, 'go away!'
-
-Можно указать заголовки:
-
- halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
-
-И, конечно, можно использовать шаблоны с +halt+:
-
- halt erb(:error)
-
-=== Передача
-
-Маршрут может передать обработку запроса следующему совпадающему маршруту, используя pass:
-
- get '/guess/:who' do
- pass unless params[:who] == 'Frank'
- 'You got me!'
- end
-
- get '/guess/*' do
- 'You missed!'
- end
-
-Блок маршрута сразу же прерывается, и контроль переходит к следующему совпадающему маршруту.
-Если соответствующий маршрут не найден, то ответом на запрос будет 404.
-
-=== Вызов другого маршрута
-
-Иногда +pass+ не подходит, например, если вы хотите получить результат
-вызова другого обработчика маршрута. В таком случае просто используйте +call+:
-
- 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+,
-то вы можете использовать его и в дальнейшем, чтобы получить доступ к телу ответа.
-
- get '/foo' do
- body "bar"
- end
-
- after do
- puts body
- end
-
-Также можно передать блок в метод +body+, который затем будет вызван
-обработчиком Rack (такой подход может быть использован для реализации поточного
-ответа, см. "Возвращаемые значения").
-
-Аналогично вы можете установить код ответа и его заголовки:
-
- 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+
-вместо написания собственных "оберток".
-
- 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.
-Другие же сервера все равно будут закрывать поток:
-
- set :server, :thin
- connections = []
-
- get '/' do
- # держать все потоки открытыми
- stream(:keep_open) { |out| connections << out }
- end
-
- post '/' do
- # написать во все открытые потоки
- connections.each { |out| out << params[:message] << "\n" }
- "message sent"
- end
-
-=== Логирование
-
-В области видимости запроса метод +logger+ предоставляет доступ к экземпляру +Logger+:
-
- get '/' do
- logger.info "loading data"
- # ...
- end
-
-Этот логер автоматически учитывает ваши настройки логирования в Rack. Если
-логирование выключено, то этот метод вернет пустой (dummy) объект, поэтому вы можете
-смело использовать его в маршрутах и фильтрах.
-
-Заметьте, что логирование включено по умолчанию только для Sinatra::Application,
-а если ваше приложение -- подкласс Sinatra::Base, то вы, наверное, захотите включить
-его вручную:
-
- 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+ для их регистрации по расширению файла:
-
- configure do
- mime_type :foo, 'text/foo'
- end
-
-Вы также можете использовать это в +content_type+ методе-помощнике:
-
- get '/' do
- content_type :foo
- "foo foo foo"
- end
-
-=== Генерирование URL
-
-Чтобы сформировать URL вам следует использовать метод +url+, например, в Haml:
-
- %a{:href => url('/foo')} foo
-
-Этот метод учитывает обратные прокси и маршрутизаторы Rack, если они присутствуют.
-
-Наряду с +url+ вы можете использовать +to+ (смотрите пример ниже).
-
-=== Перенаправление (редирект)
-
-Вы можете перенаправить браузер пользователя с помощью метода +redirect+:
-
- get '/foo' do
- redirect to('/bar')
- end
-
-Любые дополнительные параметры используются по аналогии с аргументами метода +halt+:
-
- redirect to('/bar'), 303
- redirect 'http://google.com', 'wrong place, buddy'
-
-Вы также можете перенаправить пользователя обратно, на страницу с которой он пришел,
-с помощью redirect back:
-
- get '/foo' do
- "do something"
- end
-
- get '/bar' do
- do_something
- redirect back
- end
-
-Чтобы передать какие-либо параметры вместе с перенаправлением, либо добавьте их в строку запроса:
-
- redirect to('/bar?sum=42')
-
-либо используйте сессию:
-
- enable :sessions
-
- get '/foo' do
- session[:secret] = 'foo'
- redirect to('/bar')
- end
-
- get '/bar' do
- session[:secret]
- end
-
-=== Управление кэшированием
-
-Установка корректных заголовков — основа правильного HTTP кэширования.
-
-Вы можете легко выставить заголовок Cache-Control таким образом:
-
- get '/' do
- cache_control :public
- "cache it!"
- end
-
-Совет: задавайте кэширование в +before+-фильтре:
-
- before do
- cache_control :public, :must_revalidate, :max_age => 60
- end
-
-Если вы используете метод +expires+ для задания соответствующего заголовка,
-то Cache-Control будет выставлен автоматически:
-
- before do
- expires 500, :public, :must_revalidate
- end
-
-Чтобы как следует использовать кэширование, вам следует подумать об использовании
-+etag+ или +last_modified+. Рекомендуется использовать эти методы-помощники *до*
-выполнения ресурсоемких вычислений, так как они немедленно отправят ответ клиенту,
-если текущая версия уже есть в их кэше:
-
- 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]:
-
- etag @article.sha1, :weak
-
-Эти методы-помощники не станут ничего кэшировать для вас, но они дадут
-необходимую информацию для вашего кэша. Если вы ищите легкое решение для
-кэширования, попробуйте {rack-cache}[https://github.com/rtomayko/rack-cache]:
-
- 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:
-
- get '/create' do
- etag '', :new_resource => true
- Article.create
- erb :new_article
- end
-
-Если вы хотите использовать weak ETag, задайте опцию :kind:
-
- etag '', :new_resource => true, :kind => :weak
-
-=== Отправка файлов
-
-Для отправки файлов пользователю вы можете использовать метод send_file:
-
- get '/' do
- send_file 'foo.png'
- end
-
-Этот метод имеет несколько опций:
-
- send_file 'foo.png', :type => :jpg
-
-Возможные опции:
-
-[filename]
- имя файла, по умолчанию: реальное имя файла.
-
-[last_modified]
- значение для заголовка Last-Modified, по умолчанию: mtime (время изменения) файла.
-
-[type]
- тип файла, по умолчанию: предполагается по расширению файла.
-
-[disposition]
- используется для заголовка Content-Disposition, возможные значения:
- +nil+ (по умолчанию), :attachment и :inline.
-
-[length]
- значения для заголовка Content-Length, по умолчанию: размер файла.
-
-[status]
- Код ответа. Полезно, когда отдается статический файл в качестве страницы
- с сообщением об ошибке.
-
-Этот метод будет использовать возможности Rack сервера для отправки файлов, если они
-доступны, а в противном случае, будет напрямую отдавать файл из Ruby процесса.
-Метод send_file также обеспечивает автоматическую обработку частичных (range)
-запросов с помощью Sinatra.
-
-=== Доступ к объекту запроса
-
-Объект входящего запроса доступен на уровне обработки запроса (в фильтрах, маршрутах,
-обработчиках ошибок) с помощью request метода:
-
- # приложение запущено на 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 доступны для изменения:
-
- before { request.path_info = "/" }
-
- get "/" do
- "all requests end up here"
- end
-
-request.body является IO или StringIO объектом:
-
- post "/api" do
- request.body.rewind # в случае, если кто-то уже прочитал тело запроса
- data = JSON.parse request.body.read
- "Hello #{data['name']}!"
- end
-
-=== Вложения
-
-Вы можете использовать метод +attachment+, чтобы сказать браузеру, что ответ
-сервера должен быть сохранен на диск, а не отображен:
-
- get '/' do
- attachment
- "store it!"
- end
-
-Вы также можете указать имя файла:
-
- get '/' do
- attachment "info.txt"
- "store it!"
- end
-
-=== Работа с временем и датами
-
-Sinatra предлагает метод-помощник +time_for+, который из заданного значения
-создает объект Time. Он также может конвертировать +DateTime+, +Date+ и
-подобные классы:
-
- get '/' do
- pass if Time.now > time_for('Dec 23, 2012')
- "still time"
- end
-
-Этот метод используется внутри Sinatra методами +expires+, +last_modified+ и им
-подобными. Поэтому вы легко можете расширить функционал этих методов,
-переопределив +time_for+ в своем приложении:
-
- 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
- end
- end
- end
-
- get '/' do
- last_modified :yesterday
- expires :tomorrow
- "hello"
- end
-
-=== Поиск шаблонов
-
-Для поиска шаблонов и их последующего рендеринга используется метод find_template:
-
- find_template settings.views, 'foo', Tilt[:haml] do |file|
- puts "could be #{file}"
- end
-
-Это не слишком полезный пример. Зато полезен тот факт, что вы можете переопределить
-этот метод, чтобы использовать свой собственный механизм поиска. Например, если вы
-хотите, чтобы можно было использовать несколько директорий с шаблонами:
-
- set :views, ['views', 'templates']
-
- helpers do
- def find_template(views, name, engine, &block)
- Array(views).each { |v| super(v, name, engine, &block) }
- end
- end
-
-Другой пример, в котором используются разные директории для движков рендеринга:
-
- 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
-
-Вы можете легко вынести этот код в расширение и поделиться им с остальными!
-
-Заметьте, что find_template не проверяет, существует ли файл на самом деле,
-а вызывает заданный блок для всех возможных путей. Дело тут не в производительности,
-дело в том, что +render+ вызовет +break+, как только файл не будет найден.
-Содержимое и местонахождение шаблонов будет закэшировано, если приложение запущено не
-в режиме разработки (set :environment, :development). Вы должны помнить об этих нюансах,
-если пишите по-настоящему "сумасшедший" метод.
-
-== Конфигурация
-
-Этот блок исполняется один раз при старте в любом окружении, режиме (environment):
-
- configure do
- # задание одной опции
- set :option, 'value'
-
- # устанавливаем несколько опций
- set :a => 1, :b => 2
-
- # то же самое, что и `set :option, true`
- enable :option
-
- # то же самое, что и `set :option, false`
- disable :option
-
- # у вас могут быть "динамические" опции с блоками
- set(:css_dir) { File.join(views, 'css') }
- end
-
-Будет запущено, когда окружение (RACK_ENV переменная) :production:
-
- configure :production do
- ...
- end
-
-Будет запущено, когда окружение :production или :test:
-
- configure :production, :test do
- ...
- end
-
-Вы можете получить доступ к этим опциям с помощью settings:
-
- configure do
- set :foo, 'bar'
- end
-
- get '/' do
- settings.foo? # => true
- settings.foo # => 'bar'
- ...
- end
-
-=== Настройка защиты от атак
-
-Sinatra использует
-{Rack::Protection}[https://github.com/rkh/rack-protection#readme] для
-защиты приложения от простых атак. Вы можете легко выключить эту
-защиту (что сделает ваше приложение чрезвычайно уязвимым):
-
- disable :protection
-
-Чтобы пропустить какой-либо уровень защиты, передайте хеш опций
-в параметр +protection+:
-
- set :protection, :except => :path_traversal
-
-Вы также можете отключить сразу несколько уровней защиты:
-
- set :protection, :except => [:path_traversal, :session_hijacking]
-
-=== Доступные настройки
-
-[absolute_redirects] если отключено, то Sinatra будет позволять использование
- относительных перенаправлений, но при этом перестанет
- соответствовать RFC 2616 (HTTP 1.1), который разрешает только
- абсолютные перенаправления.
-
- Включайте эту опцию, если ваше приложение работает за обратным прокси,
- который настроен не совсем корректно. Обратите внимание, метод +url+
- все равно будет генерировать абсолютные URL, если вы не передадите
- +false+ вторым аргументом.
-
- Отключено по умолчанию.
-
-[add_charsets] mime-типы, к которым метод content_type будет автоматически
- добавлять информацию о кодировке.
-
- Вам следует добавлять значения к этой опции вместо ее переопределения:
-
- settings.add_charsets << "application/foobar"
-
-[app_file] путь к главному файлу приложения, используется для нахождения корневой
- директории проекта, директорий с шаблонами и статическими файлами,
- вложенных шаблонов.
-
-[bind] используемый IP-адрес (по умолчанию: 0.0.0.0). Используется только
- встроенным сервером.
-
-[default_encoding] кодировка, если неизвестна (по умолчанию: "utf-8").
-
-[dump_errors] отображать ошибки в логе.
-
-[environment] текущее окружение, по умолчанию, значение ENV['RACK_ENV']
- или "development", если ENV['RACK_ENV'] не доступна.
-
-[logging] использовать логер.
-
-[lock] создает блокировку для каждого запроса, которая гарантирует обработку
- только одного запроса в текущий момент времени в Ruby процессе.
-
- Включайте, если ваше приложение не потоко-безопасно (thread-safe).
- Отключено по умолчанию.
-
-[method_override] использовать "магический" параметр _method, чтобы позволить
- использование PUT/DELETE форм в браузерах, которые не поддерживают
- эти методы.
-
-[port] порт, на котором будет работать сервер. Используется только
- встроенным сервером.
-
-[prefixed_redirects] добавлять или нет параметр request.script_name к редиректам,
- если не задан абсолютный путь. Таким образом, redirect '/foo'
- будет вести себя как redirect to('/foo'). Отключено по умолчанию.
-
-[protection] включена или нет защита от атак. Смотрите секцию выше.
-
-[public_folder] путь к директории, откуда будут раздаваться статические файлы.
- Используется только, если включена раздача статических файлов
- (см. опцию static ниже).
-
-[reload_templates] перезагружать или нет шаблоны на каждый запрос.
- Включено в режиме разработки.
-
-[root] путь к корневой директории проекта.
-
-[raise_errors] выбрасывать исключения (будет останавливать приложение). По умолчанию
- включено только в окружении test.
-
-[run] если включено, Sinatra будет самостоятельно запускать веб-сервер.
- Не включайте, если используете rackup или аналогичные средства.
-
-[running] работает ли сейчас встроенный сервер?
- Не меняйте эту опцию!
-
-[server] сервер или список серверов, которые следует использовать в качестве
- встроенного сервера. По умолчанию: ['thin', 'mongrel', 'webrick'],
- порядок задает приоритет.
-
-[sessions] включить сессии на основе кук (cookie) на базе
- Rack::Session::Cookie. Смотрите секцию "Использование сессий"
- выше.
-
-[show_exceptions] показывать исключения/стек вызовов (stack trace) в браузере.
- По умолчанию включено только в окружении development.
- Может быть установлено в :after_handler для запуска специфичной
- для приложения обработки ошибок, прежде чем показывать трассировку
- стека в браузере.
-
-[static] должна ли Sinatra осуществлять раздачу статических файлов.
- Отключите, когда используете какой-либо веб-сервер для этой цели.
- Отключение значительно улучшит производительность приложения.
- По умолчанию включено в классических и отключено в модульных
- приложениях.
-
-[static_cache_control] когда Sinatra отдает статические файлы, используйте эту опцию,
- чтобы добавить им заголовок Cache-Control. Для этого
- используется метод-помощник +cache_control+. По умолчанию отключено.
- Используйте массив, когда надо задать несколько значений:
- set :static_cache_control, [:public, :max_age => 300]
-
-[threaded] если включено, то Thin будет использовать
- EventMachine.defer для обработки запросов.
-
-[views] путь к директории с шаблонами.
-
-== Режим, окружение
-
-Есть 3 предопределенных режима, окружения: "development",
-"production" и "test". Режим может быть
-задан через переменную окружения +RACK_ENV+. Значение по умолчанию
-— "development". В этом режиме работы, все шаблоны
-перезагружаются между запросами. А также задаются специальные
-обработчики not_found и error, чтобы вы могли
-увидеть стек вызовов. В окружениях "production" и
-"test" шаблоны по умолчанию кэшируются.
-
-Для запуска приложения в определенном окружении, используйте
-ключ -e
-
- ruby my_app.rb -e [ENVIRONMENT]
-
-Вы можете использовать предопределенные методы +development?+, +test?+
-и +production?, чтобы определить текущее окружение.
-
-== Обработка ошибок
-
-Обработчики ошибок исполняются в том же контексте, что и маршруты, и +before+-фильтры, а это означает, что всякие
-прелести вроде haml, erb, halt и т.д. доступны и им.
-
-=== Not Found
-
-Когда выброшено исключение Sinatra::NotFound, или кодом ответа является 404,
-то будет вызван not_found обработчик:
-
- not_found do
- 'This is nowhere to be found.'
- end
-
-=== Ошибки
-
-Обработчик ошибок +error+ будет вызван, когда исключение выброшено из блока маршрута, либо из фильтра.
-Объект-исключение доступен как переменная sinatra.error в Rack:
-
- error do
- 'Sorry there was a nasty error - ' + env['sinatra.error'].name
- end
-
-Частные ошибки:
-
- error MyCustomError do
- 'So what happened was...' + env['sinatra.error'].message
- end
-
-Тогда, если это произошло:
-
- get '/' do
- raise MyCustomError, 'something bad'
- end
-
-То вы получите:
-
- So what happened was... something bad
-
-Также вы можете установить обработчик ошибок для кода состояния HTTP:
-
- error 403 do
- 'Access forbidden'
- end
-
- get '/secret' do
- 403
- end
-
-Либо набора кодов:
-
- error 400..510 do
- 'Boom'
- end
-
-Sinatra устанавливает специальные not_found и error обработчики, когда приложение запущено в режиме
-разработки (окружение :development).
-
-== Rack "прослойки"
-
-Sinatra использует Rack[http://rack.rubyforge.org/], минимальный стандартный
-интерфейс для веб-фреймворков на Ruby. Одной из самых интересных для разработчиков возможностей Rack
-является поддержка "прослоек" ("middleware") — компонентов,
-находящихся "между" сервером и вашим приложением, которые отслеживают и/или манипулируют
-HTTP запросами/ответами для предоставления различной функциональности.
-
-В Sinatra очень просто использовать такие "прослойки" с помощью метода +use+:
-
- require 'sinatra'
- require 'my_custom_middleware'
-
- use Rack::Lint
- use MyCustomMiddleware
-
- get '/hello' do
- 'Hello World'
- end
-
-Семантика +use+ идентична той, что определена для
-Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSL
-(чаще всего используется в rackup файлах). Например, метод +use+ принимает
-как множественные переменные, так и блоки:
-
- use Rack::Auth::Basic do |username, password|
- username == 'admin' && password == 'secret'
- end
-
-Rack распространяется с различными стандартными "прослойками"
-для логирования, отладки, маршрутизации URL, аутентификации, обработки сессий. Sinatra использует
-многие из этих компонентов автоматически, основываясь на конфигурации, чтобы вам не приходилось
-подключать (+use+) их вручную.
-
-Вы можете найти полезные прослойки в
-{rack}[https://github.com/rack/rack/tree/master/lib/rack],
-{rack-contrib}[https://github.com/rack/rack-contrib#readme],
-{CodeRack}[http://coderack.org/] или в
-{Rack wiki}[https://github.com/rack/rack/wiki/List-of-Middleware].
-
-== Тестирование
-
-Тесты для Sinatra приложений могут быть написаны с помощью библиотек, фреймворков, поддерживающих
-тестирование Rack. {Rack::Test}[http://rdoc.info/github/brynary/rack-test/master/frames] рекомендован:
-
- require 'my_sinatra_app'
- require 'test/unit'
- require 'rack/test'
-
- class MyAppTest < Test::Unit::TestCase
- include Rack::Test::Methods
-
- def app
- Sinatra::Application
- end
-
- def test_my_default
- get '/'
- assert_equal 'Hello World!', last_response.body
- end
-
- def test_with_params
- get '/meet', :name => 'Frank'
- assert_equal 'Hello Frank!', last_response.body
- end
-
- def test_with_rack_env
- get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
- assert_equal "You're using Songbird!", last_response.body
- end
- end
-
-== Sinatra::Base — "прослойки", библиотеки и модульные приложения
-
-Описание своего приложения самым простейшим способом (с помощью DSL верхнего уровня,
-классический стиль) отлично работает для крохотных приложений. В таких случаях
-используется конфигурация, рассчитанная на микро-приложения
-(единственный файл приложения, ./public и ./views директории,
-логирование, страница информации об исключении и т.д.). Тем не менее,
-такой метод имеет множество недостатков при создании компонентов, таких как
-Rack middleware ("прослоек"), Rails metal, простых библиотек с серверными компонентами,
-расширений Sinatra. И тут на помощь приходит Sinatra::Base:
-
- require 'sinatra/base'
-
- class MyApp < Sinatra::Base
- set :sessions, true
- set :foo, 'bar'
-
- get '/' do
- 'Hello world!'
- end
- end
-
-Методы, доступные Sinatra::Base подклассам идентичны тем, что доступны
-приложениям в DSL верхнего уровня. Большинство таких приложений могут быть
-конвертированы в Sinatra::Base компоненты с помощью двух модификаций:
-
-* Вы должны подключать sinatra/base вместо +sinatra+,
- иначе все методы, предоставляемые Sinatra, будут импортированы в глобальное пространство имен.
-* Поместите все маршруты, обработчики ошибок, фильтры и опции в подкласс Sinatra::Base.
-
-Sinatra::Base — это чистый лист. Большинство опций, включая встроенный сервер, по умолчанию отключены.
-Смотрите {Опции и конфигурация}[http://www.sinatrarb.com/configuration.html] для детальной информации
-об опциях и их поведении.
-
-=== Модульные приложения против классических
-
-Вопреки всеобщему убеждению, в классическом стиле (самом простом) нет ничего плохого.
-Если этот стиль подходит вашему приложению, вы не обязаны переписывать его в модульное
-приложение.
-
-Основным недостатком классического стиля является тот факт, что у вас может
-быть только одно приложение Sinatra на один процесс Ruby. Если вы планируете
-использовать больше, переключайтесь на модульный стиль. Вы можете смело
-смешивать модульный и классический стили.
-
-Переходя с одного стиля на другой, примите во внимание следующие изменения в настройках:
-
- Опция Классический Модульный
-
- app_file файл с приложением файл с подклассом Sinatra::Base
- run $0 == app_file false
- logging true false
- method_override true false
- inline_templates true false
- static true false
-
-
-=== Запуск модульных приложений
-
-Есть два общепринятых способа запускать модульные приложения: запуск напрямую с помощью run!:
-
- # my_app.rb
- require 'sinatra/base'
-
- class MyApp < Sinatra::Base
- # ... здесь код приложения ...
-
- # запускаем сервер, если исполняется текущий файл
- run! if app_file == $0
- end
-
-И запускаем с помощью:
-
- ruby my_app.rb
-
-Или с помощью конфигурационного файла config.ru, который позволяет использовать любой
-Rack-совместимый сервер приложений.
-
- # config.ru
- require './my_app'
- run MyApp
-
-Запускаем:
-
- rackup -p 4567
-
-=== Запуск классических приложений с config.ru
-
-Файл приложения:
-
- # app.rb
- require 'sinatra'
-
- get '/' do
- 'Hello world!'
- end
-
-И соответствующий config.ru:
-
- require './app'
- run Sinatra::Application
-
-=== Когда использовать config.ru?
-
-Вот несколько причин, по которым вы, возможно, захотите использовать config.ru:
-
-* вы хотите разворачивать свое приложение на различных Rack-совместимых серверах (Passenger, Unicorn,
- Heroku, ...);
-* вы хотите использовать более одного подкласса Sinatra::Base;
-* вы хотите использовать Sinatra только в качестве "прослойки" Rack.
-
-Совсем необязательно переходить на использование config.ru лишь потому, что вы стали
-использовать модульный стиль приложения. И необязательно использовать модульный стиль, чтобы
-запускать приложение с помощью config.ru.
-
-=== Использование Sinatra в качестве "прослойки"
-
-Не только сама Sinatra может использовать "прослойки" Rack, но и любое Sinatra приложение
-само может быть добавлено к любому Rack endpoint в качестве "прослойки". Этим endpoint (конечной точкой)
-может быть другое Sinatra приложение, или приложение, основанное на Rack (Rails/Ramaze/Camping/...):
-
- require 'sinatra/base'
-
- class LoginScreen < Sinatra::Base
- enable :sessions
-
- get('/login') { haml :login }
-
- post('/login') do
- if params[:name] == 'admin' && params[:password] == 'admin'
- session['user_name'] = params[:name]
- else
- redirect '/login'
- end
- end
- end
-
- class MyApp < Sinatra::Base
- # "прослойка" будет запущена перед фильтрами
- use LoginScreen
-
- before do
- unless session['user_name']
- halt "Access denied, please login."
- end
- end
-
- get('/') { "Hello #{session['user_name']}." }
- end
-
-=== Создание приложений "на лету"
-
-Иногда требуется создавать Sinatra приложения "на лету" (например,
-из другого приложения). Это возможно с помощью Sinatra.new:
-
- require 'sinatra/base'
- my_app = Sinatra.new { get('/') { "hi" } }
- my_app.run!
-
-Этот метод может принимать аргументом приложение, от которого
-следует наследоваться:
-
- # config.ru
- require 'sinatra/base'
-
- controller = Sinatra.new do
- enable :logging
- helpers MyHelpers
- end
-
- map('/a') do
- run Sinatra.new(controller) { get('/') { 'a' } }
- end
-
- map('/b') do
- run Sinatra.new(controller) { get('/') { 'b' } }
- end
-
-Это особенно полезно для тестирования расширений Sinatra и при
-использовании Sinatra внутри вашей библиотеки.
-
-Благодаря этому, использовать Sinatra как "прослойку" очень просто:
-
- require 'sinatra/base'
-
- use Sinatra do
- get('/') { ... }
- end
-
- run RailsProject::Application
-
-
-== Области видимости и привязка
-
-Текущая область видимости определяет методы и переменные, доступные
-в данный момент.
-
-=== Область видимости приложения / класса
-
-Любое Sinatra приложение соответствует подклассу Sinatra::Base. Если вы
-используете DSL верхнего уровня (require 'sinatra'), то этим классом будет
-Sinatra::Application, иначе это будет подкласс, который вы создали вручную.
-На уровне класса вам будут доступны такие методы, как +get+ или +before+, но вы
-не сможете получить доступ к объектам +request+ или +session+, так как существует
-только один класс приложения для всех запросов.
-
-Опции, созданные с помощью +set+, являются методами уровня класса:
-
- class MyApp < Sinatra::Base
- # Я в области видимости приложения!
- set :foo, 42
- foo # => 42
-
- get '/foo' do
- # Я больше не в области видимости приложения!
- end
- end
-
-У вас будет область видимости приложения внутри:
-
-* тела вашего класса приложения;
-* методов, определенных расширениями;
-* блока, переданного в +helpers+;
-* блоков, использованных как значения для +set+;
-* блока, переданного в Sinatra.new.
-
-Вы можете получить доступ к объекту области видимости (классу приложения) следующими способами:
-
-* через объект, переданный блокам конфигурации (configure { |c| ... });
-* +settings+ внутри области видимости запроса.
-
-=== Область видимости запроса/экземпляра
-
-Для каждого входящего запроса будет создан новый экземпляр вашего приложения,
-и все блоки обработчика будут запущены в этом контексте. В этой области
-видимости вам доступны +request+ и +session+ объекты, вызовы методов
-рендеринга, такие как +erb+ или +haml+. Вы можете получить доступ к
-области видимости приложения из контекста запроса, используя метод-помощник +settings+:
-
- class MyApp < Sinatra::Base
- # Я в области видимости приложения!
- get '/define_route/:name' do
- # Область видимости запроса '/define_route/:name'
- @value = 42
-
- settings.get("/#{params[:name]}") do
- # Область видимости запроса "/#{params[:name]}"
- @value # => nil (другой запрос)
- end
-
- "Route defined!"
- end
- end
-
-У вас будет область видимости запроса в:
-
-* get/head/post/put/delete/options блоках;
-* before/after фильтрах;
-* методах-помощниках;
-* шаблонах/отображениях.
-
-=== Область видимости делегирования
-
-Область видимости делегирования просто перенаправляет методы в область видимости класса.
-Однако, она не полностью ведет себя как область видимости класса, так как у вас нет
-привязки к классу. Только методы, явно помеченные для делегирования, будут доступны,
-а переменных/состояний области видимости класса не будет (иначе говоря,
-у вас будет другой +self+ объект). Вы можете
-непосредственно добавить методы делегирования, используя
-Sinatra::Delegator.delegate :method_name.
-
-У вас будет контекст делегирования внутри:
-
-* привязки верхнего уровня, если вы сделали require 'sinatra';
-* объекта, расширенного с помощью Sinatra::Delegator.
-
-Посмотрите сами в код: вот
-{Sinatra::Delegator примесь}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633]
-{расширяет главный объект}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30].
-
-== Командная строка
-
-Sinatra приложения могут быть запущены напрямую:
-
- ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER]
-
-Опции включают:
-
- -h # раздел помощи
- -p # указание порта (по умолчанию 4567)
- -o # указание хоста (по умолчанию 0.0.0.0)
- -e # указание окружения, режима (по умолчанию development)
- -s # указание rack сервера/обработчика (по умолчанию thin)
- -x # включить мьютекс-блокировку (по умолчанию выключена)
-
-== Системные требования
-
-Следующие версии Ruby официально поддерживаются:
-
-[ Ruby 1.8.7 ]
- 1.8.7 полностью поддерживается, тем не менее, если вас ничто не держит на
- этой версии, рекомендуем обновиться до 1.9.2 или перейти на JRuby или Rubinius.
- Поддержка 1.8.7 не будет прекращена до выхода Sinatra 2.0 и Ruby 2.0,
- разве что в случае релиза 1.8.8 (что мало вероятно). Но даже тогда, возможно,
- поддержка не будет прекращена. Ruby 1.8.6 больше не поддерживается.
- Если вы хотите использовать 1.8.6, откатитесь до Sinatra 1.2, которая будет
- получать все исправления ошибок до тех пор, пока не будет выпущена
- Sinatra 1.4.0.
-
-[ Ruby 1.9.2 ]
- 1.9.2 полностью поддерживается и рекомендована к использованию. Заметьте,
- что Radius и Markaby пока несовместимы с 1.9. Не используйте 1.9.2p0,
- известно, что эта версия очень нестабильна при использовании Sinatra.
- Эта версия будет поддерживаться по крайней мере до выхода Ruby 1.9.4/2.0,
- а поддержка последней версии 1.9 будет осуществляться до тех пор, пока
- она поддерживается командой разработчиков Ruby.
-
-[ Ruby 1.9.3 ]
- 1.9.3 полностью поддерживается. Рекомендуем подождать релиза версии с
- большим уровнем патчсета (текущий — p0) перед тем, как использовать ее
- в рабочем окружении. Заметьте, что переход на 1.9.3 с ранних версий
- сделает недействительными все сессии.
-
-[ Rubinius ]
- Rubinius официально поддерживается (Rubinius >= 1.2.4), всё, включая все
- языки шаблонов, работает. Предстоящий релиз 2.0 также поддерживается.
-
-[ JRuby ]
- JRuby официально поддерживается (JRuby >= 1.6.5). Нет никаких проблем с
- использованием альтернативных шаблонов. Тем не менее, если вы выбираете
- JRuby, то, пожалуйста, посмотрите на JRuby Rack-сервера, так как Thin не
- поддерживается полностью на JRuby. Поддержка расширений на C в JRuby все
- еще экспериментальная, что на данный момент затрагивает только RDiscount,
- Redcarpet и RedCloth.
-
-Мы также следим за предстоящими к выходу версиями Ruby.
-
-Следующие реализации Ruby не поддерживаются официально, но известно, что на
-них запускается Sinatra:
-
-* старые версии JRuby и Rubinius;
-* Ruby Enterprise Edition;
-* MacRuby, Maglev, IronRuby;
-* Ruby 1.9.0 и 1.9.1 (настоятельно не рекомендуются к использованию).
-
-То, что версия официально не поддерживается, означает, что, если что-то не
-работает на этой версии, а на поддерживаемой работает — это не наша проблема, а их.
-
-Мы также запускаем наши CI-тесты на версии Ruby, находящейся в разработке
-(предстоящей 2.0.0), и на 1.9.4, но мы не можем ничего гарантировать, так как
-они находятся в разработке. Предполагается, что 1.9.4p0 и 2.0.0p0 будут поддерживаться.
-
-Sinatra должна работать на любой операционной системе, в которой есть одна из указанных выше версий Ruby.
-
-Пока невозможно запустить Sinatra на Cardinal, SmallRuby, BlueRuby и на любой
-версии Ruby до 1.8.7.
-
-== На острие
-
-Если вы хотите использовать самый последний код Sinatra, не бойтесь запускать
-свое приложение вместе с кодом из master ветки Sinatra, она весьма стабильна.
-
-Мы также время от времени выпускаем предварительные версии, так что вы можете делать так:
-
- gem install sinatra --pre
-
-Чтобы воспользоваться некоторыми самыми последними возможностями.
-
-=== С помощью Bundler
-
-Если вы хотите запускать свое приложение с последней версией Sinatra, то
-рекомендуем использовать {Bundler}[http://gembundler.com/].
-
-Сначала установите Bundler, если у вас его еще нет:
-
- gem install bundler
-
-Затем создайте файл +Gemfile+ в директории вашего проекта:
-
- source :rubygems
- gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
-
- # другие зависимости
- gem 'haml' # например, если используете haml
- gem 'activerecord', '~> 3.0' # может быть, вам нужен и ActiveRecord 3.x
-
-Обратите внимание, вам нужно будет указывать все зависимости вашего приложения
-в этом файле. Однако, непосредственные зависимости Sinatra (Rack и Tilt) Bundler
-автоматически скачает и добавит.
-
-Теперь вы можете запускать свое приложение так:
-
- bundle exec ruby myapp.rb
-
-=== Вручную
-
-Создайте локальный клон репозитория и запускайте свое приложение с sinatra/lib
-директорией в $LOAD_PATH:
-
- cd myapp
- git clone git://github.com/sinatra/sinatra.git
- ruby -Isinatra/lib myapp.rb
-
-Чтобы обновить исходники Sinatra:
-
- cd myapp/sinatra
- git pull
-
-=== Установка глобально
-
-Вы можете самостоятельно собрать gem:
-
- git clone git://github.com/sinatra/sinatra.git
- cd sinatra
- rake sinatra.gemspec
- rake install
-
-Если вы устанавливаете пакеты (gem) от пользователя root, то вашим следующим шагом должна быть команда
-
- sudo rake install
-
-== Версии
-
-Sinatra использует {Semantic Versioning}[http://semver.org/], SemVer и
-SemVerTag.
-
-== Дальнейшее чтение
-
-* {Веб-сайт проекта}[http://www.sinatrarb.com/] — Дополнительная документация,
- новости и ссылки на другие ресурсы.
-* {Участие в проекте}[http://www.sinatrarb.com/contributing] — Обнаружили баг? Нужна помощь? Написали патч?
-* {Слежение за проблемами/ошибками}[http://github.com/sinatra/sinatra/issues]
-* {Twitter}[http://twitter.com/sinatra]
-* {Группы рассылки}[http://groups.google.com/group/sinatrarb/topics]
-* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] на http://freenode.net
-* {Sinatra Book}[http://sinatra-book.gittr.com] учебник и сборник рецептов
-* {Sinatra Recipes}[http://recipes.sinatrarb.com/] сборник рецептов
-* API документация к {последнему релизу}[http://rubydoc.info/gems/sinatra]
- или {текущему HEAD}[http://rubydoc.info/github/sinatra/sinatra] на
- http://rubydoc.info
-* {Сервер непрерывной интеграции}[http://travis-ci.org/sinatra/sinatra]