You are on page 1of 147

! " # $ % & ' ( ) * + " , - .

/
)0!*,&$,'11,&
"0*-$*++2
! " # $ % & ' ( ) * + " , - . /
)0!*,&$,'11,&
"0*-$*++2
!"#"$%&'()*+,,%
A// contcnt "2006 [crcny /cAna//y. A// Rtgbt Rcscr.cd.
Tbat ncans don't copy tt.
For ny ut/c, /rtcnds, and /ant/y,
tbank you /or tbc support and /ood.
/ost/y tbc /ood.
!"#$%&'(&)'*!%*!+
!
What'chu talkin' 'bout, Mistcr? !
What Is Ruby Anyhow?!!
Installing Ruby "
#$%&'()*"*+*,-.*/0*1*"*+*2$%34*5
Lct's try hcr out! !6
"
Wclcomc to Ruby 78
Basic Conccpts of Ruby 78
Typcs in Ruby !77
09:$%;)**77*+*<3=>?:)**7@
Collcctions!!7!
AB?*C-%;?**7D*+*AB?*E::-F**7"*+*AB?*G-)B**H8
Variablcs and thc Likc!*H@
#
Brcak it down now! H5
Mcthods!!H5
I?J$%$%;*,?9B'&)**H6*+*K)$%;*,?9B'&)**@8
Blocks and Proc Objccts!!@7
LM'.N*L-)$.)**@7*+*O:'.)*-%&*LM'.N)**@@*+*L3$M&$%;*LM'.N)*@D*
******Your objccts lack class! *@"
I?J$%$%;*PM-))?)**@5*+*,?9B'&)*-%&*Q-:$->M?)**@6*+*E99:$>39?)**!8*+
E..?))*P'%9:'M**!7*+
PM-))*0.'R?&*/>S?.9)**!H
Modulcs!!!!
P:?-9$%;*,'&3M?)**!!
Filcs**!"
!"
Hustlc and flow {control) !"
Conditionals!!!"
#$%&'(&)*+*%,%-*&&!"&.&#$%&/+)%&0*+*%,%-*&&12
Loops!!13
45-6'*'5-+7&8559)&&&13&.&:*%;+*'-<&8559)&+-6&=75/>)&&&1!&.&0*+*%,%-*&
?56'('%;)&&11&.&&45-*;577'-<&8559)&&1@
Exccptions!!1"
A+-67'-<&BC/%9*'5-)&&1"&.&D+')'-<&BC/%9*'5-)&&@2&.&?E&FG-&BC/%9*'5-&
@H&.&#$;5G&+-6&4+*/$&&@H
#"
Thc Systcm Bcncath @!
Filcsystcm Intcraction !!@!
I;'*'-<&*5&+&('7%&&@@&.&?5;%&('7%&59%;+*'5-)&&@J
Thrcads and Forks and Proccsscs, Oh My! !@"
DKLE&*$;%+6&L+)'/)&&&@"&.&45-*;577'-<&*$;%+6)&&&JM&.&N%**'-<&'-(5;,+*'5-&
(;5,&*$;%+6)&J2&.&O;5/%))%)P&*$%&5*$%;&G+E&*5&65&)*K((&&JH
For thc Environmcnt! !J3
B-Q';5-,%-*&Q+;'+L7%)&+-6&*$%&7'>%&&J3&.&#$%&/5,,+-6&7'-%&+-6&E5K&&J3&. &
DKLE&+-6&'*)&7'**7%&/5;-%;&5(&E5K;&/5,9K*%;&&J!
Win32 and Bcyond !J1
RO:&&J1&.&#$%&D%<')*;E&&JJ&.&F8B&RK*5,+*'5-&&JS
$
Looking Bcyond Homc "3
Nctworking and thc Wcb!!"3
05/>%*&O;5<;+,,'-<&&"3&.&A##O&T%*G5;>'-<&&"@&F*$%;&T%*G5;>&
0%;Q'/%)&&SH&&.&&I%L&0%;Q'/%)&&S1
It's Likc Distributcd or Somcthing... "#
Data my basc, plcasc! &S"
%
It's a Library! 2M2
String Manipulation!!2M2
:-)*+-/%&?%*$56)&&2M2&.&D%<K7+;&BC9;%))'5-)&&2M!
Datc/Timc!!2M@
U+*%)&&2M@&.&#',%)&&2MJ&.&U+*%)&+-6&#',%)&&2MS
Hashing and Cryptography!!2MS
A+)$'-<&&2MS&&.&4;E9*5<;+9$E&&22M
Unit tcsting 222
&''()*+,"& Links and thc Likc 22@
&''()*+,"- High Pcrformancc Ruby with C/C++ 22"
)&-./"
In tLe lollowing book, I will be using!"#$%!&'(') to test all tLe Ruby
code. EacL example can be copied and pasted directly into *+$/,-+*!
and it sLould work lamously. I Lave done so witL eacL one to make
sure tLey run.
EacL time I am sLowing output lrom *+$, you will see a ! cLaracter
lollowed by tLe output. Any metLod or variable name or
code/system related text is typeset in ./*0!,12. lor easy discernment
lrom otLer text.
Any time I Lave lound it necessary to dillerentiate a class object
lrom an instance object, I Lave erred on tLe side ol standard
notation and went witL tLe lorm ol 3450067$89:. (even tLougL it's
very ugly and is not wLat tLe rest ol tLe civilized world uses).
"I gotta go. 1Lere's a dude next to me and
Le's watcLing me type, wLicL is sort ol
starting to creep me out. Yes dude next to
me, I mean you."
Ntgbt-bcn-gay/c (basb.orgj
!
!"#$%&"'($#)*+,%(
%-.'$/(0+1$234
tlere is a Clapter 0. Tlere is a little bit ol introductory stull we
need to talk about belore we set you loose on Ruby. You wouldn't
want to get psycled about a new gadget, get it lome, and tlen ligure out you
need batteries, a grapelruit, and tle ability to speak tlree languages to even
open tle box would you? You would? Well tlen answer me tlis: How would
Ruby react to large, elastic monsters taking over an estuary? You don't know
tle answer!? Well, plainly we need to take a good, lard look at a lew tlings
belore we turn you loose.
Yes
!567(89(:;<=(6>=5?!4
Ruby is an open-source, multi-paradigm, interpreted programming language (a
bit ol a moutllul I know! I'm going to explain it, I promise!). Ruby was created
by Yukiliro "Matz" Matsumoto, a very line Japanese gentleman wlo currently
resides in Slimane Prelecture, Japan, Matz's work on tle language was started
on Iebruary 24, l993 (commonly considered tle birtlday ol tle language, I
lear tlat over in Japan tley roll out a two-story cake and sing) and released to
tle public in l995. Ruby is olten lailed as one tle most expressive and concise
languages available to developers today. In tlat spirit ol expressiveness, let's
look at exactly wlat it all means. Let us now eviscerate tlese verbal lurbelows
witl conviction!
Opcn Sourcc Tle ollicial implementation ol tle language is lree soltware
distributed under tle CPL and tle Ruby open source license. Il you're unaware
ol wlat "open source" means, tlen look at it tlis way: Matz programmed tle
entire Ruby interpreter and library, tlen gave away tle code le used to do it. Since tle
source code is available, people can now take it and improve it. Many people take tle
code, improve it, and Matz (and lis crack team ol maintainers) integrate tleir
clanges back into tle main source code. Tle benelit ol open source is clielly tlat you
get a lot more minds working on a pro|ect tlan a proprietary pro|ect (and typically lor
lree to boot!).
Multi-Paradigm Like to write code in a lunctional style a la Haskell or Scleme?
Tlat's cool, Ruby does tlat. Really dig ob|ect orientation like Java or Smalltalk? No
problem, Ruby landles tlat, too. Preler to use a procedural (a.k.a. imperative) style
like Iortl or Ada? Ruby can "get its procedure on" as good as any otler language!
Don't know wlat any ol tlose mean but |ust really want to program? Excellent! Ruby
is a multi-paradigm language because it doesn't constrain to a single programming
mindset, you can use any ol tle alorementioned programming paradigms witl no
problems in Ruby. You can pick tle one you preler (or tle easiest lor you to learn) and
go witl it: Ruby doesn't mind. Unlike some otler languages, it doesn't get |ealous
and give you "errors" il you break it oll witl ob|ects and decide to go steady witl
closures instead.
Intcrprctcd Il you've used sometling like Assembly, Pascal, Iortran, or C/C++,
you've used a compiled language. "Compiled" means tlat you've lad to run your code
tlrougl a little compiler and it spits out some sort ol native code tlat will run witlout
any sort ol interpretation by tle computer otler tlan by tle operating system itsell.
Tlis can become time consuming as your pro|ect grows larger and larger, and
sometimes can even be a severe lindrance to productivity. Ol, but tlere is anotler
way! Ruby is an interpreted language, wlicl means tlat tlere is an interpreter tlat
reads your code and tlen emits native code to tle operating system. Maybe tlis
diagram will make more sense...
Tlere is a bit more to it tlan tlat (e.g. you lave to coax Jell out ol lis box), but tlat's
tle general concept. Code goes in, Ruby plays around witl it, program comes out.
Wlen running Ruby code, you lave a lew options. Tle lirst option you lave is to
create a lile witl your source code in it, and tlen tell Ruby to execute tlat lile by giving
it as a command line option to tle Juby command. Your next option is tlat you can
use your editor's Ruby runner (il it las one) by using tleir slortcut, lor example,
SciTE las tlis leature tlat you can use by pressing I5. Tle last option is to use an
interactive slell like 1Jb or 1xJ1, tlese slells give you a "Ruby prompt" at wlicl you
can type in lines ol Ruby code and lave tlem immediately executed. I used 1Jb
2!!!!"#$%&"'($#)*+,%(%-.'$/(0+1$234
Figurc 1: An ovcrvicw of how Ruby handlcs programs.
extensively in writing tlis book because it allows speedier leedback tlan running lrom
a lile. Tlese prompts are possible because Ruby is a dynamic language ran in an
interpreter.
Tle interpreted nature ol Ruby allows it to lave many ol tle leatures tlat
make it so great. Compiled programs are not nearly as dynamic as interpreted ones
because tley don't (usually) allow lor runtime clange to code or tle application itsell.
Because interpreted programs are simply, well, interpreted lrom tleir original source
(or a sligltly compiled bytecode), tley can allow lor more lar-reacling runtime
interaction. We'll discuss all ol tlis a lot more later as tlese leatures reveal
tlemselves, now we need to make sure you actually lave Ruby. Otlerwise, tlese
leatures will be like clasing tle wind wlen it's running laster tlan you: meaningless!
8>976@@8>A(:;<=
Sometimes installing a programming environment can be a pain, il you've ever tried to
install a CNU compiler on Windows you know wlat I mean. Iortunately, Ruby is
relatively easy to install on most platlorms.
!+,B.C1
Installing on Windows is a snap, simply navigate over to tle web site at
lttp://www.ruby-lang.org and click on tle "Ruby" link under tle "Download"
sidebar. Tlen click on tle "Install Ruby under Windows" link. Tlat page las a link to
tle installer tlat you need along witl instructions on low to install it (basically,
download, run, and you're done).
To edit Ruby liles, you simply need a text
editor. Tlis could be sometling as simple
as Notepad or as lancy as UltraEdit. Ruby
comes witl a line editor named SciTE
wlicl will properly liglliglt, open, and
save Ruby liles lor you, it also las tle nice
leature ol running your programs lor you
so you don't lave to poke around tle
command line to get tlem going. Tlere are
otler Ruby specilic development
environments (e.g. IreeRIDE, Araclno,
Mondrian, etc.), but tlese environments
are not necessary to do development (i.e. I
don't use tlem).
Il you decide to simply use Notepad or
sometling tlat doesn't lave a leature tlat allows you to run your application lrom
!"#$%&"'($#)*+,%(%-.'$/(0+1$234!!!3
witlin it, tlen you need to lind your programs using tle command line and issue tle
Ruby command to run tlem. Ior example:
Juby mycode11e.Jb
I suggest simply using SciTE to avoid tlis, but sometimes tlis is a necessary evil
(especially il you're already very comlortable witl anotler editor).
0#&(?9(D
Il you're rolling on Jaguar (l0.2) or later, tlen you slould already lave some variant
ol Ruby on tlere. To lind out wlicl version you lave (and to make sure it's
compatible witl tlis book wlicl is based on tle l.8 versions), type tle lollowing in
Terminal:
Juby -v
Tlat slould give you a slort message telling you wlicl version you lave installed. Il
you lave a l.8 version installed, great! Il not, let's install one.
Tle easiest way to install Ruby is going to be using DarwinPorts
(lttp://www.darwinports.org/). Co to tleir website, tlen to tleir download page and
download tle proper .dmg lile lor your version (e.g., lor Tiger/l0.4 you would
download sometling like DarwinPorts-l.2-10.4.dmg). Install tlat as you normally
would. Tlen open Terminal and enter tle lollowing:
sudo poJ1 syhc
sudo poJ1 1hs1a Juby
It will download some liles, compile some tlings, calculate tle airspeed velocity ol an
unladen swallow, and tlen linally you'll lave a working Ruby distribution! Just run
tle above Juby -v command to make sure everytling is in order. Il tlat doesn't
work, tlen go to tle Darwin ports website and cleck tleir support and mailing list.
Editing Ruby liles on Mac OSX can be
done using sometling like Text Editor il
you like to keep tlings simple. Il you
require a little more out ol your
environment, you can splurge on
sometling like TextMate (my Mac IDE
ol cloice). Il you're a lardcore, UNIX-
or-die, console-only kind ol person, tlen
vi or emacs works perlectly line, and a
lot ol work las been done witl tlese
editors to actually make tlem pretty usable Ruby development environments il you
are so inclined to use tlem.
4!!!!"#$%&"'($#)*+,%(%-.'$/(0+1$234
@+,'E
To assess wletler or not you lave Ruby already installed (and you very well may),
type Juby -v on tle command line. Il Linux can't lind Ruby, tlen type wh1ch Juby
on tle command line. Il you are again conlronted witl tle lorrible lact tlat Ruby is
not on your system, prepare to install it. Iortunately, Ruby las widespread support on
Linux, and depending on your variation ol Linux/your environment, you will lave to
do one ol tle lollowing.
Install from Administrator Il you are not tle administrator ol your macline, you
may lave to tlrow yoursell at tle mercy ol your systems administrator. Beg lim or
ler to install it lor you. Oller tlem pastries. Tell tlem tlat you will urinate on tleir
desk il tley don't install it. Wlatever it takes, get Ruby installed!
Install from Packagc To install lrom a package, you will need to consult your
distribution's documentation. Eacl distribution landles tlese sorts ol tlings
dillerently, but il your distribution simply doesn't lave a package you lave options.
Iirst, cleck unollicial repositories sucl as Dag's, Mind's, Sladoi's, or Debian-
Unollicial (or tle tons ol otlers tlat Coogle will turn up il you ask it nicely). Il you
don't lave any luck tlere...
Install from Sourcc Installing lrom source is some people's lirst instinct. You do get
a smidge better perlormance, lor sure, but I'm lasty and like to get tlings done as
quickly as possible. I'd ratler |ust drop a package in and go. Il you're a masoclist or
simply can't lind a package, you can install lrom source. Iirst, go to lttp://www.ruby-
lang.org and download tle source arclive. Tlen, extract it and enter tle source
directory:
1aJ zxv1 Juby-1.8.4.1aJ.gz
cd Juby-1.8.4
Poke around in tlere a bit, you miglt want to read tle license or README to make
sure tlat tlere aren't any gotclas lor your distribution ol Linux. Tlen, you need to
conligure, build, and install it:
./coh11guJe
make
make 1hs1a
You slould be good to go at tlis point. Type Juby -v to make sure tlat it's installed
properly.
Editing Ruby liles on Linux is as simple as using a plain text editor like gEdit
or your lavorite console editor, sucl as vi, emacs, or nano, or one ol tleir X Windows
counterparts like xemacs. Tlere are also more robust environments, sucl as |Edit and
Araclno Ruby tlat you can acquire, but tley are not required.
!"#$%&"'($#)*+,%(%-.'$/(0+1$234!!!5
@F7%9(7:=(5F:(?;7G
Let's give tlis wlole Ruby tling a try. You can eitler use irb or lxri to get instant
leedback or type tle source code in a lile and use Ruby to execute it. Il you want to use
tle lormer, eitler type irb on tle command line or lind lxri in tle program group lor
Ruby. Il you want to simply type it in and execute it, tlen open your lavorite editor
and let's get cracking.
pu1s "heo, woJd."
Tlis is, ol course, tle prequisite lor any programming book. You slould've seen
"Hello, world." il you're using one ol tle interactive slells, il you're placing tlis in a
lile, save tle lile as sometling like lello.rb and tlen type Juby heo.Jb to execute it.
Now, let's make tlis a little more interesting.
pu1s "heo, woJd. Wha1 1s youJ hame?"
myhame = ge1s{)
pu1s "We, heo 1heJe " + myhame + "."
Save tle lile again and run tlis (or type it in your little interpreter), you slould see a
greeting, be asked lor your name, and tlen greeted by name. Il you didn't ligure it out,
pu1s makes text come up on tle console and ge1s gets text lrom tle user. Now tlat
you've got a little Ruby under your belt, you're good to go on Clapter l.
!"#$%&"'()*+%%
You learned a little about Ruby and low to install it. You learned...
! tle listory ol Ruby and wlere it came lrom.
! tle gist ol wlat Ruby is and low it works.
! low to install Ruby.
! a little bit ol Ruby.
6!!!!"#$%&"'($#)*+,%(%-.'$/(0+1$234
"
!2)&.H2($.(:'-IJ
Tlis section aims to introduce tle syntactic sugar and linguistic mislortunes ol Ruby
in tle quickest manner tlat will still allow lor a lull education on tle sub|ect. Il you
rate yoursell a Ruby guru, late language tutorials lor one reason or anotler, or il you
stayed at a Holiday Inn Express last niglt (or tlouglt about it but decided tleir
overpriced accommodations weren't lor you), tlen you may merrily proceed on to tle
next section.
<698K(K?>KFL79(?M(:;<=
Ruby is an ob|ect-oriented language, but belore you skip tlis section because you
tlink you know wlat tlis is all about because you lave used C++ or some otler
unlortunate excuse lor an ob|ect-oriented language, tlen please pause and at least
read tle lollowing sentence. In Ruby, everytling you manipulate will be an ob|ect.
Everytling. Even tle results ol operations on said ob|ects are ob|ects, tlis approacl
dillers lrom C++ or Java wlere primitive types exist or some statements do not return
a value.
Il you lave never delved into ob|ect-oriented programming (or programming
at all), tlen tlat is a dillerent story altogetler. Wlen writing Ruby code, or ob|ect-
oriented code in general, tle idea is to create models in your code tlat render tle
process you are trying to go tlrougl in code. Ior example, il you were creating a
cookbook application, you would probably want to create a list ol recipes (my skills ol
deduction are amazing, I know). To model tlat in a not-so-ob|ect-oriented way, you
would most likely use a series ol list structures ol some sort to lold tle various sorts ol
data witl a synclronized way to track tle position ol eacl list or some sucl nonsense.
Ob|ect-oriented programming simplilies tlis and allows you to create classes and
ob|ects to model tle needed components. Using our example, you could create a
Rec1pe class witl string attributes hame and au1hoJ and a lasl or array attribute ol
1hgJed1eh1s. A class's purpose is to modcl some thing in your application, classes
create tle "prototype" lor tle nouns in your programs: ob|ects. Class instances, or
ob|ects (tle terms are interclangeable), tlen take tlat prototype and put it into
!2)&.52($.(6'-78!!!7
action. In our example, ob|ects could be created lor eacl recipe in tle list tlat would
be instances ol tle class Rec1pe,wlicl would in turn could lold data and do tlings
related to being a recipe (i.e., lold a list ol ingredients, add ingredients to tlat list, and
so on) and enlorce constraints tlat would be enlorced on a normal recipe (i.e., only
allow numeric values lor ingredient amounts, make sure tlere is a name lor tle recipe,
and so on).
7=LF9(8>(:;<=
Just because everytling is an ob|ect in Ruby does not mean tlat everytling is generic
(in tle sense tlat specialized lunctionality does not exist) or tlat tlere are no "built-
in" classes. Ruby provides a number ol built-in classes wlicl act as building blocks
lor tle all ol tle components ol your application. Tlese types diller lrom tlose in
many otler languages in tlat tley all originate lrom tle same class originally: tle
0bec1 class. Tlis lierarcly means tlat tlere is only one "base" type ratler tlan a
number ol primitives like tlere are in languages sucl as C. Wlat lollows is a walk-
tlrougl ol low tlese types diller and wlat tley can oller you as a developer.
9$3+,N1
Tle lirst ol tlese types tlat we will look at are strings, wlicl are simply sequences ol
bytes tlat represent a sequence ol claracters. Strings can be lormed a number ol
ways, but tle most common is likely to be using a string literal. A string literal is a
constant string tlat is created by enclosing it in single or double quotes. Ior example:
pu1s `heo, 0aJ1hg.` "!!!heo, 0aJ1hg.
pu1s `Wha1\`s up?` "!!!Wha1`s up?
pu1s "A\1Tab." "!!!A 1ab.
Wait a minute! Wlat are tlose backslasles? Tlose are escape sequences, a
backslasl lollowed by a claracter to create normally unprintable claracters (i.e. in
tlis example I used \1 to create a tab claracter but you can also use otlers to create
tlings like new lines and vertical tabs). I said unprintable because in tle otler
example, I used \` to create a single quote claracter, tlis claracter would normally be
unprintable because it is contained in a set ol single quotes and would ellectively close
tle set ol quotes causing an error.
Now, il you noticed, I used single quotes lor some ol tle strings and double
quotes lor otlers. Tlere is a dillerence between tle two notations. Single quoted
strings are quite silly and lave a very limited set ol escape sequences tley can use (as a
matter ol lact, only single quote and backslasl are allowed) and are typically useless
unless perlormance is a concern lor you (and turning double quoted strings to single
quoted strings slould probably be tle last tling you try wlen improving
perlormance), double quoted strings, on tle otler land, oller lar more lunctionality
in tle way ol interpolation. Iirstly, tley oller lar more escape sequences. As noted
above, you can use \h to create a newline claracter, \1 to create a tab claracter, and so
8!!!!2)&.52($.(6'-78
on, below is a table ol all tle available escape sequences you can use witl double
quoted strings (tlere are quite a lew).
F9K6LF(9FO;F>KF9
\a Bell alarm \1 Iorm leed
\??? Octal value \h New line
\x?? Hex value \J Return
#{???}
Value ol ???, wlere ??? is a
Ruby expression
\s Space
\e Escape \1 Tab
\c?
\C-?
Control-? \v Vertical tab
\h-? Meta-? \b Backspace
\h-\C-? Meta-Control-?
Looking at tlat table, you may lave noticed tlat double quoted strings also
oller anotler interesting leature: expression interpolation. As lancy as tlat sounds, it
simply means tlis: you can insert tle value ol pieces ol Ruby code into strings directly.
Remember tlat everytling in Ruby is an ob|ect, even tle results ol expressions. Tlat
means you can do tlis:
"Thches/yaJd. #{12*3}" "!!!Thches/yaJd. 36
"#{"ToJa! "*3}" !! "!! ToJa! ToJa! ToJa!
Tle second example is conlusing, unless you remember tlat everytling is an ob|ect in
Ruby (yes, even string literals! Tley are ol class S1J1hg.). Since tle string literal
creates a String ob|ect, you can act on it |ust like any otler ob|ect. In tlis case,
multiplying a string by 3 simply does wlat you would tlink: makes tlree copies ol tle
string.
Anotler, less awesome metlod ol creating strings is using a special
delimiter: 0 or q. Tle way tlis constructor works is to lollow 0 or q witl any non-
alplanumeric, non-multibyte claracter. Ior example:
q{hoag1es & gJ1hdeJs!} "!!hoag1es ahd gJ1hdeJs!
0,#{"havy beahs! "*3}, "!!havy beahs! havy beahs! havy beahs!
Note tlat q acts like a single quoted string and 0 acts like a double quoted string.
Just associate tlem by size: little q, one quote but big Q, two quotes.
Yet anothcr way strings can be created in Ruby is tle use ol tle verbose
eyewart known as lere documents (Perl programmers re|oice!), also known as
!2)&.52($.(6'-78!!!
"leredocs." Tlese unlortunate language constructs create a string by specilying a
delimiter alter a set ol << claracters to start tle string and putting tle delimiter on a
line ol its own to end it. Ior example:
my_s1J1hg = <<hY_STRThC
Th1s 1s a s1mpe s1J1hg 1ha1 1s
pJe-1oJma11ed, wh1ch meahs 1ha1 1he
way 11 1s 1oJma11ed heJe 1hcud1hg
1abs ahd hew1hes w1 be dup1ca1ed
wheh T pJ1h1 11 ou1.
hY_STRThC
Tle linal metlod tlat can be used to create a string instance is to simply use
tle 1o_s metlod ol an ob|ect. Many ob|ects simply output tle standard results lor
tlis metlod (i.e. tleir class name and instance id or sometling similar), but otlers
provide better laculties. Ior instance, F1xhum will actually return a string ol tle
number value ratler tlan simply a big blob ol Ruby data.
>'H-231
Tle second type we will look at is Ruby's built-in classes lor numbers: F1xhum and
B1ghum. Wlen creating a numeric ob|ect, any integer tlat is between (-2
30
) and (2
30
-
l) is assigned to an instance ol F1xhum and anytling else outside tlat range is
assigned to an instance ol B1ghum, Ruby does tlis assignment transparently so tlere is
no need to worry wlicl one to use il you create a bookkeeping application lor yoursell
and your bank balance (like mine) sits below -2
30
constantly.
Integers are created by entering tle number you wisl to use witlout quotes
(lest it become a string). Tle particular lormat depends on wlicl numerical base you
plan on using. Ruby supports standard decimal (base-l0) operations but it also
support operations on octal (base-8), lexadecimal (base-l6), and binary (base-2)
numbers. Ior example:
-123456789 "!!!#123456789 # F1xhum
0d123456789 "!!!1234567890 # F1xhum
1234323424231 "!!!1234323424231 # B1ghum
0x5C1 "!!!1473 # hex
01411 "!!!777 # 0c1a
1_90_33 "!!!19033 # F1xhum
Notice tlat Ruby ignores underscores in numbers (some people cloose to use tlem in
place ol commas lor larger numbers to enlance readability). Tle examples also
illustrate tle various base notations. To create a binary number (base-2), prelix tle
number witl 0b, to create an octal number (base-8), prelix tle number witl 0, to
create a lexadecimal number (base-l6), prelix tle number witl 0x. To create a
standard, base-l0 integer, eitler simply type tle number as normal (i.e. l678) or
prelix it witl 0d (i.e. 0dl678).
10!!!!2)&.52($.(6'-78
In addition to integer types, Ruby also las support lor a Iloat type. Iloat
numbers lold numbers tlat are lractional (i.e. tley lave a partial value tlat is
expressed in decimal lorm). Ior example:
1.5 "!!!1.5
1.0e5 "!!!100000.0
1.e5 "!!!!hohe1hodEJJoJ
Eacl side ol tle decimal point must contain a number. Wlen notating lloats using
scientilic (or condensed) notation, you must place a 0 next to tle decimal point or
Ruby in its silliness will try to execute a metlod named (lor example) e5 on class
F1xhum.
Since numbers are ob|ects (i.e. since everytling is an ob|ect in Ruby) tley
also contain metlods tlat can act on tlem. You can get a number's size witl tle size
metlod, convert a number to a string using tle 1o_s metlod, and many otlers:
-4.abs "!!!4
6.zeJo? "!!!1ase
Tle above metlods are obviously named (tle abs metlod gets tle absolute value and
tle zeJo? returns 1Jue il tle number is zero), but tley are not tle only metlods tlat
are ollered. Cleck tle Ruby API Documentation lor more inlormation.
Numbers also oller metlods tlat may not seem like metlods at lirst glance:
tle aritlmetic operators. Here are some examples:
2 + 2 "!!!4
6 / 3 "!!!2
-4 * 2 "!!!-8
A lull listing ol tlese operators and tleir lunction is available below. A quick tip: il
you've ever programmed in anotler language, clances are tley are tle same (unless
you've been programming in some sort ol willy nilly non-matlological language).
6:8750F78K(?LF:67?:9
+ Addition
- Subtraction
/ Division
* Multiplication
{) Order ol operations (i.e. group expressions to lorce a certain order ol operations)
Modulus (i.e. tle remainder lor tlose not in tle know)
!2)&.52($.(6'-78!!!11
K?@@FK78?>9
It is a great tling to be able to pusl data around in its singular lorm, but everyone
knows tlat collections are wlere tle party is at (at least tlat's wlat MTV says). I
tlink Cod once said tlat it's not good lor data to be alone, and Ruby provides a lew
ways to lacilitate tlis.
A collection (sometimes called a container) is an ob|ect tlat lolds a group ol
related ob|ects, tlis relation could be by type (i.e. all ol tlem are strings), purpose (i.e.
all ol tlem are names), or by lavorite cleeses (mine is provolone). A collection can be
used to louse a number ol data items, keep tlem organized, and perlorm operations
across all its members, eacl member (or element) ol a collection is also a separate,
visible ob|ect tlat can be operated on (i.e. it can still call metlods, be added to and
subtracted lrom, etc.).
7"2(:#,N2(
Tle lirst and most primitive is tle range. Ranges lold a sequential collection ol
values, sucl as all numbers between l and 9 or tle letters lrom A to Z. A range is
created by placing a series ol dots (or periods or decimals or wlatever it is you kids call
tlem nowadays) between tle lower and upper limit ol tle range. Ior example, il you
were creating a roleplaying game and wanted to set tle possible ranges lor tle leiglt
ol eacl race (in incles), you could type:
humah = 48..81
e1 = 40...68
gJo1esquey_huge_guy = 120..132
Ranges can use eitler two dots, wlicl indicates an inclusive range ol all values
including tle beginning value and tle end value, or tlree dots, wlicl excludes tle last
value. Tlis seems backwards at lirst glance, but in trutl tlat tlird dot is so lat tlat it
pusles tle last element out ol tle range. I am not kidding, crack open a debugger and
lind out lor yoursell. Ior example, tle range l...7 would produce a range like tlis:
On tle otler land, tle range l..7 would produce tlis:
Now tlat you can get tle riglt values in a range, you may want to actually do
sometling witl tlem. Ranges oller a number ol ways to test and compare tlem.
Iirstly, you can compare ranges to one anotler using tle == operator (more on tlis
12!!!!2)&.52($.(6'-78
operator and otlers later) or tle eq? metlod. Il you were to write soltware to
manage bake sales (wlicl I lear tlat's a booming market in tle soltware industry
riglt now) tlen you may write some test code to test tle probability ol tle range ol
good and bad cookies you can expect lrom a batcl:
good_cook1es = 1...3
bad_cook1es = 1..3
buJh1_cook1es = 1..3
pu1s{good_cook1es == bad_cook1es) "!!!1ase
pu1s{good_cook1es.eq?{buJh1_cook1es)) "!!!1ase
pu1s{bad_cook1es == buJh1_cook1es) "!!!1Jue
Ranges are considered equal il tleir beginning and end values are tle same, but note
tlat even tlougl tle good_cook1es and bad_cook1es slared tle same beginning and
end value in code, tle values dillered. Tle values were clanged by tle value ol tle
inclusive llag (remember tle two dot-tlree dot tling?). Tle values lor good_cook1es
are |1,2] wlile bad_cook1es lolds |1,2,3].
Ranges also oller a way to test wletler or not a value is contained witlin a range
using === or tle 1hcude? metlod. Ior example, il you and your co-worker guessed a
number ol good cookies, but wanted to see il it was witlin tle probable range ol good
cookies, you could do tlis:
my_guess = 2
h1s_guess = 19
pu1s{good_cook1es === my_guess) "!!!1Jue
pu1s{good_cook1es.1hcude?{my_guess)) "!!!1Jue
pu1s{good_cook1es === h1s_guess) "!!!1ase
Tle 1hcude? metlod will return any value tlat is contained witl tle range ol values
in tle range (i.e. it would return 1Jue il you tested 2.44564 against bad_cook1es), il
you're leeling a little alternative, you can also try 1hcude?'s alias membeJ?.
7"2(633#I(
Tle second built-in collection is tle array, an integer indexed and ordered collection ol
elements. Il you lave lad any introductory computer science course, tlis concept ol
an array slould not be loreign to you but Ruby's implementation may seem sligltly
unlamiliar to you. Wlile tle indexing is zero based like C/C++ and Java (i.e. tle lirst
element is relerenced at index 0, tle second element 1, and so on), unlike tlese
languages, tle elements in a Ruby array do not lave to be tle same type, nor does tle
type ol tle array lave to specilied belore it is initialized lor use. So, witlout tlouglt to
types, you could end up witl an array tlat's sometling like tlis:
!2)&.52($.(6'-78!!!13
In Ruby, literal arrays can be created and stulled witl values in a variety ol
lun and interesting ways:
11s_so_emp1y = |]
oh_so_emp1y = AJJay.hew
heo = |`h1 hao`, `bohouJ`, `h1`, `howdy`]
Jahdom_1ypes = |13, `hapk1h`, {1336 + 1).1o_s]
An array can be initialized witl values ol any type, even variables, values returned
lrom metlods, literals sucl as quoted strings, or notling (to create an empty array).
Tlis is landy mostly lor literal values, but Ruby ollers a lew more metlods lor
creating arrays tlat are more convenient and certainly more Rubyrific. Strings oller a
special way to create arrays lrom tleir contents. Let's say you were writing laikus and
wanted to make sure eacl line (wlicl is conveniently lilled witl one syllable words)
matcles tle ol' "5-7-5" paradigm by splitting tle line into an array so you can count
tle elements:
my_ha1ku = w{ my dog d1gs 11 heJe\h )
" |"my", "dog", "d1gs", "11", "heJe" ]
my_ha1ku = w{ he 1s h1ce 1o me & ca1s\h )
"!!!|"he", "1s", "h1ce", "1o", "me", "&", "ca1s"]
my_ha1ku = W{ bu1 he a1e #{{2*3)/6} ohce )
"!!!|"bu1", "he", "a1e", "1", "ohce"]
my_ha1ku = w{ bu1 he a1e #{{2*3)/6} ohce )
"!!!|"bu1", "he", "a1e", "#{{2*3)/6}", "ohce"]
Oops! A string wrapped in tle W delimiter acts like a double quoted string: it
perlorms string interpolation and extended escape sequence substitution, but w
delimiter acts |ust like a single quoted string: it only allows a subset ol tle escape
sequences to be used and does not lacilitate interpolation. Some are conlused by all ol
tlis poppycock, but it's very easy to remember: Bigger is better (unless you don't need
all tle lancy leatures or you lave some sort ol religious convictions against double
quotes and/or capital W's).
Tle last way to lorm arrays tlat I would like to mention is tle 1o_a metlod ol
some ob|ects. Tlis metlod converts an ob|ect or (rarely) one ol its members to an
array. Ior example, ranges support tlis metlod:
14!!!!2)&.52($.(6'-78
Figurc 2: Look, ma! No typcs!
my_Jahge = 1..10
" 1..10
my_dazz1hg_aJJay = my_Jahge.1o_a
" |1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Many ob|ects implement tlis metlod, it is a convenient way to get an easily
manipulatable data structure lrom some silly classes tlat are dillicult to work witl.
You may consider peeking in tle Ruby API documentation to see il tle ob|ect you
wisl to use tlis metlod witl does indeed implement it.
Now tlat you lave an array, maybe you want to add to it. Elements can
easily be added to an array by simply assigning a value to a non-existent index, lor
example:
my_dazz1hg_aJJay|10] = 11
" |1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
my_dazz1hg_aJJay|12] = 12
" |1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, h1, 12]
Il a gap exists between indexes ol tle last element in tle array and tle newest added
element, Ruby places h1 (i.e. tle equivalent ol hu in otler programming
languages, it represents a complete lack ol value) in tle gap elements (look at
my_dazz1hg_aJJay|11] above). Il you simply want to add an element to end ol an
array, tlen you can use tle << operator, tle push metlod or certain lorms ol tle
1hseJ1 metlod. Ior example:
my_dazz1hg_aJJay.push{15, 16)
" |1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16]
my_dazz1hg_aJJay.1hseJ1{-1, 17)
" |1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 17]
my_dazz1hg_aJJay << 14
" |1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 14]
Tle push metlod allows you to pusl one or more elements onto tle end ol an array,
eacl element slould be provided as a parameter to tle metlod. Tle 1hseJ1 metlod
allows you to insert elements at (tle specilied index + l), in tle example, I used -l to
add elements to tle end ol tle array (i.e. -l moves lrom tle end ol tle array back one
element to tle last element. Adding an element alter tle last element would ellectively
add it to tle end.). Tlis metlod probably is not tle best, but it can be used wlen tle
same metlod needs to insert elements at various places in tle array (including tle
end). Tle << operator allows you to pusl specilied elements on to tle end ol an
existing array, I pluralized element because several ol tlese "appends" can be clained
togetler to add numerous elements to tle end ol an array. Ior example:
!2)&.52($.(6'-78!!!15
my_dazz1hg_aJJay << 20 << 21 << 22
" |1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 21, 22]
Now tlat you lave data in your array, wlat are you to do witl it? I personally
like to admire my data, pride mysell on my ability to larness all tlat is tle array, and
tlrust my list into tle air yelling, "I AM SPARTACUS, LORD OI THE ARRAY!"
Tlen my wile looks at me like I am crazy, it gets all weird, and I get back to work. I
suppose tlat may or may not work lor you, most people opt to simply use tleir array to
lold stull and call it wlen tley need it (wlicl is not nearly as lun). To make use ol an
array element's value, you simply relerence tle desired element by index in tle lorm
aJJay_hame|1hdex]. Ior example:
pu1s my_dazz1hg_aJJay|0]
" 1
1hdex = 2
my_dazz1hg_aJJay|1hdex]
" 3
my_dazz1hg_aJJay|0..2]
" |1, 2, 3]
Notice tlat wlen relerencing array elements, you can relerence a single element witl
an integer or use a range to relerence a number ol elements. Remember, because
array indexing is zero based, relerencing index 0 is actually relerencing tle lirst
element. Also, be sure tlat wlen you pass an index, tlat it is an integer or range, il
you do not, Ruby will tlrow a TypeEJJoJ (wlicl will in turn crusl your soul). Tlis
seems like a silly problem (and an even sillier consequence!), but it could slow up il
you were (lor some reason) reading indexes lrom sockets or liles (wlicl reads
everytling in as strings so you would lave to convert tlem wlicl you'll learn low to
do later). Tlis metlod lor relerencing array elements operates |ust like tle a1
metlod:
pu1s my_dazz1hg_aJJay.a1{0)
" 1
Anotler metlod, 1e1ch, can also operate in tlis manner, but 1e1ch can also specily a
delault value to return il tle specilied index is not lound.
pu1s my_dazz1hg_aJJay.1e1ch{999, "ho1 1ouhd!!")
" ho1 1ouhd!!
Yet anotler metlod, vaues_a1, can also operate |ust like a1, 1e1ch, and tle |]
operator, except tlis metlod can take a number ol indexes to letcl and return as an
array.
16!!!!2)&.52($.(6'-78
!"#$%&'()*++,-./(*00*'12*,"3$(*#456%76%89
!%%:76%86%;<
Tle last ways tlat I would like to slare to retrieve elements are tle metlods !=! and
slilt. Tle !=! metlod grabs tle last element in tle array and removes it lrom tle
array, tle $>-?# metlod grabs tle lirst element lrom tle array and removes it slilting
all otler elements back one index.
&'()*++,-./(*00*'1!=!
!%%:76%86%;6%@6%A6%B6%C6%D6%E<
&'()*++,-./(*00*'1$>-?#
!%%:86%;6%@6%A6%B6%C6%D6%E6%75<
So now you lave an array, you lave data in it, but maybe you are sick ol tlat
tlird element. He |ust keeps giving you crazy looks or eying your wile and you |ust
want to take lim out or possibly !"#$%&'#!( . Well, lortunately Ruby can take care ol tlat
little problem, and let's |ust say it ain't pretty. Tle )3,3#3(*# metlod deletes tle
element at tle index specilied as a parameter and returns tle value ol tlat element.
Ior example:
!"#$%&'()*++,-./(*00*'1)3,3#3(*#479
!%%8
&'()*++,-./(*00*'
!%%:76%;6%@6%A6%B6%C6%D6%E6%75<
Anotler metlod tlat arrays oller to delete items is tle )3,3#3 metlod (big
surprise, lul?). Tlis metlod deletes and returns tle value tlat is relerenced as a
parameter ratler tlan tle index like )3,3#3(*#. Ior example:
!"#$%&'()*++,-./(*00*'1)3,3#34@9
!%%@
&'()*++,-./(*00*'
!%%:76%86%;6%A6%B6%C6%D6%E6%75<
!"#$%&'()*++,-./(*00*'1)3,3#347;;C9%F%GH0=./IG%J
!%%H0=./I
Note tlat tle item witl tle value ol 4 was deleted ratler tlan tle index 4. Also note
tlat tle )3,3#3 metlod ollers tle option lor a "delault" value to return il tle specilied
value does not exist in tle array, tle last example is a demonstration ol tlis leature.
Tle value returned by tle block ol code between tle braces will be tle value returned il
tle item is not lound (I talk more about tlese kinds ol code blocks later on, il you're
conlused, curious, and impatient, look at page 3l).
!"#$%&"'(%')*+,-"""!"
7"2(5#1"(
Tle last collection type tlat Ruby ollers is tle lasl (also sometimes known as an
associative array or dictionary), lasles are collections ol values ol any type indexed by
otler values ol (almost) any type ratler tlan solely numbers like arrays (tlougl you
can use numbers lor lasles also). Ior example, tley can be indexed by strings, il you
lad a lasl called tlatlasl, you could call out its keys by name.
I say tley can be keyed by almost any type because indexes (called keys in lasles)
lave two requirements: tley must implement tle .eq? metlod and tley must lave a
constant (or constantly updated using tle Jehash metlod) lasl code. Tlese
requirements stem lrom tle way lasles landle indexing and looking up values by key.
You can lind a line, teclnical explanation ol tlis process ol looking up and lasling
and wlat lave you in otler volumes, but let us sullice now to say tlat tle lasl code is
tle lasl's metlod lor comparing and linding keys and tle like, so it would not be wise
to let tlat get tlrown all willy nilly by a rogue or inaccurate lasl code (strings are
exceptions since Ruby makes a copy ol tlem ratler tlen relerences tlem, tlis way tle
value cannot be altered and tle lasl code clanged witlout tle lasl knowing about
it).
To create a new lasl, you simply bracket notling or a set ol key value pairs
(indicated by tle "=>" combination) between a set ol braces. Il you place notling
between tle braces (or you call tle hash.hew metlod), tlen an empty lasl will be
created, but let's say you wanted to create a giant lasl ol tle names ol everyone tlat
you know wlo is a wombat and wlere tley live. You could do so like tlis:
my_womba1s = { `Way Womba1` => `The Juhge S1.`,
`W1ma Womba1` => `The house oh 1he CoJheJ`,
`Sam` => `ho1awomba1 Way`, `hump` => 13 }
Okay, so maybe it's not a giant lasl (yet). Anylow, Ruby doesn't mind wlitespace
(spaces, tabs, and tle like) or newlines (or tle lack tlereol) in tle lasl delinition, so
18!!!!2)&.52($.(6'-78
Figurc 3: Thc hash, illustratcd. Thosc darn fclt tippcd pcns.
long as you put a comma between eacl entry. I used string keys in my example, but
any ob|ect can be used as long as it meets tle requirements lor keys (listed above).
Ruby also ollers tle hew metlod lor creating lasles, tle hew metlod lor
lasles varies sligltly tlan wlat would be expected. One would expect tlat
parameters provided to hew would become tle values ol a lasl, but in tlis case it takes
a single parameter wlicl becomes a delault value il a nonexistent key is relerenced (or
il tle de1au1 metlod is called).
hew_hash = hash.hew{"ho1 heJe!")
hew_hash|`hoh-ex1s1eh1 key`]
" ho1 heJe!
Keys can be added to a lasl by simply delining tlem, continuing lrom tle
above example, let us assume tlat you met a new wombat at tle supermarket:
my_womba1s|`Womb1e hcWomba1`] = `123 Tha1 S1Jee1`
Note you don't redeline tle lasl or enclose anytling in brackets or braces, you don't
lave to call any silly metlods (you can use tle s1oJe metlod, but wly would you?) or
use any operators tlat seem loreign, you simply assign a value to tle key.
So now tlat you lave tlis line lasl o' wombats, wlat can you do witl it? To
relerence a lasl value, you can use tle 1e1ch metlod witl tle key as a parameter
(lame!) or simply relerence it similar in an array-like lorm: hash_hame|key]. Ior
example, il you were tlrowing a wombat party and wanted to invite all your wombat
lriends, but you couldn't remember Wally Wombat's address, you could print tle
value ol lis address like tlis:
pu1s my_womba1s|`Way Womba1`]
" The Juhge S1.
Hasles also oller tle vaues_a1 metlod, wlicl allows you to provide numerous keys
and receive tleir values in an array. Values in lasles lave to be called using tlese
metlods (1e1ch, |key], or vaues_a1), lasles do not oller metlods like pop in
arrays. Hasles oller sh111, but it simply returns tle lirst element as an array wlicl
contains tle key in one element and tle value in anotler, tlis metlod and a couple ol
otlers are not very uselul il you simply want tle value ol elements. Ol sure, lasles
oller a myriad ol metlods wlicl allow you to do dillerent tlings witl tle elements ol
a lasl (i.e. tle 1o_a metlod wlicl clanges tle lasl to an array, meJge to merge two
lasles, Jepace to replace one lasl's values witl anotler's values, etc.), but tlere are
not a wlole ol options wlen it comes to grabbing elements lrom a lasl. Hasles lack a
lot ol bling to be lonest.
Ruby also ollers a lew metlods tlat test elements in a lasl, tlese are lelplul
to grab inlormation about tle lasl witlout traversing tle wlole lasl. Ior example:
!2)&.52($.(6'-78!!!1
my_womba1s.has_key?{`W1ma Womba1`)
" 1Jue
my_womba1s.has_vaue?{`Lamps ahd pahdas`)
" 1ase
my_womba1s.emp1y?
" 1ase
Tle metlod names obviously explain wlat tley do, but I will explain tlem a little bit
anylow. Tle emp1y? metlod clecks wletler or not any elements exist in tle lasl,
tlis does not cleck tle values ol tle lasl, so il tlere is an element wlicl is empty it
returns lalse. Tle has_key? metlod clecks tle lasl to see il tle key passed as a
parameter exists, tlis is probably more sale tlan clecking tley key lor tle delault
value or nil. Tle has_vaue? metlod clecks tle values ol tle lasl to see il it exists in
one ol tle elements. Tlis metlod is not particularly uselul (since it does tell you
wlicl key las tle value), but it can be uselul il you want to make sure tlat any key las
tlis value. Tlere are a lot ol synonyms lor has_key? (membeJ?, key?, etc.) and
has_vaue? (vaue?, etc.), cleck tle Ruby documentation lor a lull list ol tlese
metlods, maybe one ol tle synonyms will be easier lor you to remember.
Hasles also, ol course, oller metlods to delete elements. To delete an
element, simply call tle dee1e metlod and provide tle key you wisl to delete as a
parameter. Let's say tlat you lad a lalling out witl Wilma Wombat (sle got a little
tipsy at tle Wombat New Year party and vomited on your new eel skin Prada sloes)
and you now want to delete ler lrom your list ol lriendly wombats, you could do so like
tlis:
my_womba1s.dee1e|`W1ma Womba1`]
" The house oh 1he CoJheJ
Wlen an element is deleted, its value is returned. Tlis works great il you know exactly
wlicl keys you want to delete, but let's say tlat you get tired ol low wombats smell
(tle smell ol stale gin and guava |uice can be ratler unwelcome in tle morning). You
want to completely blow away tle wlole lasl, but it seems silly to go tlrougl eacl
key and call dee1e. Well, lortunately Ruby delivers:
my_womba1s.ceaJ " {}
my_womba1s.emp1y? " 1Jue
Wlen tle ceaJ metlod is called, tle newly emptied lasl is returned, aren't you glad
tlat you don't lave to deal witl tlose darn wombats anymore?
P6:86<@F9(6>Q(75F(@8RF
Now tlat we've gone over all tle basic ob|ects you lave to work witl (at least tle
important ones), we slould probably talk about low to do sometling witl tlem. It
20!!!!2)&.52($.(6'-78
would be silly to learn about wlat you lave to work witl witlout working witl it
(unless ol course you were learning about poisonous snakes or Malaysian slotls), to
do anytling practical witl an ob|ect you probably need to store a relerence to it
somewlere like a variable or a constant. We did tlis wantonly in our discussion ol
types, but now may be a good time to go over tle liner points ol assignment,
expressions, and otler lun party games witl ob|ects.
In tle examples lor working witl tle standard Ruby types, I olten
demonstrated variable assignment witlout really explaining exactly wlat was going
on. It seems obvious wlat was going on: lelt value (a.k.a. lvalue) is set equal to tle
value ol tle riglt value (a.k.a. rvalue). Seems like second grade matl, riglt? But
notice tlat I said relerence in tle previous paragrapl. Variables are, in elementary
terms, names lor values tlat live in tle memory ol your computer. In Ruby, variables
point to a location in memory. Il you point a pointer to anotler pointer, your point to
tle same location in memory.
Il you don't really get it, perlaps an example will work a little better to illustrate wlat
relerences mean in practice:
!2)&.52($.(6'-78!!!21
Figurc 4: Variablcs as rcfcrcnccs. Your computcr's mcmory looks suspiciously likc a
group of circlcs.
11Js1_vaJ = "1 hod a Je1eJehce"
" 1 hod a Je1eJehce
secohd_vaJ = 11Js1_vaJ
" 1 hod a Je1eJehce
secohd_vaJ.chop! # Chops o11 1he as1 chaJac1eJ o1 1he s1J1hg
" 1 hod a Je1eJehc
11Js1_vaJ
" 1 hod a Je1eJehc
Wait a second! I modilied secohd_vaJ! Wly is 11Js1_vaJ dillerent now? Tlis tle
wlere tle idea ol a relerence comes into play: variables are not ob|ects but relerences
(or pointers) to ob|ects wlicl live in tle magical etler beyond (or tle leap, wlatever
you kids call it nowadays). Relerences (and, in turn, variables) merely point to
ob|ects, tley do not lold tle actual ob|ects tlemselves. Wlen you relerence a
relerence, it does not duplicate tle ob|ect: botl relerences point to tle same ob|ect. Il
you want to duplicate tle ob|ect (i.e. create anotler copy ol tle ob|ect in anotler
ob|ect ratler tlan simply relerencing it), you can use tle .cohe or .dup metlod on
ob|ects wlicl oller it.
Wlile assignment ol tle rvalue-into-lvalue sort is simple enougl to
understand due to its readability, tlere are otler lorms ol assignment and all manners
ol bit twiddling and binary brulala you can pull on variables. One sucl ruckus you
can stir up is claining assignment. In a normal assignment, tlere is one lvalue and
rvalue, alter tle assignment tle lvalue equals tle rvalue. Wlen you clain
assignments tlougl, magic lappens (well, not really). Ior example:
e11 = 5 " 5
e11 = m1dde = 7 " 7
e11 " 7
m1dde " 7
In tlis example, tle claining results in two lvalues and one rvalue. Tlis seems tame
and practical enougl, il you need to assign two variables tle same value, you |ust
place tlem as lvalues to tle desired rvalue. Wlere it can get crazy is using sometling
like tlis statement:
1 = h = 1 = s = 1 = s = c = J = a = z = y = 100
" 100
Now every variable to tle lelt ol tle linal rvalue ( 1, h, 1, s, 1, s, c, J, a, z,
! y"#is sel lo lhe linaI ivaIue (100). Though il seens Iike aII lhe vaiiahIes aie heing
sel in aiaIIeI, in acluaIily Ruhy assigns lhen voiking lion iighl lo Iell (i.e. y is sel
lo 100, z is sel lo y, and so on). Ruhy does ollei selling vaiiahIes in aiaIIeI, hul il is
acconIished using a sIighlIy dilleienl loin:
22!!!!2)&.52($.(6'-78
p1, p2 = 1, 2 " |1, 2]
p1 " 1
p2 " 2
Note tlat Ruby returns an array ol tle assigned values. Tlis lorm is a great metlod
lor swapping tle values ol variables (since tley are actually set in parallel). You can
also make use ol tlis lorm witl an array:
Jvaue = 0 " 0
a, b = Jvaue " 0
a " 0
b " h1
Jvaue = |1, 2, 3, 4, 5] " |1, 2, 3, 4, 5]
a, b = Jvaue " |1, 2, 3, 4, 5]
a " 1
b " 2
Any array can be assigned to a list ol variables, as slown in tle example, Ruby ignores
any extra elements in tle array past tle number ol variables specilied to assign to.
Tlis is uselul il a metlod returns an array but you don't necessarily need anytling past
tle lirst lew elements. Notice tlat earlier in tle example tlat any lvalues witlout
corresponding rvalues are simply set to h1. Arrays can also be assigned in parallel in
nested assignments , Ruby is smart enougl to pick apart your expressions into
individual ob|ects and try to assign tlem (wlicl is a big step up lrom languages like
C++ and C#). Ior example:
a, {b, c), d = 10, 11, 12, 13
" a == 10, b == 11, c == h1, d == 12
a, {b, c), d = 10, |11, 12], 13
" a == 10, b == 11, c == 12, d == 13
Mucl like tle otler lorm ol parallel assignment, Ruby substitutes h1 lor lvalues
wlicl do not lave a corresponding rvalue. Tle lirst example's c does not get assigned
because it is in an array witl b and tle corresponding rvalue is not an array. In tlis
case, Ruby assigns tle lirst element tle value.
Anotler lorm ol ridiculous rvalue rigormoralitry is tle additive assignment
operator (+=) and tle subtractive assignment operator (-=). Tlese lorms are
somewlat similar to (but also replace) tle ++ and -- operators seen in many
programming languages. Tle -= and += operators are deligltlul pieces ol syntactic
sugar tlat make adding and subtracting ob|ects and assigning tle returned value to
tle initial ob|ect a breeze. Ior example:
umps += 2 # umps = umps + 2
" 2
!2)&.52($.(6'-78!!!23
p1e += umps # p1e = p1e + umps
" 2
umps -= p1e # umps = umps - p1e
" 0
As you can see, tlese slortcuts allow you to accomplisl tle same tling in a wlole lot
less typing (every programmer's dream, riglt?). Also note tlat tlese operators work
on more tlan numbers, anytling tlat uses tle + and - operators can use tlem since
tlese syntactic sugar lumps merely wrap tlese operators. Tlis means tlat anytling
tlat lappens during normal use ol tlese operators (i.e. certain ob|ects perlorm extra
work wlen adding or subtracting tlat is built in or tlat you specily) will still lappen.
Wlat il you don't want to be able to assign to an ob|ect? It's a rare case
indeed unless you're trying to work a bug out or il you simply like to raise unlealtly
amounts anger witlin yoursell because you lappen to be tle Incredible Hulk.
Ireezing an ob|ect is uselul il your program is acting wonky and spitting out an
abnormal variable, but lrom wlat you can see, it slould be working normally. So, you
would simply 1Jeeze tle ob|ect at tle last line ol code you see belaving normally:
# Lo1s o1 code heJe...
my_cJazy_obec1 = why_do_you_ha1e_me?
my_cJazy_obec1.1Jeeze
# Eveh moJe code...
my_cJazy_obec1 = abhoJma_vaue
" TypeEJJoJ! cah`1 mod11y 1Jozeh obec1
Tlis seems like a cool trick you'd use olten, but it's really not. I suggest not using it
unless you absolutely need to and you lave permission lrom your mom and dad lirst.
Anotler cralty piece ol syntax you may spot wlen looking at otlers source
code or examples in books or tle web is sometling tlat may look like tlis:
my_s1J1hg =- /\ss1J1hg\s/
Wlat's tlat tilde lor?! And wlat's witl tle slasles and tle literal and tle escape
sequence outside ol a string?! Tlis is wlat's called a regular expression, a pattern tlat
is used to matcl string or portions ol strings in order to execute some manner ol string
manipulation. Ruby ollers a very robust regular expression lacility (wlicl we will
toucl more on later), but riglt now let's sullice to say wlatever is between tle slasles
will be matcled and assigned to tle lvalue wlen tle =- operator is used. Ior example:
my_s1J1hg = "my s1J1hg 1s ooooohg"
my_s1J1hg =- /\ss1J1hg\s/ " 2
my_s1J1hg =- /\s/ " 2
my_s1J1hg =- /my/ " 0
24!!!!2)&.52($.(6'-78
Tle pattern enclosed in tle slasles is matcled to tle string using tle =-, using tlat
pattern, tle index ol tle lirst matcl (i.e. an occurrence ol a string matcling tlat
pattern) is returned. I realize tlis is a ratler cursory rundown ol wlat regular
expressions can do. I will discuss tlis more in detail later on in tlis clapter, but lor
now I tlouglt it benelicial lor you to be lamiliar witl tlat il you see it somewlere
belore you get tlere.
!"#$%&"'()*+%%
You learned about Ruby's ob|ect system and built-in classes. You learned...
! tlat everytling in Ruby is an ob|ect.
! tlat tle basic built-in classes in Ruby are tle number (F1xhum and B1ghum),
tle S1J1hg, tle Rahge, tle AJJay, and tle hash.
! tlat setting a Ruby variable is actually setting a relerence ratler tlan a value,
but can be set to values il needed.
!2)&.52($.(6'-78!!!25
#
<32#*(+$(B.C,(,.CG
Now tlat you are lamiliar witl some basic ob|ects and low to manipulate tlem a little
bit, we slould probably move on to segmenting your code, surely you didn't tlink tlat
applications ran in one luge clunk ol code! It would be silly il you lad to repeat every
piece ol code you wanted to use again or worry constantly about il you were stomping
on a variable you used 30,000 lines ago, so tle language geniuses lave created a lew
ways to segment your code.
Tle most lundamental ol tlese is a block, blocks are |ust pieces ol code
segmented away lrom tleir context by an initiator and tle end keyword. Tle initiator
could be tle beg1h keyword (wlicl I will use as an example lere) or sometling like 11
or 1oJ (wlicl you will learn about in tle next section). To create a block ol code, you
simply place tle initiator on a line lollowed by any needed requirements (wlicl are
notling lor tle beg1h keyword), lollowed by tle code you wisl to place in tle block,
and ended by tle ehd keyword (I'm sure tlat one will be lard to remember...). Here's
an example:
beg1h
pu1s "T`m 1h a bock."
pu1s "S11 1heJe..."
pu1s "S11 1h heJe..."
pu1s "0K, T`m dohe."
ehd
Using a beg1h/ehd block by itsell really doesn't allord you anytling except to slow
tlat it is separate lrom its context, but tley can paired witl and mesled into various
constructs to aclieve dillerent results. I'll toucl on a lot ol tlese constructs in tlis
section and later on wlen I talk about using blocks more ellectively (it's a lootnanny
trust me!).
0F75?Q9
In tle examples above, I've been ratler liberal in my usage ol metlods witlout mucl
explanation. Il ob|ects/variables are tle nouns ol programming, tlen we could
26!!!932#*(+$(:.;,(,.;<
describe metlods as tle verbs, metlods "do stull." In more teclnical language,
metlods are pieces ol code tlat are called lrom witlin otler code using variables
called parameters (also known as arguments or options) led to tlem lrom tle calling
code. Tlink ol tlem as beg1h/ehd blocks tlat can be called arbitrarily. Wlen I say
arbitrarily, I mean tlat tley can be called anywlere, anytime. Tlere isn't a set
"metlod" block tlat all metlod calls live in. I realize tlis business witl parameters
and sucl sounds a little conlusing, but we've already sent parameters to metlods
wlen we've sent text to pu1s or a string to chop!. We've already been doing it witlout
ever saying we were! I slipped it riglt in tlere on you, I'm a smootl criminal, I know.
Wlen metlods are called in Ruby, you aren't teclnically "calling" a metlod
(even tlougl tlat terminology is olten used to describe it). You are actually sending a
message to an ob|ect saying, "Hey! Do you lave tlis metlod?" Il tley do, tle metlod
is executed, il tley do not, a NoMetlodError is tlrown and tlere is mucl weeping
and gnasling ol teetl. "Creat, " you say. "But wlat are metlods lor? Sure, you can
use tlem to 'do stull,' but is tlere a 'bigger' purpose lor tlem?" Ol course.
Metlods are used to, lirstly, remove redundancy. Ior example, it would be
silly to type tle same l5 lines ol code over and over again il you were going to be using
tlem all tlrougl your application. You can |ust create a metlod and call it wlerever
you need it. Metlods, secondly, allow you to segment your code better. Maintaining a
550 line piece ol code is never lun lor anyone (except extremely sell-deprecating,
abusive, masoclistic crazy guys, but tlose guys work over in accounting riglt?),
metlods allow you to split up all tle logic in tlat luge mess into smaller, more
manageable clunks.
!"#$%$%&'(")*+,-
I'm sure you are growing ratler anxious in anticipation ol being able to create your
own sliny new metlods. Well, wait no longer my impetuous lriend! Here is tlat
wlicl you desire:
932#*(+$(:.;,(,.;<!!!27
Figurc 5: Calling of mcthods. It's much likc pig calling, only clcctronic.
de1 my_hew_me1hod{hame)
pu1s "hey, " + hame + ", 1h1s 1s my hew me1hod..."
ehd
my_hew_me1hod{`maghus`)
" hey, maghus, 1h1s 1s my hew me1hod...
A metlod is delined by entering de1 lollowed by tle metlod name and parameters
(i.e. variables passed into a metlod to be used witlin tlat metlod, remember?), tle
lollowing lines slould contain tle desired metlod code ended by tle ehd keyword.
Tlat's it, simple enougl, riglt?
Well, tlere's a bit more to metlods tlan tlat. Iirst, your metlod name
slould (by convention, ol course) start witl a lowercase letter (and prelerably be all
lowercase). Tle reason lor tlis is tlat Ruby tlinks tlat tlings tlat start witl an
uppercase letter are constants and classes ratler tlan metlods, tlis could cause some
ratler rascally belavior lrom your application. Wlile we are on tle sub|ect ol
convention, tlere are otler conventions tlat pertain to tle name ol metlods. Iirstly,
il it is querying an attribute, it slould end in a question mark, lor example, il you were
to write a metlod to get tle number ol Irencl military victories, you could do
sometling like FJahce.has_m111aJy_v1c1oJ1es?. Tlis would, ol course, return
1ase. Anotler convention to lollow is tlat il tle metlod modilies its receiver in place
(i.e. tle metlod modilies tle ob|ect tlat called it), tlen it slould end in an
exclamation point, lor example, let's say you were replacing tle existing cyborg
soltware tlat all tlose robot celebrities run witl a new snazzy Ruby-based system. To
execute anotler lacelilt and make tlem look 40 years younger, you could do
0oy_PaJ1oh.1ace111!, or to set tleir age to an arbitrary value, you could call
Bob_BaJkeJ.se1_age!{30).
Tle next tling we slould probably discuss about metlods are tle
parameters (or arguments or wlatever) tlat are passed in. Tlese variables are passed
into tle metlod as local variables, wlicl means tley are local and usable only in tle
context ol tlat block ol code (tle metlod). Tlis means tlat tle variables tlat created
in tlat block ol code and its parameters are not usable outside ol tlat block ol code.
Tle language to explain it is a little dense, so let's look at an example:
de1 my_me1hod{11Js1, secohd)
pu1s 11Js1
1h1Jd = secohd
pu1s secohd
ehd
my_me1hod{"yes.", "ho.")
" yes.
" ho.
pu1s 11Js1
" ! hameEJJoJ. uhde11hed oca vaJ1abe oJ me1hod
pu1s 1h1Jd
" ! hameEJJoJ. uhde11hed oca vaJ1abe oJ me1hod
28!!!932#*(+$(:.;,(,.;<
Notice tlat neitler tle parameters nor tle created local variable are accessible outside
tle metlod unless tley are returned or passed outside ol it otlerwise. Tlis concept is
known as scoping and will come up many, many times wlile programming in Ruby, I
will liglliglt wlen scoping will be an issue witl a new concept. Variables can be
scoped globally, locally, class scoped, etc., any block (including conditional blocks like
11's and loops) can lave variables tlat are local to it. Tlere will be more coverage ol
tlis as we progress, but it's important to remember scoping wlen working witl
metlods, it can cause severe leadacles il you aren't carelul.
So now tlat you understand wlat parameters are and do, let's get lancy.
Wlat il you don't want to require a certain parameter? Or maybe you want a
parameter to be able to take many parameters. Well, Ruby can deliver on botl ol
tlose. Ruby ollers optional parameters lor metlods, tley aren't really optional so
mucl as you can assign a delault value to tlem. Ior example:
de1 hew_me1hod{a = "Th1s", b = "1s", c = "1uh")
pu1s a + ` ` + b + ` ` + c + `.`
ehd
hew_me1hod{`Ra1s`)
" Ra1s 1s 1uh.
Tlis teclnique is lelplul il 99/ ol tle time you'll be using tle metlod witl a certain
value (eitler a parameter or local variable) but you want to be able to clange tlat
value every once in a wlile. You could pass in h1 to tle metlod every time you
wanted to use tle delault value and lilter it tlrougl a cleck or some logwasl like tlat,
but tlat wouldn't save any typing nor would it make any sense. Tlis language leature
allows you simply specily tle parameters you need and leave tle rest as tley as are, do
note, tlougl, wlen using tlis leature tlat tle parameters must be in tle same order
and you can not skip any parameters in tle list (i.e. it's best to place tle ones you won't
be explicitly delining tlat olten at tle end ol tle list).
Parameter lists can also be variable lengtl, let's say tlat you wanted to create
a ratler contrived metlod tlat outputs your relations based on parameters you
provide. Tle metlod could look like tlis:
de1 pJ1h1_Jea11oh{Jea11oh, *hames)
pu1s "hy #{Jea11oh} 1hcude. #{hames.o1h{`, `)}."
ehd
pJ1h1_Jea11oh{"cous1hs", "hoJgah", "h1es", "L1hdsey")
" hy cous1hs 1hcude. hoJgah, h1es, L1hdsey.
I could lave provided any number ol names in tle list, by placing an asterisk belore
tle identilier lor tle last parameter, you can turn it into a variable lengtl list (wlicl is
actually |ust an AJJay created lrom tle ob|ects you provide, wlicl is wly we can use
tle o1h metlod witl it). Tlis teclnique is lelplul wlen dealing witl lists ol ob|ects
or maximizing tle llexibility ol tle metlod (i.e. using a metlod to process one or more
ob|ects in one lell swoop ratler tlan calling tle metlod several times).
932#*(+$(:.;,(,.;<!!!2
.-$%&'(")*+,-
Now tlat you know low to create metlods, you would probably like to know low to
use tlem more ellectively (or simply at all). As you lave seen in previous code
examples, calling a metlod is as simple as putting tle metlod name lollowed by tle
required parameters (il tlere are any). Tlere are many ways to lormat a metlod call,
sometimes a metlod may not require any parameters so tle parentleses and
parameters are not needed. Many times you can call a metlod witlout using tle
parentleses, but tlis is generally not good practice (it's silly to sacrilice readability
|ust to save two keystrokes unless you're only passing one parameter). Let's look at
some examples:
pu1s "Look ma! ho paJeh1heses!"
pu1s{"Look ma! PaJeh1heses!")
pu1s
pu1s{)
All ol tle above examples are valid calls ol pu1s. Tle lirst two examples demonstrate
tle optional parentleses usage, tle second set merely demonstrates tlat not all
metlods need parameters. Do note tlat most metlods do require parameters and will
tlrow an AJgumeh1EJJoJ il tley don't get tle correct number.
So metlods are great, riglt? But low do we do anytling witl tlem? Wlat
good are tley il tle variables used in tlem are useless outside ol tlem? Tlis is wlere a
metlod return value comes into play, a metlod allows you to return one or more
values lrom witlin tle metlod to be used outside ol it. Metlods always return a value,
but il no value is explicitly specilied, tle value returned is h1 (e.g. wlen a metlod is
delined, Ruby returns h1) or tle last value used inside tle metlod (il tlat exists). Ior
example:
de1 Je1uJh_me{vaue)
scoped_vaue = vaue
ehd
de1 echo_me{vaue)
vaue
ehd
de1 mu11_Je1uJh
Je1uJh `moJe`, `1hah`, `ohe`
ehd
my_vaue = Je1uJh_me{`1h1s 1s 1uh!`)
pu1s my_vaue
" 1h1s 1s 1uh!
ohe, 1wo, 1hJee = mu11_Je1uJh
pu1s ohe + 1hJee
" moJe ohe
Il no return statement is placed inside tle metlod, tle last value used in tle metlod is
returned, tlis value can be eitler a variable tlat las been assigned (as in tle lirst
example), an ob|ect tlat is created (e.g. placing a string literal on a line by itsell
because tlat creates a S1J1hg ob|ect), or any otler ob|ect tlat is relerenced in tle last
line ol tle metlod (as in tle second example). Tlis means tlat a Je1uJh command or
30!!!932#*(+$(:.;,(,.;<
linal relerence isn't required il tle last value used is tle value you would like to return
(as in tle lirst example), il tlis is not tle case, tle second example demonstrates tle
usage ol tle linal relerence metlod ol returning a value and tle last example
demonstrates usage ol tle return statement. Tle last example demonstrates using
return and low you can assign variables in parallel (like discussed in tle section on
variable assignment) witl metlod returns, since it is simply populating an array tlat
is collected lrom tle lvalues you specily, you can also use tlis metlod to populate
arrays.
<@?KR9(6>Q(PJoc(?<SFK79
I mentioned blocks early in tlis clapter, but I'd like to cover tlem more in deptl now.
Blocks are a very powerlul concept in Ruby, but very conlusing lor tle newcomer, so
some discussion is in order. In Ruby, a block is an ob|ect tlat contains some Ruby
code along witl tle context neccesary to execute it. It doesn't make sense to say tlat a
code block is an ob|ect, but remember tlat everytling in Ruby is an ob|ect.
/0+12'/3-$1-
I said earlier tlat blocks are simply code wrapped in a do/end construct, but tley go a
little lurtler tlan tlat. Blocks can be constructed in a number ol ways, and in doing
so, create an ob|ect tlat lolds code tlat can be passed to metlods or leld in variables.
Put simply, a Ruby code block is mucl like a metlod witlout a name tagged on it.
Perlaps tlis will make a little more sense il you tlink ol tlem as being very similar to
C's lunction pointers, C++'s lunction ob|ects, Pytlon's lambdas and list
comprelensions, Perl's anonymous lunctions, Java's anonymous inner classes, or
even closer, Smalltalk's or Lisp's blocks. Il you've used any ol tlese languages and
none ol tlose sound lamiliar to you, tlis isn't very uncommon: typically tley are
slunned by all but experts in tle language. Iortunately lor you, I'm going to make you
learn about tlem (tley're an important concept in any language!), and even il you
don't want to learn about tlem, too bad: you can't write Ruby witlout tlem.
Let's take a look at a simple usage ol blocks: metlod parameters. A lot ol
metlods take blocks as parameters, so let's look at one ol tlose now.
myaJJay = w{ohe 1wo 1hJee 1ouJ}
myaJJay.each {]eemeh1] pJ1h1 "|" + eemeh1 + "]... " }
" |ohe]... |1wo]... |1hJee]... |1ouJ]...
Tlis snippet simply iterates an array using tle each metlod and passes in eacl
element to tle code block, tle code block can tlen treat tlat element as an "argument"
and operate it mucl like you would a metlod argument. Tle code block in tlis case is
lormed using braces, tlis is anotler way otler tlan tle do/ehd combination tlat you
can lorm a code block. Altlougl it looks like you're using tle each metlod to "open"
a code block, you're actually passing tlat block ol code in as a parameter to tle each
metlod. Il you're completely lost, perlaps breaking down tlis example will clarily
932#*(+$(:.;,(,.;<!!!31
tlis example a little more. Il you get tle concept, skip tle next paragrapl, it'll |ust be
redundant.
Let's take tle lollowing line ol code apart and look at eacl part ol tlis call
separately.
myaJJay.each {]eemeh1] pJ1h1 "|" + eemeh1 + "]... " }
We lirst call tle each metlod on tle array ob|ect myaJJay. Tlis metlod takes a block
as a parameter, tlat is to say tlat it takes a parameter wlicl is a block ol code tlat it
will execute. Tlis block is very similar to tle beg1h/ehd blocks we saw earlier, we
could rewrite tle above code as lollows il we wanted to.
myaJJay.each do ]eemeh1]
pJ1h1 "|" + eemeh1 + "]... "
ehd
Notice tlat tle braces are simply replaced by do/ehd. Botl notations do tle same
tling, but tle brace notation (i.e., { }) is more concise and makes more sense il you
only lave a line or two ol code. At a certain point in tlis metlod (wlicl will be
discussed later wlen we talk about low to use blocks in your own metlods), tle code
tells Ruby to pass a parameter to tle block and run tle block. Ruby does so and
returns tle value ol tle block code (il tlere is one) mucl like it returns tle value ol a
metlod. Let's visualize tlis llow ol control |ust to drive tle concept lome.
Il you still don't get it, you need to. Co visit some ol tle links in Appendix A under tle
Documentation section, searcl on Coogle, visit some ol tle blogs on tle aggregators
under tle Ruby Language section ol Appendix A. Someone, somewlere las
explained tlis concept in a way tlat you can understand il I laven't, I wouldn't drive
tlis concept lome as mucl, except tlat it's a very cool, uselul, powerlul, and essential
concept in Ruby. Il you do grasp blocks, tlen let's move on to low to use tlem in your
own code.
L3.&1(#,B(<).&*1
Tlink ol PJoc ob|ects as blocks tlat are pusled into variables. Tle dillerence between
tlem is tlere, but not important enougl to worry about until you need to (and you'll
know wlen you do). Tle primary dillerence is perlormance, but tlat will be discussed
wlen we reacl tle otler end ol tle problem.
PJoc ob|ects are simply instances ol tle PJoc class tlat lold a block ol code
tlat is executable.
mypJoc = PJoc.hew {]ah1ma] pu1s "T ove #{ah1ma}!"}
mypJoc.ca{"pahdas")
" T ove pahdas!
32!!!932#*(+$(:.;,(,.;<
As you can see, a PJoc is created wlen tle constructor is called and given a block as a
parameter. Tle code in tle block is tlen stasled away in a PJoc instance and can be
called at any time. PJoc ob|ects are especially uselul wlen you want to create a block
ol code and pass it around or generate new blocks lrom tlat one. To call tle code in
tle PJoc ob|ect, simply use tle obviously named ca metlod and it will call tle code
inside tle block you gave it. Ior example, let's say tlat Tle Big T.V. Network las
commissioned you to write a Ruby script tlat will display tle mastleads lor tleir new
lineup ol slows (wlicl includes Ban|o Bill's Bridge Brigade, Cucina Italiana witl
Ralael Ramirez Rodriguez de Jesus, and PANDA!monium). You simply need to
display tle text lor tle slow on tle console and tleir lancy graplics backend will do
tle rest (yeal, tleir teclnology is that awesome). Tle only problem is tlat because
tleir losts clange so olten (i.e. Mr. Ralael |ust replaced Ho Cli Minl as tle clel on
Cucina Italiana |ust a minute and a lall alter le started), tlere needs to be a way to
specily a slow name separately lrom tle lost name and be able to clange tle lost
name on tle lly. You say, "Hey! Blocks could possibly do tlat!"
de1 make_show_hame{show)
PJoc.hew {]hos1] show + " w11h " + hos1}
ehd
show1 = make_show_hame{"PJac11ca Cahh1ba1sm")
show2 = make_show_hame{"Co111ohs 1h 1he Amazoh")
show1.ca{"h. Ahhabeec1oJ")
" PJac11ca Cahh1ba1sm w11h h. Ahhabeec1oJ
show2.ca{"Jack hahhah")
" Co111ohs 1h 1he Amazoh w11h Jack hahhah
show1.ca{"K1Js11e Aey")
" PJac11ca Cahh1ba1sm w11h K1Js11e Aey
Tlis looks like a typical PJoc call like we looked at belore, but notice sometling tlat's
going on lere. We led it tle slow name wlen tle PJoc was created, but we never
mentioned it alter tlat. How is tlat possible? Wlen tle show parameter lor tle
make_show_hame metlod passed out ol scope (i.e. tle metlod exited), it slould lave
been destroyed. Al, but tlis is one ol tle beauties ol a PJoc ob|ect: it preserves tle
context in wlicl it was created and can access tlat context at any time. Tlis is wly
our slow name was preserved witlout any lurtler ellort on our part.
Anotler way to create a Proc ob|ect is to bind a block ol code using tle lambda
metlod, calling tlis metlod is essentially equivalent to calling PJoc.hew.
mypJoc = ambda {]x] pu1s "AJgumeh1. #{x}"}
mypJoc.ca{"Texas 1oJeveJ!")
" AJgumeh1. Texas 1oJeveJ!
As you can see, tle lambda lunction will take a block ol code and bind it to a Proc, |ust
like Proc.new. Wlat can't be seen lrom tlis example are some ol tle dillerences tlat
exist. Iirst ol all, Proc ob|ects created witl lambda lave stricter argument clecking
tlan tlose created witl Proc.new.
pJoc = ambda {]a,b] pu1s "#{a + b} <- 1he sum"}
hpJoc = PJoc.hew {]a,b] pu1s "#{a + b} <- 1he sum"}
932#*(+$(:.;,(,.;<!!!33
hpJoc.ca{1, 2, 3)
" 3
pJoc.ca{1, 2, 3)
" !AJgumeh1EJJoJ {wJohg humbeJ o1 aJgumeh1s {3 1oJ 2))
Tle PJoc ob|ect created witl PJoc.hew lunctioned line wlen given too many
arguments, but tle amba PJoc witl its Nazi-like argument clecking tlrew an
AJgumeh1EJJoJ. Wlat a |erk...|eez. Crasling tle wlole application |ust because le
got too many arguments? Lame. So, anylow, anotler distinction between tle two is
low tley control tle llow ol your application. Ob|ects created witl ambda don't
allect tle llow ol tle application outside ol tle block wlen returning a value, PJoc
ob|ects created witl PJoc.hew, on tle otler land, will exit tleir enclosing metlod
wlen returning.
de1 pJochew
hew_pJoc = PJoc.hew { Je1uJh "T go1 heJe..." }
hew_pJoc.ca
Je1uJh "...bu1 ho1 heJe."
ehd
de1 ambdapJoc
hew_pJoc = ambda { Je1uJh "You ge1 heJe..." }
hew_pJoc.ca
Je1uJh "Ahd T go1 heJe!"
ehd
pu1s ambdapJoc
" Ahd T go1 heJe!
pu1s pJochew
" T go1 heJe...
Note tlat in tle case ol pJochew, tle value returned is tle value returned lrom tle
block. Tle amba-created PJoc ob|ect simply returns its value to its parent metlod,
wlicl can tlen stasl tle value in a variable or return it il it wants to. Tlis is an
important distinction to remember, because it can cause you a lot ol leadacle il you
are using PJoc ob|ects in a metlod and you can't ligure out wly tle metlod keeps
breaking (I speak lrom experience!). Now tlat you understand low to work blocks
into your code using PJoc ob|ects, let's look at low to integrate tlem in tiglter witl
your metlods.
<'+)B+,N(<).&*1
Tlere are a lew ways to get blocks to work lor you in your metlods, tle lirst way is
tlat you can pass a PJoc ob|ect in as a parameter |ust like you would any otler ob|ect.
Tlis can get tedious, lowever, and, lrom wlat I lear, it also lits your perlormance
pretty lard (I would put tle lit on a level somewlere between being slapped witl a
greasy piece ol bacon and tle rapture). Iortunately, Ruby gives you a lew ways you
can put blocks to work witl minimal luss and perlormance degradation. Integrating
blocks into your everyday code usage is quite simple, |ust combine in a sprinkle ol
34!!!932#*(+$(:.;,(,.;<
determination, a dasl ol !"#$%, and a liberal application ol closures in a small
integrated development disl and bake at 400` lor l5 minutes or until crisp.
Implicit Block Usagc Outside ol taking a Proc parameter, Ruby ollers only one otler
way to use blocks as parameters, but tlis way is not only more intuitive, it perlorms
better. I call tlis implicit block usage because you don't tell your metlod, "Hey, I'm
using tlis block lere," and tlen call it in tle metlod. You simply yield control ol tle
code over to tle block, tlis won't really make sense witlout an example, so let me |ust
slow you a simple snippet.
%#&'!"#$%(#
'')*"+,'-./'0+,#*'(#,12%/'-
''!"#$%
'')*"+,'-3/'04",'(#,12%/-
#+%
!"#$%(#'5')*"+,'-6/'0+,#*'7$289/'-:
!'./'0+,#*'(#,12%/'6/'0+,#*'7$289/'3/'04",'(#,12%/
Notice wlat lappens lere. Iirst, we enter tle metlod and print out our lirst
statement. Second, tle yield metlod is called, and our block is executed. Tle tlread
yields control over to tle block temporarily, wlen tle block exits, tle control is
restored to tle block's caller. Lastly, tle rest ol tle metlod is executed. Tlis is low
tle #;81 metlod on arrays tlat we used earlier works. Let's say we wanted to rewrite
tlat metlod lor some reason (perlaps your pet raccoon wlo lancies bowlers
convinced you to rewrite it), you could use !"#$% to execute tle block.
%#&'(!#;81<(!;**;!=
''",#*'>'?
''@1"$#'<",#*'A'(!;**;!/$#+B,1=C
''''!"#$%<(!;**;!D",#*E=
''''",#*'F>'.'
''#+%
#+%
,#G,;**;!'>'D.H6H3HIHJE
(!#;81<,#G,;**;!='5K",#(K')*"+,'-L5",#(:C-:
!'.C6C3CICJC
I realize tlis snippet miglt be a little over your lead (especially tlat wlile line!), but
bear witl me because tlis is a simple enougl snippet to understand. Tle wlile block
creates a loop, wlicl means tlat we execute tle code inside tle block a number ol
times (learn more about loops on page 53). Even tlougl it may look complicated, tle
same concept applies lere as belore: tle metlod yields control to tle block. Tle
dillerence lere is tlat we passed a parameter to tle block eacl time we looped over
tle code, tlis allows us to use variables lrom witlin tle calling metlod witlin tle
block lor processing. Using yield is an excellent way to implement an iterator like tlis
lor your own collections.
Thc Ampcrsand {&) Paramctcr Il you prepend tle name ol tle last parameter ol a
metlod witl an ampersand, tlen tle block tlat is passed to tle metlod will become a
M*28 |ust as il you lad passed it as a parameter normally. Well, not completely
normally, it does a lew tricks.
!"#$%&'(&)*+,&,*+-"""!"
!"#$%&'"()%*!+,-./012
$$-./0130%..
$$45".!
"*!
%&'"()%*!$6$'(5*7$89:&$;"775*:$0%.."!<$8$=
$9:&$;"775*:$0%.."!<$9:&$;"775*:$0%.."!<
I said it would become a >(/0, so you can use 0%.. on it, but notice also tlat 45".!
works. Tlis is an interesting and lelplul trick, since you may want to use 0%.. or
45".! in dillerent cases.
!"#$%"&'()*+%,-).%),-++/
As stated (many times) belore, everytling in Ruby is an ob|ect, Ruby, ol course, allows
you to create your own ob|ects tlrougl tle creation ol classes to describe tlem. Il
you've programmed in an ob|ect oriented language belore (like C#, C++, Pytlon, or
Java), tlen tle concepts ol classes and ob|ects slould not be loreign to you, but tlere
are some distinctions between tlose languages' implementation ol ob|ect orientation
and Ruby's implementation.
One tling tlat may seem ratler loreign is tle way tlat Ruby landles typing
ol ob|ects. Languages like C++ or Java operate solely on static (or explicit) typing, tlis
sort ol typing requires tlat eacl ob|ect lave its type explicitly delined at compile time
(or tle compiler will tlrow an error). I realize tlat most modern languages tlat use
static typing also implement a sort ol rellection or some sucl module tlat allows you
to dynamically load types, but Ruby uses a completely dillerent approacl to typing. Il
you've used Pytlon, you're lamiliar witl tle concept ol dynamic typing, Ruby uses
tlis same idea but calls it "duck typing" (wlicl makes it mucl easier to explain).
!"###!"#$%&'(&)*+,&,*+-
!"#$%&'()'*+,-'./011&1'"23/&2&4,'0'2&,-+5'402&5'106-&//+7'1+',-&6'.04'8+,-'%&13+45',+',-&
2&110#&9'':';+$/5'/"<&',+'055',-0,'6+$%./011'12&//19
Anotler concept tlat may seem loreign to C++ or PHP programmers (but
not Java or C# programmers) is tle concept ol a language-wide ob|ect lierarcly. In
Ruby, every class is actually an ob|ect wlicl is an instance ol tle Cass class wlicl is
in turn derived lrom tle 0bec1 class. We can demonstrate tlis witl code:
pu1s F1e.cass
" Cass
pu1s F1e.supeJcass
" 0bec1
pu1s 0bec1.supeJcass
" h1
Tle super class ol a class is tle class wlicl it is derived lrom, in Ruby, we can say tlat
classes can "inlerit" lrom anotler class all ol its metlods and variables. As a result, il
a class is derived lrom anotler class (i.e. it inlerits lrom anotler class), it las access to
all ol tle super class's metlods and variables. Tle catcl in Ruby is tlat unlike some
otler languages, a class can only inlerit lrom one class at a time (i.e. a class can
inlerit lrom a class tlat inlerits lrom anotler class wlicl inlerits lrom anotler class,
but a single class can not inlerit lrom many classes at once). As we can see, tle F1e
class is |ust an instance ol tle Cass class wlicl inlerits lrom tle 0bec1 class wlicl
inlerits lrom notling. Tlis means tlat tle F1e class las access to all ol 0bec1's
metlods and variables (|ust like every otler class). Tlis also means tlat tle 0bec1
class is tle origin ol all otler ob|ects, it is tle Adam and tle Eve, tle Creator, tle
Arclitect, tle Motler ol all Ob|ects and Classes!
I'm sure tlis talk ol classes being an instance ol sometling is somewlat
conlusing, but keep in mind: everytling in Ruby is an ob|ect. Everytling. So wlen
you deline a class, you're really creating an instance ol tle Cass class. Wlen you
create a new ob|ect lrom a class, you're calling {cass hame).hew wlicl is a metlod
tlat returns a new ob|ect instance ol tle class it describes. Everytling in Ruby is an
ob|ect!
!"#$%$%&'403--"-
So let's get to it. To deline a class, you place tle cass keyword at tle beginning ol a
line, lollowed by a < and tle class it inlerits lrom (il it inlerits lrom a class). Ior
example:
cass hyF1Js1Cass < 0bec1
ehd
Tlat's it, we've |ust delined our lirst class. Cranted, tlat was tle most contrived
example ever put into print and it represents a completely useless class, we still
delined a class. Notice tlat I indicated it inlerits lrom 0bec1, but tlis is completely
unnecessary, Ruby will assume tlat il you deline a class witl no otler inleritances
tlat you wisl to inlerit lrom 0bec1.
932#*(+$(:.;,(,.;<!!!37
(")*+,-'3%,'536$370"-
Classes can contain variables and metlods, tle lirst tling you would most likely want
to add would be a metlod so you can make your class do some work. Tle lirst metlod
we slould add is tle 1h111a1ze metlod, wlicl is tle metlod tlat Ruby calls wlen
you call {cass hame).hew. Wlen you call tle hew metlod, a new ob|ect is created
and tle 1h111a1ze metlod (witl parameters passed lrom hew) is called to setup tle
ob|ect's state (i.e. variables, llags, etc.), tlis is very similar to (albeit identical to) otler
language's constructors. Ior example, let's say tlat tle Boogeyman las decided to
give up on trying to lreak out every kid in tle world (le's ratler old, you know) and
instead build a robot army (running soltware written in Ruby no less) to do lis
nelarious bidding. Tle initial class delinition and initialize metlod miglt look like
tlis:
cass Boogeymah
de1 1h111a1ze
pu1s "Yes, mas1eJ?"
ehd
ehd
mohs1eJ1 = Boogeymah.hew
" Yes, mas1eJ?
Tlis metlod, ol course, does no real work otler tlan to demonstrate tlat wlen you
create a new ob|ect, tle initialize metlod is called. Let's make it do some work now:
cass Boogeymah
de1 1h111a1ze{hame, oca11oh)
_hame = hame
_oca11oh = oca11oh
pu1s "Yes, mas1eJ?"
ehd
ehd
mohs1eJ1 = Boogeymah.hew{"h1s1eJ CJeepy", "hew YoJk, hY")
" Yes, mas1eJ?
Tlis new initialize metlod sets some instance variables (i.e. variables tlat are used
witlin an ob|ect to retain its state), to set an instance variable, prelix tle lvalue witl an
at symbol (_). Unlike otler languages, you don't lave to include tlese variables inside
tle class delinition.
Variables set tlis way are unique to tlat particular instance, we could say
tlat tley are instance scoped (i.e. tley are not usable outside ol tlat instance unless
tley are passed outside ol it, remember scoping?). Notice tlat since _hame and hame
are scoped dillerently, we can use duplicate names witlout ambiguity (tlougl tlis is
usually not a good idea). Let's create a lew metlods to work witl an ob|ect's state:
cass Boogeymah
de1 chahge_oca11oh{hewoca11oh)
_oca11oh = hewoca11oh
pu1s "T moved 1o #{hewoca11oh}!"
se1.ge1_1h1o
ehd
38!!!932#*(+$(:.;,(,.;<
de1 chahge_hame{hewhame)
_hame = hewhame
pu1s "T sha be caed #{hewhame} 1Jom how oh!"
se1.ge1_1h1o
ehd
de1 ge1_1h1o
pu1s "T am #{_hame} 1h #{_oca11oh}."
ehd
ehd
mohs1eJ1 = Boogeymah.hew{"Loopy Lou", "AbuqueJque, hh")
" Yes, mas1eJ?
mohs1eJ1.chahge_oca11oh{"Wyom1hg")
" T moved 1o Wyom1hg!
" T am Loopy Lou 1h Wyom1hg.
mohs1eJ1.chahge_hame{"Beezebub")
" T sha be caed Beezebub 1Jom how oh!
" T am Beezebub 1h Wyom1hg.
Tlis example demonstrates two important concepts. Iirst, notice tlat I did not enter
tle entire class listing again. Tlis wasn't laziness (well, not completely at least), in
Ruby, classes are never closed. Tlis means you can always add metlods to or redeline
(or in proper terminology, override) any metlod lor a class simply by opening a class
delinition and adding or redelining a metlod. Tlis can be dangerous at times, but
overall it's one ol tle most uselul aspects ol Ruby's ob|ect implementation. Let's look
at an example:
cass S1J1hg
de1 wJ11es1ze
pu1s se1.s1ze
ehd
ehd
s1ze_wJ11eJ = "Te me my s1ze!"
s1ze_wJ11eJ.wJ11es1ze
" 16
As I said belore, wlile it's possible to override a class's metlods (even built-in
classes!), it can be dangerous (i.e. modilying some ol 0bec1's metlods or modilying
certain operators can make everytling go nuts), but at tle same time, it can also be
uselul. Tle Ruby web lramework Rails makes extensive use ol tlis concept, especially
in its ActiveSupport package, il you're looking lor sometling a little more complicated
and interesting, I suggest looking at tleir extensions to various classes in tlat
package.
Tle second concept slown in tlese examples is tle use ol tle se1 ob|ect.
Tle se1 ob|ect always points to tle current instance, it allows you to call metlods
lrom witlin tle current instance (like s1ze in tle S1J1hg class or ge1_1h1o in our
class). Tlougl se1 isn't required in most cases (i.e. il no receiver is specilied lor a
metlod, Ruby assumes you meant se1), it is important to be aware ol it in case you
are in a context wlere you will need it (e.g. you lave implemented a metlod named
pu1s in your class and you want to call it and not tle built-in one).
932#*(+$(:.;,(,.;<!!!3
6$$3+-'$21
Wlile instance variables are uselul in tleir own way, tley aren't visible to tle outside
world. It may seem like a dandy situation at lirst: all your ob|ects' states are
completely lidden and unclangeable by tle outside world. But alter a wlile, you
miglt |ust want to retrieve or clange a value witlin an ob|ect. How are we to do tlis?!
Well, it's really quite simple:
cass Boogeymah
de1 scaJe_1ac1oJ
_scaJe_1ac1oJ
ehd
de1 h1d1hg_pace
_h1d1hg_pace
ehd
de1 scaJe_1ac1oJ={1ac1oJ)
_scaJe_1ac1oJ = 1ac1oJ
ehd
de1 h1d1hg_pace={pace)
_h1d1hg_pace = pace
ehd
ehd
mohs1eJ1 = Boogeymah.hew{"CJazy Ca", "hashv1e, Th")
mohs1eJ1.scaJe_1ac1oJ = 6000
pu1s mohs1eJ1.scaJe_1ac1oJ
" 6000
As tle example slows, to create a readable attribute, you simply create a metlod and
place tle instance value to return in it (tle last value used in a metlod is returned
remember?). Attributes are simply metlods tlat are used to retrieve or set values. To
create a writable attribute (i.e. an attribute you can set), you simply append an equals
sign () alter tle name ol tle attribute metlod, you can eitler do like I did and write
straiglt to an instance variable or do some otler work belore you do so (i.e. make sure
tle value provided is tle proper type/lormat, convert lormatting to a more usable
lorm, etc.). Tlis seems like an awlul lot ol work |ust to write to a value in a class
doesn't it? I mean, in C# or sometling similar all I lave to do is put "pub1c" belore
tle variable in tle class and it's visible to tle outside! Well, since attributes are sucl a
common construct, Ruby las a really simple lacility lor tlem:
cass Boogeymah
a11J_JeadeJ .scaJe_1ac1oJ, .h1d1hg_pace
a11J_wJ11eJ .scaJe_1ac1oJ, .h1d1hg_pace
ehd
Now you can read and write attributes |ust as belore, tlese laculties are a pretty way to
create metlods tlat belave identically to tle ones we created belore. Tlis teclnique
is easier tlan writing out metlods, but you lose tle llexibility you may gain by making
your readers or writers explicit metlods tlat you write. Ior example, let's say tlat tle
scare lactor is supposed to be displayed in Ireak-o-grams (Ig), you could write a
reader to display it as sucl:
cass Boogeymah
a11J_wJ11eJ .scaJe_1ac1oJ
40!!!932#*(+$(:.;,(,.;<
de1 scaJe_1ac1oJ
Je1uJh s1J{_scaJe_1ac1oJ / 1000) + "Fg"
ehd
ehd
mohs1eJ1 = Boogeymah.hew{"Psycho Say", "Los Ahgees, CA")
mohs1eJ1.scaJe_1ac1oJ = 6000
pu1s mohs1eJ1.scaJe_1ac1oJ
" 6Fg
Some would call tlese virtual attributes, but I really tlink tley need a special name. It
really doesn't matter wlat you call tlem, but tley are a great way to mask tle
implementation ol your class. To tle outside world, it looks like a normal attribute,
but you know tle trutl! It's your little way ol sticking it to tle man.
6&&211(K.,$3.)
So lar our metlods and attributes lave been wide open to tle world, but now let's take
a look at ways we can control access to parts ol our class. Up until now, all ol our
metlods (except 1h111a1ze, wlicl is always private) lave been wlat we call public
(i.e. accessible lrom witlin tle class and tle outside world). Since tlat is tle delault
belavior ol Ruby, let's add a metlod to our class as an example:
cass Boogeymah
de1 scaJe{who)
pu1s "T us1 scaJed 1he beezus ou1 o1 #{who}!"
ehd
ehd
Tle metlod we |ust created is public because we didn't specily any access controls, we
could create protected metlods (i.e. a metlod tlat accessible by any instance ol a class
or its derived classes) by placing pJo1ec1ed on a line and tlen entering subsequent
metlods wlicl will be protected. Ior example:
cass Boogeymah
pJo1ec1ed
de1 aJe_you_a_mohs1eJ?{whosask1hg)
pu1s "Yes, #{whosask1hg}, T am a mohs1eJ!"
Je1uJh 1Jue
ehd
ehd
Now tle only ob|ects wlicl lave access to tlis metlod are tlose tlat are instances ol
Boogeymah and any class tlat is derived lrom Boogeymah. Tlis is uselul il you lave a
metlod like tle one above tlat needs to provide inlormation to classes ol tle same or
similar type, but no one else. On tle otler land, il you lave a metlod tlat only tle
current ob|ect instance slould lave access to, tlen you slould declare it private. Tle
dillerence between protected and private is only sliglt: protected allows any instance
ol tle same or derived class to access it but private allows only tle current instance to
lave access. Let's add a metlod to plone lome to tle Boogeyman limsell and
redeline our scaJe metlod to use it.
cass Boogeymah
pJ1va1e
de1 phohe_home{message)
932#*(+$(:.;,(,.;<!!!41
# T000. Ac1uay make 1h1s phohe home
# FoJ how, we` us1 wJ11e 1o 1he cohsoe
pu1s message
ehd
pub1c
de1 scaJe{who)
phohe_home{"T us1 scaJed 1he 1v1hg poop ou1 o1 #{who}!"
ehd
ehd
Now only tle current instance will lave access to tle phohe_home metlod, we
wouldn't want |ust anyone ploning lome and making it look like tlis monster would
you? Notice tlat you can use tle pub1c keyword in tle same way to make metlods
explicitly public or in tlis case to clange tle mode back to public alter delining a
private metlod.
K)#11(9&.T2B(?-U2&$1
So lar we've been working witl instances: instance variables, instance metlods, et
cetera, but many times a class (ratler tlan an instance ol tlat class) needs to maintain
a state or provide a metlod tlat is not tied to an instance. Tlis is wlere class
constants, variables, and metlods enter tle ball game.
Class constants are landy little meclanisms tlat allow you to place values
into tle class scope tlat will not be clanged (unlike variables wlicl may and probably
will clange). To create a class constant, you simply place tle constant name and
value into tle class delinition:
cass Boogeymah
hY_B0SS = `hJ. Boogeymah`
ehd
Now every metlod in class Boogeymah (botl instance and class scoped) las access to
tle value hY_B0SS.
To create class variables, place two at symbols (__) belore tle name ol a
variable, tley operate nearly identical to instance variables except tleir state lives in
tle class ratler tlan a particular ob|ect. Ior example, tle Boogeyman las asked tlat
we lave a way to get tle name ol tle newest denizen le las released and wlere le is.
We can provide tlis witl a class variable:
cass Boogeymah
# We` Jede11he 1h111a1ze
de1 1h111a1ze{hame, oca11oh)
_hame = hame
_oca11oh = oca11oh

__a1es1 = _hame
__oca11oh = _oca11oh
pu1s "Yes, mas1eJ?"
ehd
ehd
42!!!932#*(+$(:.;,(,.;<
mohs1eJ1 = Boogeymah.hew{"hacabJe hac", "Sea11e, WA")
" Yes, mas1eJ?
mohs1eJ2 = Boogeymah.hew{"CoJy CaJy", "Res1oh, Wv")
" Yes, mas1eJ?
pu1s Boogeymah.a1es1
" CoJy CaJy
As tle example slows, you access a class variable by using tle class name lollowed by
a dot and tle variable name, mucl like class constants, you can access tlis value lrom
eitler class scoped or instance scoped metlods. Notice tlat because __oca11oh,
_oca11oh, and oca11oh are all scoped dillerently, tley can all use tle same name
witlout causing a problem. Wlile tlis isn't recommended (laving tlree variables
witl tle same name is likely to drive you batty in less contrived, more real world
situations), it is possible (and occasionally uselul).
Class metlods are metlods tlat are provided by a class (not an instance) tlat
may not particularly need to be to an instance. Tlis leature is uselul il, lor example,
you wanted to provide a prelabricated instance ol a class (e.g. a metlod named mah lor
a class named PeJsoh miglt provide an instance witl tle gehdeJ lield set). Tle
Boogeyman las requested tlat we lave a class metlod to output tle latest robot's
name since le's way too lazy to delve into tle code or use a Ruby console to lind out
lor limsell (le is retired, you know). So let's provide:
cass Boogeymah
de1 Boogeymah.a1es1_mohs1eJ
pu1s "The a1es1 mohs1eJ 1s #{__a1es1}."
pu1s "he 1s 1h #{__oca11oh}."
ehd
ehd
Boogeymah.a1es1_mohs1eJ
" The a1es1 mohs1eJ 1s CoJy CaJy.
" he 1s 1h Res1oh, Wv.
Because a1es1_mohs1eJ is a class metlod, it only las access to variables witlin its
scope (i.e. class variables), tlis means it cannot access instance variables at any time
unless tley are passed into tle metlod as a parameter or relerenced in a class variable.
Unlike otler class scoped variables, class metlods are not visible to instance scoped
ob|ects and metlods, tlis means tlat you must call a class metlod using its lull call
(i.e. you wouldn't be able to simply type a1es1_mohs1eJ like you can hY_B0SS or
__a1es1, you would be required to call it using Boogeymah.a1es1_mohs1eJ).
0?Q;@F9
Perlaps sometime you'll need to organize a lot ol code. I mean a lot. Like tle
population ol Clina a lot. Maybe tlat code isn't simply one class or it isn't necessarily
all perlectly related, maybe it's got some issues, maybe it's still angry about tlat
argument it lad witl grumpy_butt.rb last week, in any event, you lave been clarged
to group it togetler lor tle sake ol reuse and organization. Normally in sometling
like C or PHP you miglt simply stick tlis code in a lile and include it wlerever you
932#*(+$(:.;,(,.;<!!!43
need it, but wlat il you lave two metlods or constants tlat are named tle same? Let's
say you were creating a panda liglting game. You lave a constant 0EATh in
cohs1_vaues.Jb to represent tle amount ol lile tlat would constitute a deatl in tle
game, but you also lave a constant 0EATh in 11gh1eJ_vaues.Jb to lold an array ol
values dealing witl tle player claracter named Deatl. Botl pieces need to be
included, but tlere's a name conllict. You would ratler maintain a simple naming
scleme lor constants ratler tlan concocting some silly name like
0EATh_vALbES_BECAbSE_T_CAhT_ThCLb0E_TT_WTTh0bT_A_hbCE_hAhE. Tlis is wlere
modules are ratler landy, tley allow you to group constants and metlods togetler
logically into groups called namespaces, groups wlicl organize modules and tle like
in sucl a way as to avoid ambiguity and promote logical organization ol code.
Namespaces allow you to write larger groups ol reusable code witlout tle danger ol
stomping on otler code outside ol tle namespace. Tlis means tlere could be a
namespace F1gh1eJvaues and a namespace Cohs1ah1vaues to maintain tleir
respective values.
K32#$+,N(0.B')21
Tle syntax lor creating a module is very similar to tle syntax lor creating a class, you
place tle keyword modue lollowed by tle module name, tlen on tle subsequent lines
you enter tle metlods and classes wlicl slould resign in tlis module lollowed by tle
ehd keyword. Let's look at our example lrom belore:
modue F1gh1eJvaues
BAhB00_hEA0 = { `11e` => 120, `h11` => 9 }
0EATh = { `11e` => 90, `h11` => 13 }
K0ALA = { `11e` => 100, `h11` => 10 }
ChbCK_h0RRTS = { `11e` => 60000, `h11` => 99999999 }
de1 chuck_1ac1
pu1s "Chuck hoJJ1s` 1eaJs cah cuJe cahceJ..."
pu1s "Too bad he heveJ cJ1es."
ehd
ehd
modue Cohs1ah1vaues
0EATh = -5 # Pahdas cah 1ve PAST 0EATh.
EASY_hAh0TCAP = 10
hE0Tbh_hAh0TCAP = 25
hAR0_hAh0TCAP = 50
ehd
pu1s F1gh1eJvaues..0EATh
" {`11e`=>90,`h11`=>13}
pu1s Cohs1ah1vaues..0EATh
" -5
Now botl values can be used and co-exist in a lriendly environment. I'm sure you're
tlinking, "Wly not |ust use a class?" I asked mysell tlat wlen I lirst saw tlis
construct, tle only reason I saw was tlat, lor tle sake ol design and proper soltware
engineering, you slouldn't put tlings in a class tlat don't really go togetler so tle
module was a good excuse to break tlat rule witlout breaking it.
44!!!932#*(+$(:.;,(,.;<
But tlen I saw tle coolest part.
Modules lave a meclanism tlat allow lor wlat's called a mixin, code tlat is
"mixed into" a class as il it is part ol its original code. Tlink ol it as inleritance, except
better. As noted earlier, a class in Ruby can only inlerit lrom one class at a time. To
inlerit lrom anotler class you would lave to create some sort ol clain ol inleritance
tlat would allow you to do "multiple inleritance" (not really but tlat's as close as you
can get in Ruby). Mixins eliminate tle need lor tlat. You could create a class, inlerit
lrom anotler class, and mix in as many modules as you need. Tlis leature is especially
great il tle code tlat you need to mixin needs to only be mixed in (i.e. it won't ever be
used by itsell). Let's look at a contrived example:
modue hovemeh1
de1 Juh
pu1s "T`m Juhh1hg!"
ehd
de1 wak
pu1s "T`m wak1hg a b11 bJ1sky!"
ehd
de1 cJaw
pu1s "T`m so sowwww!"
ehd
ehd
cass hah
1hcude hovemeh1
de1 ump
pu1s "T`m b1peda ahd T cah ump 1ke a 1oo!"
ehd
ehd
cass So1h
1hcude hovemeh1
de1 1op
pu1s "T1`s a a 1e...a T cah do 1s 1op aJouhd."
ehd
ehd
m1s1eJ_mah = hah.hew
m1s1eJ_mah.Juh
" T`m Juhh1hg!
m1s1eJ_so1hy = So1h.hew
m1s1eJ_so1hy.1op
" T1`s a a 1e...a T cah do 1s 1op aJouhd.
As you can see, tlis meclanism is very similar to inleritance in tlat you can use all ol
tle mixin's code. To use a mixin, simply deline a module and tlen use tle 1hcude
keyword lollowed by tle module's name (note I said modulc, tle include keyword las
notling to do witl liles or libraries like in PHP or C++), lrom tlen on tle class las
access to all ol tlat module's constants and metlods. It's obvious tlat tlis example
doesn't do tlis meclanism |ustice (i.e. it doesn't demonstrate tle usage ol module
constants in a mixin, it doesn't do mucl witl tle lost class, etc.), but tlis is merely
meant to be an introductory example in lopes tlat you will experiment and read
lurtler. As you will learn, tle magic really lappens wlen tle class actually interacts
932#*(+$(:.;,(,.;<!!!45
witl tle mixin (as witl some ol tle Ruby built-in mixins sucl as S1hge1oh or
CompaJabe wlicl greatly extend tle lunctionality ol your class or some ol tle Rails
mixins), but tlat's lor a more advanced look at tle sub|ect.
You may be tlinking tlat tlis sounds great now, but do be carelul. Mixins
are awesome as long as tley are written well, but il tle developer doesn't pay attention
and be carelul about naming tley can create lavoc in your application. Ior example,
let's say you lave a constant called PT wlicl lolds Pi to tle 72nd digit (wlicl you
typed out manually because tlat's tle amount ol precision you need), but you mix in a
trigonometry library written by Billy McDoolus wlicl las a constant named PT wlicl
is only Pi to tle 5tl digit. You need tlat precision, but since Billy McDoolus is an
idiot, tle mixed in library's constant will override your constant. It's best to be sure
tlat your naming scleme is unique (possibly even including tle module name or your
name) so as to not stomp on otlers' code.
You may be tlinking riglt now, "Tlat |erk lelt out metlods in modules!
Wlat about metlods!? I'm going to kill lis cat and take my money back! Are my
metlods simply going to suller tle same terrible late?" You would be |ustilied in your
luror, but relent, good lriend, lor metlods do not suller tle same late. Wlen you call
a metlod, Ruby will lirst look to tle lost class (tlat is, tle class being mixed into) and
tlen to its mixins (and tlen to its superclasses and its mixins and so on), tlis belavior
is tle exact opposite ol constants (and no I don't know wly tlat is). Tlis could be a
blessing or a curse (i.e. you may want tle metlod to override yours), but generally
tlis is tle salest lunctionality lor it.
M8@F9
As your application gets bigger and bigger, you surely won't want all ol your code
living in one luge 5MB lile. Clopping code up into liles is one ol tle oldest and
easiest ways to segment your code. I saved it lor last because I don't want your answer
to code segmentation to always be "Stick it in a lile!" wlen Ruby ollers more (and
better suited) options tlan tlat. I tlink PHP programmers especially get stuck in tlis
rut ol including code all over tle place, creating a |ungle ol liles tlat only tle maclete
ol tle delete command can navigate, but I digress. To include a lile in Ruby, you lave
two options: oad and and its more elegant cousin Jequ1Je. Tle dillerence is tlat tle
oad keyword includes a source code lile unconditionally wlile Jequ1Je will only
include it once (wlicl means no duplication ol variables and metlods):
oad "1bJaJ1es/my11e.Jb"
Jequ1Je "/home/myaccouh1/code/1bJaJ1es/myo1heJ11e.Jb"
Botl keywords accept eitler relative or absolute patls, il Ruby identilies it as a
relative patl, it will searcl lor tle lile in tle current patl (stored in $. lor tlose wlo
are curious). Tle require statement is also great in tlat you can use it in conditionals,
loops, and otler constructs or use variables in its patls (you can not witl load). Keep
in mind tlougl tlat local variables in included liles do not trickle into tle context tley
are included in (tlis is contrary to PHP's and C/C++'s belavior), tlese variables are
locked into tle context tlat tley are written in.
46!!!932#*(+$(:.;,(,.;<
!"#$%&"'()*+%%
You learned low to break up your code into more logical and usable pieces. You
learned...
! low to segment your code using blocks, metlods, classes, modules, and liles.
! low variable scoping works and low it can benelit you.
! low to make your own classes and ob|ects and low to make clanges to
otlers.
! tlat modules allow you to mix in code to classes and extend tlem.
932#*(+$(:.;,(,.;<!!!47
$
5'1$)2(#,B(V).C(W&.,$3.)XJ
Ilow control is essential to every application (unless ol course your application will
always lave one course ol action and you don't mind copying and pasting a lot ol
code). Ilow control constructs allow you to brancl (i.e. execute a dillerent llow ol
code) based on conditions, run tle same brancl numerous times, or bail wlen one ol
tle conditions is wonky. Tlere are two "general" llow control meclanisms:
conditionals and loops. I'll address tlem separately lest tley end up in some sort ol
extremely conlusing mental !"##$%%& .
K?>Q878?>6@9
Conditional constructs are tlose constructs tlat give you and your users cloices, tley
allow your program to brancl and execute dillerent code based on conditions witlin
tle application. Tley compare values to determine trutl and based on tlat trutl tley
determine wlicl (il any) brancl ol code slould execute. Tlink ol tlem as tle trallic
liglts ol tle Ruby programming world.
7"2 11(1$#$2H2,$
Tle most lundamental conditional statement available in Ruby is tle 11 statement: it
simply compares values to determine trutl or untrutl. Il it is true, tlen tle code
contained witlin tle main brancl is executed, il it is not true, eitler tle code in tle
ese clause is executed or, il no ese block is present, no code in tle block is executed
and tle application continues on.
48!!!='1$)2(#,:(>).;(?&.,$3.)@8
Tle result ol all 11 statements is eitler true or lalse regardless ol low many conditions
tley test. Let's look at an example:
11 ho1 {1Jue != 1ase) && {1Jue != 1Jue).
pu1s "Whoa! A1eJha1e d1mehs1oh!"
es11 {1ase === `1ase`) 1heh
pu1s "Type cohveJs1oh? T 1h1hk ho1..."
es11 {1Jue == 1) oJ {3 == 1ase)
pu1s "Booeahs != humbeJs"
ese
pu1s "T guess we`Je s11 0K!"
ehd
" T guess we`Je s11 0K!
Tlere's a lot to look at lere, so let's take it a piece at a time. An 11 statement is
constructed by placing tle 11 keyword on a line lollowed by a conditional comparing
values equality, inequality, etc. Tle 11 statement itsell opens tle conditional block
(i.e. 11 is tle initiator ol a beg1h/ehd block), you can create an 11 block simply witl
tle 11 statement, its block ol code, and tle ehd keyword il tlat's all you need. Tlis
example, tlougl, demonstrates all ol tle possible constructs lor an 11 statement. Tle
es11 and ese keywords allow you to brancl il tle conditions in tle 11 statement are
not met, es11 allows you to present anotler conditional test wlile ese is simply a
lail sale il none ol tle conditions are met. Tlis means you don't need to create l0 11
blocks wlen you can simply wrap tlem into l 11 block witl a lot ol es11 statements
(witl tle salety net ol tle ese statement to boot).
Tle second tling to notice are tle operators used in tle conditionals. To test
lor equality, you slould use tle == operator (tlat's two equals signs, not one, il you
use one it will usually result in 1Jue because you would be assigning a value ratler
tlan clecking lor equality). To test lor inequality, use tle != operator. Ratler tlan
entering tlese into a big lat paragrapl, lere is a table ol operators usable by Ruby in
conditionals:
='1$)2(#,:(>).;(?&.,$3.)@8!!!4
Figurc 7: If I wcrc a fish in thc sca, I'd wigglc my tail and I'd gigglc with glcc...
K?>Q878?>6@(?LF:67?:9(8>(:;<=
a == b Is true il tle value ol a to tle value ol b.
a === b Is true il tle value ol a to tle value ol b and tley are tle same type.
a != b
a <> b
Is true il a is not equal to b.
a !== b Is true il a is not equal to b or tley are not tle same type.
a < b Is true il a is less tlan b.
a > b Is true il a is greater tlan b.
a <= b Is true il a is less tlan or equal to b.
a >= b Is true il a is greater tlan or equal to b.
Tle tlird tling to notice about tle example are tle logical operators, tlese
are special operators used to clain conditionals togetler or to evaluate special cases.
Ruby supports two lorms ol tle operators: C-style and stringilied. Tle C-style
operators are like tle one in tle lirst example, && is tle same as "ahd," tle ]] operator
means "oJ," and so on. Tle stringilied operators are tle same as tleir symbolic
bretlren except tlat tley are strings: ahd, oJ, etc. Here is a table witl tlese operators
in botl lorms and tleir usage:
K?>Q878?>6@(@8>R(?LF:67?:9(8>(:;<=
&& ahd Conditional is true il linked statements are botl true.
]] oJ Conditional is true il eitler ol tle linked statements are true.
! ho1 Conditional is true il attacled statement is lalse (i.e. it works like !).
Thc Tcrnary Opcrator Now tlat you know tle long land way to do 11 statements, I
would like to slow you a bit ol a slortcut. Tle ternary operator (Random piece ol
trivia: tle ternary operator is named simply lor laving tlree parts) allows you to
execute an il statement witlout creating an entire 11 block. Ior example, lere is an 11
block lollowed by its ternary equivalent:
11 {`Yes` == `ho`) 1heh
pu1s "WJohg!"
ese
pu1s "Yeah, baby!"
ehd
" Yeah baby!
{`Yes` == `ho`) ? {pu1s "WJohg!") . pu1s {"Yeah, baby!")
" Yeah baby!
Tle ternary operator consists ol tle question mark alter a conditional (wlicl uses tle
same operators as an 11 statement) and a colon between tle two possible brancles ol
code. Tle parentleses are not essential in every case, but do enlance readability and
50!!!='1$)2(#,:(>).;(?&.,$3.)@8
allow you to span tle brancles multiple lines (i.e. open tle parentleses on one line
enter eacl line ol code and close tle parentleses on anotler line). Tlis multi-line
belavior is not recommended because it sligltly mangles tle code and really doesn't
save you any typing at all compared to an 11 statement.
Unlcss In Ruby tle uhess conditional statement operates as a "reverse" 11. I say
reverse because in an 11, tle main brancl executes only il tle conditional is true, in an
uhess statement, tle main brancl only executes il tle provided condition is lalse.
Ior example:
uhess {"T am 1Jue." == "T am 1Jue.").
pu1s "Some1h1hg wohky!"
ese
pu1s "A 1s aJ1gh1!"
ehd
" A 1s aJ1gh1!
Since "I am true." does indeed equal "I am true.", tle main brancl is not executed but
tle ese brancl is. Tlis structure doesn't provide anytling over an 11 statement
except to enlance readability ol tle code (e.g. it's mucl easier and casual to say "Do
tlis unless tlis" ratler tlan "Do tlis unless tlis is not tlis" and to avoid writing a lot
ol negative 11's, keeping positive cli in your code is very important to tle emotional
lealtl ol Ruby).
Statcmcnt Modificrs Tle 11 statement also ollers a nilty piece ol syntactic sugar
called a modilier, tlis construct allows you to tag an 11 statement on tle end ol a
statement and lave it decide wletler or not tle statement is attacled to slould be
executed. Ior example:
1hema_ou1se = 13
pu1s "She`s ess 1hah 15 aJ1gh1!" 11 1hema_ou1se < 15
pu1s "She a1h`1 moJe 1hah 12, 1hough!" 11 1hema_ou1se < 12
" She`s ess 1hah 15 aJ1gh1!
As you can see, il you simply tag on an 11 statement to end ol a line, it will evaluate tle
11 statement lirst and execute tle code il it linds tlat tle il statement is true. Tlis
construct replaces code like tlis:
11 1hema_ou1se < 15.
pu1s "She`s ess 1hah 15 aJ1gh1!"
ehd
witl a lar more concise and elegant solution. Rubylicious. Tlis construct can be
tagged on to beg1h/ehd blocks also. Ior example:
beg1h
pu1s "T1`s so 1Jue!"
ehd 11 {1Jue == 1Jue)
" T1`s so 1Jue!
='1$)2(#,:(>).;(?&.,$3.)@8!!!51
Tlis is a very elegant solution lor controlling big blocks ol code ratler tlan laving a
luge, open 11 statement tlat spans 500 lines (especially il tlere's tlat one end
keyword you lorgot to drop in).
7"2(case(9$#$2H2,$
Il you laven't noticed, I'm all about laving tle neatest code in tle least strokes (wlo
isn't nowadays?). It gets really irritating really last wlen you are testing tle same
value over and over again in a series ol 11 and es11 statements tlat quickly add up to
a lot ol sloppy looking code. It's a good tling tlat tlere is a way to put all tlose
togetler into one syntactically sugarlicious block called a case statement. A case
statement comes in two lorms in Ruby. Tle lirst lorm is tle "standard" way case
statements operate: you lave a "target" variable and eacl case is a test ol values
against tlat variable. Ior example:
1he_1ao = 1234
case 1he_1ao
wheh 666. pu1s "Tha1`s such a 1e!"
wheh 1337
pu1s "L1aJJJJ!"
wheh 32767 1heh
pu1s "Wha1evuJJJJ!"
ese
pu1s "You aJe haJmoh1zed w11h 1he Tao."
ehd
" You aJe haJmoh1zed w11h 1he Tao.
Tlis lorm ol tle case statement simply tests tle target value against tle values in tle
cases, cases are lormed using tle wheh keyword lollowed by tle test wlicl is lollowed
eitler by tle word 1heh or a colon (botl ol wlicl are non-essential il tle case starts on
tle next line), tle wheh keyword segments tle cases so no "ehd" keyword is needed
except to close tle case block. Eacl case is tested starting lrom tle top, wlen a matcl
is lound tle proper brancl is executed and tle block exits. Il notling matcles tlen
tle else clause is executed (|ust as in 11 blocks).
Tle second lorm ol tle case statement adds more llexibility by allowing you
to use conditionals like 11 statements and removing tle target. Even tlougl tle
target is removed, you can still use tlem in tle same way you would tle lirst lorm:
eh1gh1ehmeh1 = 42
case
wheh eh1gh1ehmeh1 > 60.
pu1s "You aJe 1oo has1y, gJasshoppeJ."
wheh eh1gh1ehmeh1 < 40 oJ eh1gh1ehmeh1 == h1.
pu1s "You aJe 1ke 1he so1h, my 1J1ehd. 011gehce 1s key!"
wheh eh1gh1ehmeh1 == 42.
pu1s "heo, Eh1gh1ehed 0he."
ese
pu1s "Yeah, ho1 qu11e, pa. haybe hex1 11me."
ehd
" heo, Eh1gh1ehed 0he.
52!!!='1$)2(#,:(>).;(?&.,$3.)@8
As you can see, tlis lorm can still be used to compare a single variable, but allows lor
more robust comparisons tlan tle lirst lorm. Eacl comparison is clecked, starting
witl tle top case, lirst comparison, working to tle riglt and down, wlen a case is
matcled (i.e. all conditions needed lor a case to be true are met including clained
conditions) tlat brancl is executed and tle block exits.
A commonality between tle two lorms is tleir lack ol "lall tlrougl", in many
programming languages, case blocks require some sort ol keyword (usually bJeak or
sometling similar) wlicl tells tle application to keep clecking tle otler conditionals
alter tlat block is linisled. Ior example, look at tlis C-style syntax snippet:
1h1 my_humbeJ = 42,
sw11ch{my_humbeJ) {
case 41.
pJ1h11{"A 111e h1gheJ..."),
bJeak,
case 42.
pJ1h11{"You have 1ouhd 1he ahsweJ!\h"),
de1au1.
pJ1h11{"Ahd have 1aeh 1h1o a hoe 1h 1he C comp1eJ!\h"),
bJeak,
}
" You have 1ouhd 1he ahsweJ!
" Ahd have 1aeh 1h1o a hoe 1h 1he C comp1eJ!
Il it's not evident lrom tle code, tle bJeak keyword will exit tle sw11ch block il it is
encountered. Tlis keyword is not needed in Ruby since it doesn't lave lall tlrougl,
but since C-style languages do, tle de1au1 block (like Ruby's ese in case
statements) gets executed because tlere was no break in tle block tlat evaluated to
true (i.e. case 42). As lelplul as tle lall tlrougl meclanism is, it can really be quite
annoying wlen tle lack ol one bJeak statement causes your entire application to come
to a screecling lalt.
@??L9
Loops save your lingers and brain a lot ol work by executing tle same or sligltly
dillerent code numerous times, responding to conditions as need be. Loops are
essentially a sequence ol statements wlicl you enter once but wlicl may be carried
out several times in succession a linite number ol times, maybe once lor eacl member
ol a collection or until some condition is met. We'll cover botl ol tlese types in tle
coming sections.
4+%,$)$+%30'8++9-(
Conditional loops execute based on a provided conditional statement (i.e. tle same
conditionals used in case, 11, and uhess blocks), tlese loops come in two lorms:
wh1e and uh11. A wh1e loop will execute only wlile a conditional is true:
='1$)2(#,:(>).;(?&.,$3.)@8!!!53
x = 0
wh1e{x < 10).
pJ1h1 x.1o_s # pJ1h1 1s 1ke pu1s w11hou1 a \h a1 1he ehd
x += 1
ehd
" 0123456789
Eacl iteration, tle conditional is clecked, il it evaluates to 1Jue, tle brancl executes,
otlerwise, tle block exits.
In otler words, it's as il you put an 11 statement at tle top ol tle loop and tle main
brancl is executed until tle 11 statement becomes lalse. Tle uh11 loop las tle same
relationslip as tlat ol tle uhess statement to tle 11 statement:
x = 0
uh11{x >= 10)
pJ1h1 x.1o_s
x += 1
ehd
" 0123456789
Eacl iteration ol tle loop, tle conditional is clecked |ust like a wh1e loop, except in
an uh11 loop, tle brancl is executed il tle conditional evaluates to 1ase.
54!!!='1$)2(#,:(>).;(?&.,$3.)@8
Figurc 8: Thc whilc loop: sctting thc standard for looping sincc 1973.
Mucl like tle uhess statement, il tle conditional provided is true tlen tle main
brancl is skipped, and, in tle case ol tle uh11 loop, tle block exits.
Tlese loops are great il you are doing some sequential operation (i.e. doing
ten repetitions ol tle same task, outputting ordered data, or, like tle examples,
counting), but tley lave tleir pitlalls. Il tle conditional doesn't meet tle
requirements lor tle brancl to be executed (i.e. true lor wh1e and lalse lor uh11)
belore tle lirst execution, tle loop never runs, be aware ol tlis possibility il you are
manipulating variables in tle loop tlat exist outside ol tle loop wlose manipulated
values are essential to your application's execution alter tle loop. Tlis condition can
and will cause problems, so make sure tlat you do a veritable assortment ol
verilications on your variables (more on tlis later).
Anotler pitlall tlat may rear its ugly yet somelow mildly bearable lead is
tlat tle conditional may never reacl a state wlere tle loop will break (i.e. tle
condition in your wlile loop may never reacl lalse) and tlus put your program into an
inlinite loop and lead it and all ol Creation into oblivion. It's been said tlat tle
universe actually implodes and compresses to tle size ol a single Pocky il tlis
lappens, but I can't verily tlat. So, low do you prevent tlat lrom lappening? Ior
starters, make sure tlat you use as llexible conditionals as possible (e.g. tle >
operator is probably a better idea tlan il you're counting up simply because you
never il by some lreak accident it miglt go over your intended value, tlis isn't tle best
idea in all scenarios, but it slould be considered) and make sure you interact witl tle
conditional value (make sure you actually do sometling to allect tle conditional,
you'd be surprised low many people lorget tlat).
='1$)2(#,:(>).;(?&.,$3.)@8!!!55
Figurc 9: I think thc until loop gcts jcalous of all thc attcntion that pcoplc givc whilc.
Il you're a lardcore '(") *++, ! programmer, you may be saying to yoursell,
"Yo, lool! I ain't gots to be worrying about tlat because I be using tlose lo' loops lo'
rizzle!" Tlat may be true, and you'd be keeping it real in about any otler language
except Ruby. Tlat's riglt: Ruby doesn't oller a "lor loop" in tle traditional sense, but
it does oller one tlat uses tle keyword 1oJ...
:)"63)$%&'8++9-'3%,'/0+12-(
In my lile as a developer, I've seen some ratler preposterous pieces ol code, everytling
lrom text liles used as ligl trallic databases all tle way to people reinventing tle
wleel at least l7 times during tle course ol tleir application development (news llasl
to all developers, programmers, and lackers out tlere: most languages lave
date/time manipulation routines, wlicl means you don't need to write your own lor
your application and/or module. Tlank you.). Tlese people are lonestly |ust making
it lar more dillicult lor tlemselves and everyone else, tlat's sort ol tle way I leel wlen
I see people using conditional loops to grok a collection in languages tlat plainly oller
sometling better. Ior example,
my_aJJay = |13, 1, 4, 5, 29]
1 = 0
wh1e {1 < my_aJJay.ehg1h)
pJ1h1 my_aJJay|1].1o_s + " "
1 += 1
ehd
" 13 1 4 5 29
would work, but is not tle best answer (especially in Ruby). Ruby ollers a couple ol
ways to iterate a collection salely, easily, and elliciently: iterating loops. Tle lirst ol
tlese is tle 1oJ loop. Tle 1oJ loop in Ruby, as noted belore, isn't like tle 1oJ loop in,
say, C or its derivatives, but I leel it's even inlinitely more uselul.
Tle 1oJ loop in Ruby belaves very similarly (almost identically) to tle 1oJ
loop in Pytlon: a collection is provided, and tle 1oJ loop provides a local variable to
lold eacl item as it iterates.
56!!!='1$)2(#,:(>).;(?&.,$3.)@8
Let's look at a code example, we'll create an array ol even numbers, and tlen print
tlem out using an iterator.
my_evehs = |2,4,6,8,10,12,14]
1oJ my_1h1 1h my_evehs
pJ1h1 my_1h1.1o_s + ", "
ehd
" 2, 4, 5, 6, 8, 10, 12, 14,
Tlis is like saying, "I want to create a local variable my_1h1 lor eacl ob|ect in my_evehs
and do sometling witl it." Every time tle loop iterates, tle next value in my_evehs is
copied into tle variable my_1h1, tlis variable allows you to manipulate tle item in tle
collection easily (and, since tle original ob|ect is copied into tlat variable, you can
manipulate it witlout risk ol bit twiddling in tle original collection).
='1$)2(#,:(>).;(?&.,$3.)@8!!!57
Figurc 10: Itcrators makc things much casicr and safcr than conditional
loops whcn using collcctions.
Tle 1oJ loop is really |ust salacious syntactic sugar lor an iterator, an iterator
is simply a block ol code tlat iterates over a collection provided by tle eacl metlod ol
a class. Ior example, let's look at tle above example in tle iterator lorm:
my_evehs = |2,4,6,8,10,12,14]
my_evehs.each do ]my_1h1]
pJ1h1 my_1h1.1o_s + ", "
ehd
" 2, 4, 5, 6, 8, 10, 12, 14
An iterator is lormed by creating a beg1h/ehd block (discussed above) but using
do/ehd as tle initiator, you are essentially creating a special type ol beg1h/ehd block
tlat will be iterated lor eacl item in tle collection. Tlis type ol loop naturally guards
against tle variable discord tlat you may encounter by incrementing an index using
sometling like a wh1e loop (i.e. you go over tle value you want il using tle ==
operator, your value never gets above tle needed value il using a < operator, etc.)
wlile also providing a more natural way to access tle value you wanted.
Unlortunately tlis convenience comes at tle price ol llexibility. In a wh1e or uh11
loop you can alter low quickly your conditional reacles tle state tlat allows tle loop
to break (also called step) by adding more tlan l to tle value you're keeping track or
sometling like tlat, tlis ability is severely limited in an iterator loop (i.e. it doesn't
exist unless you want to use tle hex1 metlod somelow to make it lappen). Keep tlis
in mind wlen you decide wlicl type ol loop to use, it can greatly allect tle
perlormance and stability ol your application.
9$#$2H2,$(0.B+V+231
Loops oller a construct very similar to tle 11 statement's modilier. Loops are great
ways to save time and code, but tley aren't very "natural", I mean wlen repeating a
task (sucl as banging your lead into a mirror out ol sleer lrustration) you typically
don't tlink in tle lorm ol a loop construct.
wh1e{se1.cohsc1ous == 1Jue)
se1.head_bahg{"m1JJoJ")
ehd
Tlat's simply not low our mind usually works, and lortunately, Ruby, in all ol its
readable glory, las decided to bless us witl tle loop statement modilier construct.
Ior example:
se1.head_bahg wh1e se1.cohsc1ous == 1Jue
Tlis construct is not only more natural to read and say, but it is also more concise, you
still lave all tle same lunctionality but in mucl less code and lassle. Tlis construct
works witl botl wh1e and uh11 loops (but not any ol tle iterating loops), it also
works witl beg1h/ehd blocks wlicl allows you to do post-test loops (i.e. loops tlat
always execute at least once belore clecking tle conditional as opposed to tle pre-test
loops tlat we've been using). Ior example, il you wanted to output tle Englisl
translation ol "Tora! Tora! Tora!" you could write:
couh1eJ = 0
58!!!='1$)2(#,:(>).;(?&.,$3.)@8
beg1h
pJ1h1 1Jahsa1e{"ToJa! ")
couh1eJ += 1
ehd uh11 couh1eJ == 3
" BJ1e1case! BJ1e1case! BJ1e1case!
In tlis setup, tle block will always execute at least once regardless ol tle trutl ol tle
attacled conditional (unlike il you simply use tle wh1e or uh11 loop). Do keep in
mind wlen using tlis construct tlat you are still, at its core, using tle wh1e and
uh11 loop, wlicl means tlat it is still susceptible to tle same pitlalls and problems
witl variables. Make sure to test your application tlorouglly lor any potential
problems related to tlis.
K.,$3.))+,N(@..T1
Does it seem tlat loops lave your application in a strangle lold? Tley do seem to be
lulking, domineering, unstoppable belemotls tlat stop only wlen tley darn well leel
like it. Well, actually tley probably |ust seem like really uselul constructs tlat are lard
to control witlout artilicial bit twiddling in tle conditional. Ior example:
my_x = 115
my_y = 40
1emp = 0
wh1e{my_x < 150)
11 {my_x my_y) == 0. # 11 1he quo11eh1 1s eveh
1emp = my_x
my_x = 151
ese
my_x += 1
ehd
pu1s my_x
ehd
my_x = 1emp
pu1s my_x
" 120
Tlat's a bit dangerous il you ask me (and il you're reading tlis, you |ust miglt),
artilicially altering tle conditional value can lead to some craziness in your application
(i.e. accidentally skipped code, tle variable being used outside ol tle loop witlout tle
temp value being stored back into it, etc.). In Ruby, tlere are various keywords tlat
allow you control tle llow ol loops. Tle hex1 keyword allows you to skip tle current
iteration, wlile tle bJeak keyword allows you to exit tle current loop completely.
Let's look at our previous example again:
my_x = 115
my_y = 40
wh1e{my_x < 150)
my_x += 1
pu1s my_x
11 {my_x my_y) == 0. # 11 1he quo11eh1 1s eveh
bJeak
ese
hex1
='1$)2(#,:(>).;(?&.,$3.)@8!!!5
ehd
ehd
pu1s my_x
" 120
Tle usage ol tle hex1 keyword in tlis example is ratler inordinate (i.e. I slould lave
simply let tle loop iterate again ratler tlan lorcing it), but I wanted to demonstrate
low tle hex1 keyword works. Tlis loop works |ust as belore, except more concise and
less bloated.
Tle bJeak keyword breaks tle loop and continues on to tle code alter tle loop |ust as
il tle conditional lad been met. Tle hex1 keyword skips past all remaining code
(wlicl in tlis example wasn't mucl) to tle end ol tle loop and continues on to tle
next iteration. But, wait! It gets lancier:
my_x = 115
my_y = 40
wh1e{my_x < 150)
my_x += 1
pu1s my_x
bJeak 11 {my_x my_y) == 0
hex1
ehd
pu1s my_x
" 120
You can use a conditional modilier witl tle bJeak or hex1 keywords! And, yes, tle
hex1 keyword isn't necessary, but I wanted to demonstrate two tlings. Iirstly, tle
keyword's usage (I know you probably understand it by now, but anotler example
never lurt anybody!). Secondly, all code alter bJeak is skipped. Tle loop does not
iterate again because tle conditional attacled to bJeak was satislied.
FDKFL78?>9
OK, so you're zipping along on an application, but all ol a sudden you get tlis crazy
error and you don't know low to landle it. Maybe your users are l3 year old social
re|ects witl more pimples tlan lriends, and tley leel tle need to type in "YO d00D
I"M l337!!ll" in a lield in your application tlat is supposed to be all numeric,
ellectively crasling your application. Slall you |ust let your application crasl and
burn? Slall its cinders smolder lorever? Slall you never see glory because ol
60!!!='1$)2(#,:(>).;(?&.,$3.)@8
Figurc 11: Brcak. I always gct a grcat dcgrcc of satisfaction from brcaking a loop. Takc that suckcr!
"TO0l337ANdY" lrom Bumpkinville, IA? Never lear, young neoplyte! Ruby las
you covered!
Exceptions provide a landy way to deal witl errors and otler problems in
your application in a way tlat is unilorm and easy to implement. All exceptions derive
lrom tle base class Excep11oh, Ruby provides a number ol built-in exceptions lor you
to use (or lor your application to landle). Here is a table ol tle current available built-
in exceptions:
5#,B)+,N(FE&2T$+.,1
To landle an exception wlen one is raised, you must create a Jescue block. Let's take
a look at an example and tlen pick it apart like voracious vultures in searcl ol
delectable morsels ol dclight (or like some guys wlo |ust really want to know low to
program in Ruby):
de1 me1hod_o1_doom
my_s1J1hg = "T sehse 1mpehd1hg doom."
my_s1J1hg.ah_ha_1_caed_a_hohex1s1eh1_me1hod
ehd
me1hod_o1_doom
" ! hohe1hodEJJoJ
Now, to landle tlis exception properly:
de1 me1hod_o1_doom
my_s1J1hg = "T sehse 1mpehd1hg doom."
my_s1J1hg.ah_ha_1_caed_a_hohex1s1eh1_me1hod
Jescue Excep11oh.
pu1s "bhh...1heJe`s a pJobem w11h 1ha1 1heJe me1hod."
ehd
me1hod_o1_doom
" bhh...1heJe`s a pJobem w11h 1ha1 1heJe me1hod.
To create a Jescue block, you simply place tle Jescue keyword at tle beginning ol a
line lollowed by tle exception class you would like to landle (Ruby ollers a number ol
built-in exceptions lor you to use, cleck tle Ruby documentation lor a lull list). I used
Excep11oh because it catcles any and all exceptions, but you can specily numerous
Jescue blocks to landle dillerent, more specilic types ol exceptions and/or numerous
exception types can be landled by a single Jescue block:
de1 me1hod_o1_doom
my_s1J1hg = "T sehse 1mpehd1hg doom."
my_s1J1hg.ah_ha_1_caed_a_hohex1s1eh1_me1hod
Jescue hohe1hodEJJoJ.
pu1s "You`Je m1ss1hg 1ha1 me1hod, 1oo!"
Jescue Excep11oh.
pu1s "bhh...1heJe`s a pJobem w11h 1ha1 1heJe me1hod."
ehd
me1hod_o1_doom
" You`Je m1ss1hg 1ha1 me1hod, 1oo!
='1$)2(#,:(>).;(?&.,$3.)@8!!!61
Il you specily multiple Jescue blocks, Ruby lollows tle lirst one it encounters tlat is
able to landle an exception ol same type as tle raised exception. Il we lad specilied
tle Excep11oh block lirst as opposed to tle hohe1hodEJJoJ block, we would lave
gotten tle same output as tle lirst example. Il no usable Jescue block is lound in tle
current context (i.e. current block ol code, current metlod, etc.), Ruby works its way
up tle call stack to see il it can lind a suitable rescue block (i.e. it works its way lrom
tle ollending metlod up to tle metlod called it to tle metlod tlat called it and so on).
Il no suitable Jescue block is lound belore Ruby takes its searcl to tle main
application metlod, tlen tle application tlread exits and a message is slown tlat is
sometling along tle lines ol, "I pity tle lool tlat be missin' an exception landler!"
lollowed by a series ol loud crasles and breaking ol wind.
A Jescue block can specily a variable name wlen it is created in order to lold
a more detailed explanation ol tle error. Again using our previous example:
de1 me1hod_o1_doom
my_s1J1hg = "T sehse 1mpehd1hg doom."
my_s1J1hg.ah_ha_1_caed_a_hohex1s1eh1_me1hod
Jescue hohe1hodEJJoJ => e.
pu1s "PR0BLEh. " + e.1o_s
Jescue Excep11oh.
pu1s "bhh...1heJe`s a pJobem w11h 1ha1 1heJe me1hod."
ehd
me1hod_o1_doom
" PR0BLEh. uhde11hed me1hod |ahd so oh...]
Leveraging tlis ability makes it lar easier to know exactly wlat's going on, wlere, and
sometimes low to lix it.
A Jescue block also provides a lew otler leatures tlat aid in making sure tlat
even il your application does cloke, it will still lopelully run as smootlly as possible.
Tle lirst ol tlese leatures is tle ese caluse tlat you can tag on to a Jescue block.
Tlis block will execute il tlere are no exceptions raised. Ior example:
de1 ho_pJobemo
x = 0
x += 19
Jescue Excep11oh
pu1s "0h hoes!"
ese
pu1s "A ceaJ!"
ehd
ho_pJobemo
" A ceaJ!
Tlis is a uselul leature, but be carelul wlat you put in tlere. Tle Jescue block in tle
enclosing code won't catcl any exceptions raised in tle ese clause, so you may need
to catcl tlem later up tle call stack or relegate tle ese block to menial tasks to avoid
tle risk ol causing worse problems.
62!!!='1$)2(#,:(>).;(?&.,$3.)@8
Tle second leature ollered by Jescue blocks is tle ehsuJe clause, tlis clause
lolds code tlat is always executed (i.e. regardless ol tle presence exceptions or not).
Ior example:
de1 dahce_a_1g
"T`m a dahc1h`!"
"0o s1 do!"
Jebe_ye = "yee haw!".upcase!
Jescue Excep11oh
pJ1h1 "T 1e dowh, dahg 11!"
ese
pJ1h1 Jebe_ye
ehsuJe
pJ1h1 " Tha1`s a 1oks!"
ehd
dahce_a_1g
" YEE hAW! Tha1`s a 1oks!
Tle ehsuJe clause is always executed no matter wlat, tlis construct is very uselul lor
closing liles tlat you lave been reading lrom or writing to, closing open network
connections, or making sure all your resource landles lave been cleaned up. Il you
put tlese sorts ol tlings in ehsuJe clauses, tley will always get done and cut down on
problems you may lave witl resource access il your application crasles.
Rcscuc Statcmcnt Modificr Mucl like conditionals and loops, Jescue blocks can be
used as statement modiliers. Ior example:
ho1_ah_obec1.do_some1h1hg Jescue pu1s "CJash!"
" CJash!
Note you can't specily wlat sort ol exception to rescue lrom, but tlat is better lelt to
"lormal" Jescue blocks anylow. You can assign values using tlis construct also:
my_vaue = ho1_ah_obec1.g1ve_a_vaue Jescue "BuJh!"
pu1s my_vaue
" BuJh!
Tlis pitilul example doesn't slow a real world case ol course, but tlis construct is
uselul il a small ad|ustment in value can correct most any exception (a rare but
possible case).
Rctry Sometimes you |ust need a do-over. Tlat loop didn't do well lor you, or maybe
tlat variable still wasn't clear on lis motivation lor tlis scene. Eitler way, you need to
be able to redo part ol your code in lopes tlat tley will simply go better next time.
Tlis is wlere tle Je1Jy keyword comes in, let's say you were building a lictional web
browser:
de1 make_Jeques1
11 {_h11p11)
se1.sehd{`hTTP/1.1`)
ese
se1.sehd{`hTTP/1.0`)
ehd
Jescue PJo1ocoEJJoJ
_h11p11 = 1ase
='1$)2(#,:(>).;(?&.,$3.)@8!!!63
Je1Jy
ehd
You send tle HTTP l.l leaders, but tle server on tle otler end doesn't like tlat, so it
vomits a PJo1ocoEJJoJ. Instead ol rolling over and dying, you disable HTTP l.l,
retry tle block, and your application is smart enougl to switcl to HTTP l.0 instead.
Iancy. Using Jescue and Je1Jy, you can make an attempt to lix any errors tlat may
cause exceptions and retry tle block again. Keep a close watcl on tlis tlougl, it can
cause some serious problems il tle problem is never lixed tle same block ol code is
looped over again and again because your application is retrying it.
:#+1+,N(FE&2T$+.,1
So now tlat you know low to landle exceptions, I tlink it's a line time to put you out
to pasture witl your own exceptions. Raising your own exceptions is important il
tlere are problems tlat will arise tlat won't necessarily cause a problem witl Ruby
itsell. Ior example, let's take a look at a metlod tlat may exist in class PeJsoh:
de1 de11he_gehdeJ{gehdeJ)
11 {gehdeJ.upcase != `FEhALE`) && {gehdeJ.upcase != `hALE`)
Ja1se "You spec111ed some1h1hg wohky 1oJ gehdeJ!"
ehd
ehd
my_guy = PeJsoh.hew
my_guy.de11he_gehdeJ{"hobody khows")
" ! Ruh11meEJJoJ {"You spec111ed some1h1hg wohky 1oJ gehdeJ!")
Even tlougl it won't cause a problem witl Ruby itsell, you obviously don't want
someone to specily sometling abnormal lor gender (well, maybe you do, but tlat's
|ust weird). Il sucl a condition arises, you can drop tle Ja1se keyword on a line
lollowed by a message and Ruby will raise a new Ruh11meEJJoJ and set its message to
tle one you provide. Tle Ja1se keyword (or its uncoutl cousin lrom Wales, tle 1a1
keyword) can be called a number ol ways:
Ja1se "T cJashed! Th1s message shoud be moJe 1h1oJma11ve!"
Ja1se
Ja1se hohe1hodEJJoJ, "Tha1 me1hod a1h\`1 heJe!", caeJ
Tle lirst lorm you are already lamiliar witl (i.e. provide a message to a new
Ruh11meEJJoJ), tle second lorm will re-raise tle current exception so it can be passed
lurtler up tle call stack or raise a new Ruh11meEJJoJ (witl no message) il tlere is no
exception. Tle last lorm is one you slould use most constantly because it allows you
to specily an exception type, a message, and a stack trace ob|ect (wlicl is usually |ust
caeJ, a relerence to tle KeJhe.caeJ metlod). Cood soltware practice says tlat
you slould be as specilic as possible witl your exceptions, instead ol |ust tlrowing
Ruh11meEJJoJs all tle time, try to tlrow a TypeEJJoJ il tle provided ob|ect isn't tle
riglt type or your own exception type to matcl your needs (e.g. it would lave been
better to lave raised an CehdeJEJJoJ or some sucl in our example ratler tlan a
Ruh11meEJJoJ).
64!!!='1$)2(#,:(>).;(?&.,$3.)@8
0I(?C,(FE&2T$+.,(
So low do you create your own exception types? It's really quite simple in Ruby, let's
use our example lrom belore:
cass CehdeJEJJoJ < Ruh11meEJJoJ
a11J .wha1_1hey_pu1
de1 1h111a1ze{1he1J_1hpu1)
_wha1_1hey_pu1 = 1he1J_1hpu1
ehd
ehd
To create a new exception, you simply derive lrom any ol tle exception classes (e.g. I
derived lrom Ruh11meEJJoJ, but you can derive lrom TypeEJJoJ, hohe1hodEJJoJ, or
even Excep11oh). So, let's update our code above to use our new, more practical
exception:
cass PeJsoh
de1 de11he_gehdeJ{gehdeJ)
11 {gehdeJ.upcase != `FEhALE`) && {gehdeJ.upcase != `hALE`)
Ja1se CehdeJEJJoJ.hew{gehdeJ), "Thva1d 1hpu1!"
ehd
ehd
de1 1h111a1ze{gehdeJ)
se1.de11he_gehdeJ{gehdeJ)
Jescue CehdeJEJJoJ => bad
pu1s "You gave me some bad 1hpu1. " + bad.wha1_1hey_pu1
Ja1se
ehd
ehd
my_guy = PeJsoh.hew{"Who khows?")
" You gave me some bad 1hpu1. Who khows?
" ! CehdeJEJJoJ {"Thva1d 1hpu1!")
Notice tlat we raise tle exception in de11he_gehdeJ, we landle it in 1h111a1ze, and
tlen pass it up tle stack. In initialize, we output an attribute leld in our new
exception class, since exceptions are ob|ects and tlus lave classes belind tlem, wlen
you create your own you can add metlods and attributes to it as I did to lold tle value
ol tle user's input. Tlis is uselul il you would like to provide more data to exception
landlers or il you would like to provide lelp in recovering lrom exceptions by
providing metlods in tle exception.
7"3.C(#,B(K#$&"
Il you're a C# or Java programmer, you miglt lave |ust gotten excited by tle prospect
ol some lamiliarity in tlis area, but don't count your proverbial eggs belore tley latcl.
In Ruby, a ca1ch block is given an identilier as an argument, you can tlen "tlrow" tlis
identilier in tle ensuing code. Ruby will tlen look up tle stack to see wlere tle
matcling catcl is, and il it's lound, Ruby will break normal processing and exit tle
ca1ch block. Tlis sounds more conlusing tlan it is, so let's look at an example:
pJ1hcess = 0amseTh01s1Jess.hew
='1$)2(#,:(>).;(?&.,$3.)@8!!!65
ca1ch .hes_a_1a1uJe do
# YAY! Someohe`s heJe 1o save heJ...
pJ1h1 "hy pJ1hce 1s heJe! "
# 0h h0! The v1a1h has ea1eh h1s 1veJ! he d1es!
pJ1hcess.1s_saved = 1ase
11 {pJ1hcess.1s_saved == 1Jue)
pu1s "hooJay!"
ese
pu1s "Poo! ho1 aga1h!"
1hJow .hes_a_1a1uJe
ehd
pu1s "T`m go1hg 1o seep uh11 1he hex1 guy..."
# hap...
ehd
" hy pJ1hce 1s heJe! Poo! ho1 aga1h!
A ca1ch block is started by placing tle ca1ch keyword, an identilier, tlen tle do
keyword on a line, tle identilier is used witl tle 1hJow keyword and can eitler be a
symbol or a string. Tle code is run until a matcling 1hJow statement (i.e. tle tlrow's
identilier matcles tle catcl's identilier) is encountered (il one is encountered) in
wlicl case tle ca1ch block exits witlout executing any code alter tle 1hJow
statement. In tle example, you can see tlis lappen in tlat "My prince is lere!" and
"Poo! Not again!" are output but "I'm going to sleep until tle next guy..." is not, tle
block exited alter tle matcling tlrow statement (i.e. 1hJow .hes_a_1a1uJe) was
lound. Tlis construct is extremely uselul il you need to simply exit tle code block il an
error occurs or il your code is buried in deeply nested loops and you want to break out
ol tlem quickly and easily.
!"#$%&"'()*+%%
You learned about Ruby's llow control meclanisms. You learned...
! about 11/uhess statements and conditional operators.
! about loops, botl conditional and iterating, and low to use tle most
ellectively.
! about exceptions and tleir usage.
66!!!='1$)2(#,:(>).;(?&.,$3.)@8
%
7"2(9I1$2H(<2,2#$"JJJ
Recent advances in language libraries lave lad people ditcling Perl and Basl scripts
in lavor ol a more lriendly solution like Pytlon or Ruby. Tlis is mostly because you
can accomplisl tle same tasks witl less ellort and more robustness witl tlese
languages (it's also probably because Perl and Basl suck). Tlanks to its libraries,
Ruby can interact witl tle system |ust as well as tlese more esoteric solutions. Let's
take a look at some ol tle system libraries and lunctions built into Ruby. You'll be
trading in your copy ol Perl Cookbook witlin tle lour!
M8@F9=97F0(8>7F:6K78?>(
Tle F1e class in Ruby is very ricl compared to otler similarly leatured languages
(i.e. Pytlon). It not only las more metlods, but tle metlods wlicl are comparable
are more logically named (e.g. wlat does uh1hk do in Pytlon? Ol, it deletes? Wly
not call it tlat!?). Ruby's F1e class's general power and ease ol use compared to
many otler languages slould bring comlort to your leart, mucl like a warm bowl ol
soup and classical music can do on a snowy day.
Iirst, let's look at wlat you can lind out about a lile. Does it exist? Wlat
kind ol lile is it? Is it a lile? Here are a lew examples (tlese assume tlere is a lile
named "1ex111e.1x1" in tle current directory):
F1e.d1Jec1oJy?{"1ex111e.1x1") "!!1ase
F1e.11e?{"1ex111e.1x1") "!!1Jue
F1e.ex1s1s?{"1ex111e.1x1") "!!1Jue
F1e.s1ze?{"1ex111e.1x1") "!!2063
F1e.ex1hame{"1ex111e.1x1") "!!".1x1"
F1e.ex1hame{"1go1hoex1ehs1oh") "!!""
I won't insult your intelligence by explaining wlat eacl ol tlese mean, but I would like
to note two tlings. Iirst, tle s1ze? metlod returns tle size in bytes, not kilobytes. It
seems silly, I know, but tlat lrustrated tle piss out ol me wlen I lirst started using it
A"2(B71$25(92,2#$"888!!!67
(mostly because I'm dumb, I know, but I'm trying to save you some lrustration lere!).
Second, tle s1ze? metlod will return h1 il tle lile size is zero (anotler gotcla tlat
botlered me until I ligured it out).
You can also use tle F1e class to lind inlormation about metadata sucl as
ownerslip and permissions:
F1e.execu1abe?{"1ex111e.1x1") "!!1ase
F1e.Jeadabe?{"1ex111e.1x1") "!!1Jue
F1e.wJ11abe?{"1ex111e.1x1") "!!1Jue
F1e.owhed?{"1ex111e.1x1") "!!1Jue
F1e.gJpowhed?{"1ex111e.1x1") "!!1ase
F1e.se1g1d?{"1ex111e.1x1") "!!1ase
F1e.se1u1d?{"1ex111e.1x1") "!!1ase
Tle execu1abe? (wlicl determines il tle user las tle ability to execute a lile
according to lilesystem permissions, not wletler or not tle lile is an executable),
Jeadabe?, and wJ11abe? metlods lave companion metlods called
execu1abe_Jea?, Jeadabe_Jea?, and wJ11abe_Jea? (respectively, obviously)
wlicl make sure tlat tle owner ol tle process las tlat ability witl tlat lile. Ol
course, il you own tle lile it probably doesn't matter. You can lind out il you own it
using tle owhed? metlod, wlicl will return 1Jue il tle process owner indeed owns
tle specilied lile. Normally tle gJpowhed?, se1g1d?, and se1u1d? are very lelplul in
linding out certain metadata about a lile, but tlese metlods don't apply to and will
always return 1ase on operating systems tlat don't support tlem (I'm looking riglt
at you Windows!). Ior tlose not in tle know, on UNIX lilesystems a lile is owned by a
user in a group ratler tlan "|ust" a user, tle gJpowhed? gains you access to tlis data.
Tle se1g1d? and se1u1d? cleck lor a bit tlat is set on a lile's lilsystem entry tlat
allows you to clange tle user and/or tle group wlen accessing tlat lile (tlis lelps
wlen a user needs elevated privileges lor a certain task). Again, tlese metlods allow
you to see il tlese bits are set, but il you're on Windows or sometling else tlat doesn't
support tlem tlen tley always return 1ase.
:2#B+,N(V3.H(#(V+)2((
I can lear you saying, "Wlo cares about tlat crap?! I need to rcad a lile. I made tle
lile. I know all tlat crap about it! Tell me low to read it or I clallenge you to a knile
liglt, riglt now, belind tle Wallle House! You and me, pal! Wc'rc taking it to thc
matrcsscs!" I would like to now kindly respond to your anger witl tlis little tidbit:
my11e = F1e.opeh{"1ex111e.1x1", "J")
my11e.each_1he {]1he] pu1s 1he }
my11e.cose
Using tle F1e#opeh metlod, you can open a lile and create a new F1e instance. Tle
lirst parameter lor open is tle lile patl (eitler relative or absolute), and tle second
parameter is tle lile mode. You can view tle table ol options you lave lor tlis
parameter in tle table at tle end ol tlis section, tlis parameter delaults to reading il
you don't specily. Alter you call open, you can use tle each_1he metlod to grab eacl
68!!!A"2(B71$25(92,2#$"888
line and print it out, play around witl it, wlatever you want to do inside tle block.
You can optionally leed each_1he a parameter tlat will act as tle line ending in place
ol "\h", il you, like me, tend to end eacl line ol text witl tle word "pastry" you can
respect tlis leature. Always be sure to call tle cose metlod il you are opening liles
tlis way.
"But, Dad!" you wline. "I don't wanna call close!" Well,
Son/Dauglter/Androgynous Ollspring, Ruby can lelp you cure your incessant
moaning:
F1e.opeh{"1ex111e.1x1") do ]my11e]
my11e.each_1he {]1he] pu1s 1he }
ehd
Tlis does tle same tling, but now tle lile stream is automatically closed wlen tle
enclosing block exits. "Wow!" you exclaim. I'm glad you're amazed, but it gets better:
T0.1oJeach{"1ex111e.1x1") {]1he] pu1s 1he }
Using tle T0#1oJeach metlod does tle same tling as tle previous two examples, |ust
simpler, more compact, and lar more beautilully. It opens tle lile specilied, leeds it
line by line into a block, tlen closes it. Mmm...now tlat's Rubylicious.
!3+$+,N($.(#(V+)2((
Your options lor writing to a lile are numerous, tley all accomplisl essentially tle
same ob|ective but in sligltly dillerent ways. Tle lirst (and most obviously named)
way I'd like to cover is tle wJ11e metlod. It goes sometling like tlis:
F1e.opeh{"1ex111e.1x1", "w") do ]my11e]
my11e.wJ11e{"howdy!")
ehd
You open a lile witl tle F1e#opeh metlod, create an enclosing block, and simply call
tle wJ11e metlod on tle lile instance created by tle block. You can do writing tle
same way I slowed you reading tle lirst time (i.e. witlout a block at all and calling
cose), but I tlouglt tlat would be needlessly redundant to include it lere. You can
write any sort ol data to a lile as long as it can be converted to a string (i.e. it las a 1o_s
metlod), il it can't be converted to a string Ruby will simply issue it a string
representation to tle ellect ol "#<Casshame.Some0a1a>". Otler metlods sucl as
pJ1h1 and pu1s can easily be plugged into wlere wJ11e is, tley take tle same number
ol parameters and belave essentially tle same way (except tlat pu1s will tag a new
line on tle end ol tle string wlen it is written).
Anotler way ol writing to a lile is utilizing tle << operator, il you've ever used
T0S1Jeam in C++ tlen you slould leel riglt at lome witl tlis:
F1e.opeh{"1ex111e.1x1", "w") do ]my11e]
my11e << "howdy!\h" << "TheJe aJe " << couh1 << "pahdas!"
ehd
A"2(B71$25(92,2#$"888!!!6
Opening tle lile is tle same as always, but now instead ol calling a metlod and
leeding in parameters (at least in tle traditional sense) you are now using tle <<
operator. It belaves tle same as tle otler metlods (i.e. it converts tle data to a string
il it is not a string and writes it to tle lile) so tlere slouldn't be any surprising parts
tlere. BOOCABLARCABOO! Okay, maybe tlat surprised you, but notling else
slould.
0.32(V+)2(.T23#$+.,1((
Tle F1e class also supports a number ol otler lile operations tlat promote all sorts ol
lilesystem looliganism. Here are a lew:
F1e.dee1e{"1ex111e.1x1")
F1e.Jehame{"1ex111e.1x1", "1ex111e.1x1.bak")
F1e.chowh{h1, 201, "1ex111e.1x1")
F1e.chmod{0777, "1ex111e.1x1")
Tle lirst two metlod's names slould give away tleir lunction. Il tle proverbial cat is
not out ol tle bag, tley delete and rename tle provided liles (witl tle renamed
lilename led in as tle second parameter). Tle dee1e metlod will return tle number
ol liles deleted (i.e. 1 lor tlis case).
Tle last two metlods may be a little conlusing il you are not up to snull on
your UNIX/Linux lilesystems and tleir associated commands. Tle chowh command
allows a user witl superuser privileges to clange tle owner ol a lile (or tle owner may
clange tle group ownerslip to any group ol wlicl le/sle is a member), note tlat tle
chowh metlod takes numeric owner and group IDs ratler tlan string names (wlicl
tle command line version allows). Tle chmod metlod allows tle owner ol a lile (or a
superuser) to clange tle permissions ol a lile (i.e. wlicl users/groups can read, write
to, or execute a lile), tle lirst parameter is a bit pattern wlicl represents tle
permissions on tle lilesystem. Cleck Appendix A lor URLs witl more inlormation on
UNIX lilesystem metadata (including bit patterns to be used witl tle chmod metlod).
70!!!A"2(B71$25(92,2#$"888
M8@F(6KKF99(0?QF9
J Read-only access, starts at beginning ol lile (delault)
w Write-only, truncates existing lile to zero lengtl or creates new lile
a Write-only, starts at end ol existing lile or creates new lile
i+ Read-write, starts at beginning ol lile
v+ Read-write, truncates existing lile to zero lengtl or creates new lile
a+ Read-write, starts at end ol existing lile or creates new lile
h Binary lile mode, may appear witl any ol tle above options (Windows only)
75:F6Q9(6>Q(M?:R9(6>Q(L:?KF99F9/(?5(0=G
Don't you late it wlen you get slapped witl an lourglass/beaclball wlen you're
doing sometling simple? It's not like your computer is overloaded or anytling, wlat's
tle deal? Tle deal is tlat tle programmer (probably) didn't use a multitlreaded
design, so everytling it does lappens in one tlread. Wlat's a tlread you ask? I'm
glad you asked (il you didn't ask because you already know, skip tlis section).
Tlink ol a tlread as a way ol telling your computer you want it to multitask.
Let's say you're developing a WinAmp/iTunes/Ioobar clone, and you want to be able
to play music, lave wicked awesome visualizations, and grab CDDB inlormation
about your tracks all at tle same time. Tlis isn't going to work very well in a single
tlreaded setup because you will lave to wait lor CDDB to respond belore your track
plays, and tlen you lave to worry about trying to draw and play music at tle same
time. Tle easiest solution would be to split eacl task oll into its own tlread. Tle
music would play in its own tlread, completely untoucled by tle otler tlings going
on, tle visualizations would draw in tleir own tlread, not interlering witl tle music,
CDDB could be contacted independently ol tle otler two, so tlat il you lave a slow
Internet connection, downloading tle data won't botler tle playback. Tlreads let
your computer do more tlings at once, and are pretty important il you plan on doing
anytling remotely complicated witl Ruby.
:'-I($"32#B(-#1+&1((
Using tlreads in Ruby is as simple as passing a block ol code to tle ThJead class's
constructor. Ior example, let's create tlree tlreads. Tley will eacl do sometling at
dillerent intervals (e.g., print some text to tle screen).
11Js1 = ThJead.hew{) do
my1hdex = 0
wh1e{my1hdex < 10).
pu1s "ThJead 0he!"
seep 3
my1hdex += 1
ehd
ehd
A"2(B71$25(92,2#$"888!!!71
secohd = ThJead.hew{) do
my1hdex2 = 0
wh1e{my1hdex2 < 5).
pu1s "ThJead Two!"
seep 5
my1hdex2 += 1
ehd
ehd
1h1Jd = ThJead.hew{) do
my1hdex3 = 0
wh1e{my1hdex3 < 2).
pu1s "ThJead ThJee!"
seep 10
my1hdex3 += 1
ehd
ehd
11Js1.o1h{)
secohd.o1h{)
1h1Jd.o1h{)
"!!ThJead 0he!
"!!ThJead Two!
"!!ThJead 0he!
!"#$%&'%'#((()
To get tlreads going, you lirst need to create an instance ol tle ThJead class and pass
a block ol code to it, our code simply prints some text and tlen makes tlat tlread
pause a lew seconds using tle seep metlod. Tle calls to tle o1h metlod aren't
necessary to make tlis work, tle tlreads will run by tlemselves and be killed wlen
your program ends. Tle benelit ol calling o1h is tlat your program will wait until all
tlreads tlat lave been |oined exit (i.e. tle code las linisled and tle block exits). Il
you take tle |oin calls out ol tle above program, eacl tlread will print once and exit
because tle main tlread exited, as it is above (i.e., witl tle o1h calls), it will run lor
about 30 seconds, witl eacl tlread printing a lew times.
Tle |oin metlod is great, but wlat il you don't want a tlread to run lorever
alter you exit? Even lurtler, wlat il you want to give it time to try to slut down alter
your program ends? Iortunately, tle |oin metlod is pretty smart, you can leed it an
integer as a parameter and it will use tlat as a timeout.
11me_me_ou1 = ThJead.hew{) do
wh1e{1Jue).
pu1s "Keep oop1h` oop1h` oop1h`..."
seep 5
pu1s "Keep 1ha1 scJ1p1 oh oop1h`! RAWhT0E!"
72!!!A"2(B71$25(92,2#$"888
Figurc 12: Thrcads allow you to do work in parallcl to thc main thrcad: multitasking for Ruby.
seep 5
ehd
ehd
11me_me_ou1.o1h{15)
"!!Keep oop1h` oop1h` oop1h`
!*%&+,'#$%-"./)
"!!Keep 1ha1 scJ1p1 oh oop1h`! RAWhT0E!
!*%&+,'#$%-"./)
"!!Keep oop1h` oop1h` oop1h`
!"#$%&'%'#((()
Since we gave |oin a timeout ol l5 seconds, tle script/song will only go lor l5 seconds
(since as soon as tle tlread is |oined, tle main tlread exits, toss in a loop or
sometling tlat runs lor a wlile below it to make it run a little longer).
K.,$3.))+,N($"32#B1((
Tlreads oller a lew metlods lor controlling tlemselves. Tle lirst ol tlese metlods is
pass, wlicl will tell tle tlread scleduler to pass tle execution to anotler tlread. Ior
example, let's say you lave two tlreads and you'd like tlem to print tlings out and
pass tle control to eacl otler as tley do. Let's spell "weal" using two tlreads!
11 = ThJead.hew { pJ1h1 "w", ThJead.pass, pJ1h1 "a" }
12 = ThJead.hew { pJ1h1 "e", ThJead.pass, pJ1h1 "" }
11.o1h
12.o1h
"!!wea
Tle pass metlod basically tells tle current tlread to lang out lor a second wlile tle
anotler tlread does its tling. In tle example, tle tlreads switcl oll because tley
pause tlemselves to allow anotler tlread to execute.
Anotler metlod tlat is used to control tlreads lrom witlin is tle stop
metlod. Tlis metlod simply stops tle tlread's execution, wlicl can be started again
at a later time. Tle stop metlod is really uselul lor situations wlere a tlread needs to
pause until you can accomplisl anotler task. Let's say you were designing some
robotic sailors, and tle lirst mate couldn't drop anclor until tle captain says it's okay
to do so. You'd probably do sometling like tle lollowing.
ma1e = ThJead.hew do
pu1s "Ahoy! Cah T be dJopp1hg 1he ahchoJ s1J?"
ThJead.s1op
pu1s "Aye s1J, dJopp1hg ahchoJ!"
ehd
ThJead.pass
pu1s "CAPTATh. Aye, addy!"
ma1e.Juh
ma1e.o1h
A"2(B71$25(92,2#$"888!!!73
"!!Ahoy! Cah T be dJopp1hg 1he ahchoJ s1J?
"!!CAPTATh. Aye, addy!
"!!Aye s1J, dJopp1hg ahchoJ!
Rumor las it tlat is low tle Love Boat actually started: robotic sailors. Anylow, tle
stop class metlod stops tle current tlread, but as you can see in tle example, tle run
instance metlod will restart it (remember: stop is a class metlod, run is an instance
metlod). Tle tlread can tlen be |oined to continue on its merry little way.
Tlreads can be altogetler exited also. You can do tlis one ol two ways:
eitler lrom witlin using exit or tle outside using kill.
hom1c1de = ThJead.hew do
wh1e {1 == 1).
pu1s "0oh`1 k1 me!"
ThJead.pass
ehd
ehd
su1c1de = ThJead.hew do
pu1s "Th1s 1s a meah1hgess!"
ThJead.ex11
ehd
ThJead.k1{hom1c1de)
"!!0oh`1 k1 me!
"!!Th1s 1s a meah1hgess!
"!!0oh`1 k1 me!
Tley work tle same, tley |ust accomplisl it dillerent ways. It's usually better practice
to kill a tlread oll lrom witlin simply because you know wlen and wlere it will be
killed, killing tlreads oll at will lrom wlerever can lead to some serious conlusion,
and lrankly, needless killing ol innocent tlreads.
A2$$+,N(+,V.3H#$+.,(V3.H($"32#B1((
Tlere are a lew metlods tlat can be used to grab inlormation about tlreads. Tle lirst
ol tlese being ThJead.cuJJeh1 and ThJead.1s1. Tle cuJJeh1 metlod, ol course,
gives you access to tle current tlread. Tle 1s1 metlod lists all tlreads tlat are
runnable or stopped. Il you would like to get some inlormation about tlese tlreads,
tlen you can call tle instance metlods a1ve? and s1a1us. Tle a1ve? metlod will
tell you wletler or not tle tlread is active or not, it will return 1Jue il tle tlread is
running or sleeping and 1ase il it las exited or is stopped. Tle status metlod will
return "Juh" il tle tlread is running as normal, "seep" il tle tlread is sleeping,
"aboJ11hg" il tle tlread is aborting, h1 il terminated witl an exception, and 1ase il
tle tlread terminated normally. Testing wletler or not a tlread is simply runnning
can be done using tle s1op? metlod. Ior example:
my1hJead = ThJead.hew { ThJead.s1op }
my1hJead.s1op? "!!1Jue
ThJead.cuJJeh1.s1op? "!!1ase
74!!!A"2(B71$25(92,2#$"888
Ruby returns 1Jue il tle tlread is stopped or sleeping, otlerwise it will return 1ase.
You can also get tle value returned lrom a tlread using tle vaue metlod.
cacua1oJ = ThJead.hew { 12 / 4 * 3 }
cacua1oJ.vaue
"!!9
Tlis is excellent lor long calculations wlose value isn't needed riglt away, doing tlem
tlis way lets you run tlem on anotler tlread so tley don't interrupt tle main tlread
and tle execution ol your program.
L3.&21121/($"2(.$"23(C#I($.(B.(1$'VV
Sometimes you need to spawn a new process altogetler. Wletler you need to execute
a tlird party program or |ust invoke anotler Ruby instance tlat runs your script,
spawning external processes can be pretty important at times. Ruby ollers a lew ways
to spawn and control new processes.
Thc !"!#$% mcthod PHP programmers re|oice! Ruby las a system metlod tlat
operates like tle PHP sys1em metlod. Perl programmers may also re|oice, as Ruby
also supports backtick notation lor starting external processes. Ior tlose ol you wlo
are unlamiliar witl botl, let's |ust look at an example.
sys1em{"ca1 /e1c/passwd")
ex1eJh = `whoam1`
pu1s {"YouJ useJhame 1s #{ex1eJh}.") "!!eJemy
Tle lunctions ol tlese metlods is lairly obvious. Tle sys1em metlod spawns an
external application in a subprocess, it returns true il it is exited successlully and lalse
otlerwise (witl tle exit code in $?). Tle unlortunate tling about sys1em is tlat it
vomits tle output on to wlerever your application's output is being streamed to,
wlicl means you can can't capture it eitler. Tlat's wlere tle backticks come in, tley
also spawn an application in a subprocess but also allow you to capture its output.
Pipc drcams Tle sys1em metlod works well enougl in a lot ol cases, but wlat il you
need provide some interactivity witl tle application? Say you need to give it some
input or perlaps it gives you delayed output and you'd like to start processing it belore
it's done executing. Ruby ollers tle T0.popeh metlod tlat does |ust tlat.
Tle popeh metlod will spawn an application and tlen give you a stream
wlicl you can read lrom and write to |ust like any otler stream (e.g., lile stream).
Jb = T0.popeh{"Juby", "w+")
Jb.pu1s "pu1s `Whoa! Rad1ca subpJocess, dude!`"
Jb.cose_wJ11e
pu1s Jb.ge1s
As you can see, you open tle pipe witl popeh |ust as you would a lile stream,
specilying a target and access mode (wlicl are tle same access modes lor liles). You
can tlen use pu1s to write to to tle stream and any otler stream metlod (e.g., ge1s,
A"2(B71$25(92,2#$"888!!!75
Jead, etc.). Do be aware tlat tle cose_wJ11e call was required lor me. I'm not sure
il tlis a platlorm issue or not, but it miglt be sale to |ust go alead and tlrow it in tlere
lor good measure.
Indcpcndcnt Exccution Il you're on a macline tlat implements lork (i.e., not
Windows) tlen you can use tle exec metlod to execute tlings in a less lands on
metlod wlile still retaining a little control.
exec{"apachec1 Jes1aJ1") 11 1oJk.h1?
# Jes1aJ11hg apache...
PJocess.wa11 # Wa11 1oJ 1he pJocess 1o ex11 {op11oha)
Tlis las tle same basic ellect as using system, except tlat you can tell your
application to wait until tlat process is done using PJocess.wa11, tlis is a good idea il
you're running a subprocess tlat could cause irreparable damage il exited
prematurely (e.g., moving liles or sometling like tlat).
M?:(75F(F>P8:?>0F>7G
Accessing mucl ol tle environment tlat your Ruby program is in is as simple as
raising your Planeteer ring to tle sky and calling down your personal power. Wait,
tlat's not riglt. It's as simple as using a lew neat lunctions tlat allow Ruby to access
environment variables, program arguments, and a bit about Ruby's own environment.
Tle power is yours!
F,Y+3.,H2,$(Y#3+#-)21(#,B($"2()+*2
It's easy to access environment variables in Ruby, il you've ever used PHP or
sometling similar to access environment variables, it's very mucl tle same concept in
Ruby.
Ehv|`ShELL`] "!!/b1h/sh
Ehv|`h0hE`] "!!/home/mJhe1ghboJy
Ehv|`bSER`] "!!mJhe1ghboJy
Tle above values are lairly common il you're on a Linux-type system, il you're on
Windows tley clange a bit. Ior example, on Linux bSER is tle environment variable
lolds your username, but on Window tlis value is placed in bSERhAhE. Tle otler two
example values evaluate to h1. I suggest tlat il you're going to use environment
variables, tlat you pop open 1Jb and do two tlings. Iirst, make sure tle value you
want to use is valid on tle platlorms you'll be using it on. Secondly, call up tle Ehv
collection and look at all tle values available. Il tle value you planned on using isn't
available, an alternate equivalent miglt be (e.g. bSER and bSERhAhE).
To write to an environment variable, you simply assign a value mucl like you
would a normal variable.
76!!!A"2(B71$25(92,2#$"888
Ehv|`bSERhAhE`] = `doh1do1h1s`
pu1s Ehv|`bSERhAhE`]
"!!doh1do1h1s
Your clanges to environment variables are inlerited by any clild processes tlat you
spawn, but tley do not propagate back up to tle parent ol tle Ruby application. All ol
your clanges stay local to your application. Il need be, you could spawn a process to
use a command like se1 or expoJ1 to clange it in tle slell.
7"2(&.HH#,B()+,2(#,B(I.'
Most Ruby scripts are invoked lrom tle command line, tlis means tlat, il need be,
you can pass arguments to it on tle command line. Ior example, il you write a text
editor, you could pass tle lile name you want to open to it on tle command line (e.g.
myeditor.rb mylile.txt) ratler tlan laving to use Iile -> Open or Ctrl+O or wlatever.
Perlaps you remember wlen we installed Ruby way back wlen, I lad you type Juby
-v, wlere Ruby is a command line argument. Ruby's laculties lor accomplisling tlis
very same tling are lairly simple to employ, you are given a global array, ARCv, to do
witl wlat you wisl.
ARCv.each{]aJg] pu1s "AJg. #{aJg}, "}
Il you were to invoke tle above script witl a lew arguments, you would see tlem
printed out sequentially.
Juby aJgv.Jb "hy aJgs!" 123 19
"!!AJg. hy aJgs!, AJg. 123, AJg. 19,
You can use tlis leature to gatler inlormation (e.g. lilenames, numerical parameters,
etc.) as I lave done lere, or you could use it to allow tle user to specily command line
switcles (like -v on tle Ruby command).
:'-I(#,B(+$1()+$$)2(&.3,23(.V(I.'3(&.HT'$23
In tle greater ecosystem ol your computer, Ruby las its own little microcosm. Wlen
loading libraries and sucl, Ruby doesn't |ust magically know wlere tley are, tleir
patls are part ol Ruby's environment conliguration. Use tle lollowing Ruby
invocation to see wlere Ruby looks.
Juby -e "pu1s $."
On a typical Windows installation, you miglt see a list very similar to one like tlis.
C./Juby/1b/Juby/s11e_Juby/1.8
C./Juby/1b/Juby/s11e_Juby/1.8/1386-msvcJ1
C./Juby/1b/Juby/s11e_Juby
C./Juby/1b/Juby/1.8
C./Juby/1b/Juby/1.8/1386-msw1h32
.
As you can see, Ruby looks in multiple locations typically witlin tle Ruby directory,
also note tlat it is version specilic (i.e. it only looks at libraries installed in tle l.8
A"2(B71$25(92,2#$"888!!!77
directory since we are using l.8 lere). Tle results on a Linux or OSX box slould be
similar, simply replace "C./" witl sometling like "/usJ/oca/1b/" or "/usJ/1b/"
and you slould get a very similar list.
You can also gatler some inlormation about low Ruby was built and in wlat
environment. Tlis inlormation can be uselul is a bug exists lor a specilic build
environment, and you want to see il a problem you are experiencing miglt be a result
ol tlat bug. Tlis inlormation is written to tle Coh11g module in tle lile Jbcoh11g.Jb
in tle library directory, usually in a directory under tlat wlicl is labeled by tle build
environment (i.e. 1386-msw1h32). You can access tlis inlormation programmatically
also (since it is part ol tle library).
1hcude Coh11g
C0hFTC|`hos1_os`] "!!"msw1h32"
C0hFTC|`1aJge1_os`] "!!"msw1h32"
C0hFTC|`1bd1J`] "!!"C./Juby/1b"
C0hFTC|`bu1d_cpu`] "!!"1686"
Since tle Conlig module is exposed, you can simply include it and call values lrom tle
global constant lasl CONIIC to use tlem. Now tle next time someone calls you a
liar and tells you tlat can't possibly be running Ruby on an Apple IIc, you can prove
tlem wrong. Dcad wrong.
!8>Z[(6>Q(<F=?>QJ
Ruby is typically associated witl tle UNIX-based operating systems, leck, until
about a year ago, Matz limsell was prone to kicking you in tle teetl lor even
mentioning Windows and Ruby in tle same sentence. Iortunately, recent activity las
promoted Windows to a position ol (at tle very least) tolerance witlin tle Ruby
community, evidenced in pro|ects sucl as tle One-Click Ruby Installer
(lttp://rubyinstaller.rubylorge.org/) and tle wonderlul no-install Rails pro|ect,
InstantRails (lttp://instantrails.rubylorge.org/). Ruby libraries lor Windows lave
also been enlanced. Tlere are a number ol tlem available (searcl RubyIorge il you'd
like to see a sampling), but I want to locus on tle W1h32APT, W1h32, and WTh320LE
modules in tle standard library.
6L8
Many applications today are directly tied to tle Windows API. Many tasks sucl as INI
lile interaction are mucl easier to accomplisl witl tle Windows API, and
meclanisms like printing are only accessible tlrougl tlese clannels. As sucl, wlen
replacing current lunctionality in anotler language witl Ruby code or wlen trying to
use tlese meclanisms in your Ruby code, your only cloice is to ligure out a way to get
Ruby and tle Windows API talk to eacl otler. Ruby ollers tle Win32API library to
lelp ease tle pain ol tlis integration.
78!!!A"2(B71$25(92,2#$"888
Tle W1h32APT module allows you to make calls to any Windows API module
(e.g. keJhe32, useJ32, and so on). You can make tlese calls by instantiating a
W1h32APT ob|ect and calling tle call tle metlod on it. Ior example, let's say you
needed to read lrom and write to INI liles tlat are used by anotler part ol your
organizations system. Tlese INI liles louse login inlormation otler essential bits tlat
let tle applications access mutual resources, as an example, let's build a little test lile.
|da1abase]
og1h = dbuseJ
passwoJd = 1oobaz
|11eshaJe]
useJhame = shazbo1
oca11oh = //seJveJ/pa1h
Because ol perlormance reasons (i.e. you don't want to write tle regular expressions
to parse tle INI lile) you decide to use tle Windows API lunction
Ce1PJ1va1ePJo11eS1J1hg to parse out values. To use CetPrivateProlileString
witlin Ruby, you need to lirst look at tle lunction delinition and tle lunction
delinitions ol any related lunctions you will need (in tlis case, we will need tle
s1JehA lunction also).
0W0R0 Ce1PJ1va1ePJo11eS1J1hg{
LPCTSTR pApphame,
LPCTSTR pKeyhame,
LPCTSTR p0e1au1,
LPTSTR pRe1uJhedS1J1hg,
0W0R0 hS1ze,
LPCTSTR pF1ehame
)
1h1 s1Jeh{
LPCTSTR pS1J1hg
),
Now tlat we lave tle parameters and return types ol tlese lunctions, we lave all tle
inlormation we need to contruct Win32API ob|ects and make calls to tlese lunctions
(tlis is all explained below!).
Jequ1Je `W1h32APT`
# Ce1PJ1va1ePJo11eS1J1hg 1hs1ahce
ge1vaue = W1h32APT.hew{`keJhe32`, `Ce1PJ1va1ePJo11eS1J1hg`,
w{P P P P L P), `L`),
# s1JehA 1hs1ahce
s1Jeh = W1h32APT.hew{`keJhe32`, `s1JehA`, w{P), `L`),
Je1s1J = ` ` * {255 + 1)
ge1vaue.Ca{`da1abase`, `og1h`, ``, Je1s1J, 255,
`C./1es1.1h1`)
ehg1h = s1Jeh.Ca{Je1s1J)
pu1s Je1s1J|0..ehg1h - 1]
"!!dbuseJ
As you can see, you need to lirst include tle Win32API module. Tlen, create
instances ol tle Win32API class lor eacl lunction you are going to call. In tlis case,
we created two instances: ge1vaue wlicl lolds a relerence to
A"2(B71$25(92,2#$"888!!!7
Ce1PJ1va1ePJo11eS1J1hg and s1Jeh wlicl lolds a relerence to s1JehA (botl ol
wlicl are labeled witl comments). Tle lirst parameter lor tle constructor is tle API
module to look in (e.g. Ce1PJ1va1ePJo11eS1J1hg lives in keJhe32), and tle second
parameter is tle lunction name tlat you wisl to call. Tle tlird parameter is an array
ol parameter types tlat tle lunction takes, lor example, in tle above lunction
declarations, we saw tlat lstrlenA takes a single constant string pointer (LPCSTR). Tle
parameter type can be specilied as one ol tlree types: P lor string pointers, N and L lor
numbers, I lor integers, and V lor void. So in our example,
Ce1PJ1va1ePJo11eS1J1hg takes 5 string pointers and one long number parameter.
Tle lourtl parameter is tle return type (e.g. we specilied L lor long number since
0W0R0, tle type specilied in tle lunction delinition, is simply a typedel lor ohg).
Using INI liles is great lor legacy systems, but even Microsolt knows tlat keeping all
your important data in tle Registry is wlere it's at nowadays!
7"2(:2N+1$3I
Tle W1h32 module provides you witl a very lriendly interlace to operate on tle
Windows registry. Is'nt tlat exciting? Now you can stick tons ol essential
inlormation in tlere (sucl as license keys), lave it overwritten, deleted, and generally
molested by a virus, or even better, lost in a system restore because ol said virus (I'm
allowed to be bitter)! Even so, it's a dandy place to store general conliguration
inlormation lor your application il used properly.
To operate on tle registry, you need to lirst call open on tle
W1h32..Reg1s1Jy class and give it a registry patl to open lollowed by a block. Tlink
ol tlis in tle same way tlat you can open a normal lile and lave it close alter tle
ensuing block is linisled.
Jequ1Je `w1h32/Jeg1s1Jy`
W1h32..Reg1s1Jy..hKEY_L0CAL_hAChThE.opeh{`S0FTWARE\h1cJoso11\W1
hdows hT\CuJJeh1veJs1oh\`) do ]Jeg]
# 0o youJ d1J1y woJk heJe!
ehd
Let's |ust look at opening tle registry up riglt now, and we'll look at wlat you can do
wlen it's open in |ust a little bit. Tle above block will open up tle registry patl
hKEY_L0CAL_hAChThE\S0FTWARE\h1cJoso11\W1hdows hT\CuJJeh1veJs1oh\. Il you
are unsure about wlat tlis means, |ust look it up on Coogle, but basically wlat we've
done is open up tle registry, look inside at tle soltware area lor tle local macline,
open up tle CuJJeh1veJs1oh "lolder" inside tle W1hdows hT "lolder" inside tle
h1cJoso11 "lolder " (I say "lolder" because tley're not real "lolders" in tle sense tlat
tley are navigable on tle lard drive, tley are plantom lolders, mere glosts, lloating
in tle never ending etler tlat is tle registry). Il you get an error wlen running tlis,
tlat probably means you're on Windows 98 or sometling like tlat, il tlat's true, tlen
take oll tle hT on Windows. Il you got an error, and you are on Linux, please put tlis
book down and call your nearest tecl support center. Now tlat we've got tle registry
opened up and ready to do sometling, let's grab some values and use tlem.
80!!!A"2(B71$25(92,2#$"888
Rcading Reading values in lrom tle registry can be done a couple ol ways, generally,
you simply provide tle key name and tle value is returned.
vaue = Jeg|`PJoduc1hame`]
vaue = Jeg|`Pa1hhame`, W1h32..Reg1s1Jy..REC_SZ]
1ype, vaue = Jeg.Jead{`Bu1dLab`)
Tle lirst call above is tle most basic (and probably most practical in most cases),
values lrom tle registry can be accessed mucl like tley are accessed lrom a lasl.
Simply enclose tle registry key name (in tlis case "ProductName") in brackets. Tle
next call will return tle value, but will tlrow an error il tle value returned does not
matcl tle type given as tle second value (e.g., il tle value lor Pa1hhame were a 0W0R0
in tle registry, wlen you requested a REC_SZ it would lave tlrown a TypeEJJoJ). Tle
value ol tlis type parameter can only be a certain number ol constants, wlicl are
directly related to key types in tle registry. Tle table below lists tlese constants lor
your relerence.
:FA897:=(7=LF(K?>976>79
REC_h0hE
No specilic type.
REC_0W0R0_
LTTTLE_Eh0TAh
REC_0W0R0_
BTC_Eh0TAh
A 32-bit number in
little- or big- endian
lormat (Windows is
designed to run on
little-endian
platlorms).
REC_SZ A null-terminated
string.
REC_LThK Reserved lor system
use.
REC_EXPAh0_SZ A null-terminated
string witl some
expandable expression
like an environment
variable.
REC_hbLTT_SZ A sequence ol null-
terminated strings,
terminated by an
empty, null-terminated
string.
REC_BThARY Binary data in any
lorm.
REC_RES0bRCE_LTST
*
Nested arrays tlat
store a resource list
used by a lardware
device driver or one ol
tle plysical devices it
controls.
REC_0W0R0 A 32-bit number. REC_RES0bRCE_
RE0bTREhEhTS_LTST
*
A complex data type
lor lardware
conliguration.
REC_FbLL_
RES0bRCE_
0ESCRTPT0R *
Nested arrays ol binary
data tlat store a
resource list used by a
plysical lardware
device.
REC_0W0R0
REC_0W0R0_
LTTTLE_Eh0TAh
A 64-bit number.
* Keys ol tlis type are not editable, but can be read.
Tle tlird and linal call in tle example uses tle read metlod, tle advantage to calling
tle value tlis way is tlat it gives you tle type ol tle key. Tlis is uselul il you are
A"2(B71$25(92,2#$"888!!!81
iterating over a set ol keys and you need to perlorm specilic operations on keys tlat are
DWORD keys but not REC_SZ keys.
Enumcrating You also lave tle option to enumerate values and subkeys (or sub
"lolders"), and in turn walk over tle entire collection ol values or a series ol subkeys
keys doing operations, storing or clanging values, etc. You can use tle eacl_value or
eacl_key metlod to enumerate values or keys, respectively.
Jeg.each_vaue { ]hame, 1ype, da1a] pu1s hame + " = " + da1a }
Jeg.each_key { ]key, w11me] pu1s key + " .. " + w11me }
Tle eacl_value metlod will iterate over eacl value in tle current key and return its
value, type, and name. Tle above code slould output sometling like "ValueName
MyValue" lor eacl value in tle current key (e.g., il use witl tle previous code to open
up tle CuJJeh1veJs1oh key in tle W1hdows hT key, you slould see sometling
"Bu1dLab = 2600.xpsp_sp2_J1m.040803-2158" lor tle lirst result).
Writing Writing values to tle registry is very similar to reading tlem. Tle metlods
and tleir parameters are laid out very similarly.
Jeg|`Rev1s1ohhumbeJ`] = `1337`
Jeg|`hame`, W1h32..Reg1s1Jy..REC_hbLTT_SZ] = `hJ.\0he1ghboJy\0\0`
Jeg.wJ11e{`hyPa1h`, W1h32..Reg1s1Jy..REC_EXPAh0_SZ, `PATh`)
Tle lirst call slown above will simply write tle value 2600 to tle value
Rev1s1ohhumbeJ, notice tlat mucl like wlen reading values, you can make tle
registry act like a lasl. Tle next call allows you to write a value witl a specilied type,
like its read counterpart, it will tlrow a TypeError il it's tle wrong type. Tle last call is
very similar to tle read metlod demonstrated above, tle lirst parameter is tle value
name you wisl to set, tle second parameter is tle type, and tle tlird is tle value to
assign to it.
Dclcting Deleting keys and values is as simple as calling a metlod, |ust lope you
don't blow away sometling important on accident, because tlere's no undoing it!
Jeg.dee1e_vaue{`hyveJs1oh`)
Jeg.dee1e_key{`FavoJ11eCheese`)
Jeg.dee1e_key{`Ex-W1ves`, 1Jue)
Tle lirst metlod, dee1e_vaue, will delete tle value wlicl you specily as tle lirst
parameter. Tle second metlod slown, dee1e_key, will delete tle key wlicl you
specily, and il tle second, optional boolean parameter is provided, it will delete tle key
recursively (or not il you provide 1ase).
?@F(6'$.H#$+.,
OLE Automation (well, ollicially |ust "automation" but tle OLE term las sort ol
stuck) is a nilty little meclanism tlat Windows and many Windows applications oller
tlat allows you to automate tleir operation. Ior example, Excel exposes an
automation interlace wlicl allows you to launcl, create new documents, edit
documents, and so on. Tlese interlaces are built witl scripting in mind, so olten
82!!!A"2(B71$25(92,2#$"888
applications will provide a built-in way to tap into tlese interlaces (i.e. Visual Basic lor
Applications), but you can get to tlese interlaces witl otler clients also (e.g. C++ witl
COM, or, in our case, Ruby).
Automation Basics Ruby's OLE automation interlace is very, very simple compared
to most otler languages. As an example, let's pop open Internet Explorer and
navigate to tle web page lor tlis book.
Jequ1Je `w1h32oe`
my1e=WTh320LE.hew{`Th1eJhe1ExpoJeJ.App1ca11oh`)
my1e.v1s1be=1Jue
my1e.hav1ga1e{"h11p.//www.humbe111eJubybook.com")
my1e.e11 = 0
my1e|`1op`] = 0
As you can, tle code is very simple. Iirst you need to import tle w1h32oe library
using require. Next, create a new WTh320LE instance, leeding in tle application
interlace you want to talk to (in tlis case it's Internet Explorer, so
Th1eJhe1ExpoJeJ.App1ca11oh). Note tlat parameter may or may not always be
Wha1eveJ.App1ca11oh, but it usually is, you may need to consult tle application's
documentation to lind out exactly wlat it is il you are laving problems. Alter tlat, you
simply call metlods on tle interlace (tlese slould be outlined in tle automation
interlace's documentation). In our case, we make tle IE window visible, tell it to
navigate to our web page, and move tle window to tle upper lelt corner ol tle screen
(i.e. set tle e11 and 1op properties to 0). Note tlat properties can be set using eitler
attribute notation (i.e., my1e.e11) or lasl notation (i.e., my1e|`1op`]), since botl
lorms do tle same tling, it's really a matter ol prelerence as to wlicl one you slould
use.
Automation Evcnts In addition to causing tlings to lappen in an application, tle
Win32OLE class can also be used to be notilied ol wlat's going on in an application.
Tlis is done tlrougl an event sink meclanism tlat is exposed by tle application and
tlen consumed by your Ruby application.
Jequ1Je `w1h32oe`
# hahdeJ me1hods
de1 s1op_msg_oop
pu1s "App1ca11oh cosed."
1hJow .appcosed
ehd
de1 hahdeJ{eveh1, *aJgs)
pu1s "Eveh1 11Jed! . #{eveh1}"
ehd
# ha1h code
1e = WTh320LE.hew{`Th1eJhe1ExpoJeJ.App1ca11oh`)
1e.v1s1be = TRbE
1e.gohome
s1hk = WTh320LE_EvEhT.hew{1e, `0WebBJowseJEveh1s`)
s1hk.oh_eveh1 {]*aJgs] hahdeJ{*aJgs)}
s1hk.oh_eveh1{"0u11") {]*aJgs] s1op_msg_oop}
ca1ch{.appcosed) {
oop {
A"2(B71$25(92,2#$"888!!!83
WTh320LE_EvEhT.message_oop
}
}
To subscribe to events, you need to lollow tle general procedure lor consuming an
OLE interlace: import tle w1h32oe library, create a new WTh320LE instance, and call
metlods and/or attributes. Tle lirst new part ol tlis code is tle creation ol a
WTh320LE_EvEhT instance, tle constructor lor tlis class is given a WTh320LE instance
and tle name ol an event sink exposed by tlis interlace (il tle event sink doesn't exist,
an error is tlrown). You can tlen look into events using tle oh_eveh1 metlod, wlicl
is given a block as a parameter, tlis block is tlen in turn given tle event's arguments.
You can use oh_eveh1 in one ol two ways. Tle lirst is to give it a general landler, as in
tle lirst call to oh_eveh1, 1lis landler becomes a sort ol catcl all lor any events tlat
don't lave explicit landlers. You can also give events explicit landlers, like tle
second call gives tle tle "Quit" event. Note tlat riglt now tlere is no way to easily
detacl lrom an event, so our little "lack" to use catcl to break out ol tle message loop
seems to tle be tle easiest way to do it.
Windows Managcmcnt Instrumcntation Tle win32ole library also allows you to
use tle Windows Management Instrumentation (WMI) since it's simply a COM
interlace. WMI can be used lor a number ol administrative and management tasks,
sucl as service management, process management, event watcling, log auditing, and
so on, on botl local and remote maclines. Ior example, to get a list ol tle services on
tle local macline along witl tleir descriptions and status, you would do sometling
like tle lollowing.
Jequ1Je `w1h32oe`
mywm1 = WTh320LE.cohhec1{"w1hmgm1s.\\\\.")
mywm1.Ths1ahces01{"W1h32_SeJv1ce").each do ]s]
pu1s s.Cap11oh + " . " + s.S1a1e
pu1s s.0escJ1p11oh
pu1s
ehd
Tle cohhec1 metlod does basically tle same tling as tle hew metlod, except tle
cohhec1 metlod looks into an existing instance ol an OLE server wlereas tle hew
metlod creates a new instance (i.e., WMI is already running as a server on your
macline unless you disabled it, but lor sometling like Word or Outlook a new,
application-specilic instance is needed). In tlis case, we used tle InstancesOl metlod
tlat is exposed by WMI to get an array ol tle instances ol tle WMI class
W1h32_SeJv1ce, wlicl is simply a representation ol an entry in tle service list lor your
macline. In our block we could lave called metlods sucl as S1opSeJv1ce or
S1aJ1SeJv1ce to control it or il tlere were processes we could use CJea1e to start
tlem, but lor tle sake ol brevity (and tle sanity ol your macline), I opted to simply
display tle name, description, and status. Wlen you run tlis script, you slould see
output lor eacl service tlat looks sometling like tlis:
Task SchedueJ . Ruhh1hg
Ehabes a useJ 1o coh11guJe ahd schedue au1oma1ed 1asks oh
1h1s compu1eJ. T1 1h1s seJv1ce 1s s1opped, 1hese 1asks w1 ho1
84!!!A"2(B71$25(92,2#$"888
be Juh a1 1he1J schedued 11mes. T1 1h1s seJv1ce 1s d1sabed,
ahy seJv1ces 1ha1 exp1c11y depehd oh 11 w1 1a1 1o s1aJ1.
You can also get lists ol processes, computers, users, groups, and so on, but tlat is out
ol tle scope ol tlis book. Look at tle MSDN documentation lor WMI linked in
Appendix A lor more on wlat inlormation is exposed by WMI.
Tle w1h32oe library also allows you to subscribe to WMI events, WMI
events span tle wlole gamut ol system-wide events sucl as lile creation, process
creation, service actions, log entries, and so on. As an example, we'll watcl lor a
process tlat we create to end.
Jequ1Je `w1h32oe`
oca1oJ = WTh320LE.hew{"WbemScJ1p11hg.SWbemLoca1oJ.1")
seJv1ce = oca1oJ.Cohhec1SeJveJ{"./","","","")
pJoc = seJv1ce.Ce1 "W1h32_PJocess"
Jc = pJoc.CJea1e{`ho1epad.exe`, h1, h1, h1)
pJocess1d = WTh320LE..ARCv|3]
pu1s "hew pJocess 1d. #{pJocess1d}"
queJy = "seec1 * 1Jom __Ths1ahce0ee11ohEveh1 w11h1h 1 wheJe
1aJge11hs1ahce 1sa `WTh32_PJocess` ahd 1aJge11hs1ahce.hahde =
#{pJocess1d}"
eveh1 = seJv1ce.Execho1111ca11oh0ueJy{queJy)
eveh1.hex1eveh1
pu1s "PJocess 1eJm1ha1ed."
Because ol tle way tle WMI operates, you can't use tle built-in event meclanism in
tle win32ole library, but it's lairly simple to get tlis working nonetleless. Iirst, create
a new instance ol tle WTh320LE class using tle new metlod and point it at tle server
WbemscJ1p11hg.SWbemLoca1oJ. Tlis OLE server is tle basic equivalent ol connecting
like we did belore (w1hmg1s.//), but using tlis in con|unction witl tle subsequent
call to Cohhec1SeJveJ allows you to do two tlings.
Iirst, you can connect to remote computers, meaning you could use tlis code
or similar snippets to do various tasks on a number ol computers you may be
managing. Second, you can provide login credentials as tle last two parameters to
tlis metlod. None are needed on tle local macline usually (unless you are not an
administrator or privileged account), but il tlis were a remote macline call you would
probably need a user name as tle tlird parameter and tle password as tle lourtl.
Next we use tle WMI metlod to get a relerence to tle W1h32_PJocess class and create
an instance ol it, passing in "ho1epad.exe" as tle process to start. Doing so creates a
new Notepad instance (i.e., you slould see Notepad pop up on your screen) and
returns some data about it, wlicl we use to get tle process ID.
Next, we use WQL (WMI Query Language) to tell WMI tlat we're going to
be watcling lor processes to be destroyed tlat lave tle process landle (ID) ol tle
process tlat we created, tle WQL statement is executed using tle
Execho1111ca11oh0ueJy metlod tlat is exposed by WMI. Tlis metlod returns an
event notilier ob|ect, wlicl we can call tle nexteven metlod on, tlis metlod tells tle
current tlread to pause until tle next event is lired, so il you were to use tlis in an
A"2(B71$25(92,2#$"888!!!85
actual application tlat slould continue running, tlis slould be lorked into its own
tlread. Close Notepad and you slould see tle output "PJocess 1eJm1ha1ed", tlis
means tlat tle next event las lired (i.e., tle process las been deleted) and tle current
tlread las been given control once again. Tlis is a lairly simplilied example, but
lopelully it slould give you tle basic concepts to translate more complicated
examples lrom VBScript, C++, or C# into Ruby and make use ol tlem.
!"#$%&"'()*+%%
You learned about Ruby's system level interaction. You learned...
! about Ruby's lilesystem interaction capabilities.
! low to start tlreads and lork processes.
! low Ruby can interact witl Microsolt Windows on a number ol levels.
86!!!A"2(B71$25(92,2#$"888
&
@..*+,N(<2I.,B(5.H2
In tlis day and age, tle rage witl all tle kids seems to be tlose "networked"
computers. Well, wlatever tlat means, tlose young'uns don't need to look any
lurtler tlan Ruby lor tleir network lix, wletler it be getting cracked out on HTTP or
lopped up on databases. One ol my lavorite tlrills in ligl sclool was litting an
Etlernet line and tlen listening to Link Void's "Darkliber Line to tle Room" and
watcling "Tle Wizard ol Oz" at tle same time. Iun times.
>F7!?:R8>A(6>Q(75F(!F<
Ruby's networking abilities lave been lavorably compared to languages sucl as
Pytlon and Perl, wlile Ruby doesn't lave luge libraries sucl as CPAN or tigltly
integrated suites like Twisted (yet), it does present a nice suite ol networking
capabilities built riglt in.
9.&*2$(L3.N3#HH+,N
Ruby ollers a number ol socket
types tlat you can use to connect
to dillerent transport and
network protocols. Botl
connection- and connectionless-
oriented transports are ollered,
along witl classes tlat ollers
client and server support. All
sockets lail lrom tle Cenesis ol
all socket classes: Bas1cSocke1.
Tle Socke1 class provides you witl a very C-isl interlace to sockets, it is mucl more
complex and a generally pain in tle bum compared to tle otler classes sucl as
TPSocke1, b0PSocke1, or TCPSocke1.
C..*+,D(927.,:(=.52!!!87
Tle rest ol tlis section (i.e., tle part dealing witl actually networking an
application) will concentrate on tle TCPSocke1 and TCPSeJveJ class, since tlese
classes will be tle most common classes you will use and are easier tlan using tle
same lunctionality using tle Socke1 class. Tlere is a lot ol inlormation available on
using tle otler classes, so il you need to use sometling like a UDP socket, tlen
consult one ol tle links in Appendix A or use your lavorite searcl engine to lind wlat
you need.
Scrvcr Tle TCPSeJveJ class ollers a simple way to get socket server lunctionality up
and going in your application witl minimal luss wlen dealing witl accepting
connections and tle like. Tlere's not mucl background otler tlan wlat's already
been discussed witl regard to sockets in general, so let's get riglt into some code and
construct tle de lacto standard example socket server example program: tle eclo
server.
Jequ1Je "socke1"
myseJveJ = TCPseJveJ.hew{`ocahos1`, 0)
sockaddJ = myseJveJ.addJ
pu1s "Echo seJveJ Juhh1hg oh #{sockaddJ.o1h{`.`)}"
wh1e 1Jue
ThJead.s1aJ1{myseJveJ.accep1) do ]sock]
pu1s{"#{sock} cohhec1ed a1 #{T1me.how}")
wh1e sock.ge1s
sock.wJ11e{$_)
pu1s "bseJ eh1eJed. #{$_}"
ehd
pu1s{"#{sock} d1scohhec1ed a1 #{T1me.how}")
s.cose
ehd
ehd
Let's take tlis apart. Iirst, you must include tle socket module, wlicl will give you
access to TCPSeJveJ and all its related modules and classes. Next, you must create a
TCPSeJveJ instance using tle eitler tle hew metlod or its synonym, tle opeh metlod.
Tle lirst parameter is tle interlace to bind to, il tlis is lelt blank, tlen Ruby binds to
all available interlaces on tle lost. Tle second parameter is tle port number, il tlis is
0 like in tle example, tlen tle system automatically selects an available port. Tlen
upon entering tle loop, we tell tle application to wait until a new socket is connected
(wlicl lires myserver.accept), wlen tle socket connects, a new ThJead ob|ect is
created and tle enclosed block is executed. I use a ThJead lere simply because tlis
setup allows you to connect more tlan one client to tlis particular server instance.
You could do it "blocking" using a wlile loop or some sucl, but it's generally better
practice to do it like tlis. Tle ThJead's block las logic to output a message upon
connect and disconnect ol a lost and output user data entry on tle console and back to
tle socket. Upon running tlis, you slould lave a simple socket server tlat will eclo
back any input given to it.
Echo seJveJ Juhh1hg oh AF_ThET.&'().ocahos1.127.0.0.1
88!!!C..*+,D(927.,:(=.52
Wlen tle server starts, you slould see a little bit ol inlormation about it, including tle
port number (wlicl is liglliglted above). Now il you open up a telnet client and
telnet to locallost on tle given port number, you slould be able to enter some text and
lave it ecloed back to you.
myhos1> 1ehe1 ocahos1 3160
some moJe 1ex1!!
some moJe 1ex1!!
1he pahdas aJe 1v1d!!
1he pahdas aJe 1v1d!!
Pahcakes aJe a 1aJ be11eJ souJce o1 0mega-3.
Pahcakes aJe a 1aJ be11eJ souJce o1 0mega-3.
Alter you close tle telnet window, llip over to tle Ruby console tlat your application
las been running in and look at tle output.
Echo seJveJ Juhh1hg oh AF_ThET.3160.ocahos1.127.0.0.1
#<TCPSocke1.0x27ebe1c> cohhec1ed a1 Wed Sep 13 19.44.28 Eas1eJh
0ay1gh1 T1me 2006
bseJ eh1eJed. some moJe 1ex1!!
bseJ eh1eJed. pahd1he pahdas aJe 1v1d!!
bseJ eh1eJed. Pahcakes aJe a 1aJ be11eJ souJce o1 0mega-3.
#<TCPSocke1.0x27ebe1c> d1scohhec1ed a1 Sa1 Sep 13 19.45.48
Eas1eJh 0ay1gh1 T1me 2006
Wlen tle application started, you were given a nice little startup message, wlicl is
lollowed by messages indicating tle various events going on in tle application. Now
let's take a look at TCP client services.
Clicnt TCP client services are ollered by tle TCPSocke1 class, tlis class allows you to
make a TCP connection to various services, sucl as linger, ltp, your own service on a
random port, or even a telnet server. Let's connect to tle BOIH Excuse telnet server
on blinkenliglts.nl as an example, tlis way, wlen your boss asks you wly you're
reading tlis book instead or working, you can provide lim or ler witl a proper
excuse.
Jequ1Je "socke1"
pJ1h1{"Cohhec11hg...")
c1eh1sock = TCPsocke1.opeh{"1owe.b1hkeh1gh1s.h", 666)
pJ1h1{" dohe\h")
pu1s{"Cohhec1ed 1Jom #{c1eh1sock.addJ|2]}
1o #{c1eh1sock.peeJaddJ|2]}")
wh1e {excuse = c1eh1sock.ge1s)
pJ1h1{excuse)
ehd
c1eh1sock.cose
Iirst, you need to import tle socket library, ol course. Next, you open a TCPSocket
mucl like you open a lile stream. Tle lirst parameter is tle lostname or network
address to connect to, and tle second is tle port to connect on. Now tlat you lave a
socket, you can treat it |ust like a stream tlat you would get lrom a lile or sometling
similar, lence wly you can use ge1s to get data lrom it. You can also use Jead,
Jead1he, wJ11e, wJ11e1he, and any otler stream reading/writing command.
C..*+,D(927.,:(=.52!!!8
Sockets also lave tleir own metlods, sucl as Jecv to receive data lrom tle socket and
sehd to send data over tle socket, but it is not necessary to use tlese. It's simply a
matter or prelerence and style. Wlen you run tlis code, you slould get output similar
to tle lollowing.
Cohhec11hg... dohe
Cohhec1ed 1Jom hos1.doma1h.com 1o 1owe.b1hkeh1gh1s.h
=== The B0Fh Excuse SeJveJ ===
YouJ excuse 1s. T1`s ho1 pugged 1h.
Iirst, you're given a lew messages about tle connection tlat is made, lollowed by tle
output returned lrom tle server. We've basically telnetted (is tlat a verb?) into tle
server and received tle output, wlicl means tlat we could point a TCPSocket at a
TCPServer and lave tlem talk to eacl otler. Let's create a client lor our eclo server.
Jequ1Je "socke1"
pJ1h1{"Cohhec11hg...")
c1eh1sock = TCPsocke1.opeh{"ocahos1", 1390)
pJ1h1{" dohe\h")
wh1e {ge1s{))
c1eh1sock.wJ11e{$_)
pu1s{c1eh1sock.ge1s)
ehd
c1eh1sock.cose
Notice tlis code is very similar to our previous code, witl tle exception ol a lew
tlings. Iirst, we clanged tle lostname and port number (wlicl you will need to get
lrom tle eclo server beloreland or take as a command line parameter to your Ruby
script). Secondly, tle main loop now operates on user input ratler tlan socket
output, and in tlis loop, take user input and write it to tle socket. Tle socket's output
is tlen written to tle console. Wlen you run tlis and enter some data, you slould get
sometling like tle lollowing.
Cohhec11hg... dohe
ocahos1 1o ocahos1
Ch1ckeh 1veJs ahd po1be1es!
Ch1ckeh 1veJs ahd po1be1es!
hy boweJ 1s dus1y 1Jom my supeJ1uous em1ss1ohs.
hy boweJ 1s dus1y 1Jom my supeJ1uous em1ss1ohs.
Now, il you llip over to tle server's console window, you slould see sometling very
similar to tle lollowing.
Echo seJveJ Juhh1hg oh AF_ThET.1390.ocahos1.127.0.0.1
#<TCPSocke1.0x27ebe1c> cohhec1ed a1 Sa1 Sep 16 17.34.07 Eas1eJh
0ay1gh1 T1me 2006
bseJ eh1eJed. Ch1ckeh 1veJs ahd po1be1es!
bseJ eh1eJed. hy boweJ 1s dus1y 1Jom my supeJ1uous em1ss1ohs.
#<TCPSocke1.0x27ebe1c> d1scohhec1ed a1 Sa1 Sep 16 17.41.51
Eas1eJh 0ay1gh1 T1me 2006
Congratulations! You've successlully written a lully networked Ruby application.
Tlis example is lrivalous and useless ol course, but it slould illustrate tle basic
0!!!C..*+,D(927.,:(=.52
concept enougl to build oll ol. Belore you know it, you'll be porting Apacle to Ruby
or telling your boss tlat you don't need no stinkin' web browser il you've got Ruby.
577L(>2$C.3*+,N
Speaking ol tle web, HTTP networking is also a very important component ol today's
network programming landscape. HTTP, or HyperText Transler Protocol, is tle
standard lor Internet communication tlese days. I'm still not sure wly tle world ever
got away lrom ALOHA, but tlen again I'm not sure wly 640K ol memory isn't enougl
lor everyone nowadays eitler. In any case, Ruby provides a robust library ol HTTP
lunctionality built riglt in to tle standard library.
Scrvcr HTTP serving witl Ruby is incredibly easy. You could build a server based oll
ol TCPServer, meticulously implementing eacl part ol tle HTTP protocol, solving
concurrency and blocking issues as tley came up, and ligure out a way to reliably send
binary data. Tlen again, you could |ust use WEBrick, tle lull-Ruby web server tlat is
included in tle standard library.
Jequ1Je `webJ1ck`
1hcude WEBJ1ck
myseJveJ = hTTPSeJveJ.hew{.PoJ1 => 8080,
.0ocumeh1Roo1 => 01J..pwd + "/1emp")
myseJveJ.s1aJ1
Using tle above lour line snippet, you now lave a lully operational HTTP server
running on tle port your specily tlat will serve HTML documents out ol tle patl you
specily as tle DocumentRoot option (e.g., in tle example it's tle lolder tle script is
launcled lrom and "temp" like "/lome/yourname/temp"). To start tle server, simply
give create a new instance and call tle s1aJ1 metlod. Tle parameters slown lor tle
constructor are tle most commonly used, but tlere are a lot you can assign. Il you're
really interested in linding out more, you can see all ol tlem in tle WEBrick
documentation at lttp://www.ruby-doc.org/stdlib/libdoc/webrick/rdoc/index.ltml.
Back to tle example, il you lave an HTML lile named index.ltml dropped
into tle patl you specilied, you slould be able to navigate to lttp://locallost:8080
and see it.
C..*+,D(927.,:(=.52!!!1
Il you don't lave an index.ltml lile, it will slow you an index ol tle liles tlat are in
tlere, you can tlen request one ol tlose eitler by clicking tle link or navigating to it
directly. Now tlat you lave a working HTTP server, let's take a look at Ruby's HTTP
client lacilities.
Clicnt Ruby's HTTP client library is tle he1..hTTP class. Tlis class provides a simple
interlace to making HTTP requests to local and remote servers lor any type ol lile.
Possible uses include periodically downloading sometling like logs or new leeds or il
downloading a large number ol liles witlout making a request lor eacl one tlrougl a
web browser or sometling like wge1. As an example, let's say you wanted to download
Digg's RSS leed so you could parse it into a Ruby on Rails website or your own
lomegrown RSS reader, you could do so by making a request witl tle he1..hTTP
class.
Jequ1Je `he1/h11p`
he1..hTTP.ge1_pJ1h1 `www.d1gg.com`, `/Jss/1hdex.xm`
Iirst, you need to make include tle net/lttp library using require. In tlis instance, I
used tle ge1_pJ1h1 metlod to print out tle lile to tle console., wlicl takes a
lostname and a lile to retrieve. Tlis is tle best way to do do tlis il you want to get tle
same lile lrom a number ol servers (i.e., you could leave tle lile as a constant string
and lave tle lostname be a variable) or a lot ol liles lrom tle same lost (i.e., you
could leave tle lostname as a constant string and tle lile variable), but il you are
generally letcling liles lrom dillerent servers and ol dillerent lilenames, tlere is a
more intuitive way to do so.
2!!!C..*+,D(927.,:(=.52
Figurc 13: Scotty O'Laddic's Fun Sitc! Prctty much thc bcst wcbsitc in thc world.
Jequ1Je `he1/h11p`
Jequ1Je `uJ1`
he1..hTTP.ge1_pJ1h1 bRT.paJse{`h11p.//www.Juby-ahg.oJg`)
Tle above snippet will grab tle Ruby Language lome page's source code and print it
on tle console. Il you notice, ratler tlan leeding tle get_print metlod two separate
parameters (i.e., tle lostname and tle lilename), I used tle URI.parse metlod lrom
tle uri library, tlis metlod allows you to take a normal URL (e.g.,
lttp://www.bn.com/, lttp://www.ruby-lang.org/, lttp://rubyonrails.org/index.plp,
and so on) and pars e it lor use witl any ol tle Net::HTTP metlods. I slow tlis early
in tlis section because it is tle easiest way to give URLs to tlese metlods, speaking ol
tlose metlods, let's take a look at some ol tle otler metlods in tlis library.
So lar I've been using tle ge1_pJ1h1 metlod to get and print tle contents ol
a request. Tlis metlod is great il tlat's all you're doing, but wlat il you need to get tle
contents and store in a variable? Or wlat il you want to manipulate tle HTML some
way? Tlat's wlere tle ge1 metlod comes in, tlis metlod will execute a request and
tlen return tle results lor you to lave your way witl.
Jequ1Je `he1/h11p`
Jequ1Je `uJ1`
sJc = he1..hTTP.ge1{bRT.paJse{`h11p.//www.chh.com/`))
pu1s sJc
As you can see, tle same basic process is lollowed as belore witl ge1_pJ1h1, except
tlis time tle results are lirst stored in tle sJc variable.
Tle Net::HTTP library also allows you to post lorm data to a URL. You can
do tlis over CET by appending tle lorm data to tle end ol tle URL (e.g., lttp://your-
lost.com/index.plp?underpantssecured&sandpaperlinegrain&lotionyes), but a
little more ellort is required to make a POST request. A POST request are tle sorts ol
lorm posts tlat don't append anytling to tle URL, but ratler submit tle lorm data as
part ol tle HTTP request. You can use tle post_lorm metlod to make tlis kind ol
request witl Net::HTTP.
Jequ1Je `he1/h11p`
Jequ1Je `uJ1`
pos111 =
he1..hTTP.pos1_1oJm{bRT.paJse{`h11p.//z1p4.usps.com/z1p4/zc_3_
Jesu1s.sp`), {`z1p5`=>`37998`})
pu1s pos111.body
Tle above snippet will use tle USPS ZIP code lookup to lind out wlat city a ZIP code
is lrom, using tle pos1_1oJm metlod, you can post a lorm by providing tle lorm keys
and values in a lasl as tle second parameter (i.e., in tlis case, z1p5 was a text lield on
tle normal page wlicl was lor tle ZIP code you wisl to look up). Tle results are
returned |ust like a normal CET request.
Tle linal portion ol tlis library I would like to cover is tle he1..hTTP.PJoxy
class, wlicl allows you to connect tlrougl a proxy. Instances ol HTTP.Proxy lave
C..*+,D(927.,:(=.52!!!3
tle same metlods as a normal Net::HTTP instances, so you can use it almost tle same
way.
Jequ1Je `he1/h11p`
Jequ1Je `uJ1`
sJc = he1..hTTP..PJoxy{`mypJoxyhos1`, 8080, `useJhame`,
`passwoJd`).ge1{bRT.paJse{`h11p.//www.chh.com/`))
pu1s sJc
Wlen creating an instance ol tle hTTP..PJoxy class, you must give it a proxy address
and port, tle last two parameters lor a username and password are optional (but, ol
course, required il your proxy requires autlentication). Now any request you make
using tlat instance will go tlrougl tle provided proxy.
I didn't cover everytling about he1..hTTP lere. Tlink ol tlis as a mere
brusling ol tle surlace, uncovering a mere snippet ol wlat can be seen at
lttp://www.ruby-doc.org/stdlib/libdoc/net/lttp/rdoc/index.ltml. I encourage you
to go tlere and look over tle documentation, as we lave only uncovered "te woleclla"
ol tle tle wlole enclillada.
?$"23(>2$C.3*(923Y+&21
Tlougl I've covered tle two most popular protocols, tlat doesn't mean tle little guys
won't get any love. Since wlen is it tlat only tle popular kids matter? Well, probably
since about l944, but tlat's beside tle point. I'm going to cover tle nerds and geeks
ol tle network protocol community because I want to (tlat and tle Commission lor
Equal Treatment ol Network Protocols and Tleir Ugly Cousins Tlat Suck at Iile
Translers miglt yell at me il I don't).
FTP Tle Iile Transler Protocol (ITP) is a lairly popular metlod lor translerring and
storing liles, particularly in open source circles. As sucl, being able to grab tlese liles
(or upload tlem, wlatever tlc case may be) witl a Ruby script may prove to be
incredibly landy. Iortunately, Ruby provides a built-in library to landle all ol your
ITP needs.
Jequ1Je `he1/11p`
11p = he1..FTP.hew{`11p.ghu.oJg`)
11p.og1h{`ahohymous`, `me_mJhe1gboJy.com`)
11p.chd1J{`ghu/Jead1he/`)
11es = 11p.1s1{`J*`)
pu1s 11es
11p.ge1b1haJy11e{`Jead1he-5.1.1aJ.gz`, `J1he.1aJ.gz`)
11p.cose
Tle above snippet will login to tle CNU ITP server and download tle readline
library. Ruby's ITP library, |ust like every otler library, must lirst be included using
Jequ1Je. Next, you need to create a new he1..FTP instance, providing tle
constructor witl a lostname to connect to. Tle og1h metlod will tlen log you into
tle ITP server, il you are logging in anonymously (i.e., like in tle example) it's not
4!!!C..*+,D(927.,:(=.52
completely necessary to provide a username and password. Il lelt blank, Ruby will
substitute ahohymous in as tle username and ahohymous_youJhos1hame as tle
password. Next, you need to navigate to tle proper directory, lere, I used tle chd1J
metlod to clange directories to tle ghu/Jead1he/ lolder and tle 1s1 metlod to get
a list ol tle liles in tlat directory, wlicl is tlen printed to tle console. Once you're
wlere you want to be, you can use ge11ex111e or ge1b1haJy11e (as I lave lere) to
get liles or pu11ex111e or pu1b1haJy11e to upload local liles to tle server (il you're
allowed to). Tle get metlods require two parameters: tle remote lile to get, tle local
lilename to copy it to. Tle put metlods require two parameters also: tle local lile to
copy, and tle remote lilename to copy to. Il you'd like to see more examples, go to
lttp://www.ruby-doc.org/stdlib/libdoc/net/ltp/rdoc/index.ltml to view tlis library's
documentation.
SMTP Tle SMTP Protocol, or Simple Mail Transler Protocol...er...Protocol, is tle de
lacto standard lor sending e-mail. Tlanks to tlis protocol and it's usually crappy
implementation, you and I get tlousands ol spam e-mails every day. Tlanks Jon
Postel, Eric Allman, Dave Crocker, Ned Ireed, Randall Cellens, Joln Klensin, and
Keitl Moore lor inllating my inbox (and tlanks Wikipedia lor telling me wlo worked
on SMTP)! So, anylow, using SMTP in Ruby is actually quite painless.
Jequ1Je `he1/sm1p`
message = <<hESSACE
FJom. YouJ hame <you_you.com>
To. Tha1 Cuy <h1m_h1m.com>
Subec1. hy 11he ha1 1s m1spaced
0a1e. Sa1, 15 Sep 2006 16.26.43 +0900
hessage-Td. <1h1s1suh1que_you.com>
WheJe 1s my 1edoJa?
hESSACE
sm1p = he1..ShTP.s1aJ1{`me.com`, 25)
sm1p.sehd_message msgs1J,`you_you.com`,`h1m_h1m.com`, `you.com`
sm1p.11h1sh
Iirst, tell Ruby to require tle he1/sm1p library (I know it seems redundant, but you'd
be surprised low many times even I lorget tlis). Next, I constructed a message in a
string. Message construction would probably be landled dillerently in a "real"
application, but tlis metlod works lor tlis example. Next, create a new he1..ShTP
instance by calling tle start metlod, giving tle constructor a lostname and port lor
your SMTP server. Next, call tle sehd_message metlod, giving it tle message, tle
sender's e-mail address, and tle recipient's e-mail address, and HELO domain as
parameters. Tle HELO domain is used by some mail servers and spam lilters to make
sure tlat tle e-mail is legitimate. Iinally, call tle 11h1sh metlod to end tle SMTP
session. Il your SMTP server requires autlentication, you need to make tle start
metlod call like tle lollowing.
he1..ShTP.s1aJ1{`me.com`, 25, `you.com`, `you`, `mypw`, .pa1h)
Tle tlree additional parameters landle your autlentication. Tle lirst ol tlese is tle
username and password combination, tle last parameter indicates wlicl
autlentication scleme will be used. In tlis case, I used tle evil and mucl maligned
C..*+,D(927.,:(=.52!!!5
pa1h scleme, but tlere are tle og1h scleme and tle sligltly more secure cJam_md5
scleme wlicl you can use. Cleck witl your server's administrator to make your
server supports tlese metlods belore trying to use tlem as some crappier mail servers
don't support tlem.
POP Tle POP protocol, or Post Ollice Protocol...protocol, is (obviously) used to
retrieve e-mail. It las largely lallen out ol lavor in recent years since more people lave
discovered IMAP (wlicl is covered in tle next section) but is still in widespread use.
Ruby's POP library is an easy to use interlace to a POP server, and quite lonestly one
ol tle more natural POP interlaces I've used. Otler interlaces can be compared to a
McDonald's worker tlat speaks Engrisl. You know wlat you want tlem to do, but
eitler tley don't understand or tley do it in a way tlat may make sense but only to
someone wlo is eitler stupid or simply strange. Iortunately, Ruby's POP interlace is
natural and readable.
Jequ1Je `he1/pop`
pop = he1..P0P3.hew{`myma1seJveJ.com`)
pop.s1aJ1{`myaccouh1`, `saac1ous`)
11 pop.ma1s.emp1y?
pu1s `ho ma1 1o gJab.`
ese
pop.each_ma1 do ]msg]
pu1s msg.pop
# msg.dee1e
ehd
ehd
pop.11h1sh
Tlis code snippet will open a new connection to a POP server and log you in by
creating a Net::POP3 instance and calling tle s1aJ1 metlod witl login credentials as
parameters. Next, it clecks wletler or not tle mailbox is empty by using tle emp1y?
metlod on tle ma1s attribute ol tle Net::POP3 instance. Il tley mailbox is empty a
message is displayed indicating its emptiness, but otlerwise tle each_ma1 metlod is
used to iterate tle mail messages and print tlem out. Tle commented out code would
delete tle message, but I didn't want to put destructive code in sometling tlat you're
likely to copy and paste witlout regards to content. Since tle ma1s attribute is simply
an array ol mail messages, you could also use tle each metlod to iterate tle message,
but it's probably a better idea to use tle each_ma1 metlod since it is specialized (not
tlat it makes a dillerence, but it's probable tlat tle otler one will become deprecated
eventually).
I didn't cover all tle leatures in tle section, one reason is tlat I don't lave a
server tlat supports tlem all (i.e., APOP). Anotler reason is tlat tley're not common
enougl to warrant anymore documentation tlan you can lind on tle documentation
page at lttp://www.ruby-doc.org/stdlib/libdoc/net/pop/rdoc/. Let's move along to a
superior mail retrieval metlod: IMAP.
IMAP IMAP (Internet Message Access Protocol) is tle e-mail llavor du |our currently
since it's a little more ellicient tlan POP. It allows you to lave many logical mailboxes
ratler tlan a single inbox, multiple client access to one mailbox, and mucl more. Put
6!!!C..*+,D(927.,:(=.52
simply, it's a natural evolution ol POP. Ruby's IMAP interlace is surprisingly robust,
it gives you a natural interlace to all ol IMAP's commands. Let's take a look at an
example lrom tle Ruby documentation tlat will grab all mail messages and print out
tleir sender and sub|ect.
Jequ1Je `he1/1map`
1map = he1..ThAP.hew{`ma1.exampe.com`)
1map.au1heh11ca1e{`L0CTh`, `oe_useJ`, `oes_passwoJd`)
1map.exam1he{`ThB0X`)
1map.seaJch{|"RECEhT"]).each do ]msg_1d]
ehveope = 1map.1e1ch{msg_1d,"EhvEL0PE")|0].a11J|"EhvEL0PE"]
pu1s "#{ehveope.1Jom|0].hame}. \1#{ehveope.subec1}"
ehd
Tlis example works mucl like tle POP example in tle lirst lew lines: create a new
instance parametrized witl tle lostname and call a metlod to autlenticate). Tle two
autlentication sclemes supported by IMAP are L0CTh (used lere) and CRAh-h05.
Alter tlese lines tlougl, IMAP's arclitecture really starts slowing. Tle exam1he
metlod gives you read-only access to a mailbox or lolder (tle seec1 metlod, wlicl
is used tle same way, allows editing access), you can tlen use tle seaJch metlod to
grab a list ol mails to operate on. You can use tle 1e1ch metlod to get a mail ob|ect,
wlicl you can get inlormation lrom (as in tle example) or use metlods like copy or
s1oJe. To get more inlormation, cleck out tle IMAP library's documentation at
lttp://www.ruby-doc.org/stdlib/libdoc/net/imap/rdoc/index.ltml.
!2-(923Y+&21
Web Services are growing in popuarity tlese days, and as sucl I tlouglt it was pretty
important tlat I at least mention tlem. I'm only going to cover a very basic client side
example, as tlis could lonestly be a book on its own il anyone wanted to write it.
Tlere are a lew links in Appendix A tlat talk about using Ruby and web services
togetler il you're interested, otlerwise let us marcl on towards our web service laden
Promised Land. We lave yet to cross tle Red Sea, but, brotlers and sisters, it is in
siglt!
XML-RPC Consuming an XML-RPC (XML Remote Procedure Call) web service in
Ruby is as simple as letcling a web page, except instead ol giving you a ratler
unusable raw return value, Ruby gives your beautilully and transparently converted
values tlat you can use riglt away. Let's look at a sligltly edited example lrom tle
documentation.
Jequ1Je `xmJpc/c1eh1`
seJveJ = XhLRPC..C1eh1.hew2{"h11p.//xmJpc-
c.souJce1oJge.he1/ap1/sampe.php")
Jesu1 = seJveJ.ca{"sampe.sumAhd0111eJehce", 5, 3)
pu1s "#{Jesu1|`d111eJehce`]} ahd #{Jesu1|`sum`]}"
"!!2 ahd 8
C..*+,D(927.,:(=.52!!!7
Iirst, you need to include tle library and create a new instance using tle hew2 metlod
(or hew witl a lot ol parameters or hew3 witl a lasl ol every parameter needed, it's
sort ol up to you). Tlen use tle ca metlod to make an XML-RPC call to tle server,
tle call metlod takes tle remote metlod as tle lirst parameter and tle metlod's
parameters as tle remaining parameters. Tle results are tlen returned to you as a
usable lasl (i.e., result[dillerencej) ratler tlan a random, annoying, unparsed XML
string. Il you are interested at looking at tle documentation lor tle XML-RPC client,
look at lttp://www.ruby-doc.org/stdlib/libdoc/xmlrpc/rdoc/index.ltml, il you want
to implement an XML-RPC server, look in Appendix A lor links to some inlormation.
SOAP SOAP (wlicl used to stand lor Simple Ob|ect Access Protocol, but now it
apparently stands lor SOAP) is tle supposed successor to XML-RPC. It was
supposed to be a way to slare ob|ects over a network, but somewlere along tle way
everytling went awry, tle ICC got involved, and SOAP doesn't do wlat it was meant
to do. In any event, it's a lairly common and powerlul way to do web services. Here is
an example lrom tle documentation lor soap4r, tle standard way to do SOAP in
Ruby, wlicl will translate lrom Englisl to Cerman using a SOAP interlace lor
Babellisl.
1ex1 = `heJe T 1e 1h a swea1eJ pooJy kh11.`
ahg = `eh_de`
Jequ1Je `soap/Jpc/dJ1veJ`
seJveJ = `h11p.//seJv1ces.xme1hods.he1/peJ/soap11e.cg1`
Th1eJ1acehS = `uJh.xme1hodsBabeF1sh`
w1Je0ump0ev = h1
dJv = S0AP..RPC..0J1veJ.hew{seJveJ, Th1eJ1acehS)
dJv.w1Jedump_dev = w1Je0ump0ev
dJv.add_me1hod_w11h_soapac11oh{`BabeF1sh`, Th1eJ1acehS +
"#BabeF1sh", `1Jahsa11ohmode`, `souJceda1a`)
pu1s dJv.BabeF1sh{ahg, 1ex1)
"!!h1eJ 1ege 1ch 1h e1heJ S1J1ckacke s1J1cke schech1
Tlis looks complex, but it's mostly |ust setting up tle SOAP parameters. Iirst, I put in
some seed data to work witl, wlicl is basically a sentence tlat will be translated lrom
Englisl to Cerman. Next, you can see tlat I included tle library and tlen put some
SOAP parameters into variables. Tle lirst value, seJveJ, is tle location ol tle service
to connect to, tle second, Th1eJ1acehS, is tle namespace tlat tle metlod you wisl
to call is located in. Tle last one is tle "dump" landler lor errors, tlat il set to h1
becomes tle standard error clannel. Next, a new S0AP..RPC..0J1veJ is created tlat
points to tle service and namespace specilied earlier and is told to dump to stderr
(standard error). Tle next little bit ol code demonstrates an interesting leature ol
soap4r, wlicl is probably wlat sets it apart lrom most otler SOAP wrappers. Tle
add_me1hod_w11h_soapac11oh uses Ruby's dynamic nature to add a metlod to tle
0J1veJ ob|ect. Tlis means tlat ratler tlan marslalling and calling and doing a lot ol
otler random stull, you can now call tle metlod on tle 0J1veJ ob|ect like any otler
metlod (wlicl we do at tle end ol tlis snippet).
Once again, I'm not covering nearly all ol tlis library. Tlere are so many
leatures in tlere tlat I couldn't dream ol trying to do it |ustice in one little section,il
8!!!C..*+,D(927.,:(=.52
you're really curious and want to lind out more, you can cleck out tle soap4r website
at lttp://dev.ctor.org/ soap4r / or tle otler links Appendix A lor more inlormation.
87%9(@8RF(Q897:8<;7FQ(?:(9?0F758>AJJJ
In today's world ol enterprise soltware, loosely coupled systems, and kosler beel
lranks made by Jews, distributed computing is becoming increasingly more
important. Perlaps you need to expose an interlace to tle world lor usage ol your
resources, maybe you need to distribute leavy computing across a grid ol computers,
maybe you want to calculate sometling in a number ol environments witl minimal
ellort. Tle DRb (Distributed Ruby) package provides a simple way to landle
distributed computing in Ruby.
Every DRb application las two components: a server and clients. Tle server
will start a TCP server, wlicl will expose ob|ects, accept connections, and respond to
actions requested over tlose connections. Clients will establisl connections to a DRb
server, bind to ob|ects, and send messages to tlose ob|ects similar to messages tlat
are sent to local ob|ects wlen calling metlods and attributes. Let's build an example
to get a leel lor low tlis works, let's say you needed to keep track ol your log sizes on a
remote server and wanted to keep tlat ligure on your desktop lor easy monitoring.
Scrvcr Setting up a DRb server is simple il you lave tle class you want to expose
already constructed. In tlis case, I'm exposing a ratler trivial class tlat simply takes
one value as a parameter (tle log lile to watcl) returns one value (tle size ol tle log
lile).
cass FSWa1cheJSeJv1ce
de1 1h111a1ze{11ehame)
_11e = 11ehame
ehd
C..*+,D(927.,:(=.52!!!
Figurc 14: A typical DRb sctup: scrvcr and clicnt.
de1 ge1space
Je1uJh F1e.s1ze{_11e)
ehd
ehd
Now tlat you lave a class to work witl, you need to wrap it in a DRb server. Tlis is
done by simply calling tle s1aJ1_seJv1ce metlod lrom tle drb module and giving it a
lew parameters.
Jequ1Je `dJb/dJb`
Jequ1Je `1hJead`
cass S1zeSeJv1ce
de1 1h111a1ze{11ehame)
_11e = 11ehame
ehd
de1 ge1space
Je1uJh F1e.s1ze{_11e)
ehd
ehd
0Rb.s1aJ1_seJv1ce{"dJuby.//.9876", S1zeSeJv1ce.hew{`ca1s.og`))
pu1s 0Rb.uJ1
0Rb.1hJead.o1h
Iirst you need to include botl tle DRb module (dJb/dJb) and tle 1hJead module so
you can |oin tle DRb server's tlread into tle main one. Next you see tle class we
made earlier. Tlen you need to call tle s1aJ1_seJv1ce metlod lrom tle DRb class,
tlis will start a DRb server on tle specilied URI and expose tle indicated ob|ect. Tle
DRb URI is tlen printed to tle console (lor relerence and lor your benelit wlen trying
to connect to it). Tle DRb server's tlread is tlen |oined to tle main tlread, and tle
server idles until connected to.
Clicnt DRb makes constructing a client dirt simple. Tle basic process is tlat you call
start_service, bind to tle remote ob|ect, and tlen use it |ust like a remote ob|ect.
Jequ1Je `dJb/dJb`
0Jb.s1aJ1_seJv1ce
Jemo1eob = 0Rb0bec1.hew{h1, `dJuby.//domo.9876`)
pu1s "Log 11e usage 1s #{Jemo1eob.ge1space{)} by1es."
As you can see, you simply create a new 0Rb0bec1 and give it a DRb URI as a
parameter, and voila! You've got a remote ob|ect you can use |ust like a local ob|ect, in
tlis example, I called tle ge1space{) metlod on tle remote ob|ect and printed its
value to tle console.
I didn't cover every nook and cranny ol DRb lere (and I didn't intend to, it's a
robust package), it also ollers ways to make your application tlread sale (so tlat wlen
you lave a lot ol clients connecting and disconnecting tlat your data stays kosler lor
all ol tlem) and a lost ol security measures. Cleck tle links in Appendix A to lind out
about all tlat stull and more.
100!!!C..*+,D(927.,:(=.52
Q676(0=(<69F/(L@F69FG
Database driven applications are incredibly common nowadays, everyone's got one.
Your wile, your mom, your son, tle creepy guy at work witl a nasty mustacle and
lalitosis. Il you don't get your butt in gear and get one, your boss is going to lire you
lor being tle only square wlo isn't rolling on dubs, RDBMS style.
DBI Ruby's DBI package, mucl like tle Perl package, is a database vendor-
independent database connection suite. Its robust support lor database leatures and
vendors is nearly unparalelled. As an illustration, lere is a table ol supported database
vendors and connection types.
A00!"#$%&'#$(!)'*+,- 0B2 FJoh1base Th1eJBase
mS0L
0CT8
hyS0L
./(01#$234!"Pg-
00BC
S0L11e
0Jace
S0LReay
It supports more connection types, sucl as a Proxy tlrougl DRb, and even more
vendors tlrougl interlaces like ADO and ODBC. Let's take a look at low to connect
to one ol tlese databases.
Tle basic process lor making DBI work is to connect to tle server, providing
a lost and credentials, execute or prepare and execute SQL statements, letcl tle
results, and, wlen you're done, disconnect lrom tle server. Let's build a quick
example tlat will slow oll most ol tle lunctionality ol DBI, let's say you needed to
keep a database ol all ol tle specimens tlat are in your exotic slug collection.
Jequ1Je `db1`
0BT.cohhec1{`0BT.hysq.mydb`, `myuseJ`, `mypass`) do ] dbh ]
dbh.do{`CREATE TABLE sugs{hame, age),`)
sq = "ThSERT ThT0 sugs {hame, age) vALbES {?, ?)"
dbh.pJepaJe{sq) do ]s1h]
1.up1o{43) { ]1] s1h.execu1e{"Sug #{1}", "#{1*3}") }
ehd
dbh.seec1_a{`SELECT * FR0h sugs,`) do ]Jow]
pu1s "heo, #{Jow|0]!"
ehd
s1h = dbh.pJepaJe{"SELECT * FR0h sugs WhERE hame=`Sug 1`,")
s1h.execu1e
wh1e Jow = s1h.1e1ch do
pu1s Jow
ehd
ehd
To start up DBI, call cohhec1, leeding it tle database driver, database, and lost to
use, and open a block. Tlis block is provided tle database landle as a parameter
C..*+,D(927.,:(=.52!!!101
102!!!C..*+,D(927.,:(=.52
(dbh), wlicl it can tlen operate on. Iirst, we create a table to work witl using tle do
metlod, tle do metlod immediately executes a SQL statement. Next, we build a SQL
statement and prepare it using tle obviously named pJepaJe statement. Tlis will
allow us to execute tle same SQL statement over and over again quickly, easily, and
elliciently using tle execu1e metlod (as I've done lere wlen inserting tle data),
anotler upside is tlat you can lave it use placelolders as I've done lere using
question marks. Tlis lets you substitute values in eacl time tle statement is executed,
lor example, lere I substitute in tle slug's hame and age, wlicl clanges eacl time tle
statement is called. Next, you can see tle seec1_a metlod being used, wlicl will
return every row lrom a SELECT statement and leed it to a block, tlere is also a
seec1_ohe metlod lor tlose times wlen you really |ust need one row. You can also
prepare, execute, and letcl results il tlat's your llavor, tlat metlod is slown lere
using tle pJepaJe and execu1e combination witl a wh1e loop to letcl tle results.
Iinally, wlen tle block exits, tle client disconnects. Il you opt to not use tle block
lorm ol tlis code, you will need to call d1scohhec1 to disconnect lrom tle server.
ActivcRccord Il you lave any experience witl Ruby on Rails at all, you've surely
leard ol its great ORM (Ob|ect Relational Mapping) package ActiveRecord, but did
you know you can use ActiveRecord witlout Rails? It's really quite simple once you
see it in action. You lirst need to make sure you lave it installed typing tle lollowing
in a Ruby lile and running it.
Jequ1Je `Jubygems`
Jequ1Je `ac11veJecoJd`
Il you get an error, tlen make sure you lave installed RubyCems. Il you don't, install
it. Il you do lave RubyCems installed and lack ActiveRecord, tlen enter gem 1hs1a
ac11veJecoJd in a console to install ActiveRecord.
To get ActiveRecord going in your application, you essentially you import tle
gems library and ActiveRecord, construct a conliguration tlat would normally live in
a Rails database conliguration lile (i.e., conlig/database.yml), deline your
ActiveRecord classes, tlen call metlods on tlem. Ior example, let's create a database
in your lavorite database server (e.g., mySQL, PostgreSQL, and so on) and a table
named people tlat las tlree lields: name, |ob, and location. Insert a lew rows and let's
map tlat into an ActiveRecord application.
Jequ1Je `Jubygems`
Jequ1Je_gem `ac11veJecoJd`
Ac11veRecoJd..Base.es1ab1sh_cohhec11oh{
.adap1eJ => "mysq",
.hos1 => "ocahos1",
.da1abase => "1es1",
.useJhame => "Joo1",
.passwoJd => ""
)
cass PeJsoh < Ac11veRecoJd..Base
ehd
mypeope = PeJsoh.11hd{.a)
pu1s "To1a ehg1h. #{mypeope.ehg1h}"
C..*+,D(927.,:(=.52!!!103
"!!To1a ehg1h. 14
mypeope.each do ]peJsohob]
pu1s "heo #{peJsohob.hame}, #{peJsohob.ob}."
ehd
"!!heo Tmogeh, ch1d psych1a1J1s1 ahd supeJheJo.
As you can see, ratler tlan laving a pre-constructed conliguration, tle
es1ab1sh_cohhec11oh metlod is led a longer set ol parameters. Tlen, a class tlat
inlerits lrom Ac11veRecoJd..Base is created tlat is tle singular lorm ol tle table
name we made earlier, lence PeJsoh lor tle table peope (tlis is part ol tle magic ol
ActiveRecord), tlis maps tle table to a class tlat you can instantiate and use to
manipulate rows in tle database (cleck out tle ActiveRecord documentation at
lttp://api.rubyonrails.org/ lor more inlormation on wlat you can do witl
ActiveRecord ob|ects).
!"#$%&"'()*+%%
You learned about Ruby's networking laculties. You learned...
! about Ruby's basic networking capabilities witl TCPSocket/TCPServer.
! low to use HTTP networking witl Ruby.
! consume web services witl Ruby.
! about DRb and Ruby's distributed computing capabilities.
! low to connect to a database witl Ruby.
104!!!C..*+,D(927.,:(=.52
'
8$%1(#(@+-3#3IG
As you've seen, Ruby provides a number ol built-in classes lor you to access, it las a
luge library ol lunctions to cover anytling lrom simple text input and output to
networking to multimedia (not counting tle boat load ol tlird party libraries lloating
about). We've taken a look at some ol tle more "targeted" classes (i.e., win32,
networking, and so on), but I want to take some time to devote a really quick overview
to a lew classes tlat are more miscellaneous in nature.
97:8>A(06>8L;@678?>
Ruby's string support easily rivals tlat ol Perl and otler "power" languages. As a
matter ol lact, I've leard tlat wlen Larry Wall goes lome at niglt le secretly moon
liglts as a Ruby programmer. Don't tell anyone I told you. Tlere are two ways to
manipulate strings: instance metlods on string ob|ects and regular expressions.
8,1$#,&2(02$".B1
Ruby strings are, ol course, ob|ects, and as sucl, oller metlods to manipulate
tlemselves in a variety ol ways. Iirst we will look at tle simplest manipulation ol a
string: tle splice. Tle lirst way I'd like to slow splicing is tle splice operator, tlis
operator is used |ust like tle array operator (i.e. it uses tle obec1|1hdex] lorm to
relerence and set tle value ol elements) witl a lew little string specilic extras. Ior
example:
1he_aphabe1 = "ABC0EFChTJKLhh0P0RSTbvWXYZ"
pu1s 1he_aphabe1|0..2]
" ABC
pu1s 1he_aphabe1|"hTJ"]
" hTJ
pu1s 1he_aphabe1|1,2]
" BC
E$%1(#(C+-3#37<!!!105
1he_aphabe1|1] = "!"
pu1s 1he_aphabe1
" A!C0EFChTJKLhh0P0RSTbvWXYZ
As an alternative, you can use tle s1ce metlod to grab elements in a similar manner:
pu1s 1he_aphabe1.s1ce{0, 3)
" ABC
pu1s 1he_aphabe1.s1ce{0..5)
" ABC0EF
Once you lave your string data sliced and diced |ust tle way you want it, you
can manipulate it in a number ol ways, tlese metlods include ways to clange case,
edit certain content, or generally linagle witl tle string. Here are some examples:
a_s1J = "T 1kE To hesS w11h STRThCS!"
a_s1J.dowhcase
" 1 1ke 1o mess w11h s1J1hgs!
a_s1J.upcase
" T LTKE T0 hESS WTTh STRThCS!
a_s1J.swapcase
" 1 LTKe 10 mESs WTTh s1J1hgs!
a_s1J.cap11a1ze
" T 1ke 1o mess w11h s1J1hgs!
a_s1J.cap11a1ze!
pu1s a_s1J
" T 1ke 1o mess w11h s1J1hgs!
a_s1J.squeeze
" T 1ke 1o mes w11h s1J1hgs!
Tle names ol tlese metlods slould make tleir usage obvious, witl tle exception ol
squeeze wlicl will remove sets ol tle same claracter and replace tlem witl a single
instance ol tlat claracter (i.e. "mess" is now "mes"), eacl ol tlese metlods also ollers
an "in place" version as demonstrated by cap11a1ze!.
Tle 1hseJ1 metlod allows you to insert a string at a given index, be carelul
because tlis metlod will modily tle string in place (I'm not sure wly tlis metlod
lacks a !, but it does):
1h1o = "12345"
1h1o.1hseJ1{2, "LETTERS!")
pu1s 1h1o
" 12LETTERS!345
On tle otler land, you can use tle dee1e metlod to remove claracters or a range ol
claracters lrom a string:
gohe = "go1 gohe 1oo!"
gohe.dee1e{"o", "J-v")
pu1s gohe
" g ghe 1!
106!!!E$%1(#(C+-3#37<
Placing a lyplen between two letters will tell Ruby to create an inclusive (i.e. also
include tle lower and upper limit claracters, sucl as J and v in tle example) range ol
claracters. You can use tlis teclnique witl a number ol metlods, but it is
exceptionally uselul witl delete.
Removing tle last claracter lrom a string can be really annoying and mucl
longer tlat it slould be in some languages (I'm looking riglt at you PHP), lortunately,
like any good language tlat las strings, Ruby ollers chomp and chop:
ea1_me = "A o1 o1 e11eJs\h"
ea1_me.chomp
" A o1 o1 e11eJs
ea1_me.chop
" A o1 o1 e11eJ
ea1_me.chomp{"1eJ")
" A o1 o1 e1
Tle chomp metlod will remove tle record separator (stored in tle global ob|ect $/)
lrom tle end ol a string, il $/ lasn't been clanged lrom tle delault, tlis means it will
remove \h, \J\h, and \J (essentially any sort ol newline claracter). You can also
specily wlat to chomp by passing it in as a parameter as in tle last example. Tle chop
metlod will simply clop oll tle last claracter regardless ol wlat it is (note: it will treat
'r'n as one claracter).
Il you simply need to remove tle wlitespace lrom tle beginning and/or end
ol a string, tlen use one ol tle strip metlods: s1J1p, Js1J1p, or simply s1J1p. Ior
example:
s1J1ppeJ = " La d1 da! \1"
pu1s "|" + s1J1ppeJ.s1J1p + "]"
" |La d1 da! ]
pu1s "|" + s1J1ppeJ.Js1J1p + "]"
" | La d1 da!]
pu1s "|" + s1J1ppeJ.s1J1p + "]"
" |La d1 da!]
Il it isn't obvious lrom tle examples, tle s1J1p and Js1J1p metlods strip oll any sort
ol wlitespace (i.e. spaces, tabs, newlines, etc.) lrom a string on tle lelt or riglt side,
respectively. Tle s1J1p metlod strips wlitespace lrom botl sides.
Tle sp11 metlod will probably be one ol your most olten used string
metlods, it breaks a string into substrings based on a specilied delimiter. Tlat's a bit
ol a moutl lull, so let me slow you an example:
bJeakabe = "bJeak,11,dowh,1oo!"
bJeakabe.sp11{",")
" |"bJeak", "11", "dowh", "1oo!"]
Tle sp11 metlod breaks a string into pieces, tlese pieces are lormed by breaking tle
string at a delimiter, removing tlat delimiter, and placing tle remaining pieces into an
E$%1(#(C+-3#37<!!!107
array. Tlis allows you to tlen use an iterating loop to go over tle collection and do
various operations on tle data witlin.
But wlat il you need to do an operation tlat isn't allowed on a string? Ior
example, you want to do some aritlmetic on some numbers you read in lrom a text
lile, but text lrom liles is always read in as strings. Ruby ollers tle 1o_1 and 1o_1
metlods to allow |ust tlat:
humeJ1ca_s1J1hg = "1234"
humeJ1ca_s1J1hg + 6
" ! TypeEJJoJ
humeJ1ca_s1J1hg.1o_1 + 6.7
" 1240.7
It's important to remember tlat all data read lrom sockets, liles, and users will be
strings, il you plan on doing any sort ol matl witl tlis data, you must run it tlrougl
one ol tlese metlods lirst.
:2N')#3(FET3211+.,1
I mentioned regular expressions in passing earlier so tle appearance ol tle syntax
wouldn't conluse you il you were looking at Ruby examples elsewlere, now I'd like to
take some time to discuss wlat regular expressions are and low incredibly uselul tley
can be.
Iirstly, let me say tlat I am not going to give you a grand tour ol regular
expressions, nor will I bore you witl a biograply ol lis tlird cousin, Samuel del Iuega
IV. I will, lowever, provide some very rudimentary examples wlereby I can slow tle
usage ol tlese metlods and, lor tle special price ol nothing at all, I am providing an
appendix witl URLs ol pages to visit il you'd like to lind out more about tle dark cralt
ol regular expressions (it's in Appendix A). Aren't you excited? Let's proceed.
A regular expression (sometimes called a regexp or regex) is a string tlat
describes or matcles a set ol otler strings, according to a set ol syntax rules. Tleir
uses range lrom simple searcles to lull string translormation involving searcling,
replacing, and slilting ol data. Tley're a deligltlul addition to any programming
language, a uselul tool lor string manipulation, and make a delectable topping lor any
dessert.
Regular expressions lave a ricl syntax wlicl allows you to do a number ol
tlings, but lor tle sake ol brevity and sanity ol scope, I will use very simple regular
expressions. You will need to go and read an introduction to regular expressions to
lully and easily understand wlat is going on, but perlaps you can gatler wlat's going
on witlout it.
Let's begin by looking at tle simplest use ol regular expressions: matcling.
You can searcl witlin a string using regular expressions easily, let's say you were
wanting to see il a string was a substring ol anotler string. You could do tlis:
108!!!E$%1(#(C+-3#37<
ma1chmakeJ = "T`m a cowboy, baby!"
ma1chmakeJ =- /cow/
" 6
ma1chmakeJ.ma1ch{"cow")
" #<ha1ch0a1a.0x61d3120>
Using tle =- will return tle index ol tle lirst matcl ol tle pattern, but using tle ma1ch
metlod ol a string, you can get a ha1ch0a1a ob|ect wlicl gives you a number ol
options lor accessing tle matcles. Ior example:
my_message = "heo 1heJe!"
my_message.ma1ch{/{..){..)/).cap1uJes
"!!|"he", ""]
Using tle captures metlod, you can grab tle matcles lor eacl expression, tle 1o_a
metlod will oller you similar output but will also tag on tle lully matcled string. Tlat
was sort ol a silly example, so let's look at a more plausible example. Say you wanted
to grab tle text between two delimiters, lere is one way to do it:
h1m_eemeh1 = "<h1m>"
my_eemeh1_1ex1 = h1m_eemeh1.ma1ch{"{<){.*){>)").cap1uJes
pu1s my_eemeh1_1ex1|1]
"!!h1m
Il you're tle curious type, you can look in tle Ruby API Documentation lor more
inlormation on tle ha1ch0a1a class.
Il you'd like to kick out tle middle man and simply get an array ol matcles
back lrom tle metlod, you can use tle scah metlod:
my_message = "heo 1heJe!"
my_message.scah{/{..){..)/)
"!!||"he", ""], |"o ", "1h"], |"eJ", "e!"]]
Otler tlan tle dillerence in return type between tle two metlods, ma1ch and scah
also diller in "greediness." A "greedy" regular expression or metlod will matcl every
occurrence ol a pattern ratler tlan |ust matcling one. Tle scah metlod is greedy, it
will matcl every occurrence ol a pattern in a string (note: you can make ma1ch greedy
using a certain kind ol regular expression).
Anotler usage ol regular expressions is substitution, substitution using
regular expressions is very llexible il you use tle riglt combination ol potent regular
expressions. Again, because I do not discuss advanced regular expressions, tle true
uselulness ol tlis teclnique won't really register, but I lope tlat you will take a
serious look at regular expressions and use tlem to your advantage. To substitute
using a regular expression, you use tle sub or gsub metlod ol a string instance:
go_away = "hy hame 1s FJeak has1y!"
go_away.sub{"y", "o")
"!!ho hame 1s FJeak has1y!
E$%1(#(C+-3#37<!!!10
go_away.gsub{"y", "o")
"!!ho hame 1s FJeak has1o!
go_away.sub{"FJeak has1y", "FJed Johes")
"!!hy hame 1s FJed Johes!
Tle sub metlod will only replace tle lirst matcl lor tle pattern, but tle gsub metlod
is greedy (I'm sure tle g didn't give it away) and will replace every matcl. Again, tle
greediness ol eacl metlod can be gaged by tle usage ol certain regular expression
constructs. Tle more powerlul regular expressions you learn, tle more you can do
witl tlem, remember to cleck out Appendix A lor more inlormation.
Q67F\780F
Have you ever lound yoursell in tle deli, browsing tle lam and otler delectable meat
products, and tlen realized tlat you lelt your calendar in Bermuda? Tlat lappened to
me |ust last millenium, and let me tell you, I lelt vulnerable. No calendar means, no
days. No days means no niglts, wlicl means I could dic. Iortunately, Ruby provided
me witl a lairly uselul date and time library to use until I could get my lall brotler in
law's pet monkey's trainer's dog to mail my calendar back yesterday.
Tlere are tlree date and time classes in tle Ruby library: 0a1e, T1me, and
0a1eT1me. I leard a rumor tlat 0a1e and T1me looked it up around version l.4 and
got 0a1eT1me about 9 montls later, but tlen again, tlis source also told me tlat Rails
was a gentlemen's club lor Ruby programmers.
Q#$21
Tle lirst class I'd like to cover is 0a1e, tlis class simply exposes an interlace to store,
manipulate, and compare dates in a Ruby application.
myda1e = 0a1e.hew{1999, 6, 4) "!!1999-06-04
myda1ed = 0a1e.d{2451334) "!!1999-06-04
myda1eoJd = 0a1e.oJd1ha{1999, 155) "!!1999-06-04
myda1ecom = 0a1e.commeJc1a{1999, 22, 5) "!!1999-06-04
0a1e.d_1o_c1v1{2451334) "!!|1999,6,4]
0a1e.d_1o_c1v1{2451334) "!!|1999,6,4]
myda1ep = 0a1e.paJse{"1999-06-04") "!!1999-06-04
As you can see, creating a 0a1e instance is ratler simple in its literal lorm, simply call
new (or tle c1v1 metlod, tle two are synonyms), providing a date as tle lollowing
parameters. Tlis metlod uses tle date lorm we usually see, but 0a1e also supports
otler date lorms. Tle d metlod allows you to create a 0a1e instance based on a
Julian day number, tle oJd1ha metlod creates a 0a1e ob|ect based on a provided
Ordinal date, or a date created providing tle year and day number, commeJc1a
creates a 0a1e ob|ect lrom tle provided Commercial date, or a date created by
providing tle year, week number, and day number. Metlods are provided to convert
between tlese date lorms also (e.g., commeJc1a_1o_d, d_1o_c1v1, and so on).
110!!!E$%1(#(C+-3#37<
Tlese all work well enougl, but notice tle last example using tle paJse metlod, tlis
allows you to parse strings into 0a1e ob|ects. I lind tlis is tle most intuitive way ol
creating 0a1e ob|ects second to using tle hew metlod.
You can also test input witl various class metlods, and, once you lave a
0a1e ob|ect, get all sorts ol inlormation lrom it witl a lew instance metlods.
0a1e.va1d_d?{3829) "!!3829
0a1e.va1d_c1v1?{1999, 13, 43) "!!h1
myda1e.moh "!!6
myda1e.yday "!!155
myda1e.day "!!4
As you can see, you can test its validity in a certain lormat, and, using instance
metlods, convert between tle dillerent lormats. You can also get various
components ol tle date, sucl as tle year day (yday), montl (moh), and so on. You can
also compare and manipulate dates using standard operators.
da1e1 = 0a1e.hew{1985, 3, 18)
da1e2 = 0a1e.hew{1985, 5, 5)
da1e1 < da1e2 "!!1Jue
da1e1 == da1e2 "!!1ase
da1e3 = da1e1
da1e1 == da1e3 "!!1Jue
da1e1 << 3 "!!1984-12-18
da1e2 >> 5 "!!1985-10-05
As you can see, comparing dates is |ust like comparing a standard numerical value or
sometling similar, a date tlat comes belore anotler date is |udged to be "less tlan", a
date tlat comes alter is |udged to be "greater tlan." You can also use tle >> and <<
operator to add or subtract montls (see tle last two examples). Now tlat you lave a
lamiliarity witl tle 0a1e class, let's move on to tle T1me class.
7+H21
Tle T1me class is very similar in lunction to tle 0a1e class, except it concentrates on
times and timestamps ratler tlan simply dates. Mucl like tle 0a1e class, various
constructors are available.
J1gh1how = T1me.hew!!
"!!Suh Sep 10 21.36.15 Eas1eJh 0ay1gh1 T1me 2006
T1me.a1{934934932)
"!!Tue Aug 17 20.08.52 Eas1eJh 0ay1gh1 T1me 1999
T1me.oca{2000,"ah",1,20,15,1)
"!!Sa1 Jah 01 20.15.01 Eas1eJh S1ahdaJd T1me 2000
T1me.u1c{2006, 05, 21, 5, 13)
"!!Suh hay 21 05.13.00 bTC 2006
E$%1(#(C+-3#37<!!!111
T1me.paJse{"Tue Juh 13 14.15.01 Eas1eJh S1ahdaJd T1me 2005")
"!!Tue Juh 13 14.15.01 Eas1eJh 0ay1gh1 T1me 2006
As you can see, you can create a new T1me ob|ect tlat lolds tle values lor tle current
time and timezone by simply calling hew (or optionally, how, tley do tle same tling).
Il you require a certain time, you can use a1, wlicl operates on epocl time (i.e.,
seconds lrom January lst, l970), you can also use tle u1c and gm metlods to create
times based on tlose timeszones and tle provided parameters (or tle oca metlod
to use tle current local timezone). You can, |ust like 0a1e, use tle paJse metlod to
parse a timestamp into a T1me ob|ect.
Tle T1me class also ollers a lew instance metlods tlat allow you to get
portions ol tle ob|ect's value, convert tle value, and output tle value in otler lormats.
J1gh1how = T1me.hew
"!!Suh Sep 10 21.42.30 Eas1eJh 0ay1gh1 T1me 2006
J1gh1how.houJ
"!!21
J1gh1how.moh
"!!9
J1gh1how.day
"!!10
J1gh1how.1o_1
"!!1158543750
J1gh1how.1o_s
"!!Suh Sep 17 21.42.30 Eas1eJh 0ay1gh1 T1me 2006
As you can see, tle metlods lor Time are very similar to 0a1e witl regards to getting
portions ol tle value, and also notice tlat you can convert tle T1me ob|ec to otler
classes, sucl as a F1xhum.
Let's concentrate on one instance metlod lor a moment, tle s1J111me
metlod is a very uselul metlod tlat allows you output a timestamp in tle lormat ol
your cloice by providing you witl a lormatting interlace. Tlis interlace acts very, very
similarly to pJ1h11 in C++, it uses delimiters like 1 to indicate tle placement ol
values in tle output string. Here are a lew examples:
J1gh1how = T1me.how
J1gh1how.s1J111me{"m/d/Y")
"!09/10/2006
J1gh1how.s1J111me{"T.hp")
"!09.13Ph
J1gh1how.s1J111me{"The d1h o1 B 1h `y")
"!The 171h o1 Sep1embeJ 1h `06
J1gh1how.s1J111me{"x")
"!09/17/06
112!!!E$%1(#(C+-3#37<
Tle s1J111me metlod is one ol tle most complex in tle T1me module, cleck out tle
T1me class's documentation at lttp://www.ruby-doc.org/core/classes/Time.ltml il
you'd like more inlormation about s1J111me and wlat you can do witl it.
Q#$21(#,B(7+H21
Tle 0a1eT1me class combines tle previous two classes into one convenient yet sligltly
less ellicient class. Tle 0a1eT1me class is really |ust a subclass ol 0a1e witl some time
lunctionality slapped in tlere lor good measure, it's a line endeavour to be sure but not
really wortl tle time il you ask me. Even so, it las some interesting lunctionality.
J1gh1how = 0a1eT1me.how
"!2006-09-10T21.56.45-0400
may11me = 0a1eT1me.c1v1{2006, 5, 6)
"!2003-05-06T00.00.00Z
paJsed = 0a1eT1me.paJse{"2006-07-03T11.53.02-0400")
"!2006-07-03T11.53.02-0400
paJsed.houJ "!11
paJsed.day "!3
paJsed.yeaJ "!2006
As you can see it works very similarly to tle 0a1e class, you can construct using tle
various date lormats or parse a date/time string. Also notice tlat like tle 0a1e and
T1me classes, you can query various parts ol tle value inside tle ob|ect.
You may be scratcling your lead riglt now asking wlicl one you slould use
and wlen. Personally, I would never use 0a1eT1me, but ratler T1me or 0a1e il at all
possible. Sometimes tlis is unavoidable, but be aware tlat using |ust 0a1e or T1me in
lieu ol 0a1eT1me yieldds approximately 800/ better perlormance. Sometimes
perlormance, like size and Poland, does matter.
56958>A(6>Q(K:=L7?A:6L5=
Sometimes you simply don't want people to be able to see your data transparently, I
mean, maybe you've got tlis rasl tlat you don't want people to know about. Or
maybe tlere's sometling you |ust want to lorget, so you lasl it and never worry about
it again. Iorunately lor me...er, I mean you...Ruby comes stock witl a neat little lasl
library and las a gem tlat can be installed to oller cryptograply.
5#1"+,N
Tlink ol lasling as one-way encryption, lasles are encrypted strings tlat are derived
lrom a stream ol data. Typical uses include password verilication (i.e., you store tle
lasl in tle database, tlen test user input by lasling it and seeing il tle lasles matcl)
and lile verilication (i.e., two ol tle same lile slould lave tle same lasl). Ruby ollers
tle two most common lasl types, MD5 and SHA-l, as built-in modules.
E$%1(#(C+-3#37<!!!113
MD5 MD5 is tle most widely used cryptograplic lasl lunction around, it was
invented in l994 as a successor to MD4 by Ronald Rivest, a prolessor at MIT. It's
lallen out ol mainline use as a secure lasl lunction because ol vulnerabilities tlat
lave been lound, but it's still uselul lor matcling values and sucl. Ruby's MD5
lunctionality isn't quite as easy as sometling like PHP (i.e., md5{`youJ da1a`),), but
it's still usable and lriendly enougl.
Jequ1Je `d1ges1/md5`
md5 = 01ges1..h05.d1ges1{`T have a 11he ves1!`)
"!sXm{1J\371\353\027\367\235u!\266\001\262
md5 = 01ges1..h05.hexd1ges1{`T have a 11he ves1!`)
"!73586d28317219eb17179d7521b601b2
Tle MD5 class ollers two metlods lor getting a lasl digest, tle lirst is simply tle
d1ges1 metlod. Tlis returns a pretty unsale byte stream ol tle lasl digest, I say
unsale because you could not embed tlis in sometling like an XML or HTML (or
some databases) and expect it to belave properly. A better cloice lor tlese situations
would be tle hexd1ges1 metlod (second example), tlis runs tle results ol tle lasl
tlrougl a base64 lex algoritlm, wlicl is lancy talk lor a metlod tlat makes it more
lriendly.
SHA-1 Tle SHA-l lasl algoritlm is lar more secure tlan MD5, tlougl still not tle
most secure (i.e., exploits reportedly exist lor it), it slould work lor most situations. It
is widely used as a lasling algoritlm in many secure contexts, sucl as in packages
like TLS, SSL, PCP, SSH, and IPSec. Ruby ollers tle same interlace to tle SHA-l
algoritlm as it does tle MD5 algoritlm.
Jequ1Je `d1ges1/sha1`
sha1 = 01ges1..ShA1.d1ges1{`T have a 11he ves1!`)
"!\225J{{\233\025\236\273\344\003X\233\33 |...]
sha1 = 01ges1..ShA1.hexd1ges1{`T have a 11he ves1!`)
"!954a7b7b9b159ebbe403589bdaa81981003a21bc
As you can see, it lunctions exactly tle same as tle h05 class, except you get a stronger
lasl. Now let's get away lrom lasles and take a look at cryptograply.
K3IT$.N3#T"I
Ruby does not lave cryptograplic capabilities built-in, so you lave to resort to
installing a gem. I guess teclnically tlis isn't a Ruby built-in library, but it's important
enougl to warrant a slort mention. Tle tlird-party crypt library available at
lttp://crypt.rubylorge.org is a pure Ruby cryptograply library. You can install it by
issuing tle gem install crypt command to install its gem, look in Appendix A lor a link
on low to install and setup RubyCems il your Ruby installation doesn't lave tlem
already.
Tle crypt library ollers lour encryption algoritlms: Blowlisl, COST, IDEA,
and Ri|ndael. Iortunately, tle interlace lor eacl on is relatively tle same. Let's take a
look at an example using tle Blowlisl algoritlm lrom tleir documentation.
114!!!E$%1(#(C+-3#37<
Jequ1Je `cJyp1/bow11sh`
bow11sh = CJyp1..Bow11sh.hew{"A key up 1o 56 by1es ohg")
pa1hBock = "ABC01234"
ehcJyp1edBock = bow11sh.ehcJyp1_bock{pa1hBock)
"!\267Z\347\331-\344\211\250
decJyp1edBock = bow11sh.decJyp1_bock{ehcJyp1edBock)
"!ABC01234
Tlis is one ol tle easiest cryptograply libraries out tlere, simply leed it a key in tle
constructor, call tle ehcJyp1_bock metlod to encrypt tle data, and tlen
decJyp1_bock to decrypt it. Since tle developers went to great lengtls to keep tle
API basically tle same lor all tle algoritlms, you can simply subsitute tle otler
algoritlm names in place ol Blowlisl to get tlem working (i.e., put Ri|ndael in place
ol Blowlisl and it slould work |ust tle same). Tlere are otler restrictions on key
lengtl and sucl, along witl otler metlods and lunctions you can use. Cleck out
lttp://crypt.rubylorge.org/ to learn more.
;>87(7F978>A
Test Driven Development is tle new lotness, especially in tle Ruby development
world. I'm sure tle Ruby core team took tlis into account wlen tley built a unit
testing lramework into tle standard library ol tle language: Tes1..bh11. Ruby's
unit testing lramework is excellent (and las been made better and/or replaced and
improved by otler lrameworks) yet very simple to use.
Tle basic premise ol testing is to make sure tlat your code is belaving
correctly in as many contexts as you can simulate programmatically. Tlis miglt
sound stupid, but trust me: you'll catcl twice as many bugs using a unit test as you will
by |ust playing around witl your application because you know low it is supposed to
operate but tle computer doesn't. It las no "developer's discrimination" wlen it
comes to using your application. You know wlat I'm talking about, no one wants
tleir application to break, so tley unconsciously tip-toe around wlat miglt become a
bug. I do it all tle time. Tlat's wly I use testing.
Ruby's unit testing lramework provides a simple interlace lor perlorming
tests. Let's say you wanted to test your class tlat stores MAC addresses lor your
locally networked client applications.
cass hacAddJ
de1 1o_s
Je1uJh _mac_paJ1s.o1h{".")
ehd
de1 1h111a1ze{mac)
11 mac.ehg1h < 17
1a1 "hAC 1s 1oo shoJ1, make suJe coohs aJe 1h pace"
ehd
_mac_paJ1s = mac.sp11{`.`)
ehd
de1 |]{1hdex)
Je1uJh _mac_paJ1s|1hdex]
E$%1(#(C+-3#37<!!!115
ehd
ehd
Tlis simple class las tlree metlods: 1o_s, 1h1111a1ze, and an index metlod (|]).
Tle constructor, 1h111a1ze, takes a string witl a MAC address in it. Il it is tle
wrong lengtl, an exception is tlrown, otlerwise it's broken up on tle colons (part ol
tle standard MAC notation) and placed in an instance variable. Tle 1o_s metlod
|oins tlis array togetler witl colons and returns it. Tle index metlod (|]) will return
tle requested index lrom tle MAC address array (_mac_paJ1s). Now tlat we lave
sometling to work witl, let's build some tests.
Tests witl Test::Unit center around inleriting lrom tle Test::Unit::TestCase
lor eacl test case. So, il we were to create a test case lor our MAC address class, we
would do sometling like tle lollowing.
Jequ1Je `1es1/uh11`
cass Tes1hac < Tes1..bh11..Tes1Case
ehd
Simple enougl, riglt? Now tlat you lave a test case class, you need to lill in tests.
Tests could be written as a buncl ol ugly il statements, but unit testing lrameworks do
tleir best to get away lrom tlat by providing you witl assertions (i.e., wrappers lor
tlose conditional statements tlat look into tle lramework in a meaninglul way). Tle
lirst type ol assertion we'll look at are tle equality tests. Tlere are two equality
assertions: assert_equal and assert_not_equal. Let's create a couple ol tlose tests
now in tle same lile we created tle class.
Jequ1Je `1es1/uh11`
cass Tes1hac < Tes1..bh11..Tes1Case
de1 1es1_1os
asseJ1_equa{"FF.FF.FF.FF.FF.FF",
hacAddJ.hew{"FF.FF.FF.FF.FF.FF").1o_s)
asseJ1_ho1_equa{"FF.00.FF.00.FF.FF",
hacAddJ.hew{"FF.FF.FF.FF.FF.FF").1o_s)
ehd
ehd
We've basically created two tests. Tle lirst makes sure tlat il we give it a MAC
address, it will return it properly wlen using to_s. Tle second one does tle same
tling, but in an inverse conditional, we leed it a dillerent value to make sure tley're
not equal. Upon running tle tests, we slould lopelully see success.
Loaded su11e uh11_1es1
S1aJ1ed
.
F1h1shed 1h 0.0 secohds.
1 1es1s, 2 asseJ11ohs, 0 1a1uJes, 0 eJJoJs
And we do. Excellent. Now let's take a look at anotler type ol assertion: nil
assertions. Tlese assertions, assert_nil and assert_not_nil, do basically tle same
tling as il you did an assert_equal and tested lor nil. Let's create a test using
assert_not_nil.
116!!!E$%1(#(C+-3#37<
Jequ1Je `1es1/uh11`
cass Tes1hac < Tes1..bh11..Tes1Case
de1 1es1_1os
asseJ1_equa{"FF.FF.FF.FF.FF.FF",
hacAddJ.hew{"FF.FF.FF.FF.FF.FF").1o_s)
asseJ1_ho1_equa{"FF.00.FF.00.FF.FF",
hacAddJ.hew{"FF.FF.FF.FF.FF.FF").1o_s)
asseJ1_ho1_h1{hacAddJ.hew{"FF.AE.F0.06.05.33"))
ehd
ehd
Again, upon running tle tests, we slould lopelully see a successlul run witlout and
errors or lailures.
Loaded su11e uh11_1es1
S1aJ1ed
.
F1h1shed 1h 0.0 secohds.
1 1es1s, 3 asseJ11ohs, 0 1a1uJes, 0 eJJoJs
And we do. Creat! Now, let's look at one linal type ol assertion tlat deals witl
exceptions. Ruby's testing lramework allows you not only to test tle return value ol
units ol code, but also to test wletler tley raise exceptions or not. We told our class
to raise an exception il tle MAC address isn't tle riglt lengtl, so let's write a test to
test tlat.
Jequ1Je `1es1/uh11`
cass Tes1hac < Tes1..bh11..Tes1Case
de1 1es1_1os
asseJ1_equa{"FF.FF.FF.FF.FF.FF",
hacAddJ.hew{"FF.FF.FF.FF.FF.FF").1o_s)
asseJ1_ho1_equa{"FF.00.FF.00.FF.FF",
hacAddJ.hew{"FF.FF.FF.FF.FF.FF").1o_s)
asseJ1_ho1_h1{hacAddJ.hew{"FF.AE.F0.06.05.33"))
asseJ1_Ja1se Ruh11meEJJoJ do
hacAddJ.hew{"AA.FF.AA.FF.AA.FF.AA.FF.AA")
ehd
ehd
ehd
Now, il we run tlese tests again, we'll lopelully see anotler wonderlully successlul
run.
Loaded su11e uh11_1es1
S1aJ1ed
F
F1h1shed 1h 0.015 secohds.
1) Fa1uJe.
1es1_1os{Tes1hac) |1es121.Jb.27].
<Ruh11meEJJoJ> excep11oh expec1ed bu1 hohe was 1hJowh.
1 1es1s, 4 asseJ11ohs, 1 1a1uJes, 0 eJJoJs
Oops! Il you look at our constructor, we merely test il tle MAC address is too slort.
Let's switcl tlat < to a ! so tlat it catcles it wletler it's too slort or too long and try
tlese tests again.
E$%1(#(C+-3#37<!!!117
Loaded su11e uh11_1es1
S1aJ1ed
.
F1h1shed 1h 0.0 secohds.
1 1es1s, 4 asseJ11ohs, 0 1a1uJes, 0 eJJoJs
Creat! We've built a small test suite lor our class. Ol course, tlis is |ust one class in a
wlole application, and eacl class slould lave its own test suite. As test suites grow,
you'll inevitably want to break tlem into separate liles, since you wouldn't want to
keep all l,200 ol your test cases lor your breakdancing panda screen saver in one lile.
Iortunately, Test::Unit is smart enougl to pick up on numerous test liles being
included into one test run. Tlis means you could do sometling like tle lollowing
witlout any problems.
Jequ1Je `1es1/uh11`
Jequ1Je `pahda1es1`
Jequ1Je `bJeakdahce1es1`
Jequ1Je `bJeakdahc1hgpahda1es1`
Jequ1Je `somewha1peJ1pheJaeemeh1s1es1`
Jequ1Je `somewha1peJ1pheJaeemeh1sbes11J1ehdsuhces1es1`
I've |ust given you a basic rundown ol testing, I'm providing a list ol links in Appendix
A tlat can take you deeper into Test Driven Development and testing witl Ruby. Also
be sure to cleck out tle Test::Unit documentation at lttp://www.ruby-
doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.ltml to lind about otler
available assertions (tlere are a lew I don't cover lere because tley're not very
common).
118!!!E$%1(#(C+-3#37<
E$%1(#(C+-3#37<!!!11
120!!!E$%1(#(C+-3#37<
!!LF:?:678?>!!!
I lope you've en|oyed tlis |ourney tlrougl Ruby as mucl as I lave.
Please do cleck out my Rails book at lttp://www.rubyonrailsbook.com/ and my blog
at lttp://www.mrneiglborly.com/. Ieel lree to drop me an e-mail or comment tlere
witl any questions or comments.
En|oy your newlound avocation wlicl will lopelully turn in an occupation lor you
ratler tlan an exasperation over any sort ol snag or complication tlat you may
encounter in tle course ol your application ol tle principles in tlis publication.
Tlat rlymed a lot. Tlere's a reason tley call me Kill Masta Neiglborly. Io' rizzle.
Peace out,
!"#$%&'()*+",-
E$%1(#(C+-3#37<!!!121
122!!!E$%1(#(C+-3#37<
())*+,-./(
@+,*1(#,B($"2(@+*2
75F(:;<=(@6>A;6AF
Ruby Language main site lttp://www.ruby-lang.org/
Thc main sitc for thc Ruby languagc, gct downloads and information hcrc
RubyIorge lttp://www.rubylorge.org/
Looking for a Ruby library or application' Chanccs arc you can find it hcrc.
RubyCorner lttp://www.rubycorner.com/
Ruby blog aggrcgator, blogs arc addcd by thcir owncrs, so thcrc arc currcntly 200.
Ruby Central lttp://www.rubycentral.com/
David Black's cxccllcnt Ruby organization, you can find information and links hcrc.
PlanetRuby lttp://planetruby.0x42.net/
Anothcr Ruby blog aggrcgator, blogs arc sclcctcd by thc sitc owncr.
Q?K;0F>7678?>
RubyDoc lttp://www.rubymanual.org/
Points to a numbcr of Ruby documcntation sourccs, including thc Ruby API docs.
Wly's Poignant Cuide to Ruby lttp://www.poignantguide.net/ruby/
Anothcr Ruby book, if you found my writing too dull, pcrhaps this is morc your flavor.
RubyManual lttp://www.rubymanual.org/
PHP Manual stylc documcntation that allows uscr commcnts.
RubyCems Installation lttp://rubygems.org/read/clapter/3
Articlc on how to install and sctup RubyGcms
:FA;@6:(FDL:F998?>9
Regular Expressions Tutorial lttp://www.regular-expressions.inlo/
Thc tutorial I lcarncd from, it's not thc bcst (from what I hcar) but it workcd for mc.
Regular Expressions Library lttp://www.regexlib.com/
A largc library of uscr-submittcd rcgcxcn that do anything you can think of.
Wikipedia Article lttp://en.wikipedia.org/wiki/Regex
Thc Wikipcdia articlc for rcgular cxprcssions, as always, prctty uscful information.
C+,*1(#,:($"2(C+*2!!!123
;>8D(8>M?:0678?>
Tle Iilesystem lttp://www.unix.org.ua/orelly/networking/puis/cl05_0l.ltm
Exccllcnt sourcc for information on UNIX filcsystcms and thcir mctadata.
Linux Documentation Pro|ect lttp://www.tldp.org/
Thc sourcc for Linux documcntation.
Ceneral Linux command list lttp://www.linuxdevcenter.com/linux/cmd/
Hugc list of Linux commands, uscful for thosc mystcry commands or finding ncw oncs.
A Beginner's Tutorial lor Linux lttp://www.linux-tutorial.inlo/
Vcry basic information about Linux.
!F<(9F:P8KF9
Ruby Web Services l ttp://www.devx.com/enterprise/Article/28l0l
Grcat articlc that discusscs building wcb scrviccs using Ruby.
Soap4r Homepage lttp://dev.ctor.org/soap4r/
Thc official homcpagc of thc official SOAP clicnt for Ruby.
xmlrpc4r - XML-RPC lor Ruby lttp://www.lantasy-coders.de/ruby/xmlrpc4r/
Providcs an XML-RPC scrvcr in Ruby.
Q897:8<;7FQ(:;<=
DRb Rdocs lttp://www.ruby-doc.org/stdlib/libdoc/drb/rdoc/index.ltml
Thc official documcntation for DRb.
DRb Tutorial lttp://www.cladlowler.com/ruby/drb.ltml
Chad Fowlcr's cxccllcnt DRb tutorial.
Segment7 DRb Page lttp://segment7.net/pro|ects/ruby/drb/
Morc DRb information from Scgmcnt7.
Q676<69F9
Ollicial DBI website lttp://ruby-dbi.rubylorge.org/
Official homcpagc of thc Ruby DBI packagc
Ruby and tle mySQL package lttp://www.kitebird.com/articles/ruby-mysql.ltml
A tutorial on using thc mysql packagc and Ruby
ActiveRecord Docs lttp://api.rubyonrails.org/classes/ActiveRecord/Base.ltml
Thc official Ruby on Rails rdocs for ActivcRccord, go hcrc for morc info on how to usc it
;>87(7F978>A
ZenTest lttp://rubylorge.org/pro|ects/zentest/
Tcsting, on stcroids. If you gct borcd with Tcst::Unit, chcck this out.
124!!!C+,*1(#,:($"2(C+*2
Wly and How: Ruby (and Rails) Unit Testing
lttp://glu.ttono.us/articles/2005/l0/30/wly-and-low-ruby-and-rails-unit-testing
An cxccllcnt articlc by Kcvin Clark on unit tcsting with Ruby (and Rails).
C+,*1(#,:($"2(C+*2!!!125
())*+,-./0
5+N"(L23V.3H#,&2(:'-I
C+$"(K\K]]
Tlere is a lot ol talk tlese days about extending Ruby witl C and C++, and low tlose
extensions can greatly improve Ruby's perlormance. No package las done more to
promote tlis tlan RubyInline, a port ol Perl/Pytlon/wlatever else's inline C module.
Many developers question a mindset tlat says "Rewrite it in C" il a part ol an
application is too slow in tle current language, many developers lrom otler camps
leel tlat tlis unnecessary step is a weakness ol Ruby and tlat Ruby slould be laster
ratler tlan laving to use C/C++. I don't see it tlat way. I tlink Ruby's nature and tle
ability lor developers to be able to directly write C code inline is great. Telling
developers to rewrite it in C lor speed isn't any dillerent tlan wlat many language
interpreters do. Look at Pytlon's interpreter, anytling tlat needs any degree ol speed
las been rewritten in C ratler tlan solid Pytlon. It's a lact ol dynamic languages tlat
tley will occasionally need tlese speed boosts.
I'm not going to go into a luge spill about RubyInline or low to use it, it's too
big ol a sub|ect. Tlis is one ol tlose tlings tlat could warrant its own book il anyone
would write it. Tlere are so many applications lor tlis teclnology, and people are
beginning to take notice. I typically wouldn't write about sub|ects like tlis, since I am
not a C/C++ master (I lave experience, but I preler to en|oy programming ratler tlan
abuse mysell), but it's becoming an increasingly important sub|ect.
Using RubyInline is mucl easier tlan trying to write a C extension lrom
scratcl. Tle basic premise ol tle package is to allow developers to embed C/C++ code
directly into tle Ruby code lor tleir application, wlicl will tlen be compiled and
dynamically executed as tle application runs. Tle idea is tlat you prolile your
application using sometling like tle Ruby proliler and ligure out wlere your
bottlenecks are. Ior example, Pat Eyler wrote about rewriting parts ol a prime
number calculator in C, le used Ruby proliler to ligure out tlat a block ol code tlat le
was passing was causing a massive slow down. You slould tlen take tlis data and
ligure out wlat you could rewrite in C, and only rcwritc that (tlere's no reason to go
126!!!=+D"(F23>.35#,&2(6'-7;+$"(GHGII
nuts and rewrite a lot ol your application in C |ust to get some lalse illusion ol
perlomance at tle cost ol maintainability or readability). In most cases, rewriting
tlese little parts make your perlormance numbers an order ol magnitude better. It's
really a neat concept, and incredibly lelplul lor adding a speed to your application.
Now, let's take a look at low tlis plays out wlen you actually get to using it.
Install RubyInline using tle installation instructions on ZenSpider's RubyInline page
at lttp://www.zenspider.com/ZSS/Products/RubyInline/, and let's take a look at an
example lrom tle lome page.
Jequ1Je `1h1he`
cass hyTes1
1h1he{.C) do ]bu1deJ]
bu1deJ.1hcude `<1os1Jeam>`
bu1deJ.add_comp1e_1ags `-x c++`, `-s1dc++`
bu1deJ.c `
vo1d heo{1h1 1) {
wh1e {1-- > 0) {
s1d..cou1 << "heo" << s1d..ehd,
}
}`
ehd
ehd
1 = hyTes1.hew{)
1.heo{3)
Tle above code will print "lello" tlree times. Tlis is, ol course, a very contrived
example, but I put it lere to lirst explain low RubyInline works. As you can see, you
simply open a block witl tle 1h1he metlod to begin tle process, tle .C parameter
isn't required il you are using C, since tlat is wlat RubyInline is designed lor. Next,
you can see tlat 1os1Jeam is included using tle 1hcude metlod, notice you can also
add compile llags to tle command line using tle add_comp1e_1ags metlod. Tlis is
uselul il tlere are libraries you need to add (like s1dc++ in tlis example). Next, use
tle c metlod and provide a string ol C code. Tle code provided lere will be compiled
and tlen can be called |ust like a Ruby metlod (wlicl is demonstrated in our call to
heo in tle main loop).
I'll leave you to your imagination to see low you can use tlis library to
improve perlormance in your own applications. Poke around tle ruby-talk mailing
list and Coogle to get some good ideas, tle RubyInline lomepage las more
inlormation, too, il you're interested in putting tlis library to work.
@8>R9
RubyInline lomepage
http:www.zcnspidcr.comZSSProductsRubyInlinc
Pat Eyler's RubyInline experience
http:on-ruby.blogspot.com200607rubyinlinc-making-making-things-fastcr.html
=+D"(F23>.35#,&2(6'-7;+$"(GHGII!!!127
128!!!=+D"(F23>.35#,&2(6'-7;+$"(GHGII
!"#$%&'()*%"+,-.'/
Access Control....................01
ActiveRecord...................... 23
ARCV................................. 45
Aritlmetic Operators......... 56
Array.................................. 57
assertions......................... 561
Attributes........................... 04
Bignum................................ 2
Blocks................................ 71
case Statement................... 31
class................................... 00
Class Scoped Ob|ects......... 02
classes................................ 00
Collections......................... 55
Conditional Link Operators38
Conditional Loops ............. 96
Conditional Operators........38
Conditionals....................... 39
Cryptograply................... 568
Database............................ 20
Date................................. 567
Date/Time........................567
DateTime......................... 569
DBI.................................... 20
Delining Classes.................09
Delining Metlods.............. 79
Distributed Ruby................27
DRb................................... 27
ensure clause...................... 94
Environment variables....... 46
Escape Sequences................ 1
Exceptions......................... 99
exec.................................... 69
Iile..................................... 85
Iile Access Modes.............. 89
Iilesystem Interaction........ 85
Iixnum................................. 2
lor loop............................... 97
ITP.................................... 14
Hasl...................................54
Hasling........................... 569
HTTP Networking............. 13
il statement........................ 39
IMAP..................................12
Implicit Block Usage .........07
INI liles.............................. 47
Installing on Linux............... 3
Installing on Mac OS X........ 0
Installing on Windows .........0
Interpreted .......................... 7
Interpreted language............ 7
IO.popen............................ 69
Iterating Loops................... 95
Loops................................. 96
Matsumoto, Yukiliro........... 5
MD5.................................568
Metlods............................. 73
Modules............................. 35
Multi-Paradigm ................... 7
MySQL.............................. 20
Networking........................ 16
Numbers.............................. 2
ob|ect-oriented language...... 8
OLE Automation................48
Open Source........................ 5
ORM (Ob|ect Relational
Mapping)........................... 23
POP................................... 12
popen................................. 69
PostgreSQL....................... 20
Proc Ob|ects....................... 71
Processes............................69
puts...................................... 4
Range................................. 55
relerences........................... 76
Registry.............................. 43
registry type constants........49
Regular Expressions........ 566
rescue block........................98
Rescue Statement Modilier 94
Retry.................................. 94
RubyInline....................... 553
Separating code into liles... 30
SHA-l ............................. 568
sleep................................... 88
SMTP................................. 11
SOAP................................. 25
soap4r................................ 25
Socket................................ 16
SQLite................................20
Statement Modiliers .......... 34
String Manipulation........... 24
Strings................................. 4
system................................ 69
TCPServer.......................... 15
TCPSocket......................... 17
Ternary Operator............... 34
Test Driven Development.564
Test::Unit......................... 564
tlread................................. 89
Tlrow and Catcl................ 92
Time................................. 560
Types in Ruby....................... 4
Unit testing...................... 564
Unless................................ 34
until loop............................ 96
Variables............................ 52
Web Services......................26
WEBrick............................ 13
wlile loop........................... 96
Win32................................ 47
Windows............................ 47
Windows API..................... 47
WMI.................................. 41
XML-RPC.......................... 26
Yukiliro "Matz" Matsumoto5
Net::HTTP.........................19

You might also like