= Sinatra Внимание: Этот документ является переводом Английской версии и может быть устаревшим Sinatra это предметно-ориентированный язык для быстрого создания приложений на Ruby с приложением минимума усилий: # myapp.rb require 'sinatra' get '/' do 'Hello world!' end Установите gem и запустите его используя: gem install sinatra ruby -rubygems myapp.rb Результат будет тут: http://localhost:4567 == Пути В Sinatra, путь это HTTP метод на пару с сочетающимся шаблоном URL. Каждый путь ассоциирован с блоком: get '/' do .. что-то показать .. end post '/' do .. что-то создать .. end put '/' do .. что-то обновить .. end delete '/' do .. что-то удалить .. end Пути проверяются по очередности определения. Первый же путь сочетающийся c запросом будет вызван. Шаблоны путей могут включать в себя параметры доступные в params xэше: get '/hello/:name' do # сочетается с "GET /hello/foo" и "GET /hello/bar", # где params[:name] 'foo' или 'bar' "Hello #{params[:name]}!" end Можно также использовать именные параметры с помощью блоков: get '/hello/:name' do |n| "Hello #{n}!" end Шаблоны путей также могут включать splat (или постановочные) параметры доступные в 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 %r{/hello/([\w]+)} do "Hello, #{params[:captures].first}!" end Или используя блоки: get %r{/hello/([\w]+)} do |c| "Hello, #{c}!" end === Условия Пути могут включать различные сочетающиеся условия, такие как user agent: get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do "You're using Songbird version #{params[:agent][0]}" end get '/foo' do # сочетается с non-songbird браузерами end Другими доступными условиями являются +host_name+ и +provides+: get '/', :host_name => /^admin\./ do "Admin Area, Access denied!" end get '/', :provides => 'html' do haml :index end get '/', :provides => ['rss', 'atom', 'xml'] do builder :feed end Довольно легко также описать собственные условия: set(:probability) { |value| condition { rand <= value } } get '/win_a_car', :probability => 0.1 do "You won!" end get '/win_a_car' do "Sorry, you lost."программн end === Возвращаемые значения Возвращаемое значения блока определяет как минимум тело ответа переданное HTTP клиенту, или хотя бы следующему подпрограммному обеспечению из Rack стека. Чаще всего это строка, как в выше изложенных примерах. Но другие значения также доступны. Вы можете вернуть любой объект, который должен быть либо действительным Rack ответом, Rack объектным телом либо HTTP статус кодом: * Массив с тремя переменными: [status (Fixnum), headers (Hash), response body (должен отвечать на #each)] * Массив с двумя переменными: [status (Fixnum), response body (должен отвечать на #each)] * Объект отвечающий на #each который передает только строковые типы данных в этот блок * Fixnum соответствующий статус коду Таким образом мы легко можем создать поточный пример: class Stream def each 100.times { |i| yield "#{i}\n" } end end get('/') { Stream.new } == Статические файлы Статические файлы поставляются из ./public директории. Вы можете настроить на другую локацию использую :public опцию: set :public, File.dirname(__FILE__) + '/static' Учтите что имя public директории не включено в URL. Например файл ./public/css/style.css будет доступен как http://example.com/css/style.css. == Виды / Шаблоны Шаблоны видов по умолчанию будут использованы из директории ./views. Для использования другой директории: set :views, File.dirname(__FILE__) + '/templates' Важно помнить что вы всегда должны указывать шаблоны с помощью символов даже если это подкаталог (в этом случае успользуйте :'subdir/template'). Вы должны использовать символ иначе методы ответственные за рендеринг отобразят только строку переданную им. === Haml шаблоны Haml gem/библиотека необходима для визуализации HAML шаблонов: ## Вам нужно будет затребовать haml gem в приложении require 'haml' get '/' do haml :index end Визуализирует ./views/index.haml. {Опции Haml'а}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options] могут быть установлены глобально через конфигурацию Sinatra'ы, посмотрите {Опции и Конфигурации}[http://www.sinatrarb.com/configuration.html], и переписаны на индивидуальной основе. set :haml, :format => :html5 # :xhtml - Haml формат по умолчанию get '/' do haml :index, :format => :html4 # переписан end === Erb шаблоны ## Вам нужно будет затребовать erb в приложении require 'erb' get '/' do erb :index end Визуализирует ./views/index.erb === Erubis шаблоны Erubis gem/библиотека необходима для визуализации erubis шаблонов: ## Вам нужно будет затребовать erubis в приложении require 'erubis' get '/' do erubis :index end Визуализирует ./views/index.erubis === Builder шаблоны Builder gem/библиотека необходима для визуализации builder шаблонов: ## Вам нужно будет затребовать builder в приложении require 'builder' get '/' do builder :index end Визуализирует ./views/index.builder. === Nokogiri шаблоны Nokogiri gem/библиотека необходима для визуализации nokogiri шаблонов: ## Вам нужно будет затребовать nokogiri в приложении require 'nokogiri' get '/' do nokogiri :index end Визуализирует ./views/index.nokogiri. === Sass шаблоны Haml gem/библиотека необходима для визуализации Sass шаблонов: ## Вам нужно будет затребовать haml или sass в приложении require 'sass' get '/stylesheet.css' do sass :stylesheet end Визуализирует ./views/stylesheet.sass. {Опции Sass}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options] могут быть установлены глобально через конфигурацию Sinatra'ы, посмотрите {Опции и Конфигурации}[http://www.sinatrarb.com/configuration.html], и переписаны на индивидуальной основе. set :sass, :style => :compact # :nested - стиль Sass по умолчанию get '/stylesheet.css' do sass :stylesheet, :style => :expanded # переписан end === Scss шаблоны Haml gem/библиотека необходима для визуализации Scss шаблонов: ## Вам нужно будет затребовать haml или sass в приложении require 'sass' get '/stylesheet.css' do scss :stylesheet end Визуализирует ./views/stylesheet.scss. {Опции Scss}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options] могут быть установлены глобально через конфигурацию Sinatra'ы, посмотрите {Опции и Конфигурации}[http://www.sinatrarb.com/configuration.html], и переписаны на индивидуальной основе. set :scss, :style => :compact # :nested - стиль Scss по умолчанию get '/stylesheet.css' do scss :stylesheet, :style => :expanded # переписан end === Less шаблоны less gem/библиотека необходима для визуализации Less шаблонов: ## Вам нужно будет затребовать less в приложении require 'less' get '/stylesheet.css' do less :stylesheet end Визуализирует ./views/stylesheet.less. === Liquid шаблоны liquid gem/библиотека необходима для визуализации liquid шаблонов: ## Вам нужно будет затребовать liquid в приложении require 'liquid' get '/' do liquid :index end Визуализирует ./views/index.liquid. Так как в Liquid шаблоне невозможно вызывать методы из Ruby (кроме +yield+), то вы почти всегда будете передавать локальные переменные: liquid :index, :locals => { :key => 'value' } === Markdown шаблоны rdiscount gem/библиотека необходима для визуализации Markdown шаблонов: ## Вам нужно будет затребовать rdiscount в приложении require "rdiscount" get '/' do markdown :index end Визуализирует ./views/index.markdown (+md+ и +mkd+ являются также допустимыми файловыми расширениями). В markdown невозможно вызывать методы или передавать локальные переменные. Следовательно вам скорее всего прийдется использовать этот шаблон совместно с другим рендер движком: erb :overview, :locals => { :text => markdown(:introduction) } Заметьте что в можете вызывать метод markdown из других шаблонов: %h1 Hello From Haml! %p= markdown(:greetings) === Textile шаблоны RedCloth gem/библиотека необходима для визуализации Textile шаблонов: ## Вам нужно будет затребовать redcloth в приложении require "redcloth" get '/' do textile :index end Визуализирует ./views/index.textile. В textile невозможно вызывать методы или передавать локальные переменные. Следовательно вам скорее всего прийдется использовать этот шаблон совместно с другим рендер движком: erb :overview, :locals => { :text => textile(:introduction) } Заметьте что в можете вызывать метод textile из других шаблонов: %h1 Hello From Haml! %p= textile(:greetings) === RDoc шаблоны RDoc gem/библиотека необходима для визуализации RDoc шаблонов: ## Вам нужно будет затребовать rdoc в приложении require "rdoc" get '/' do rdoc :index end Визуализирует ./views/index.rdoc. В rdoc невозможно вызывать методы или передавать локальные переменные. Следовательно вам скорее всего прийдется использовать этот шаблон совместно с другим рендер движком: erb :overview, :locals => { :text => rdoc(:introduction) } Заметьте что в можете вызывать метод rdoc из других шаблонов: %h1 Hello From Haml! %p= rdoc(:greetings) === Radius шаблоны radius gem/библиотека необходима для визуализации Radius шаблонов: ## Вам нужно будет затребовать radius в приложении require 'radius' get '/' do radius :index end Визуализирует ./views/index.radius. Так как в Radius шаблоне невозможно вызывать методы из Ruby (кроме +yield+), то вы почти всегда будете передавать локальные переменные: radius :index, :locals => { :key => 'value' } === Markaby шаблоны markaby gem/библиотека необходима для визуализации Markaby шаблонов: ## Вам нужно будет затребовать markaby в приложении require 'markaby' get '/' do markaby :index end Визуализирует ./views/index.mab. === CoffeeScript шаблоны coffee-script gem/библиотека и `coffee` бинарный файл необходимы для визуализации CoffeeScript шаблонов: ## Вам нужно будет затребовать coffee-script в приложении require 'coffee-script' get '/application.js' do coffee :application end Визуализирует ./views/application.coffee. === Внутристроковые шаблоны get '/' do haml '%div.title Hello World' end Визуализирует стороковые переменные. === Доступ к переменным в шаблонах Шаблоны определяются/интерпретируются в том же контексте что и обработчики путей. Переменные установленные в процесе обработки путей будут доступны напрямую в шаблоне: get '/:id' do @foo = Foo.find(params[:id]) haml '%h1= @foo.name' end Либо через хеш локальных переменных: get '/:id' do foo = Foo.find(params[:id]) haml '%h1= foo.name', :locals => { :foo => foo } end Это обычно используется когда шаблоны рендерятся как частные из других шаблонов. === Внутристроковые шаблоны Шаблоны так же могут быть определены в конце файла-исходника: 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-макет с помощью :layout => false. get '/' do haml :index, :layout => !request.xhr? end == Методы помощники Используйте helpers метод для определения методов помощников для дальнейшего использования в обработчиках путей и шаблонах: helpers do def bar(name) "#{name}bar" end end get '/:name' do bar(params[:name]) end == Фильтры Before-фильтры будут определены/интерпретированы перед каждым запросом с тем же контекстом что и пути и фильтры могут изменять как запрос там и ответ на него. Переменные установленные в фильтрах доспутны в путях и шаблонах: before do @note = 'Hi!' request.path_info = '/foo/bar/baz' end get '/foo/*' do @note #=> 'Hi!' params[:splat] #=> 'bar/baz' end After-фильтры будут определены/интерпретированы после каждого запроса с тем же контекстом что и пути и фильтры могут изменять как запрос там и ответ на него. Переменные установленные в before-фильтрах и путях будут доступны в after-фильтрах: after do puts response.status end Фильтры могут принимать схемы/шаблоны и будут интерпретированы только если путь запроса совпадает с этим шаблоном: before '/protected/*' do authenticate! end after '/create/:slug' do |slug| session[:last_slug] = slug end == Прерывание Для того чтобы незамедлительно прервать запрос внутри фильтра либо пути используйте: halt Можно также указать статус при прерывании: halt 410 Либо тело: halt 'this will be the body' Либо и то, и другое: halt 401, 'go away!' Можно указать заголовки: halt 402, {'Content-Type' => 'text/plain'}, 'revenge' == Передача Путь может передать процесс следующему совпадающему пути используя pass: get '/guess/:who' do pass unless params[:who] == 'Frank' 'You got me!' end get '/guess/*' do 'You missed!' end Блок пути сказу же закончен и контроль переходить к следующему совпадающему пути. Если совпадающий путь не найден то ответом на запрос будет 404. == Доспут к объекту запроса Входящий объект запроса может быть доступен в уровне запроса (в фильтрах, путях, обработчиках ошибок) используя `request` метод: # приложение запущено на http://example.com/example get '/foo' do request.body # тело запроса посланное клиентом (см ниже) request.scheme # "http" request.script_name # "/example" request.path_info # "/foo" request.port # 80 request.request_method # "GET" request.query_string # "" request.content_length # длина тела запроса request.media_type # медиа тип тела запроса request.host # "example.com" request.get? # true (для других участвующих глаголах есть похожие методы) request.form_data? # false request["SOME_HEADER"] # значение SOME_HEADER заголовка request.referer # источник запроса клиента либо '/' request.user_agent # user agent (используется для :agent условия) request.cookies # хеш куки браузера request.xhr? # является ли запрос ajax запросом? request.url # "http://example.com/example/foo" request.path # "/example/foo" request.ip # IP адрес клиента request.secure? # false request.env # env хеш как получено Rack-ом end Некоторые опции, такие как 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 == Конфигурация Будет запущено про загрузке в любом из сред: configure do ... end Будет запущено когда среда (RACK_ENV переменная) установлена на :production: configure :production do ... end Будет запущено когда среда на :production или на :test: configure :production, :test do ... end == Обработка ошибок Обработчики ошибок будут запущены с тем же контекстом что и пути и before-фильтры, что означаем что всякие прелести какие как haml, erb, halt и т.д. будут доступны. ===  Исключение NotFound Когда 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...' + request.env['sinatra.error'].message end Тогда, если это произошло: get '/' do raise MyCustomError, 'something bad' end То вы получите: So what happened was... something bad Помимо вы можете установить обработчик ошибок для статус кода: error 403 do 'Access forbidden' end get '/secret' do 403 end Либо набора кодов: error 400..510 do 'Boom' end Sinatra устанавливает специальные not_found и error обработчики когда является запущенной в среде разработки. == Mime типы Когда используете send_file либо статические файлы мы можете использовать mime типы которые Sinatra не понимает по умолчанию. Используйте +mime_type+ для их регистрации по файловому расширению: mime_type :foo, 'text/foo' Вы также можете использовать +content_type+ помощник: content_type :foo == Rack подпрограммное обеспечение Sinatra использует Rack[http://rack.rubyforge.org/], минимальный стандартный интерфейс для Ruby веб систем. Одна из самых интересных способностей Rack'а является возможность поддержки подпрограммного обеспечения-"middleware" -- компоненты сидящие между сервером и вашем приложении которые наблюдают и/или манипулируют HTTP запросы/ответы для предоставления различных типов общей функциональности. Sinatra позволяет создание подпрограммного обеспечения Rack пустяком используя метод вверхнего уровня +use+: require 'sinatra' require 'my_custom_middleware' use Rack::Lint use MyCustomMiddleware get '/hello' do 'Hello World' end Семантика +use+ идентичная той что определена для Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSL (чаще всего используется в rackup файлах). Например, +use+ метод принимает множественные переменные, также как и блоки: use Rack::Auth::Basic do |username, password| username == 'admin' && password == 'secret' end Rack распространяется с различным стандартным подпрограммным обеспечением для логирования, дебагинга, URL пути следования, аудентикации и обработки сессий. Sinatra использует многие из этих компонентов автоматически основываясь на конфигурации чтобы вам не проходилось регистрировать/использовать +use+ их вручную. == Тестирование Sinatra тесты могут быть написаны на любой из библиотек либо систем поддерживающих Rack тестирование. {Rack::Test}[http://gitrdoc.com/brynary/rack-test] рекомендован: require 'my_sinatra_app' require 'test/unit' require 'rack/test' class MyAppTest < Test::Unit::TestCase include Rack::Test::Methods def app Sinatra::Application end def test_my_default get '/' assert_equal 'Hello World!', last_response.body end def test_with_params get '/meet', :name => 'Frank' assert_equal 'Hello Frank!', last_response.body end def test_with_rack_env get '/', {}, 'HTTP_USER_AGENT' => 'Songbird' assert_equal "You're using Songbird!", last_response.body end end Заметьте: Встроенные модули Sinatra::Test и Sinatra::TestHarness являются устаревшими начиная с 0.9.2 релиза. == Sinatra::Base - Подпрограммное обеспечение, библиотеки и модульные приложения Определяя свое приложение как приложение вверхнего уровня работает отлично что микро приложений, но имеет множество недостатков когда надо создать используемые компоненты такие как Rack middleware, Rails metal, простые библиотеки с серверными компонентами либо Sinatra разрешениями. Предметно-ориентированный язык вверхнего уровня загрязняет пространство имен объекта и подразумевает стиль конфигурации микро приложения (например: единый файл приложения, ./public и ./views директории, создание логов, страницу деталей об исключениях и т.д.). В такой случае идеально использование Sinatra::Base: require 'sinatra/base' class MyApp < Sinatra::Base set :sessions, true set :foo, 'bar' get '/' do 'Hello world!' end end MyApp класс является независимым Rack компонентом который может исполнять роли Rack подпрограммного обеспечения, Rack приложения, либо Rails metal. Вы можете +use+(использовать) либо +run+(запустить) этот класс из rackup +config.ru+ конфиругационного файла; или, контролировать компонент сервера созданного библиотекой: MyApp.run! :host => 'localhost', :port => 9090 Методы доступные Sinatra::Base сабклассам являются идентичными тем, что доступны в предметно-ориентированном языке вверхнего уровня. Большинство приложений вверхнего уровня могут быть конвертированы в Sinatra::Base компоненты с помощью двух модификаций: * Все файлы должны запрашивать +sinatra/base+ вместо +sinatra+; иначе, все методы предоставляемые Sinatra'ой будут импортированные в главное пространство имен. * Все блоки путей, обработчики ошибок, фильтры и опции должны быть в Sinatra::Base сабклассе. Sinatra::Base это чистый лист. Большинство опций, включая встроенный сервер, по умолчанию отключены. Смотрите {Опции и Конфигурации}[http://www.sinatrarb.com/configuration.html] для детальной информации об опциях и их поведения. === Использование Sinatra как подпрограммное обеспечение Не только Sinatra сама может использовать другое подпрограммное обеспечение Rack, так и любое Sinatra приложение само может быть добавлено к любому Rack эндпоинту в качестве подпрограммного обеспечения. Этим эндпоинтом может быть другое Sinatra приложение либо приложение основанное на Rack (Rails/Ramaze/Camping/...). require 'sinatra/base' class LoginScreen < Sinatra::Base enable :session get('/login') { haml :login } post('/login') do if params[:name] = 'admin' and params[:password] = 'admin' session['user_name'] = params[:name] else redirect '/login' end end end class MyApp < Sinatra::Base # подпрограммное обеспечение будет запущено перед фильтрами use LoginScreen before do unless session['user_name'] halt "Access denied, please login." end end get('/') { "Hello #{session['user_name']}." } end == Область видимости и привязка Текучая область видимости определяет методы и переменные доступные в данный момент. === Область видимости приложения / класса Любое Sinatra приложение соответствует сабклассу Sinatra::Base. Если вы используете предметно-ориентированный язык вверхнего уровня (require 'sinatra'), то этим классом будет Sinatra::Application, иначе это будет сабкласс которые вы создали вручную. На уровне класса вам будут доступны методы как `get` или `before`, но вы не сможете иметь доступ с объекту `request` или `session`, так как существует только единый application класс для всех запросов. Опции созданные используя `set` являются методами на уровне класса: class MyApp << Sinatra::Base # Я в области видимости приложения! set :foo, 42 foo # => 42 get '/foo' do # Я больше не в области видимости приложения! end end У вас будет область видимости приложения внутри: * Тела вашего application класса * Методов определенных расширениями * Блока переданного в `helpers` * Блоках использованных как значения для `set` Вы можете достать объект области видимости (класс) через: * объект переданный блокам конфигурации (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 блоков * before/after фильтрах * методах помощниках * шаблонах/видах === Контекст делегирования Область видимости делегирования просто переправляет методы в контекст класса. Однако, оно не полностью на 100% ведет себя как область видимости класса, так как у вас нету привязи к классу: только методы помеченные для делегирования будут доступны и переменные/состояние области видимости класса не будут поделены (иначе говоря: у вас будет другой `self` объект). Вы можете непосредственно добавить методы делегирования вызывая Sinatra::Delegator.delegate :method_name. У вас будет контекст делегирования внутри: * Привязь вверхнего уровня, если вы запросили require "sinatra" * Объекта расширенного с помощью `Sinatra::Delegator` примеси Посмотрите на непосредствено сам код: тут {Sinatra::Delegator примесь}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128] будет {включена в глобальное именное пространство}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28]. == Командная строка Sinatra приложения могут быть запущены напрямую: ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER] Опции включают: -h # помощь -p # настроить порт (по умолчанию 4567) -o # настроить хост (по умолчанию 0.0.0.0) -e # настроить среду (по умолчанию development) -s # настроить rack сервер/обработчик (по умолчанию thin) -x # включить семафор взаимного исключения (по умолчанию выключено) == The Bleeding Edge (Новейший и потенциально нестабильный код) Если вы хотите использовать новейший код Sinatra'ы, то создайте локальный клон и запускайте свое приложение с sinatra/lib директорией в LOAD_PATH: cd myapp git clone git://github.com/sinatra/sinatra.git ruby -Isinatra/lib myapp.rb Альтернативно вы можете добавить sinatra/lib директорию в LOAD_PATH приложения: $LOAD_PATH.unshift File.dirname(__FILE__) + '/sinatra/lib' require 'rubygems' require 'sinatra' get '/about' do "I'm running version " + Sinatra::VERSION end Чтобы обновить Sinatra исходники в будущем: cd myproject/sinatra git pull == Больше информации * {Вебсайт проекта}[http://www.sinatrarb.com/] - Дополнительная документация, новости и ссылки на другие ресурсы. * {Способствание/Помощь}[http://www.sinatrarb.com/contributing] - Нашли баг? Нужна помощь? Написали патч? * {Слежение за проблемами}[http://github.com/sinatra/sinatra/issues] * {Twitter}[http://twitter.com/sinatra] * {Лист рассылки}[http://groups.google.com/group/sinatrarb/topics] * {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] on http://freenode.net