= Sinatra Внимание: Этот документ является переводом Английской версии и может быть устаревшим Sinatra — это предметно-ориентированный язык (DSL) для быстрого создания приложений на Ruby с приложением минимума усилий: # myapp.rb require 'sinatra' get '/' do 'Hello world!' end Установите gem и запустите приложение с помощью: gem install sinatra ruby -rubygems myapp.rb Результат будет тут: http://localhost:4567 == Маршруты В Sinatra маршрут — это пара: HTTP метод и шаблон (образец) URL. Каждый маршрут ассоциирован с блоком: get '/' do .. что-то показать .. end post '/' do .. что-то создать .. end put '/' do .. что-то обновить .. end delete '/' do .. что-то удалить .. end Маршруты сверяются с запросом по очередности определения. Первый же совпавший с запросом маршрут и будет вызван. Шаблоны маршрутов могут включать в себя параметры доступные в 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 (wildcard, *, любая строка символов) параметры доступные в 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 Маршруты также могут использовать регулярные выражения в качестве шаблона URL: get %r{/hello/([\w]+)} do "Hello, #{params[:captures].first}!" end Или с параметром блока: get %r{/hello/([\w]+)} do |c| "Hello, #{c}!" end === Условия Маршруты могут включать различные условия совпадений, такие как user agent: get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do "You're using Songbird version #{params[:agent][0]}" end get '/foo' do # соответствует с non-songbird браузерам end Другими доступными условиями являются +host_name+ и +provides+: get '/', :host_name => /^admin\./ do "Admin Area, Access denied!" end get '/', :provides => 'html' do haml :index end get '/', :provides => ['rss', 'atom', 'xml'] do builder :feed end Довольно легко можно задать собственные условия: set(:probability) { |value| condition { rand <= value } } get '/win_a_car', :probability => 0.1 do "You won!" end get '/win_a_car' do "Sorry, you lost." end === Возвращаемые значения Возвращаемое значение блока маршрута ограничивается телом ответа, которое будет передано HTTP клиенту, или следующей подпрограммой (middleware) в Rack стеке. Чаще всего это строка, как в вышеизложенных примерах. Но и другие значения также приемлемы. Вы можете вернуть любой объект, который будет либо корректным Rack ответом, Rack телом ответа, либо кодом состояния HTTP: * Массив с тремя переменными: [status (Fixnum), headers (Hash), response body (должен отвечать на #each)] * Массив с двумя переменными: [status (Fixnum), response body (должен отвечать на #each)] * Объект, отвечающий на #each, который передает только строковые типы данных в этот блок * Fixnum, соответствующий коду состояния HTTP Таким образом мы легко можем создать поточный пример: class Stream def each 100.times { |i| yield "#{i}\n" } end end get('/') { Stream.new } == Статические файлы Статические файлы отдаются из ./public директории. Вы можете указать другое место, используя :public опцию: set :public, File.dirname(__FILE__) + '/static' Учтите, что имя директории со статическими файлами не включено в 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. === Slim шаблоны slim gem/библиотека необходима для рендеринга slim шаблонов: ## Вам нужно будет подключить slim в приложении require 'slim' get '/' do slim :index end Отрисует ./views/index.slim. === 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 Это обычно используется когда шаблоны рендерятся как частные (partials) из других шаблонов. === Вложенные шаблоны Шаблоны также могут быть определены в конце файла-исходника: 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 Фильтры могут использовать шаблоны URL и будут интерпретированы, только если путь запроса совпадет с этим шаблоном: 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 (для других участвующих HTTP глаголов есть похожие методы) request.form_data? # false request["SOME_HEADER"] # значение SOME_HEADER заголовка request.referer # источник запроса клиента либо '/' request.user_agent # user agent (используется для :agent условия) request.cookies # хеш куки браузера request.xhr? # является ли запрос ajax запросом? request.url # "http://example.com/example/foo" request.path # "/example/foo" request.ip # IP адрес клиента request.secure? # false request.env # env хеш как получено Rack end Некоторые опции, такие как 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 == Конфигурация Этот блок исполняется один раз при старте в любом окружении, режиме (environment): 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 Также вы можете установить обработчик ошибок для кода состояния HTTP: error 403 do 'Access forbidden' end get '/secret' do 403 end Либо набора кодов: error 400..510 do 'Boom' end Sinatra устанавливает специальные not_found и error обработчики, когда запущена в режиме разработки (окружение :development). == 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 - Подпрограммы, библиотеки и модульные приложения Описание своего приложения самым простейшим способом (с помощью DSL верхнего уровня, как в примерах выше) работает отлично для крохотных приложений, но имеет множество недостатков, когда надо создать компоненты, такие как Rack middleware, Rails metal, простые библиотеки с серверными компонентами, Sinatra расширения. DSL верхнего уровня загрязняет пространство имен Object и подразумевает стиль конфигурации микро-приложения (например, единый файл приложения, ./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 сабклассам идентичны тем, что доступны в DSL верхнего уровня. Большинство приложений верхнего уровня могут быть конвертированы в 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 :sessions get('/login') { haml :login } post('/login') do if params[:name] = 'admin' and params[:password] = 'admin' session['user_name'] = params[:name] else redirect '/login' end end end class MyApp < Sinatra::Base # подпрограмма будет запущена перед фильтрами use LoginScreen before do unless session['user_name'] halt "Access denied, please login." end end get('/') { "Hello #{session['user_name']}." } end == Области видимости и привязка Текущая область видимости определяет методы и переменные, доступные в данный момент. === Область видимости приложения / класса Любое 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` Вы можете получить доступ к объекту области видимости (классу приложения) следующими способами: * объект, переданный блокам конфигурации (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 # включить мьютекс (по умолчанию выключен) == На острие Если вы хотите использовать новейший код 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