sinatra/README.ru.rdoc

1264 lines
48 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

= 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