Rack provides a minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call.
Rack в GitHub: rack/rack
Запазете следния код във файл config.ru
и пуснете с rackup config.ru
:
hello_world_app = proc do |env|
[200, {'Content-Type' => 'text/plain'}, ['Hello, World!']]
end
run hello_world_app
Предполага се, че сте си инсталирали библиотеката rack
. Можете да направите това с gem install rack
.
env
dump_env = proc do |env|
body = env.map { |key, value| "#{key}: #{value}" }.join("\n")
[200, {'Content-Type' => 'text/plain'}, [body]]
end
run dump_env
SERVER_SOFTWARE: thin 1.3.1 codename Triple Espresso SERVER_NAME: localhost rack.input: #<Rack::Lint::InputWrapper:0x007fbe7b8484b0> rack.version: [1, 0] rack.errors: #<Rack::Lint::ErrorWrapper:0x007fbe7b848438> rack.multithread: false rack.multiprocess: false rack.run_once: false REQUEST_METHOD: GET REQUEST_PATH: / PATH_INFO: / REQUEST_URI: / HTTP_VERSION: HTTP/1.1 HTTP_HOST: localhost:9292 HTTP_CONNECTION: keep-alive HTTP_CACHE_CONTROL: max-age=0 HTTP_USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11 HTTP_ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 HTTP_ACCEPT_ENCODING: gzip,deflate,sdch HTTP_ACCEPT_LANGUAGE: en-US,en;q=0.8 HTTP_ACCEPT_CHARSET: ISO-8859-1,utf-8;q=0.7,*;q=0.3 GATEWAY_INTERFACE: CGI/1.2 SERVER_PORT: 9292 QUERY_STRING: SERVER_PROTOCOL: HTTP/1.1 rack.url_scheme: http SCRIPT_NAME: REMOTE_ADDR: 127.0.0.1 async.callback: #<Method: Thin::Connection#post_process> async.close: #<EventMachine::DefaultDeferrable:0x007fbe7b8443b0>
[http_status_code, headers, content]
http_status_code
е число, по HTTP спецификацията (напр. 200 или 404)
headers
е хеш с HTTP хедъри
content
е нещо, което трябва да отговаря на each
и да връща парчета данниrackup lobster.ru
use SomeMiddleware
Rack::Runtime
добавя X-Runtime
хедър
Rack::ShowExceptions
Rack::ShowStatus
Rack::Sendfile
Rack::Logger
Rack::Lock
Rack::ETag
, Rack::ContentLength
, Rack::Chunked
...
Mongrel Ebb EventedMongrel Fuzed SwiftipliedMongrel Glassfish v3 WEBrick Phusion Passenger FCGI Puma CGI Rainbows! SCGI Unicorn LiteSpeed unixrack Thin Zbatery
Ruby on Rails Coset Padrino Halcyon Sinatra Mack Sin Maveric Vintage Merb Waves Camping Wee Ramaze
…и много други.
Sinatra is a domain-specific language for building websites, web services, and web applications in Ruby. It emphasizes a minimalistic approach to development, offering only what is essential to handle HTTP requests and deliver responses to clients.
— Из "Sinatra Up and Running"
Официален сайт: sinatrarb.com, GitHub проект: sinatra/sinatra
gem install sinatra
require 'sinatra'
и сте в играта
gem install thin
)
require 'sinatra'
require 'sinatra/base'
Sinatra::Base
require 'sinatra'
get '/' do
'Booyah!'
end
Пускате с ruby booyah.rb
и тествате, например с telnet
Какво става, ако поискаме несъществуващ адрес?
GET
, POST
, PUT
, DELETE
, PATCH
, HEAD
, OPTIONS
GET
, HEAD
, PUT
, DELETE
get '/'
!= post '/'
params[:param_name]
get '/greet/:name' do
"Hey there, #{params[:name]}!"
end
*
в дефиниця на маршрут
params[:splat]
get
, post
, ...ruby game.rb
require 'sinatra'
weaker_moves = {rock: :scissors, paper: :rock, scissors: :paper}
valid_moves = weaker_moves.keys
before { content_type :txt }
get '/play/:move' do
# Игрова логика
end
get '/play/:move' do
player_move = params[:move].to_sym
unless valid_moves.include?(player_move)
halt 403, "You must throw one of the following: #{valid_moves.join(', ')}"
end
end
get '/play/:move' do
# Проверка за валиден ход...
computer_move = weaker_moves.keys.sample
end
get '/play/:move' do
# Проверка за валиден ход...
# Изкуствен интелект...
if player_move == computer_move
"Oh, my!\nIt's a tie!"
elsif computer_move == weaker_moves[player_move]
"Nicely done, #{player_move} beats #{computer_move}!"
else
"Ouch... You got trashed, #{computer_move} beats #{player_move}. Better luck next time!"
end
end
before
и after
такива
get 'route'
, post 'route'
, ...
before
може да спрете изпълнението на request-а
after
може да модифицирате резултата, върнат от даден маршрут
use SomeMiddleware
use Rack::Runtime
, или use Rack::Chunked
...Sinatra::Application
(подробности за това по-късно)halt
прекратява обработката на request-а и връща отговор
pass
предава изпълнението на следващия маршрут, според приоритета
redirect
кара HTTP клиента да зареди нов URL
halt
вътрешно
redirect
за да укажете дали е временен или траенpublic
public/foo/bar.js
редом до вашето приложение, следното ще работи:
public
липсва от пътя)
get '/' do
# Ще върне низ с рендерирания Erb темплейт, кръстен "index"
erb :index
end
template_handler_name :path_to_template
markdown '# This is a heading!'
ще ви върне '<h1>This is a heading!</h1>'
__END__
@@template_name
get '/' do
erb :index
end
__END__
@@index
<h1>Hey, there!</h1>
<p>The time now is <%= Time.now %></p>
views/
по подразбиране
erb :index
ще търси views/index.erb
erb :'users/show'
ще рендерирате views/users/show.erb
self
-ът на маршрутите и на view-тата е един и същ
:locals
, подадена на template handler-аget '/posts/:id' do
@info = "This is post ID #{params[:id]}"
erb :post, locals: {title: 'My Blog'}
end
__END__
@@post
<h1><%= title %></h1>
<p><%= @info %></p>
not_found
и error
:
not_found { handle 404 errors }
error { handle internal server errors }
configure
set :option, 'value'
enable/disable :option
set :option, true/false
settings
production
, development
, test
, ...
development
RACK_ENV
export RACK_ENV="production"
в ~/.bashrc
, ако сте на Unix и ползвате Bashconfigure do
# Важи за всички обкръжения
set :public_folder, File.expand_path('../assets', __FILE__)
set :my_custom_var, 'bar'
end
configure :production do
# Важи само за "production" обкръжение
set :public_folder, '/var/www/assets'
end
set
, ще получите "динамична" опция
foo
, foo=
и foo?
)
set
всъщност дефинира тези методи като класови в Sinatra::Application
settings
(връщат self
)
headers
ви позволява да задавате хедъри на отовора към клиента
request
е обект, обгръщащ env
(което идва от Rack)
response
е обект, с който може да манипулирате отговора при нуждаrequest.accept # ['text/html', '*/*'] request.accept? 'text/xml' # true request.preferred_type(t) # 'text/html' request.body # request body sent by the client (see below) request.scheme # "http" request.script_name # "/example" request.path_info # "/foo" request.port # 80 request.request_method # "GET" request.query_string # "" request.content_length # length of request.body request.media_type # media type of request.body request.host # "example.com"
request.get? # true (similar methods for other verbs) request.form_data? # false request["SOME_HEADER"] # value of SOME_HEADER header request.referrer # the referrer of the client or '/' request.user_agent # user agent (used by :agent condition) request.cookies # hash of browser cookies request.xhr? # is this an ajax request? request.url # "http://example.com/example/foo" request.path # "/example/foo" request.ip # client IP address request.secure? # false (would be true over ssl) request.forwarded? # true (if running behind a reverse proxy) request.env # raw env hash handed in by Rack
request.cookies
(връща хеш)
response.set_cookie
и response.delete_cookie
configure { enable :sessions }
session
rack.session
)
set :session_secret, 'foo'
expires
(клиентът праща If-Modified-Since
)
cache_control
etag
(слага ETag
, клиентът праща If-None-Match
)get '/stream' do
stream do |connection|
connection << "Start of the stream\n"
sleep 1
connection << "Some more data\n"
sleep 1
connection << "And even more data\n"
end
end
subscribers = []
get '/listen' do
stream :keep_open do |subscriber|
subscriber << "Welcome!\n"
subscribers << subscriber
end
end
get '/broadcast/:message' do
message = params[:message]
subscribers.each do |subscriber|
subscriber << "#{Time.now}: #{message}\n"
end
"Message #{message} sent.\n"
end
require 'sinatra/base'
вместо require 'sinatra'
class MyApp < Sinatra::Base ... end
Sinatra::Base
require 'sinatra/base'
class Welcome < Sinatra::Base
get '/' do
'Welcome to modular Sinatra!'
end
run!
end
class Blog < Sinatra::Base
set :root, 'some/path'
Article.each do |article|
get "/#{article.slug}" do
erb :post, locals: {article: article}
end
end
get '/' do
@articles = Article.all_sorted_by_publish_date
erb :index
end
end
Collection of common Sinatra extensions, semi-officially supported.
GitHub repo: sinatra/sinatra-contrib