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.).

Uruchamianie

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

Metoda call ma zwracać tablicę z trzema elementami:

config.ru

Najczęściej uruchamia się Racka za pomocą komendy rackup, która korzysta z pliku konfiguracyjnego config.ru. Przykładowy plik mógłby wyglądać następująco:

 

Middleware

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ć.

Tworzenie

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.

FIXME 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.