mirror of
https://github.com/sinatra/sinatra
synced 2023-03-27 23:18:01 -04:00
1263 lines
48 KiB
Text
1263 lines
48 KiB
Text
= Sinatra
|
||
<i>Внимание: Этот документ является переводом Английской версии и может быть устаревшим</i>
|
||
|
||
Sinatra — это предметно-ориентированный язык (DSL) для быстрого создания приложений на Ruby с
|
||
приложением минимума усилий:
|
||
|
||
# myapp.rb
|
||
require 'sinatra'
|
||
|
||
get '/' do
|
||
'Hello world!'
|
||
end
|
||
|
||
Установите gem и запустите приложение с помощью:
|
||
|
||
gem install sinatra
|
||
ruby -rubygems myapp.rb
|
||
|
||
Результат будет тут: http://localhost:4567
|
||
|
||
== Маршруты
|
||
|
||
В Sinatra маршрут — это пара: HTTP метод и шаблон (образец) URL.
|
||
Каждый маршрут ассоциирован с блоком:
|
||
|
||
get '/' do
|
||
.. что-то показать ..
|
||
end
|
||
|
||
post '/' do
|
||
.. что-то создать ..
|
||
end
|
||
|
||
put '/' do
|
||
.. что-то обновить ..
|
||
end
|
||
|
||
delete '/' do
|
||
.. что-то удалить ..
|
||
end
|
||
|
||
options '/' do
|
||
.. что-то ответить ..
|
||
end
|
||
|
||
Маршруты сверяются с запросом по очередности определения. Первый же совпавший с запросом маршрут и будет вызван.
|
||
|
||
Шаблоны маршрутов могут включать в себя параметры доступные в
|
||
<tt>params</tt> xэше:
|
||
|
||
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 (wildcard, *, любая строка символов) параметры доступные
|
||
в <tt>params[:splat]</tt> массиве.
|
||
|
||
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
|
||
|
||
Маршруты также могут использовать регулярные выражения в качестве шаблона URL:
|
||
|
||
get %r{/hello/([\w]+)} do
|
||
"Hello, #{params[:captures].first}!"
|
||
end
|
||
|
||
Или с параметром блока:
|
||
|
||
get %r{/hello/([\w]+)} do |c|
|
||
"Hello, #{c}!"
|
||
end
|
||
|
||
=== Условия
|
||
|
||
Маршруты могут включать различные условия совпадений, такие как user agent:
|
||
|
||
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
|
||
"You're using Songbird version #{params[:agent][0]}"
|
||
end
|
||
|
||
get '/foo' do
|
||
# соответствует non-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
|
||
|
||
=== Возвращаемые значения
|
||
|
||
Возвращаемое значение блока маршрута ограничивается телом ответа, которое будет передано HTTP клиенту,
|
||
или следующей "прослойкой" (middleware, промежуточная программа) в Rack стеке. Чаще всего это строка, как в вышеизложенных примерах.
|
||
Но и другие значения также приемлемы.
|
||
|
||
Вы можете вернуть любой объект, который будет либо корректным Rack ответом, Rack
|
||
телом ответа, либо кодом состояния HTTP:
|
||
|
||
* Массив с тремя переменными: <tt>[status (Fixnum), headers (Hash), response body (должен отвечать на #each)]</tt>
|
||
* Массив с двумя переменными: <tt>[status (Fixnum), response body (должен отвечать на #each)]</tt>
|
||
* Объект, отвечающий на <tt>#each</tt>, который передает только строковые типы данных в этот блок
|
||
* Fixnum, соответствующий коду состояния HTTP
|
||
|
||
Таким образом мы легко можем создать поточный пример:
|
||
|
||
class Stream
|
||
def each
|
||
100.times { |i| yield "#{i}\n" }
|
||
end
|
||
end
|
||
|
||
get('/') { Stream.new }
|
||
|
||
== Статические файлы
|
||
|
||
Статические файлы отдаются из <tt>./public</tt> директории. Вы можете указать другое место,
|
||
используя <tt>:public</tt> опцию:
|
||
|
||
set :public, File.dirname(__FILE__) + '/static'
|
||
|
||
Учтите, что имя директории со статическими файлами не включено в URL. Например, файл
|
||
<tt>./public/css/style.css</tt> будет доступен как
|
||
<tt>http://example.com/css/style.css</tt>.
|
||
|
||
== Виды / Шаблоны
|
||
|
||
Шаблоны по умолчанию будут использованы из директории <tt>./views</tt>.
|
||
Для использования другой директории:
|
||
|
||
set :views, File.dirname(__FILE__) + '/templates'
|
||
|
||
Важно помнить, что вы всегда должны указывать шаблоны
|
||
с помощью символов, даже если это подкаталог (в этом случае
|
||
используйте <tt>:'subdir/template'</tt>). Вы должны использовать
|
||
символ, иначе методы, ответственные за рендеринг, отобразят просто переданную им строку.
|
||
|
||
=== Haml шаблоны
|
||
|
||
<tt>haml</tt> gem/библиотека необходима для рендеринга HAML шаблонов:
|
||
|
||
# Вам нужно будет подключить haml gem в приложении
|
||
require 'haml'
|
||
|
||
get '/' do
|
||
haml :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.haml</tt>.
|
||
|
||
{Опции Haml}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options]
|
||
могут быть установлены глобально через конфигурацию Sinatra,
|
||
см. {Опции и Конфигурация}[http://www.sinatrarb.com/configuration.html],
|
||
и переопределены локально.
|
||
|
||
set :haml, :format => :html5 # :xhtml - Haml формат по умолчанию
|
||
|
||
get '/' do
|
||
haml :index, :format => :html4 # переопределен
|
||
end
|
||
|
||
|
||
=== Erb шаблоны
|
||
|
||
# Вам нужно будет подключить erb в приложении
|
||
require 'erb'
|
||
|
||
get '/' do
|
||
erb :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.erb</tt>.
|
||
|
||
=== Erubis шаблоны
|
||
|
||
<tt>erubis</tt> gem/библиотека необходима для рендеринга Erubis шаблонов:
|
||
|
||
# Вам нужно будет подключить Erubis в приложении
|
||
require 'erubis'
|
||
|
||
get '/' do
|
||
erubis :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.erubis</tt>.
|
||
|
||
Также возможно заменить Erb на Erubis:
|
||
|
||
require 'erubis'
|
||
Tilt.register :erb, Tilt[:erubis]
|
||
|
||
get '/' do
|
||
erb :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.erb</tt> с помощью Erubis.
|
||
|
||
=== Builder шаблоны
|
||
|
||
<tt>builder</tt> gem/библиотека необходима для рендеринга builder шаблонов:
|
||
|
||
# Вам нужно будет подключить builder в приложении
|
||
require 'builder'
|
||
|
||
get '/' do
|
||
builder :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.builder</tt>.
|
||
|
||
=== Nokogiri шаблоны
|
||
|
||
<tt>nokogiri</tt> gem/библиотека необходима для рендеринга nokogiri шаблонов:
|
||
|
||
# Вам нужно будет подключить nokogiri в приложении
|
||
require 'nokogiri'
|
||
|
||
get '/' do
|
||
nokogiri :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.nokogiri</tt>.
|
||
|
||
=== Sass шаблоны
|
||
|
||
<tt>haml</tt> gem/библиотека необходима для рендеринга Sass шаблонов:
|
||
|
||
# Вам нужно будет подключить haml или sass в приложении
|
||
require 'sass'
|
||
|
||
get '/stylesheet.css' do
|
||
sass :stylesheet
|
||
end
|
||
|
||
Отрисует <tt>./views/stylesheet.sass</tt>.
|
||
|
||
{Опции Sass}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
|
||
могут быть установлены глобально через конфигурацию Sinatra,
|
||
см. {Опции и Конфигурация}[http://www.sinatrarb.com/configuration.html],
|
||
и переопределены локально.
|
||
|
||
set :sass, :style => :compact # :nested - стиль Sass по умолчанию
|
||
|
||
get '/stylesheet.css' do
|
||
sass :stylesheet, :style => :expanded # переопределен
|
||
end
|
||
|
||
=== Scss шаблоны
|
||
|
||
<tt>haml</tt> gem/библиотека необходима для рендеринга Scss шаблонов:
|
||
|
||
# Вам нужно будет подключить haml или sass в приложении
|
||
require 'sass'
|
||
|
||
get '/stylesheet.css' do
|
||
scss :stylesheet
|
||
end
|
||
|
||
Отрисует <tt>./views/stylesheet.scss</tt>.
|
||
|
||
{Опции Scss}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
|
||
могут быть установлены глобально через конфигурацию Sinatra,
|
||
см. {Опции и Конфигурация}[http://www.sinatrarb.com/configuration.html],
|
||
и переопределены локально.
|
||
|
||
set :scss, :style => :compact # :nested - стиль Scss по умолчанию
|
||
|
||
get '/stylesheet.css' do
|
||
scss :stylesheet, :style => :expanded # переопределен
|
||
end
|
||
|
||
=== Less шаблоны
|
||
|
||
<tt>less</tt> gem/библиотека необходима для рендеринга Less шаблонов:
|
||
|
||
# Вам нужно будет подключить less в приложении
|
||
require 'less'
|
||
|
||
get '/stylesheet.css' do
|
||
less :stylesheet
|
||
end
|
||
|
||
Отрисует <tt>./views/stylesheet.less</tt>.
|
||
|
||
=== Liquid шаблоны
|
||
|
||
<tt>liquid</tt> gem/библиотека необходима для рендеринга liquid шаблонов:
|
||
|
||
# Вам нужно будет подключить liquid в приложении
|
||
require 'liquid'
|
||
|
||
get '/' do
|
||
liquid :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.liquid</tt>.
|
||
|
||
Так как в Liquid шаблонах невозможно вызывать методы из Ruby (кроме +yield+),
|
||
то вы почти всегда будете передавать локальные переменные:
|
||
|
||
liquid :index, :locals => { :key => 'value' }
|
||
|
||
=== Markdown шаблоны
|
||
|
||
<tt>rdiscount</tt> gem/библиотека необходима для рендеринга Markdown шаблонов:
|
||
|
||
# Вам нужно будет подключить rdiscount в приложении
|
||
require "rdiscount"
|
||
|
||
get '/' do
|
||
markdown :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.markdown</tt> (+md+ и +mkd+ также являются допустимыми файловыми
|
||
расширениями).
|
||
|
||
В Markdown невозможно вызывать методы или передавать локальные переменные.
|
||
Следовательно, вам, скорее всего, придется использовать этот шаблон совместно с другим
|
||
движком рендеринга:
|
||
|
||
erb :overview, :locals => { :text => markdown(:introduction) }
|
||
|
||
Заметьте, что вы можете вызывать метод +markdown+ из других шаблонов:
|
||
|
||
%h1 Hello From Haml!
|
||
%p= markdown(:greetings)
|
||
|
||
Вы не можете вызывать Ruby из Markdown, соответственно, вы не можете использовать
|
||
лэйаут-шаблоны (layouts) на Markdown. Тем не менее, есть возможность использовать один
|
||
движок рендеринга для шаблона, а другой для лэйаута с помощью опции `:layout_engine`:
|
||
|
||
get '/' do
|
||
markdown :index, :layout_engine => :erb
|
||
end
|
||
|
||
Отрисует <tt>./views/index.md</tt> с <tt>./views/layout.erb</tt> в качестве
|
||
лэйаута.
|
||
|
||
Также вы можете задать такие опции рендеринга глобально:
|
||
|
||
set :markdown, :layout_engine => :haml, :layout => :post
|
||
|
||
get '/' do
|
||
markdown :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.md</tt> (и любой другой шаблон на Markdown) с
|
||
<tt>./views/post.haml</tt> в качестве лэйаута.
|
||
|
||
Также возможно обрабатывать Markdown с помощью BlueCloth, а не RDiscount:
|
||
|
||
require 'bluecloth'
|
||
|
||
Tilt.register 'markdown', BlueClothTemplate
|
||
Tilt.register 'mkd', BlueClothTemplate
|
||
Tilt.register 'md', BlueClothTemplate
|
||
|
||
get '/' do
|
||
markdown :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.md</tt> с помощью BlueCloth.
|
||
|
||
=== Textile шаблоны
|
||
|
||
<tt>RedCloth</tt> gem/библиотека необходима для рендеринга Textile шаблонов:
|
||
|
||
# Вам нужно будет подключить redcloth в приложении
|
||
require "redcloth"
|
||
|
||
get '/' do
|
||
textile :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.textile</tt>.
|
||
|
||
В Textile невозможно вызывать методы или передавать локальные переменные. Следовательно, вам, скорее всего,
|
||
придется использовать этот шаблон совместно с другим движком рендеринга:
|
||
|
||
erb :overview, :locals => { :text => textile(:introduction) }
|
||
|
||
Заметьте, что вы можете вызывать метод +textile+ из других шаблонов:
|
||
|
||
%h1 Hello From Haml!
|
||
%p= textile(:greetings)
|
||
|
||
Вы не можете вызывать Ruby из Textile, соответственно, вы не можете использовать
|
||
лэйаут-шаблоны на Textile. Тем не менее, есть возможность использовать один
|
||
движок рендеринга для шаблона, а другой для лэйаута с помощью опции `:layout_engine`:
|
||
|
||
get '/' do
|
||
textile :index, :layout_engine => :erb
|
||
end
|
||
|
||
Отрисует <tt>./views/index.textile</tt> с <tt>./views/layout.erb</tt> в качестве
|
||
лэйаута.
|
||
|
||
Также вы можете задать такие опции рендеринга глобально:
|
||
|
||
set :textile, :layout_engine => :haml, :layout => :post
|
||
|
||
get '/' do
|
||
textile :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.textile</tt> (и любой другой шаблон на Textile) с
|
||
<tt>./views/post.haml</tt> в качестве лэйаута.
|
||
|
||
=== RDoc шаблоны
|
||
|
||
<tt>rdoc</tt> gem/библиотека необходима для рендеринга RDoc шаблонов:
|
||
|
||
# Вам нужно будет подключить rdoc в приложении
|
||
require "rdoc"
|
||
|
||
get '/' do
|
||
rdoc :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.rdoc</tt>.
|
||
|
||
В RDoc невозможно вызывать методы или передавать локальные переменные. Следовательно, вам, скорее всего,
|
||
придется использовать этот шаблон совместно с другим движком рендеринга:
|
||
|
||
erb :overview, :locals => { :text => rdoc(:introduction) }
|
||
|
||
Заметьте, что вы можете вызывать метод +rdoc+ из других шаблонов:
|
||
|
||
%h1 Hello From Haml!
|
||
%p= rdoc(:greetings)
|
||
|
||
Вы не можете вызывать Ruby из RDoc, соответственно, вы не можете использовать
|
||
лэйаут-шаблоны на RDoc. Тем не менее, есть возможность использовать один
|
||
движок рендеринга для шаблона, а другой для лэйаута с помощью опции `:layout_engine`:
|
||
|
||
get '/' do
|
||
rdoc :index, :layout_engine => :erb
|
||
end
|
||
|
||
Отрисует <tt>./views/index.rdoc</tt> с <tt>./views/layout.erb</tt> в качестве
|
||
лэйаута.
|
||
|
||
Также вы можете задать такие опции рендеринга глобально:
|
||
|
||
set :rdoc, :layout_engine => :haml, :layout => :post
|
||
|
||
get '/' do
|
||
rdoc :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.rdoc</tt> (и любой другой шаблон на RDoc) с
|
||
<tt>./views/post.haml</tt> в качестве лэйаута.
|
||
|
||
=== Radius шаблоны
|
||
|
||
<tt>radius</tt> gem/библиотека необходима для рендеринга Radius шаблонов:
|
||
|
||
# Вам нужно будет подключить radius в приложении
|
||
require 'radius'
|
||
|
||
get '/' do
|
||
radius :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.radius</tt>.
|
||
|
||
Так как в Radius шаблоне невозможно вызывать методы из Ruby (кроме +yield+),
|
||
то вы почти всегда будете передавать локальные переменные:
|
||
|
||
radius :index, :locals => { :key => 'value' }
|
||
|
||
=== Markaby шаблоны
|
||
|
||
<tt>markaby</tt> gem/библиотека необходима для рендеринга Markaby шаблонов:
|
||
|
||
# Вам нужно будет подключить markaby в приложении
|
||
require 'markaby'
|
||
|
||
get '/' do
|
||
markaby :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.mab</tt>.
|
||
|
||
Вы также можете использовать внутристроковые Markaby шаблоны:
|
||
|
||
get '/' do
|
||
markaby { h1 "Welcome!" }
|
||
end
|
||
|
||
=== Slim шаблоны
|
||
|
||
<tt>slim</tt> gem/библиотека необходима для рендеринга slim шаблонов:
|
||
|
||
# Вам нужно будет подключить slim в приложении
|
||
require 'slim'
|
||
|
||
get '/' do
|
||
slim :index
|
||
end
|
||
|
||
Отрисует <tt>./views/index.slim</tt>.
|
||
|
||
=== CoffeeScript шаблоны
|
||
|
||
Вам понадобится <tt>coffee-script</tt> gem/библиотека и что-то <b>одно</b> из следующего списка,
|
||
чтобы запускать JavaScript:
|
||
|
||
* +node+ (из Node.js)
|
||
* вы можете использовать OSX (есть встроенные средства для выполнения JavaScript)
|
||
* +therubyracer+ gem/библиотека
|
||
|
||
Подробнее смотрите на {странице проекта}[http://github.com/josh/ruby-coffee-script].
|
||
|
||
Таким образом вы можете использовать CoffeeScript шаблоны.
|
||
|
||
# Вам нужно будет подключить coffee-script в приложении
|
||
require 'coffee-script'
|
||
|
||
get '/application.js' do
|
||
coffee :application
|
||
end
|
||
|
||
Отрисует <tt>./views/application.coffee</tt>.
|
||
|
||
=== Встроенные шаблоны
|
||
|
||
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= foo.name', :locals => { :foo => foo }
|
||
end
|
||
|
||
Это обычный подход, когда шаблоны рендерятся как частные (partials) из других шаблонов.
|
||
|
||
=== Вложенные шаблоны
|
||
|
||
Шаблоны также могут быть определены в конце файла-исходника:
|
||
|
||
require 'sinatra'
|
||
|
||
get '/' do
|
||
haml :index
|
||
end
|
||
|
||
__END__
|
||
|
||
@@ layout
|
||
%html
|
||
= yield
|
||
|
||
@@ index
|
||
%div.title Hello world!!!!!
|
||
|
||
Заметьте: Вложенные шаблоны, определенные в файле-исходнике, который подключил Sinatra, будут
|
||
автоматически загружены. Вызовите <tt>enable :inline_templates</tt> напрямую, если у вас вложенные
|
||
шаблоны в других файлах.
|
||
|
||
=== Именные шаблоны
|
||
|
||
Шаблоны также могут быть определены, используя <tt>template</tt> метод:
|
||
|
||
template :layout do
|
||
"%html\n =yield\n"
|
||
end
|
||
|
||
template :index do
|
||
'%div.title Hello World!'
|
||
end
|
||
|
||
get '/' do
|
||
haml :index
|
||
end
|
||
|
||
Если шаблон с именем "layout" существует, то он будет использован каждый раз,
|
||
когда шаблоны будут отрисовываться. Вы можете отключать лэйаут в каждом конкретном случае с помощью
|
||
<tt>:layout => false</tt> или отключить его для всего приложения, например, так: <tt>set :haml, :layout => false</tt>.
|
||
|
||
get '/' do
|
||
haml :index, :layout => !request.xhr?
|
||
end
|
||
|
||
=== Привязка файловых расширений
|
||
|
||
Чтобы связать расширение файла и движок рендеринга, используйте
|
||
<tt>Tilt.register</tt>. Например, если вы хотите использовать расширение +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
|
||
|
||
Отрисует <tt>./views/index.myat</tt>. Чтобы узнать больше о Tilt,
|
||
смотрите https://github.com/rtomayko/tilt
|
||
|
||
== Методы-помощники
|
||
|
||
Используйте метод <tt>helpers</tt>, чтобы определить методы-помощники, которые
|
||
в дальнейшем можно будет использовать в обработчиках маршрутов и шаблонах:
|
||
|
||
helpers do
|
||
def bar(name)
|
||
"#{name}bar"
|
||
end
|
||
end
|
||
|
||
get '/:name' do
|
||
bar(params[:name])
|
||
end
|
||
|
||
== Фильтры
|
||
|
||
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
|
||
|
||
Фильтры могут использовать шаблоны 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
|
||
|
||
== Прерывание
|
||
|
||
Чтобы незамедлительно прервать обработку запроса внутри фильтра или маршрута, используйте:
|
||
|
||
halt
|
||
|
||
Можно также указать статус при прерывании:
|
||
|
||
halt 410
|
||
|
||
Тело:
|
||
|
||
halt 'this will be the body'
|
||
|
||
И то, и другое:
|
||
|
||
halt 401, 'go away!'
|
||
|
||
Можно указать заголовки:
|
||
|
||
halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
|
||
|
||
== Передача
|
||
|
||
Маршрут может передать обработку запроса следующему совпадающему маршруту, используя <tt>pass</tt>:
|
||
|
||
get '/guess/:who' do
|
||
pass unless params[:who] == 'Frank'
|
||
'You got me!'
|
||
end
|
||
|
||
get '/guess/*' do
|
||
'You missed!'
|
||
end
|
||
|
||
Блок маршрута сразу же прерывается, и контроль переходит к следующему совпадающему маршруту.
|
||
Если соответствующий маршрут не найден, то ответом на запрос будет 404.
|
||
|
||
== Доспут к объекту запроса
|
||
|
||
Объект входящего запроса доступен на уровне обработки запроса (в фильтрах, маршрутах, обработчиках ошибок)
|
||
с помощью `request` метода:
|
||
|
||
# приложение запущено на http://example.com/example
|
||
get '/foo' do
|
||
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_HEADER"] # значение заголовка SOME_HEADER
|
||
request.referer # источник запроса клиента либо '/'
|
||
request.user_agent # user agent (используется для :agent условия)
|
||
request.cookies # хеш с куками браузера
|
||
request.xhr? # является ли запрос ajax запросом?
|
||
request.url # "http://example.com/example/foo"
|
||
request.path # "/example/foo"
|
||
request.ip # IP-адрес клиента
|
||
request.secure? # false
|
||
request.env # "сырой" env хеш, полученный Rack
|
||
end
|
||
|
||
Некоторые опции, такие как <tt>script_name</tt> или <tt>path_info</tt> доступны для записи:
|
||
|
||
before { request.path_info = "/" }
|
||
|
||
get "/" do
|
||
"all requests end up here"
|
||
end
|
||
|
||
<tt>request.body</tt> является IO или StringIO объектом:
|
||
|
||
post "/api" do
|
||
request.body.rewind # в случае, если кто-то уже прочитал тело запроса
|
||
data = JSON.parse request.body.read
|
||
"Hello #{data['name']}!"
|
||
end
|
||
|
||
== Конфигурация
|
||
|
||
Этот блок исполняется один раз при старте в любом окружении, режиме (environment):
|
||
|
||
configure do
|
||
...
|
||
end
|
||
|
||
Будет запущено, когда окружение (RACK_ENV переменная) установлена в
|
||
<tt>:production</tt>:
|
||
|
||
configure :production do
|
||
...
|
||
end
|
||
|
||
Будет запущено, когда окружение <tt>:production</tt> или <tt>:test</tt>:
|
||
|
||
configure :production, :test do
|
||
...
|
||
end
|
||
|
||
== Обработка ошибок
|
||
|
||
Обработчики ошибок исполняются в том же контексте, что и маршруты, before-фильтры, а это означает, что всякие
|
||
прелести вроде <tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt> и т.д. доступны и им.
|
||
|
||
=== NotFound
|
||
|
||
Когда возбуждено исключение <tt>Sinatra::NotFound</tt>, или кодом ответа является 404,
|
||
то будет вызван <tt>not_found</tt> обработчик:
|
||
|
||
not_found do
|
||
'This is nowhere to be found.'
|
||
end
|
||
|
||
=== Ошибки
|
||
|
||
Обработчик ошибок +error+ будет вызван, когда исключение возбуждено из блока маршрута, либо из фильтра.
|
||
Объект-исключение доступен как переменная <tt>sinatra.error</tt> в Rack:
|
||
|
||
error do
|
||
'Sorry there was a nasty error - ' + env['sinatra.error'].name
|
||
end
|
||
|
||
Частные ошибки:
|
||
|
||
error MyCustomError do
|
||
'So what happened was...' + request.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 устанавливает специальные <tt>not_found</tt> и <tt>error</tt> обработчики, когда запущена в режиме
|
||
разработки (окружение <tt>:development</tt>).
|
||
|
||
== Mime-типы
|
||
|
||
Когда вы используете <tt>send_file</tt> или статические файлы, у вас могут быть mime-типы, которые Sinatra
|
||
не понимает по умолчанию. Используйте +mime_type+ для их регистрации по расширению файла:
|
||
|
||
mime_type :foo, 'text/foo'
|
||
|
||
Вы также можете использовать это в +content_type+ помощнике:
|
||
|
||
content_type :foo
|
||
|
||
== 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+) их вручную.
|
||
|
||
== Тестирование
|
||
|
||
Тесты для Sinatra приложений могут быть написаны с помощью библиотек, фреймворков, поддерживающих
|
||
тестирование Rack. {Rack::Test}[http://gitrdoc.com/brynary/rack-test] рекомендован:
|
||
|
||
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::Test и класс Sinatra::TestHarness являются
|
||
устаревшими, начиная с релиза 0.9.2.
|
||
|
||
== Sinatra::Base — "прослойки", библиотеки и модульные приложения
|
||
|
||
Описание своего приложения самым простейшим способом (с помощью DSL верхнего уровня, как в примерах выше)
|
||
работает отлично для крохотных приложений, но имеет множество недостатков, когда надо создать компоненты, такие как
|
||
Rack middleware ("прослойки"), Rails metal, простые библиотеки с серверными компонентами,
|
||
расширения Sinatra.
|
||
DSL верхнего уровня загрязняет пространство имен <tt>Object</tt> и подразумевает стиль конфигурации
|
||
микро-приложения (например, единый файл приложения, ./public и
|
||
./views директории, создание логов, страницу деталей об исключениях
|
||
и т.д.). И тут на помощь приходит 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.
|
||
|
||
<tt>Sinatra::Base</tt> — это чистый лист. Большинство опций, включая встроенный сервер, по умолчанию отключены.
|
||
Смотрите {Опции и Конфигурация}[http://www.sinatrarb.com/configuration.html] для детальной информации
|
||
об опциях и их поведении.
|
||
|
||
=== Запуск модульных приложений
|
||
|
||
Есть два общепринятых способа запускать модульные приложения: запуск напрямую с помощью <tt>run!</tt>:
|
||
|
||
# my_app.rb
|
||
require 'sinatra/base'
|
||
|
||
class MyApp < Sinatra::Base
|
||
# ... здесь код приложения ...
|
||
|
||
# запускаем сервер, если исполняется текущий файл
|
||
run! if app_file == $0
|
||
end
|
||
|
||
И запускаем с помощью:
|
||
|
||
ruby my_app.rb
|
||
|
||
Или с помощью конфигурационного файла <tt>config.ru</tt>, который позволяет использовать любой
|
||
Rack-совместимый сервер приложений.
|
||
|
||
# config.ru
|
||
require 'my_app'
|
||
run MyApp
|
||
|
||
Запускаем:
|
||
|
||
rackup -p 4567
|
||
|
||
=== Запуск "классических" приложений с config.ru
|
||
|
||
Файл приложения:
|
||
|
||
# app.rb
|
||
require 'sinatra'
|
||
|
||
get '/' do
|
||
'Hello world!'
|
||
end
|
||
|
||
И соответствующий <tt>config.ru</tt>:
|
||
|
||
require 'app'
|
||
run Sinatra::Application
|
||
|
||
=== Когда использовать config.ru?
|
||
|
||
Вот несколько причин, по которым вы, возможно, захотите использовать <tt>config.ru</tt>:
|
||
|
||
* вы хотите разворачивать свое приложение на различных Rack-совместимых серверах (Passenger, Unicorn,
|
||
Heroku, ...).
|
||
* вы хотите использовать более одного сабкласса <tt>Sinatra::Base</tt>.
|
||
* вы хотите использовать Sinatra только в качестве "прослойки" Rack.
|
||
|
||
<b>Совсем необязательно переходить на использование <tt>config.ru</tt> лишь потому, что вы стали
|
||
использовать модульный стиль приложения. И необязательно использовать модульный стиль, чтобы
|
||
запускать приложение с помощью <tt>config.ru</tt>.</b>
|
||
|
||
=== Использование Sinatra в качестве "прослойки"
|
||
|
||
Не только сама Sinatra может использовать "прослойки" Rack, но и любое Sinatra приложение
|
||
само может быть добавлено к любому Rack эндпоинту в качестве "прослойки". Этим эндпоинтом
|
||
может быть другое 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' and 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 <a href='/login'>login</a>."
|
||
end
|
||
end
|
||
|
||
get('/') { "Hello #{session['user_name']}." }
|
||
end
|
||
|
||
== Области видимости и привязка
|
||
|
||
Текущая область видимости определяет методы и переменные, доступные
|
||
в данный момент.
|
||
|
||
=== Область видимости приложения / класса
|
||
|
||
Любое Sinatra приложение соответствует сабклассу Sinatra::Base. Если вы
|
||
используете DSL верхнего
|
||
уровня (<tt>require 'sinatra'</tt>), то этим классом будет
|
||
Sinatra::Application, иначе это будет сабкласс, который вы создали вручную.
|
||
На уровне класса вам будут доступны такие методы, как `get` или `before`, но вы
|
||
не сможете иметь доступ к объектам `request` или `session`, так как существует
|
||
только единый класс приложения для всех запросов.
|
||
|
||
Опции, созданные с помощью `set`, являются методами уровня класса:
|
||
|
||
class MyApp < Sinatra::Base
|
||
# Я в области видимости приложения!
|
||
set :foo, 42
|
||
foo # => 42
|
||
|
||
get '/foo' do
|
||
# Я больше не в области видимости приложения!
|
||
end
|
||
end
|
||
|
||
У вас будет область видимости приложения внутри:
|
||
|
||
* Тела вашего класса приложения
|
||
* Методов, определенных расширениями
|
||
* Блока, переданного в `helpers`
|
||
* Блоков, использованных как значения для `set`
|
||
|
||
Вы можете получить доступ к объекту области видимости (классу приложения) следующими способами:
|
||
|
||
* объект, переданный блокам конфигурации (<tt>configure { |c| ... }</tt>)
|
||
* `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 фильтрах
|
||
* методах помощниках
|
||
* шаблонах/видах
|
||
|
||
=== Область видимости делегирования
|
||
|
||
Область видимости делегирования просто перенаправляет методы в область видимости класса.
|
||
Однако, оно не полностью на 100% ведет себя как область видимости класса,
|
||
так как у вас нету привязки к классу: только методы, явно помеченные для
|
||
делегирования, будут доступны, а переменных/состояний области видимости класса
|
||
не будет (иначе говоря, у вас будет другой `self` объект). Вы можете
|
||
непосредственно добавить методы делегирования, используя
|
||
<tt>Sinatra::Delegator.delegate :method_name</tt>.
|
||
|
||
У вас будет контекст делегирования внутри:
|
||
|
||
* Привязки верхнего уровня, если вы сделали <tt>require "sinatra"</tt>
|
||
* Объекта, расширенного с помощью примеси `Sinatra::Delegator`
|
||
|
||
Посмотрите сами в код: тут
|
||
{Sinatra::Delegator примесь}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128]
|
||
будет {включена в глобальное пространство имен}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28].
|
||
|
||
== Командная строка
|
||
|
||
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 # включить мьютекс (по умолчанию выключен)
|
||
|
||
== На острие
|
||
|
||
Если вы хотите использовать самый последний код 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
|
||
|
||
=== Вручную
|
||
|
||
Создайте локальный клон репозитория и запускайте свое приложение с <tt>sinatra/lib</tt>
|
||
директорией в <tt>LOAD_PATH</tt>:
|
||
|
||
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
|
||
|
||
== Дальнейшее чтение
|
||
|
||
* {Вебсайт проекта}[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] on http://freenode.net
|