Utforsker rack

Hvis du er en Ruby-programmør som har gjort noen form for webutvikling, har du nesten sikkert brukt Rack, uansett om du vet det eller ikke, da det er grunnlaget som de fleste Ruby-webrammer (Rails, Sinatra, etc.) er bygget på. på. La oss grave i noen av de grunnleggende konseptene til Rack og til og med bygge en liten app eller to.


Hva er rack, nøyaktig?

Rack er flere ting, faktisk:

  • et webservergrensesnitt
  • en protokoll for å bygge og komponere webapplikasjoner
  • en samling av mellomvareverktøy

Et webservergrensesnitt

En del av det som er fint om Rack, er at det gir en standardisert måte for Ruby-applikasjoner å snakke med webservere, som trekker ut serverens ting (lytter på en port, aksepterer tilkoblinger, analyserer HTTP-forespørsler og svar etc.) slik at du kan fokus på hva søknaden din gjør.


Protokollen

Rack-protokollen er veldig enkel: en Rack-applikasjon er rett og slett en Ruby-gjenstand med a anrop metode. Denne metoden bør godta en miljøhash som beskriver den innkommende forespørselen og returnerer en treelement-array i form av: [status, headers, body], hvor:

  • status er HTTP-statuskoden.
  • overskrifter er en hash av HTTP headers for svaret.
  • kropp er den faktiske kroppen av svaret (for eksempel HTML du vil
    vise). Kroppen må også svare på Hver.

Den enkleste måten å forstå Racks protokoll på, er å se på noen kode.

Først må du få rack-perlen og sette opp en katalog:

 $ perle installasjonshylle $ mkdir hellorack $ cd hellorack

Lag nå en fil med navnet config.ru og fyll det inn med følgende:

 klasse Hei def self.call (env) [200, # 200 indikerer suksess "Content-Type" => "text / plain", # hash of headers ["Hello from Rack!"] # vi pakker inn kroppen en Array slik at den reagerer på "hver"] slutten # Tell Rack hva du skal kjøre vår app kjøre Hello

Lagre filen, åpne terminalen og kjør rackup kommando:

 $ rackup [2012-12-21 17:48:38] INFO WEBrick 1.3.1 [2012-12-21 17:48:38] INFO rubin 1.9.2 (2011-07-09) [x86_64-darwin11.0.1] [2012-12-21 17:48:38] INFO WEBrick :: HTTPServer # start: pid = 1597 port = 9292

De nederste utgangslinjene i koden ovenfor forteller deg at Rack kjører appen din på port 9292 ved hjelp av Ruby's innebygde WEBrick webserver. Pek nettleseren din til http: // localhost: 9292 for å se en lykkelig velkomstmelding fra Rack.

Drep appen (ctrl-c) og la oss snakke om hva som skjer her.

Når du kjører rackup kommando, ser Rack etter en rackup config fil (konvensjonelt navngitt config.ru, men du kan nevne det du vil). Den starter så en webserver (WEBrick som standard) og kjører appen din.

Denne protokollen er grunnlaget for hvilke populære rammer som Rails og Sinatra er bygget. Det de gjør er lagfunksjonalitet som malgjenoppretting, ruteforsendelse, administrering av databaseforbindelser, innholdsforhandlinger etc. på toppen av denne grunnleggende abstraksjonen.

Hvordan de gjør dette, er det som bringer oss til begrepet mellomvare.


Middleware

Middleware gir deg mulighet til å komponere Rack-programmer sammen.

En middleware (ja, det er både singular og flertall) er ganske enkelt en Rack-applikasjon som blir initialisert med en annen Rack-applikasjon. Du kan definere forskjellig mellomvare for å gjøre forskjellige jobber og deretter stable dem sammen for å gjøre nyttige ting.

For eksempel, hvis du har en Rails-app som ligger rundt (sjansen er, hvis du er en Ruby-utvikler, det du gjør), kan du cd inn i appen og kjør kommandoen rake middleware for å se hvilke middleware Rails bruker:

 $ cd my-rails-app $ rake mellomvarebruk ActionDispatch :: Statisk bruk Rack :: Lås bruk # bruk Rack :: Runtime bruk Rack :: MethodOverride bruk ActionDispatch :: RequestId bruk Rails :: Rack :: Logger bruker ActionDispatch :: ShowExceptions bruk ActionDispatch :: DebugExceptions bruk ActionDispatch :: RemoteIp bruk ActionDispatch :: Reloader bruk ActionDispatch :: Tilbakeringinger bruker ActiveRecord :: ConnectionAdapters :: ConnectionManagement bruk ActiveRecord :: QueryCache bruk ActionDispatch :: Cookies bruker ActionDispatch :: Session :: CookieStore bruk ActionDispatch :: Flash bruk ActionDispatch :: ParamsParser bruker ActionDispatch :: Head use Rack :: ConditionalGet bruk Rack :: ETag bruk ActionDispatch :: BestStandardsSupport kjør MyRailsApp :: Application.routes

Hver forespørsel som kommer inn i denne appen starter på toppen av denne stakken, bobler seg ned, treffer ruteren nederst, som sendes til en kontroller som genererer en slags respons (vanligvis noen HTML), som deretter bobler seg tilbake opp gjennom stakken før du sendes tilbake til nettleseren.


Et mellomvareeksempel

Ingenting foster å forstå et nytt konsept som koding gjør, så la oss bygge en veldig enkel mellomvare som bare konverterer svaret kroppen til store bokstaver. Åpne opp vår config.ru filen fra før og legg til følgende:

 klasse ToUpper # Vår klasse vil bli initialisert med en annen Rack app def initialiserer (app) @app = app end def call (env) # Først ring "@app" status, overskrifter, body = @ app.call (env) # Iterate gjennom kroppen, opphøier hver brikke upcased_body = body.map | chunk | chunk.upcase # Pass på vår nye kropp på gjennom [status, headers, upcased_body] slutten # Dette er det samme Hello app fra før, bare uten alle kommentarsklassene Hei def self.call (env) [200, "Innhold -Type "=>" tekst / vanlig ", [" Hei fra rack! "]] Sluttbruker ToUpper # Fortell rack for å bruke vår nylig minteed middleware-run Hello

Kjør rackup kommandoen igjen og besøk http: // localhost: 9292 for å se vår nye middleware i action.

Hva Rack gjorde her var å bygge en Rack-applikasjon som var sammensetningen av ToUpper og Hallo applikasjoner. Intern til Rack, det er en Bygger klasse som effektivt konstruerte en ny app ved å gjøre tilsvarende:

 app = ToUpper.new (Hei) kjøre app

Hvis det var mer mellomvare tilstede (som i Rails stack), ville det bare neste dem helt ned:

 bruk Middleware1 bruk Middleware2 bruk Middleware3 kjøre MyApp # => Koker ned til Middleware1.new (Middleware2.new (Middleware3.new (MyApp)))

Forespørsel og responsklasse

Når du begynner å skrive Rack-applikasjoner og mellomvare, manipulerer du [status, headers, body] array blir raskt kjedelig.

Rack gir et par bekvemmelighetsklasser, Rack :: Request og Rack :: Response, å gjøre livet litt lettere.

Rack :: Request wraps an env hash og gir deg enkle metoder for å trekke ut informasjonen du måtte trenge:

 def call (env) req = Rack :: Request.new (env) req.request_method # => GET, POST, PUT, etc. req.get? # er dette en GET-forespørsel req.path_info # banen denne forespørselen kom inn på req.session # tilgang til sesjonsobjektet, hvis du bruker # Rack :: Session middleware req.params # en hash av fusjonerte GET og POST-parametere, nyttig for # trekke verdier ut av en spørringsstreng # ... og mange flere ender

Rack :: Response er utfyllende til Rack :: Request, og gir deg en mer praktisk måte å konstruere et svar på. For eksempel, vår Hallo appen kunne omskrives på følgende måte:

 klasse Hei def self.call (env) res = Rack :: Response.new # Dette vil automatisk sette Innholdslengde header for deg res.write "Hei fra Rack!" # return ["Content-Type"] = "text / plain" # Du kan angi og slette informasjonskapsler # res.set_cookie ("user_id", 1) # res.delete_cookie ("user_id") endeenden

Konklusjon

I denne artikkelen har vi dekket de grunnleggende konseptene for Rack, som skal være nok til at du får en bedre forståelse av hva som ligger under hetten til de mange populære rammene der ute, og også hjelper deg å få føttene våte hvis du er interessert i å utvikle seg direkte med rack.

Koden er en utmerket lærer, og så hvis du er interessert i Rack, anbefaler jeg sterkt å se på kilden. Den leveres med mange svært nyttige innbakt verktøy og mellomvare (og mye mer på rack-contrib) som du kan bruke og lære av.