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 шаблоны + + + + + + + + + + + + + + +
Зависимости + + CoffeeScript + и способ + + запускать JavaScript + +
Расширения файлов.coffee
Примерcoffee :index
+ +### 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]