Rack to bardzo prosty protokół, który specyfikuje sposób komunikacji serwera HTTP z obiektem aplikacji. Protokół ten mówi, że obiekt aplikacji musi odpowiadać na metodę call
. Serwer wywoła tą metodę z jednym parametrem, w którym zawarte będą wszystkie niezbędne informacje o żądaniu (jest to Hash
zawierający takie informacje jak ścieżka żądania, słowo HTTP, nagłówki, etc.).
Najprostszy przykład ilustrujący Racka mieści się w kilku ledwie linijkach:
require 'rack' require 'pp' class Racking def call(env) pp env return [200, {}, [""]] end end Rack::Handler::Thin.run(Racking.new, Port: 9000)
Po wizycie na localhost:9000
na wyjściu programu pojawi się wspomniany wcześniej Hash
z informacjami o żądaniu:
{"SERVER_SOFTWARE"=>"thin 1.5.0 codename Knife", "SERVER_NAME"=>"localhost", "rack.input"=>#<StringIO:0x007ff6c4b0c968>, "rack.version"=>[1, 0], "rack.errors"=>#<IO:<STDERR>>, "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:9000", "HTTP_CONNECTION"=>"keep-alive", "HTTP_USER_AGENT"=> "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.101 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"=>"9000", "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:0x007ff6c4b32ac8>}
Metoda call
ma zwracać tablicę z trzema elementami:
hash
lub podobny hashowi obiekt, zawierający nagłówki odpowiedzi (rozmiar odpowiedzi, czy cache'ować, etc.),each
i wywołać podany blok string
ami.
Najczęściej uruchamia się Rack
a za pomocą komendy rackup
, która korzysta z pliku konfiguracyjnego config.ru
. Przykładowy plik mógłby wyglądać następująco:
Specyfikacja przewiduje również możliwość tworzenia łańcuchów filtrów i routerów przed aplikacją. Te są ogólnie nazywane jako middleware
. One również odpowiadają na metodę call
i zwracają omówioną wcześniej tablicę. Taki filter może zmodyfikować żądanie, przekazać je lub zatrzymać.
middleware
jest tworzony poprzez obiekt factory
. Taki obiekt musi odpowiadać na metodę new
, przymującą conajmniej jeden parametr - aplikację (może nią być kolejny middleware
), do której ma przekazać zmodyfikowany hash
z żądaniem.
Utworzyć własny
class MyMiddleware def initialize(app) @app = app end def call(env) if env['PATH_INFO'] == '/' @app.call(env) else [404, {'Content-Type' => 'text/plain'}, ['not ok']] end end end
Na żądania poza ścieżkę główną, wysłana zostanie odpowiedź 404, not ok
.