A Quick Introduction to Rack

Embed Size (px)

Citation preview

  • 8/12/2019 A Quick Introduction to Rack

    1/21

    A Quick Introduction to Rack

    Whats Rack?

    In the words of the author of Rack Christian Neukirchen: Rack aims to provide a minimalAPI for connecting web servers supporting Ruby (like W!rick" #ongrel etc$% and Ruby

    web frameworks (like Rails" &inatra etc$%$

    Web frameworks such as &inatra are built on top of Rack or have a Rack interface for

    allowing web application servers to connect to them$

    'he premise of Rack is simple it ust allows you to easily deal with )''P re*uests$

    )''P is a simple protocol: it basically describes the activity of a client sending a )''P

    re*uest to a server and the server returning a )''P response$ !oth )''P re*uest and )''P

    response in turn have very similar structures$ A )''P re*uest is a triplet consisting of a

    method and resource pair" a set of headers and an optional body while a )''P response is in

    triplet consisting of a response code" a set of headers and an optional body$

    Rack maps closely to this$ A Rack application is a Ruby obect that has a callmethod"

    which has a single argument" the environment" (corresponding to a )''P re*uest% and returns

    an array of + elements"status" headersand body(corresponding to a )''P response%$

    Rack includes handlersthat connect Rack to all these web application servers (W!rick"

    #ongrel etc$%$

    Rack includes adaptersthat connect Rack to various web frameworks (&inatra" Rails etc$%$

    !etween the server and the framework" Rack can be customi,ed to your applications needs

    using middleware$ 'he fundamental idea behind Rack middleware is come between the

    calling client and the server" process the )''P re*uest before sending it to the server" and

    processing the )''P response before returning it to the client$

    A Rack App

    Documentation

    http:--rack$rubyforge$org-doc-

    .

    http://rack.rubyforge.org/doc/http://rack.rubyforge.org/doc/
  • 8/12/2019 A Quick Introduction to Rack

    2/21

    Installing Rack

    /ote that I have Ruby .$0$1 installed on a Windows bo2 and all the programs in this article

    have been tested using that$

    3et4s check if we already have rack with us$ 5pen a command window and type:

    irb --simple-prompt>> require 'rack'=> true>>

    6es" rack4s already there on your machine$ If rack4s not there you will get an error like:

    LoadError: no such file to load -- rack

    6ou can install rack by opening a new command window and typing:

    gem install rack

    A quick visit to Rubys roc ob!ect

    Remember theproc obect from Ruby7Blocks are not objects" but they can be converted into

    obects of class Proc$ 'his can be done by calling the lambdamethod of the class Object$ A

    block created with lambdaacts like a Ruby method$ 'he class Prochas a method callthat

    invokes the block$

    In irb type:

    >> m!rack!proc = lambda "puts '#ack $ntro'%=> &Proc:()*fc+(,./irb0:1/lambda0>>> & method call in2okes the block3> m!rack!proc4call#ack $ntro=> nil>>

    A simle Rack a " my#rack#roc

    As mentioned earlier" our simple Rack application is a Ruby obect (not a class% that respondsto calland takes e2actly one argument" the environment$ 'he environmentmust be a true

    instance of 5ash$ 'he app should return an Array of e2actly three values: thestatuscode (it

    must be greater than or e*ual to .88%" the headers(must be a hash%" and the body(the body

    commonly is an Array of &trings" the application instance itself" or a 9ilelike obect$ 'he

    bodymust respond to method eachand must only yield 6tringvalues$% 3et us create our

    new procobect$ 'ype:

    >> m!rack!proc = lambda " 7en27 81((9 "%9 85ello4 ;he time is&";ime4no &Proc:()*fc,?./irb0:?/lambda0>

    >>

    1

    http://rubylearning.com/satishtalim/ruby_procs.htmlhttp://rubylearning.com/satishtalim/ruby_procs.html
  • 8/12/2019 A Quick Introduction to Rack

    3/21

    /ow we can call the proc obect my;rack;proc with the callmethod$ 'ype:

    >> m!rack!proc4call/"%0=> 81((9 "%9 85ello4 ;he time is 1(**-*(-1 (+:*:?@ A(?,(>>

    my#rack#rocis our single line Rack application$

    In the above e2ample" we have used an empty hash for headers$ Instead" let4s have something

    in the header as follows:

    >> m!rack!proc = lambda " 7en27 81((9 "Bontent-;pe => te)tCplain%985ello4 ;he time is &";ime4no &Proc:()*fc,?./irb0:?/lambda0>>>

    /ow we can call the proc obect my;rack;proc with the callmethod$ 'ype:

    >> m!rack!proc4call/"%0=> 81((9 "Bontent-;pe => te)tCplain%9 85ello4 ;he time is 1(**-*(-1(+:*:?@ A(?,(>>

    We can run our Rack application (my#rack#roc% with any of the Rack handlers$

    'o look at the Rack handlers available" in irb type:

    >> #ack::5andler4constants=> 8:BD$9 :astBD$9 :Fongrel9 :E2entedFongrel9 :6>

    'o get a handler for say W$%rick(the default W$%rick" web application server" that comes

    along with Ruby%" type:

    >> #ack::5andler::GEHrick=> #ack::5andler::GEHrick>>

    All of these handlers have a common method called runto run all the Rack based

    applications$

    >> #ack::5andler::GEHrick4run m!rack!proc81(**-*(-1 *(:((:? $IO GEHrick *4,4*81(**-*(-1 *(:((:? $IO rub *4+41 /1(**-(J-(+0 8i,@-ming

  • 8/12/2019 A Quick Introduction to Rack

    4/21

    Note: If you already have something running at port > def m!method en2>> 81((9 "%9 8method called>> end=> nil

    We declare a method my;method that takes an argument env$ 'he method returns three

    values$

    /e2t type:

    >> #ack::5andler::GEHrick4run method/:m!method081(**-*(-1 *:,1:(? $IO GEHrick *4,4*81(**-*(-1 *:,1:(? $IO rub *4+41 /1(**-(J-(+0 8i,@-ming

  • 8/12/2019 A Quick Introduction to Rack

    5/21

    rackup is a useful tool for running Rack applications$ rackup automatically figures out the

    environment it is run in" and runs your application as 9astBI" BI" or standalone with

    #ongrel or W!rick all from the same configuration$

    'o use rackup" you4ll need to supply it with a rackup config file$ !y convention" you should

    use $ru e2tension for a rackup config file$ &upply it a run Rack5bect and you4re ready to go:

    K rackup config4ru

    !y default" rackup will start a server on port 0101$

    'o view rackup help" open a command window and type:

    K rackup --help

    3et us create a config$ru file that contains the following:

    run lambda " 7en27 81((9 "Bontent-;pe => te)tCplain%9 85ello4 ;hetime is &";ime4no te)tChtml%9 85ello #ack Participantsend

    end

    Also" our config$ru will change to:

    require '4Cm!app'run Fpp4ne te)tChtml%9 85ello #ack Participants fromacross the globeend

    end

    'o run our rack app" in the same folder that contains config$ru" type:

    K rackup config4ru

    5pen a browser window and type the url: http:--localhost:0101-$

    In your browser window" you should see something like this:

    5ello #ack Participants from across the globe

    #odify my;app$rb and instead of F)ello Rack Participants from across the globeH type

    F)ello Rack Participants from across the worldL

    Refresh the browser window and you should see:

    5ello #ack Participants from across the

  • 8/12/2019 A Quick Introduction to Rack

    10/21

    9or all practical purposes" a middleware is a rack application that wraps up an inner

    application$

    5ur middleware is going to do something very simple$ We will append some te2t to the body

    being sent by our inner application$ 3et us write our own middleware$ We will create a

    middleware class called F#ackFiddle

  • 8/12/2019 A Quick Introduction to Rack

    11/21

    81((9 "Bontent-;pe => te)tChtml%9 85ello #ack Participants fromacross the globeend

    end

    dit config$ru to have:

    require '4Cm!app'require '4Cmrackmiddle

  • 8/12/2019 A Quick Introduction to Rack

    12/21

    In order for )eroku to know what to do with our &inatra app" create a config$ru in the folder

    racksinatra:

    require 4Cm!sinatrarun 6inatra::pplication

    #odify the &inatra app to use our middleware:

    & m!sinatra4rbrequire 'sinatra'require '4Crackmiddle

  • 8/12/2019 A Quick Introduction to Rack

    13/21

    K git push heroku master

    'he app is now running on )eroku$ 6ou can take a look at it" in your browser type:

    http:--racksinatra$heroku$com-$

    'o check whether the MMM Response 'ime MMM: has been displayed on the )eroku console"type:

    K heroku logs -s app -n ?

    where -s appshows the output from your app and -n ?retrieves C log lines$

    9or me it showed:

    38,@m1(**-*(-1@;(?:(:(@A((:(( app8

  • 8/12/2019 A Quick Introduction to Rack

    14/21

    Rack from the Beginning

    Nuly .@" 18.1 O rack tutorials

    Rack is the )''P interface for Ruby$ Rack defines a standard interface for interacting with

    )''P and connecting web servers$ Rack makes it easy to write )''P facing applications inRuby$ Rack applications are shockingly simple$ 'here is the code that accepts a re*uest and

    code serves the response$ Rack defines the interface between the two$

    Dead Simple Rack Applications

    Rack applications are obects that respond to call$ 'hey must return a triplet$ A triplet

    contains the status code" headers" and body$ )ereQs an e2ample class that shows hello

    world$

    class 5elloGorld def response 81((9 "%9 '5ello Gorld' endend

    'his class is not a Rack application$ It demonstrates what a triplet looks like$ 'he first

    element is the )''P response code$ 'he second is a hash of headers$ 'he third is an

    enumerable obect representing the body$ We can use our hello world class to create a simple

    rack app$ We know that we need to create an obect that responds to call$ calltakes one

    argument: the rack environment$ WeQll come back to the en2later$

    class 5elloGorldpp def self4call/en20 5ello

  • 8/12/2019 A Quick Introduction to Rack

    15/21

    #ack::6er2er4start :app => 5elloGorldpp

    )ereQs what happens when you run this script:

    K rub hello!> ;hin > Fa)imum connections set to *(1>> Listening on (4(4(4(:((9 B;#LAB to stop

    &imply open http:CClocalhost:((and youQll see )ello World in the browser$ ItQs not

    fancy but you ust wrote your first rack appL We didnQt write our own server and thatQs ok$

    #atter of fact" thatQs fantastic$ 5dds are you will never need to write your own server$ 'here

    are plenty of servers to choose from: 'hin" Gnicorn" Rainbows" Boliath" Puma" and

    Passenger$ 6ou donQt want to write those$ 6ou want to write applications$ 'hatQs what we

    wrote$

    Env

    I skipped over over what en2is in the previous section$ 'hatQs because we didnQt need it yet$

    'he en2is a 5ashthat meets the rack spec$ 6ou can read the spec here$It defines incoming

    information$ 5utgoing data must be triplets$ 'he en2gives you access to incoming headers"

    host info" *uery string and other common information$ 'he en2is passed to the application

    which decides what to do$ 5ur 5elloGorldppdidnQt care about it$ 3etQs update our

    5elloGorldppto interact with incoming information$

    class 5elloGorldpp def self4call/en20 81((9 "%9 5ello Gorld4 Mou said: &"en28'QRE#M!6;#$ID'% endend

    #ack::6er2er4start :app => 5elloGorldpp

    /ow visit http:CClocalhost:((3message=foo and youQll see messagefoo on the

    page$ If your more curious about en2you can do this:

    class En2$nspector def self4call/en20 81((9 "%9 en24inspect endend

    #ack::6er2er4start :app => En2$nspector

    )ereQs the tl?dr of what basic en2looks like$ ItQs ust a standard )ash instance$

    " 6E#SE#!6O;G#E=>thin *44* codename Bhromeo9 6E#SE#!IFE=>localhost9 rack4input=>&6tring$O:()((Jfa*bce(,+f>9 rack42ersion=>8*9 (9

    rack4errors=>&$O:6;TE##>>9 rack4multithread=>false9

    .C

    http://rack.rubyforge.org/doc/SPEC.htmlhttp://rack.rubyforge.org/doc/SPEC.htmlhttp://rack.rubyforge.org/doc/SPEC.htmlhttp://rack.rubyforge.org/doc/SPEC.html
  • 8/12/2019 A Quick Introduction to Rack

    16/21

    rack4multiprocess=>false9 rack4run!once=>false9 #EQRE6;!FE;5OT=>DE;9 #EQRE6;!P;5=>Cfa2icon4ico9 P;5!$IO=>Cfa2icon4ico9 #EQRE6;!R#$=>Cfa2icon4ico9

    5;;P!SE#6$OI=>5;;PC*4*9 5;;P!5O6;=>localhost:((9 5;;P!BOIIEB;$OI=>keep-ali2e9 5;;P!BBEP;=>C9 5;;P!R6E#!DEI;=> FoillaC?4( /FacintoshU $ntel Fac O6 V *(!J!0 ppleGebWitC?,@4**/W5;FL9 like Decko0 BhromeC1(4(4**,14J 6afariC?,@4**9 5;;P!BBEP;!EIBOT$ID=>gip9deflate9sdch9 5;;P!BBEP;!LIDRDE=>en-R69enUq=(49 5;;P!BBEP;!B5#6E;=>$6O-?+-*9utf-Uq=(4J9Uq=(4,9 5;;P!BOOW$E=> !gauges!unique!ear=*U !gauges!unique!month=*9 D;EGM!$I;E#BE=>BD$C*419 6E#SE#!PO#;=>((9

    QRE#M!6;#$ID=>9 6E#SE#!P#O;OBOL=>5;;PC*4*9 rack4url!scheme=>http9 6B#$P;!IFE=>9 #EFO;E!TT#=>*1J4(4(4*9 asnc4callback=>&Fethod: ;hin::Bonnection&post!process>9 asnc4close=>&E2entFachine::TefaultTeferrable:()((Jfa*bce,?b%

    6ou may have noticed that the en2doesnQt do any fancy parsing$ 'he *uery string wasnQt a

    hash$ It was the string$ 'he en2is raw data$ I like this design principle a lot$ Rack is very

    simple to understand and use$ If you wanted you could only work with hashes and triplets$

    )owever thatQs ust tedious$ omple2 applications need abstractions$ nter #ack::#equestand #ack::#esponse$

    Abstractions

    #ack::#equestis an abstraction around the en2hash$ It provides access to thinks like

    cookies" P5&' paramters" and other common things$ It removes boiler plate code$ )ereQs an

    e2ample$

    class 5elloGorldpp def self4call/en20

    request = #ack::#equest4ne< en2 request4params & contains the union of DE; and PO6; params request4)hr3 & requested

  • 8/12/2019 A Quick Introduction to Rack

    17/21

    #ack::#esponseis an abstraction around generating response triplets$ It simplifies access to

    headers" cookies" and the body$ )ereQs an e2ample:

    class 5elloGorldpp def self4call/en20 response = #ack::#esponse4ne< response4

  • 8/12/2019 A Quick Introduction to Rack

    18/21

    that Rack includes a class to make this easy$ !efore we move to the ne2t step" letQs define

    what a middleware looks like:

    class Fiddle

  • 8/12/2019 A Quick Introduction to Rack

    19/21

    endendclass ;imer def initialie/app0 .app = app end

    def call/en20 before = ;ime4no< status9 headers9 bod = .app4call en2

    headers8'V-;iming' = /;ime4no< - before04to!i4to!s

    8status9 headers9 bod endend

    /ow we can use those middlewares in our app$

    app = #ack::Huilder4ne< douse ;imer & put the timer at the top so it captures e2erthing belo< it

    use EnsureXson#esponse run 5elloGorldppend

    #ack::6er2er4start :app => app

    WeQve ust written our own middeware and learned how to generate a runnable application

    with a middleware stack$ 'his is how rack apps are written in practice$ /ow onto the final

    piece of the pu,,le: config4ru

    Rackup

    6ou may have seen the rackupcommand referenced before$ ItQs provided by the rack gem$ It

    provides a simple way to start a web process using one of the rack servers installed on the

    system$ It looks for config4ruby default$ config4rudefines what ruby code the server

    should call$ ItQs wrapped in a #ack::Huilderas shown before$ )ereQs all the work weQve

    done up to now in config4ru

    & config4ru

    & 5elloGorldpp defintion& EnsureXson#esponse defintion& ;imer definition

    use ;imeruse EnsureXson#esponserun 5elloGorldpp

    /ow navigate into the correct directory and run: rackupand youQll see:

    K rackup>> ;hin > Fa)imum connections set to *(1

    >> Listening on (4(4(4(:((9 B;#LAB to stop

    .0

  • 8/12/2019 A Quick Introduction to Rack

    20/21

    Rackup will prefer better servers like 'hin over We!rick$ 'hereQs nothing super fancy going

    on here$ 'he code inside config4ruis evaluated and built using a #ack::Huilderwhich

    generates an API compliant obect$ 'he obect is passed to the rack server ('hin% in this case$

    'hin puts the app online$

    Rails & Rack

    Rails +S is fully Rack compliant$ A Rails + application is more comple2 Rack app$ It has a

    comple2 middleware stack$ 'he dispatcher is the final middlware$ 'he dispatcher reads the

    routing table and calls the correct controller and method$ )ereQs the stock middleware stack

    used in production:

    use #ack::Bacheuse ctionTispatch::6taticuse #ack::Lockuse

    &cti2e6upport::Bache::6trateg::LocalBache::Fiddleuse #ack::#untimeuse #ack::FethodO2errideuse ctionTispatch::#equest$duse #ails::#ack::Loggeruse ctionTispatch::6ho

  • 8/12/2019 A Quick Introduction to Rack

    21/21

    & the super class methoddef call/en20 app4call/en24mergeN/en2!config00end

    & app method in the super class

    def app .app 77= begin config4middle