Download - A Quick Introduction to Rack
-
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