Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
9795 lines (6007 sloc) 385 KB
--- !whytheluckystiff.net,2003/^book
title: Why's (Poignant) Guide to Ruby
author: why the lucky stiff
terms: >
bq(#terms). The text of this book and the encompassing web matter are released under the wholy sympathetic
"Attribution-ShareAlike":http://creativecommons.org/licenses/by-sa/2.0/
license, part of the brilliant family of licenses produced
by the "Creative Commons":http://creativecommons.org/.
deed: |
Attribution-ShareAlike 2.0
You are free:
* to copy, distribute, display, and perform the work
* to make derivative works
* to make commercial use of the work
Under the following conditions:
*Attribution*. You must give the original author credit.
*Share Alike*. If you alter, transform, or build upon this work, you may distribute the
resulting work only under a license identical to this one.
* For any reuse or distribution, you must make clear to others the license terms of this work.
* Any of these conditions can be waived if you get permission from the author.
Your fair use and other rights are in no way affected by the above.
This is a human-readable summary of the Legal Code (the full license).
image: !^img poignant.guide.png
teaser:
- Read This Paragraph: >
At my local Barnes and Noble, there is a huge wall of Java books just waiting
to tip over and crush me one day. And one day it will. At the rate things are going,
one day that bookcase will be tall enough to crush us all. It might even loop the world
several times, crushing previous editions of the same Java books over and over again.
- And This Paragraph Too: >
This is just a small Ruby book. It won't crush you. It's light as a
feather (because I haven't finished it yet-- hehe). And there's a reason this book
will stay light: because Ruby is simple to learn.
- But Don't Read This One!: >
_Why's (Poignant) Guide to Ruby_ is released under the
"Attribution-ShareAlike":http://creativecommons.org/licenses/by-sa/2.0/
License. So, yes, please distribute it and print it and read it
leisurely in your housecoat. In fact, there will be a contest at the
end of the book for Best Housecoat. It's a coveted award and you should
feel honored to even read about it! (Especially if you are reading about
it in your soon-to-be-prize-winning housecoat.)
- Now Back to Your Regularly Scheduled Paragraph: >
I'll try not to feel utterly rejected if this book doesn't capture
your fancy. I might experience a few long days of emptiness, accompanied
with profuse weeping, but other than that I'll be fine. It's at least
comforting to know that the following fine texts cover my topic and will
doubtless fill you with Ruby lust:
* "Learning to Program":http://pine.fm/LearnToProgram/
** A very basic, ground-level tutorial for the beginner to Ruby. By Chris Pine.
* "Programming Ruby":http://www.whytheluckystiff.net/ruby/pickaxe/
** The original tome and complete reference for Ruby. By Dave Thomas and Andy Hunt
(also known as _The Pickaxe_)
* "A Little Ruby, A Lot of Objects":http://www.visibleworkings.com/little-ruby/
** Lovely little in-progress work to teach Ruby purely with simple code examples.
By Brian Marick.
* "Ruby User's Guide":http://www.ruby-doc.org/docs/UsersGuide/rg/
** A guide to learning Ruby (with code examples). By Matz, the creator of
Ruby. Translated into English.
* "Ruby Book List":http://www.rubygarden.org/ruby?RubyBookList
** A current list of printed material covering Ruby.
* "Ruby-Lang.org":http://www.ruby-lang.org/
** The official home page for the Ruby language.
Now, if you can't seem to find the contents link on the left-hand side of the page,
then here's a link to the "first page":chapter-1.html
of the (Poignant) Guide. If you want to venture onto my other projects, go
"whytheluckystiff.net":http://www.whytheluckystiff.net.
Welcome to the pirate radio of technical manuals.
chapters:
- About this Book:
- >
!i/about.the.poignant.guide-1.gif(What a fantastic voyage!)!
!i/about.the.poignant.guide-2.jpg(Small infants can do it.)!
!i/about.the.poignant.guide-3.gif(The thoughtful reader.)!
!i/about.the.poignant.guide-4.gif(Meep.)!
- Kon'nichi wa, Ruby:
- Opening This Book: >
Pretend that you've opened this book (although you probably _have_ opened this book),
just to find a huge onion right in the middle crease of the book. (The manufacturer of the book
has included the onion at my request.)
So you're like, "Wow, this book comes with an onion!" (Even if you don't particularly like onions,
I'm sure you can appreciate the logistics of shipping any sort of produce discreetly inside of an alleged
programming manual.)
Then you ask yourself, "Wait a minute. I thought this was a book on Ruby, the incredible new
programming language from Japan. And although I can appreciate the logistics of shipping any sort of
produce discreetly inside of an alleged programming manual: Why an onion? What am I supposed to do
with it?"
No. Please don't puzzle over it. You don't need to do anything with the onion. Set the onion aside
and let _it_ do something with _you_.
I'll be straight with you. I want you to cry. To weep. To whimper sweetly. This book is a *poignant*
guide to Ruby. That means code so beautiful that tears are shed. That means gallant tales and somber truths
that have you waking up the next morning in the arms of this book. Hugging it tightly to you all the day long.
If necessary, fashion a makeshift hip holster for _Why's (Poignant) Guide to Ruby_, so you can always have this
book's tender companionship.
You really must sob once. Or at least sniffle. And if not, then the onion will make it all happen for you.
- !^sidebar
What I'm Going to Do With the Massive Proceeds from this Book: >
Anyone who's written a book can tell you how easily an author is distracted by visions of grandeur.
In my experience, I stop twice for each paragraph, and four times for each panel of a comic, just to
envision the wealth and prosperity that this book will procure for my lifestyle. I fear that the writing
of this book will halt altogether to make way for the armada of SUVs and luxury towne cars that are
blazing away in my head.
Rather than stop my production of the (Poignant) Guide, I've reserved this space as a safety zone
for pouring my empty and vain wishes.
Today I was at this Italian restaraunt, Granado's, and I was paying my bill. Happened to notice (under
glass) a bottle of balsamic vinegar going for $150. Fairly small. I could conceal it in my
palm. Aged twenty-two years.
I've spent a lot of time thinking about that bottle. It is often an accessory in some of these
obsessive fantasies. In one fantasy, I walk into the restaraunt, toss a stack of greenery on the counter
and earnestly say to the cashier, "Quick! I have an important salad to make!"
In another, related fantasy, I am throwing away lettuce. Such roughage isn't befitting of my new
vinegar. No, I will have come to a point where the fame and the aristocracy will have corrupted me
to my core. My new lettuce will be cash. Cold, hard cash, Mrs. Price.
Soon, I will be expending hundreds for a block of myzithra cheese.
My imaginations have now gone beyond posessions, though. Certainly, I have thought through my
acquisition of grecian urns, motorcades, airlines, pyramids, dinosaur bones. Occassionally I'll
see wind-tossed cities on the news and I'll jot down on my shopping list: _Hurricane_.
But, now I'm seeing a larger goal. Simply put: what if I amassed such a fortune that the mints
couldn't print enough to keep up with my demand? So, everyone else would be forced to use Monopoly money as
actual currency. And you would have to win in Monopoly to keep food on the table. These would be
some seriously tense games. I mean you go to mortgage St. James Place and your kids start crying.
In addition, I think you'll begin to see the end of those who choose to use the Free Parking square
as "the underground coffers":http://groups.yahoo.com/group/monopoly/message/37 for city funds.
You've got to hand it to fun money, though. Fake money rules. You can get your hands on it
so quickly. For a moment, it seems like you're crazy rich. When I was a kid, I got with some
of the neighborhood kids and we built this little Tijuana on our street. We made our own pesos and
wore sombreros and everything!
One kid was selling hot tamales for two pesos each. _Two pesos!_ Did this kid know that the money was fake?
Was he out of his mind? Who invited this kid? Didn't he know this wasn't really Tijuana? Maybe he was really from Tijuana!
Maybe these were _real_ pesos! Let's go make more _real_ pesos!
I think we even had a tavern where you could get totally hammered off Kool-Aid. There's nothing like
a bunch of kids stumbling around, mumbling incoherently with punchy red clown lips.
- The Dog Story: >
So try this first bit of poignancy on for size:
One day I was walking down one of those busy roads covered with car dealerships (this was shortly after my wedding
was called off) and I found an orphaned dog on the road. A wooly, black dog with greenish red eyes. I was kind
of feeling like an orphan myself, so I took a couple balloons that were tied to a pole at the dealership
and I relocated them to the dog's collar. Then, I decided he would be my dog. I named him Bigelow.
We set off to get some Milkbones for Bigelow and, afterwards, head over to my place, where we could sit in
recliners and listen to Gorky's Zygotic Mynci. Oh, and we'd also need to stop by a thrift store and get
Bigelow his own recliner.
But Bigelow hadn't accepted me as his master. So five minutes later, the stupid dog took a different
crosswalk than I did and I never caught up. So whereas he had previously only been lost once, he was now lost
twice. I slowed my pace towards the life of Milkbones and an extra recliner. I had a dog for five minutes.
Stupid Benedict Arnold of a dog. I sat on a city bench and threw pinecones at a statue of three sheep
crossing a bridge. After that, I wept for hours. The tears just came. Now there's a little something
poignant to get you started.
I wonder where he went with all those balloons. That crazy dog must have looked like a party with
legs.
It wasn't much later that I pulled my own Bigelow. I printed out a bunch of pages on Ruby. Articles
found around the Web. I scanned through them on a train ride home one day. I flipped through them for
five minutes and then gave up. Not impressed.
I sat, staring out the window at the world, a life-sized blender mixing graffiti and iron
smelts before my eyes. _This world's too big for such a a little language_, I thought.
_Poor little thing doesn't stand a chance. Doesn't have legs to stand on. Doesn't have
arms to swim._
And yet, there I was. One little man on a flimsy little train (and I even still
had a baby tooth to lose at the time) out of billions of people living on a floating
blue rock. How can I knock Ruby? Who's to say that I'm not going to happen to
choke on my cell phone and die later that evening. Why's dead, Ruby lives on.
The gravestone:
bq. What's in his trachea?
Oh, look, a Nokia!
Just my luck. Finally get to have a good, long sleep underground, only to be constantly disturbed by
_Pachelbel's Canon_ going off in my stomach.
- The Red Sun Rises: >
So, now you're wondering why I changed my mind about Ruby. The quick answer is:
we clicked.
Like when you meet Somebody in college and they look like somebody who
used to hit you in the face with paintbrushes when you were a kid. And so, impulsively,
you conclude that this new Somebody is likely a non-friend. You wince at their
hair. You hang up phones loudly during crucial moments in their anecdotes. You use
your pogo stick right there where they are trying to walk!
Six months later, somehow, you and Somebody are sitting at a fountain having a perfectly good
chat. Their face doesn't look so much like that childhood nemesis. You've met
the Good Twin. You clicked.
So whereas I should probably be pounding your teeth in with hype about Ruby and the
tightly-knit cadre of pertinent ancronyms that accompany it everywhere
(whetting the collective whistles of
your bosses and their bosses' bosses), instead I will just let you coast. I'll let
you freefall through some code, interjecting occassionally with my own heartfelt
experiences. It'll be quite easy, quite natural.
I should offer you some sort of motivation, though. So, Smotchkkiss, I'm going to give my three
best reasons to learn Ruby and be done with it.
# *Brain health.* <p>Vitamin R. Goes straight to the head. Ruby will
teach you to _express_ your ideas through a computer. You will be writing
stories for a machine.</p> <p>Creative skills, people. Deduction. Reason. Nodding intelligently.
The language will become a tool for you to better connect your mind to the world.
I've noticed that many experienced users of Ruby seem to be clear thinkers and
objective. (In contrast to: heavily biased and coarse.)</p>
# *One man on one island.* <p>Ruby was born in Japan. Which is freaky. Japan is not known
for its software. And since programming languages are largely written in English, who
would suspect a language to come from Japan?</p> <p>And yet, here we have Ruby. Against the odds,
Yukihiro Matsumoto created Ruby on February 24, 1993. For the past ten years, he has steadily
brought Ruby to a global audience. It's triumphant and noble and all that. Support diversity.
Help us tilt the earth just a bit.</p>
# *Free.* <p>Using Ruby costs nothing. The code to Ruby itself is open for all of the world
to inhale/exhale. Heck, this book is free. It's all part of a great, big giveaway that should
have some big hitch to it.</p> <p>You'd think we'd make you buy vacuums or timeshare or fake Monets.
You'd think there'd be a 90 minute presentation where the owner of the company comes out at
the end and knuckles you into sealing the deal.</p> <p>Nope, free.</p>
With that, it's time for the book to begin. You can now get out your highlighter and start dragging
it along each captivating word from this sentence on. I think I have enough hairspray and
funny money on my person to keep me sustained until the final page.
- How Books Start: >
Now, if you ever have read a book, you know that no book can properly start without an exorbitant
amount of synergy. Yes, synergy. Maybe you didn't know this. Synergy means that you and I are
supposed to cooperate to make this a great reading experience.
We start off the book by getting
along well in the Introduction. This togetherness, this *synergy*, propels us through the book,
with me guiding you on your way. You give me a reassuring nod or snicker to indicate your progress.
I'm Peter Pan holding your hand. Come on, Wendy! Second star to the right and on till morning.
One problem here. I don't get along well with people. I don't hold hands very well.
Any of my staff will tell you. At the Opening Ceremonies of This Book (a catered event
with stadium seating), I discovered that the cucumber sandwiches weren't served in tea towels.
As a result, the butter hadn't set with the cucumbers right... Anyways, I made a big scene and
set fire to some of the advertising trucks outside. I smashed this spotlight to pieces and
so on. I had this loud maniacal laughing thing going on deep into that night. It was a real mess.
But, since I don't get along well with people, I hadn't invited anyone but myself to the Opening
Ceremonies of This Book. So it wasn't really that embarassing. I kept it under wraps and no
one found out about the whole ordeal.
So you've got to know that *synergy* doesn't actually mean *synergy* in this book. I can't do normal *synergy*.
No, in this book, *synergy* means *cartoon foxes*. What I'm saying is: this book will be
starting off with an exorbitant amount of *cartoon foxes*.
And I will be counting on you to turn them into *synergy*.
- A Quick (and Hopefully Painless) Ride Through Ruby (with Cartoon Foxes):
- >
!i/the.foxes-1.png(The foxes show up.)!
Yeah, these are the two. My asthma's kickin in so I've got to go take a puff of
medicated air just now. Be with you in a moment.
!i/the.foxes-2.png(Foxes in boxes.)!
I'm told that this chapter is best accompanied by a rag. Something you can mop your
face with as the sweat pours off your face.
Indeed, we'll be racing through the whole language. Like striking every match in a box
as quickly as can be done.
- Language and I MEAN Language: >
!i/the.foxes-3.png(Our friends, those two helpless foxies, finally realize
the gravity of their predicament.)!
My conscience won't let me call Ruby a _computer_ language. That would imply that the
language works primarily on the computer's terms. That the language is designed to accomodate
the computer, first and foremost. That therefore, we, the coders, are foreigners, seeking citizenship in the
computer's locale. It's the computer's language and we are translators for the world.
But what do you call the language when your brain begins to think in that language? When you
start to use the language's own words and colloquialisms to express yourself. Say, the computer
can't do that. How can it be the computer's language? It is ours, we speak it natively!
We can no longer truthfully call it a _computer_ language. It is _coderspeak_. It is the language
of our thoughts.
*Read the following aloud to yourself.*
bq. @5.times { print "Odelay!" }@
In English sentences, punctuation (such as periods, exclamations, parentheses) are silent. Punctuation
adds meaning to words, helps give cues as to what the author intended by a sentence. So let's read the
above as: _Five times print "Odelay!"._
Which is exactly what this small Ruby program does. Beck's
"mutated Spanish":http://www.whiskeyclone.net/ghost/L/lordonlyknows.html exclamation will print
five times on the computer screen.
*Read the following aloud to yourself.*
bq. @exit unless "restaurant".include? "aura"@
Here we're doing a basic reality check. Our program will *exit* (the program will end) *unless*
the word *restaurant* contains (or *includes*) the word *aura*. Again, in English: _Exit unless the word
restaurant includes the word aura._
Ever seen a programming language use question marks so effectively? Ruby uses some punctuation,
such as exclamations and question marks, to enhance readability of the code. We're asking a question
in the above code, so why not make that apparent?
*Read the following aloud to yourself.*
bq. @['toast', 'cheese', 'wine'].each { |food| print food.capitalize }@
While this bit of code is less readable and sentence-like than the previous examples, I'd still encourage
you to read it aloud. While Ruby may sometimes read like English, it sometimes reads as a shorter
English. Fully translated into English, you might read the above as: _With the words 'toast', 'cheese',
and 'wine': take each food and print it capitalized._
The computer then courteously responds: @Toast@, @Cheese@ and @Wine@.
At this point, you're probably wondering how these words actually fit together. Smotchkkiss is wondering
what the dots and brackets mean. I'm going to discuss the various _parts of speech_ next.
All you need to know thus far is that Ruby is basically built from sentences. They aren't exactly English
sentences. They are short collections of words and punctuation which encompass a single thought. These
sentences can form books. They can form pages. They can form entire novels, when strung together.
Novels that can be read by humans, but also by computers.
- !^sidebar
Concerning Commercial Uses of the (Poignant) Guide: >
This book is released under a Creative Commons license which allows unlimited commercial
use of this text. Basically, this means you can sell all these bootleg copies of my book
and keep the revenues for yourself. I trust my readers (and the world around them) to rip me off.
To put out some crappy Xerox edition with that time-tested clipart of praying hands on the cover.
Guys, the lawsuits just ain't worth the headache. So I'm just going to straight up endorse
authorized piracy, folks. Anybody who wants to read the book should be able to read it.
Anybody who wants to market the book or come up with special editions, I'm flattered.
Why would I want the $$$? IGNORE ALL OTHER SIDEBARS: I've lost the will to be a rich slob.
Sounds inhuman, but I like my little black-and-white television. Also my hanging plastic flower lamp.
I don't want to be a career writer. Cash isn't going inspire me. Pointless.
So, if money means nothing to the lucky stiff, why rip me off when you could co-opt shady
business practices to literally crush my psyche and leave me wheezing in some sooty iron lung?
Oh, and the irony of using my own works against me! Die, Poignant Boy!
To give you an idea of what I mean, here are a few underhanded concepts that could seriously
kill my willpower and force me to reconsider things like existence.
*IDEA ONE: BIG TOBACCO*
Buy a cigarette company. Use my cartoon foxes to fuel an aggressive ad campaign.
Here's a billboard for starters:
!i/the.foxes-0a.png(Addiction is like Pokemon!)!
Make it obvious that you're targeting children and the asthmatic. Then, once you've got
everyone going, have the *truth* people do an expose on me and my farm of inky foxes.
bq. *Sensible Hipster Standing on Curb in Urban Wilderness*: He calls himself the lucky stiff.
bq. (Pulls aside curtain to reveal grey corpse on a gurney.)
bq. *Hipster*: Some stiffs ain't so lucky.
bq. (Erratic zoom in. Superimposed cartoon foxes for subliminal Willy Wonka mind trip.)
Yo. Why you gotta dis Big Smokies like dat, Holmes?
*IDEA TWO: HEY, FIRING SQUAD*
Like I said, start selling copies of my book, but corrupt the text. These altered copies
would contain numerous blatant (and libelous) references to government agencies, such as the
U.S. Marshals and the Pentagon. You could make me look like a complete traitor. Like I have
all these plans to, you know, kill certain less desirable members of the U.S. Marshals or
the Pentagon.
Not that there are any less desirable members of the U.S. Marshals or the Pentagon. Yeah,
I didn't mean it like that.
Oh, crap.
Oh, crap. Oh, crap. Oh, crap.
Turn off the lights. Get down.
*IDEA THREE: BILLBOARDS, PART II*
How about making fun of asthmatics directly?
!i/the.foxes-0b.png(Call it a puffer! ROFL!)!
*IDEA FOUR: ALEC BALDWIN*
Adapt the book into a movie. And since, you know, I'm a character in this book,
you could get someone like Alec Baldwin to play me. Someone who's at a real lowpoint
in his career.
You could make it seem like I did tons of drugs. Like I was insane to work with.
Like I kept firing people and locking them in the scooter room and making them
wear outfits made of bread. Yeah, like I could actually be _baking_ people into
the outfits.
You could have this huge mold that I strap people into. Then, I pour all the dough
on them and actually bake them until the bread has risen and they've almost died.
And when the television crews come and I'm on Good Morning America, they'll ask,
"So, how many people have you employed in the production of your book?" And I'd
respond, "A baker's dozen!" and erupt into that loud maniacal laughing that would
force audience members to cup their hands over their ears.
Of course, in the throes of my insanity, I would declare war on the world. The
bread people would put up quite a fight. Until the U.S. Marshals (or the Pentagon)
engineer a giant robotic monkey brain (played by Burt Lancaster) to come after me.
Here's where you'll make me look completely lame. Not only will I sacrifice all of
the bread people (the Starchtroopers) to save myself, not only will I surrender to
the great monkey brain like a coward, but when I narrowly escape, I'll yell at the
audience. Screaming insistently that it's _MY_ movie and no one should see it any
more, I'll rip the screen in half and the film projector will spin with its reel
flapping in defeat. And that will be the end of the movie. People will be _so_
pissed.
Now, I've got to thinking. See, and actually, Alec Baldwin did a decent voiceover
in _The Royal Tenenbaums_. His career might be okay. You might not want to use him.
He might not do it.
Tell ya what. I'll play the part. I've made a career out of lowpoints.
- The Parts of Speech: >
Just like the white stripe down a skunk's back and the winding, white train of a bride, many of
Ruby's parts of speech have visual cues to help you identify them. Punctuation and capitalization
will help your brain to see bits of code and feel intense recognition.
Your mind will frequently yell _Hey, I know that guy!_ You'll also be able to name-drop in
conversations with other Rubyists.
Try to focus on the look of each of these parts of speech. The rest of the book will
detail the specifics. I give short descriptions for each part of speech, but you don't
have to understand the explanation. By the end of this chapter, you should be
able to recognize every part of a Ruby program.
h3. Variables
Any plain, lowercase word is a variable in ruby. Variables may consist of
letters, digits and underscores.
bq. @x@, @y@, @banana2@ or @phone_a_quail@ are examples.
Variables are like nicknames. Remember when everyone used to call you Stinky Pete?
People would say, "Get over here, Stinky Pete!" And everyone miraculously knew that
Stinky Pete was you.
With variables, you give a nickname to something you use frequently. For instance, let's
say you run an orphanage. It's a mean orphanage. And whenever Daddy Warbucks comes to buy
more kids, we insist that he pay us *one-hundred twenty-one dollars and eight cents* for the
kid's teddy bear, which the kid has become attached to over in the darker moments of living in
such nightmarish custody.
bq. @teddy_bear_fee = 121.08@
Later, when you ring him up at the cash register (a really souped-up cash register which runs
Ruby!), you'll need to add together all his charges into a *total*.
bq. @total = orphan_fee + teddy_bear_fee + gratuity@
Those variable nicknames sure help. And in the seedy underground of child sales, any help is
appreciated I'm sure.
!i/the.foxes-4a.png(They mock my examples.)!
h3. Numbers
The most basic type of number is an _integer_, a *series of digits* which can start
with a *plus or minus sign*.
bq. @1@, @23@, and @-10000@ are examples.
Commas are not allowed in numbers, but underscores are. So if you feel the need to
mark your thousands so the numbers are more readable, use an underscore.
bq. @population = 12_000_000_000@
Decimal numbers are called _floats_ in Ruby. Floats consist of numbers with *a decimal
place* or *scientific notation*.
bq. @3.14@, @-808.08@ and @12.043e-04@ are examples.
h3. Strings
Strings are any sort of characters (letters, digits, punctuation) surrounded by
quotes. Both single and double *quotes* are used to create strings.
bq. @"sealab"@, @'2021'@, or @"These cartoons are hilarious!"@
are examples.
When you enclose characters in quotes, they are stored together as a single string.
Think of a reporter who is jotting down the mouthnoises of a rambling celebrity. "I'm a lot
wiser," says Avril Lavigne. "Now I know what the business is like -- what you have to do and
how to work it."
<pre>
avril_quote = "I'm a lot wiser. Now I know
what the business is like -- what you have
to do and how to work it."
</pre>
So, just as we stored a number in the *teddy_bear_fee* variable, now we're storing a collection
of characters (a string) in the *avril_quote* variable. The reporter sends this quote
to the printers, who just happen to use Ruby to operate their printing press.
<pre>
<setup>
oprah_quote = "O"
avril_quote = "A"
ashlee_simpson_debacle = "D"
</setup>
print oprah_quote
print avril_quote
print ashlee_simpson_debacle
</pre>
!i/the.foxes-4b.png(They desire to be in my examples.)!
h3. Symbols
Symbols are words that look just like variables. Again, they may contain letters,
digits, or underscores. But they *start with a colon*.
bq. @:a@, @:b@, or @:ponce_de_leon@ are examples.
Symbols are lightweight strings. Usually, symbols are used in situations where you need a string
but you won't be printing it to the screen.
You could say a symbol is a bit easier on the computer. It's like an antacid. The colon indicates
the bubbles trickling up from your computer's stomach as it digests the symbol. Ah. Sweet, sweet
relief.
!i/the.foxes-4c.png(Chunky bacon!!)!
h3. Constants
Constants are words like variables, but constants are *capitalized*. If variables are the
nouns of Ruby, then think of constants as the proper nouns.
bq. @Time@, @Array@ or @Bunny_Lake_is_Missing@ are examples.
In English, proper nouns are capitalized. The Empire State Building. You can't just move The Empire
State Building. You can't just decide that the Empire State Building is something else.
Proper nouns are like that. They refer to something very specific and usually don't
change over time.
In the same way, constants can't be changed after they are set.
bq. @EmpireStateBuilding = "350 5th Avenue, NYC, NY"@
If we try to change the constant, Ruby will complain to us. Such things are frowned upon.
!i/the.foxes-4d.png(Come on, chunky bacon.)!
h3. Methods
If variables and constants are the nouns, then methods are the verbs.
Methods are usually attached to the end of variables and constants by a *dot*. You've already
seen methods at work.
bq. @front_door.open@
In the above, *open* is the method. It is the action, the verb. In some cases, you'll see actions chained together.
bq. @front_door.open.close@
We've instructed the computer to open the front door and then immediately close it.
bq. @front_door.is_open?@
The above is an action as well. We're instructing the computer to test the door to see if it's open.
The method could be called @Door.test_to_see_if_its_open@, but the @is_open?@ name is succinct and
just as correct. Both exclamation marks and question marks may be used in method names.
h3. Method arguments
A method may require more information in order to perform its action. If we want the computer
to paint the door, we should provide a color as well.
Method arguments are attached to the end of a method. The arguments are usually surrounded by
*parentheses* and separated by *commas*.
bq. @front_door.paint( 3, :red )@
The above paints the front door 3 coats of red.
Think of it as an inner tube the method is pulling along, containing its extra instructions.
The parentheses form the wet, round edges of the inner tube. The commas are the feet of each argument,
sticking over the edge. The last argument has its feet tucked under so they don't show.
Like a boat pulling many inner tubes, methods with arguments can be chained.
bq. @front_door.paint( 3, :red ).dry( 30 ).close()@
The above paints the front door 3 coats of red, dries for 30 minutes, and closes the door. Even though
the last method has no arguments, you can still put parentheses if you like. There is
no use dragging an empty inner tube, so the parentheses are normally dropped.
Some methods (such as @print@) are kernel methods. These methods are used throughout Ruby. Since they
are so common, you won't use the dot.
bq. @print "See, no dot."@
h3. Class methods
Like the methods described above (also called _instance_ methods), class methods
are usually attached after variables and constants. Rather than a dot, a *double colon* is used.
bq. @Door::new( :oak )@
As seen above, the @new@ class method is most often used to create things. In the above example, we're asking
Ruby to make a new oak door for us. Of course, Ruby has to have an understanding of how to make a
door--as well as a wealth of timber, lumberjacks, and those long, wiggily, two-man saws.
!i/the.foxes-4e.png(Plenty of chunky bacon to go around.)!
h3. Global variables
Variables which begin with a *dollar sign* are global.
bq. @$x@, @$1@, @$chunky@ and @$CHunKY_bACOn@ are examples.
Most variables are rather temporary in nature. Some parts of your program are like little houses.
You walk in and they have their own variables. In one house, you may have a @dad@ that represents
Archie, a travelling salesman and skeleton collector. In another house, @dad@ could represent
Peter, a lion tamer with a great love for flannel. Each house has its own meaning for @dad@.
With global variables, you can be guaranteed that the variable is the same in every little house.
The dollar sign is very appropriate. Every American home respects the value of the dollar. We're
crazy for the stuff. Try knocking on any door in America and hand them cash. I can guarantee
you won't get the same reaction if you knock on a door and offer Peter, a lion tamer with a great
love for flannel.
Global variables can be used anywhere in your program. They never go out of sight.
h3. Instance variables
Variables which begin with an *at* symbol are instance variables.
bq. @@x@, @@y@, and @@only_the_chunkiest_cut_of_bacon_I_have_ever_seen@ are examples.
These variables are often used to define the attributes of something. For example, you might
provide Ruby with the width of the @front_door@ by setting the @@width@ variable inside that
@front_door@. Instance variables are used to define characteristics of a single object in
Ruby.
Think of the *at* symbol as meaning *attribute*.
h3. Class variables
Variables which begin with *double at* symbols are class variables.
bq. @@@x@, @@@y@, and @@@i_will_take_your_chunky_bacon_and_raise_you_two@ are examples.
Class variables, too, are used to define attributes. But rather than defining an attribute
for a single object in Ruby, class variables give an attribute to many related objects in
Ruby. If instance variables set attributes for a single @front_door@, then class variables
set attributes for everything that is a @Door@.
Think of the *double at* prefix as meaning *attribute all*. Additionally, you can think of a swarm
of *AT-ATs* from _Star Wars_, which are all commanded by Ruby. You change a class variable
and not just one changes, they all change.
!i/the.foxes-4f.png(Woohoo! Chunky bacon accomplished!)!
h3. Blocks
Any code surrounded by *curly braces* is a block.
bq. @2.times { print "Yes, I've used chunky bacon in my examples, but never again!" }@ is an example.
With blocks, you can group a set of instructions together so that they can be passed
around your program.
The curly braces give the appearance of crab pincers that have snatched the code
and are holding it together. When you see these two pincers, remember that the
code inside has been pressed into a single unit.
It's like one of those little Hello Kitty boxes they sell at
the mall that's stuffed with tiny pencils and microscopic paper, all crammed into
a glittery transparent case that can be concealed in your palm for covert stationary
operations. Except that blocks don't require so much squinting.
The curly braces can also be traded for the words *do* and *end*, which is nice if your
block is longer than one line.
<pre>
loop do
print "Much better."
print "Ah. More space!"
print "My back was killin' me in those crab pincers."
end
<stdout>Much better.Ah. More space!My back was killin' me in those crab pincers.</stdout>
</pre>
h3. Block arguments
Block arguments are a set of variables surrounded by *pipe*
characters and separated by *commas*.
bq. @|en||x|@, @|x,y|@, and @|up, down, all_around|@ are examples.
Block arguments are used at the beginning of a block.
bq. @{ |x,y| x + y }@
In the above example, @|x,y|@ are the arguments. After the arguments, we have a bit of
code. The expression @x + y@ adds the two arguments together.
I like to think of the pipe characters as representing a tunnel. They give the appearance
of a chute that the variables are sliding down. (An @x@ goes down spread eagle, while the @y@
neatly crosses her legs.) This chute acts as a passageway between blocks and the world around them.
Variables are passed through this chute (or tunnel) into the block.
!i/the.foxes-4g.png(And then, the dismal truth.)!
h3. Ranges
A range is two values surrounded by *parentheses* and separated by *an ellipsis* (in the
form of two or three dots).
bq. @(1..3)@ is a range, representing the numbers 1 through 3.
bq. @('a'..'z')@ is a range, representing a lowercase alphabet.
Think of it as an accordion which has been squeezed down for carrying. (Sure, you can build
a great sense of self-worth by carrying around an unfolded accordion, but sometimes a person
needs to wallow in self-doubt, carefully concealing the squeeze-box.) The parentheses are
the handles on the sides of a smaller, handheld accordion. The dots are the chain, keeping
the folds tightly closed.
Normally, only two dots are used. If a third dot is used, the last value in the range is
excluded.
bq. @(0...5)@ represents the numbers 0 through 4.
When you see that third dot, imagine opening the accordion slightly. Just enough to let one
note from its chamber. The note is that end value. We'll let the sky eat it.
h3. Arrays
An array is a list surrounded by *square brackets* and separated by *commas*.
bq. @[1, 2, 3]@ is an array of numbers.
bq. @['coat', 'mittens', 'snowboard']@ is an array of strings.
Think of it as a caterpillar which has been stapled into your code. The two square brackets
are staples which keep the caterpillar from moving, so you can keep track of which end is the head and
which is the tail. The commas are the caterpillar's legs, wiggling between each section
of its body.
Once there was a caterpillar who had commas for legs. Which meant he had to allow a literary
pause after each step. The other caterpillars really respected him for it and he came to have
quite a commanding presence. Oh, and talk about a philanthropist! He was notorious for
giving fresh leaves to those less-fortunate.
Yes, an array is a collection of things, but it also keeps those things in a specific order.
h3. Hashes
A hash is a dictionary surrounded by *curly braces*. Dictionaries match words
with their definitions. Ruby does so with *arrows* made from an equals sign, followed by
a greater-than sign.
bq. @{'a' => 'aardvark', 'b' => 'badger'}@ is an example.
This time, the curly braces represent little book symbols. See how they look like
little, open books with creases down the middle? They represent opening and closing
our dictionary.
Imagine our dictionary has a definition on each of its pages. The commas represent
the corner of each page, which we turn to see the next definition. And on each page:
a word followed by an arrow pointing to the definition.
<pre>
{
'name' => 'Peter',
'profession' => 'lion tamer',
'great love' => 'flannel'
}
</pre>
I'm not comparing hashes to dictionaries because you can only store definitions in
a hash. In the example above, I stored personal information for Peter, the lion
tamer with a great love for flannel. Hashes are like dictionaries because they
can be very easy to search through.
Unlike arrays, the items in a hash are not kept in a specific order.
!i/the.foxes-5.png(The foxes think silence will kill the comic.)!
h3. Regular Expressions
A regular expression (or _regexp_) is a set of characters surrounded by *slashes*.
bq. @/ruby/@, @/[0-9]+/@ and @/^\d{3}-\d{3}-\d{4}/@ are examples.
Regular expressions are used
to find words or patterns in text. The slashes on each side of the expression are pins.
Imagine if you had a little word with pins on
both side and you held it over a book. You pass the word over the book and when it
gets near a matching word, it starts blinking. You pin the regular expression onto the book,
right over the match and it glows with the letters of the matching word.
Oh, and when you poke the pins into the book, the paper sneezes, _reg-exp!_
Regular expressions are much faster than passing your hand over pages of a book. Ruby
can use a regular expression to search volumes of books very quickly.
h3. Operators
You'll use the following list of operators to do math in Ruby or to compare things.
Scan over the list, recognize a few. You know, addition @+@ and subtraction @-@
and so on.
<pre class="text">
** ! ~ * / % + - &
<< >> | ^ > >= < <= <=>
|| != =~ !~ && += -= == ===
.. ... not and or
</pre>
h3. Keywords
Ruby has a number of built-in words, imbued with meaning. These words cannot
be used as variables or changed to suit your purposes. Some of these we've already
discussed. They are in the safe house, my friend. You touch these and you'll be
served an official syntax error.
<pre class="text">
alias and BEGIN begin break case class def defined
do else elsif END end ensure false for if
in module next nil not or redo rescue retry
return self super then true undef unless until when
while yield
</pre>
Good enough. These are the illustrious members of the Ruby language. We'll be having
quite the junket for the next three chapters, gluing these parts together into sly bits
of (poignant) code.
I'd recommend skimming all of the parts of speech once again. Give yourself a broad
view of them. I'll be testing your metal in the next section.
!i/the.foxes-6.png(Out in the pickup truck.)!
# h3. You and Your Dictionary
#
# You are out in a public park with your dictionary, enjoying a nice afternoon in the park.
# A light breeze ruffles lexicon's pages carelessly in your lap. A bird is gently beating
# its head against a tree. A tatterdemalion careens by on his unicycle. All is well.
#
# Hold up. Your brain wonders about that word. Tatterdemalion. Does it really mean
# "a shabbily clothed street urchin" like you think it means? You look at your dictionary.
#
# Now, here is the question. How will you answer this question with your dictionary? Where
# will you start? Will you go page-by-page, scanning for "a shabbily clothed street urchin,"
# the definition? Or will you use the dictionary's tabs to jump straight to the T section,
# narrowing down the pages toward "tatterdemalion," the word itself?
#
- !^sidebar
Seven Moments of Zen from My Life: >
# 8 years old. Just laying in bed, thinking. And I realize.
_There's nothing stopping me from becoming a child dentist._
# 21. Found a pencil on the beach. Embossed on it: _I cherish serenity._ Tucked
it away into the inside breast pocket of my suit jacket. Watched the waves come and
recede.
# 22. Found a beetle in my bathroom that was just about to fall into a heating vent.
Swiped him up. Tailored him a little backpack out of a leaf and a thread.
In the backpack: a skittle and a AAA battery. That should last him. Set him loose
out by the front gate.
# Three years of age. Brushed aside the curtain. Sunlight.
# 14. Riding my bike out on the pier with my coach who is jogging behind me
as the sun goes down right after I knocked out Piston Honda in the original
Nintendo version of Mike Tyson's Punch-Out.
# 11. Sick. Watching Heathcliff on television. For hours, it was Heathcliff.
And he was able to come right up close to my face. His head spun toward me.
His face pulsed back and forth, up close, then off millions of miles away.
Sound was gone. In fractions of a second, Heathcliff filled the universe, then
blipped off to the end of infinity. I heard my mother's voice trying to cut
through the cartoon. Heathclose, Heathaway, Heathclose, Heathaway.
It was a religious rave with a cat strobe and muffled bass of mother's voice.
(I ran a fever of 105 that day.)
# 18. Bought myself a gigapet. A duck. Fed it for awhile. Gave it a bath.
Forgot about it for almost a couple months. One day, while cleaning, I found a chain and he
was there on the end. Hey, little duck. Mad freaky, hoppin' around with his hair out,
squawking diagonal lines. In a tuxedo.
- If I Haven't Treated You Like a Child Enough Already: >
I'm proud of you. Anyone will tell you how much I brag about you. How I go on and on about
this great anonymous person out there who scrolls and reads and scrolls and reads. "These
kids," I tell them. "Man, these kids got heart. I never..." And I can't even finish a sentence
because I'm absolutely blubbering.
And my heart glows bright red under my filmy, translucent skin and they have to administer 10cc
of JavaScript to get me to come back. (I respond well to toxins in the blood.) Man, that stuff will
kick the peaches right out your gills!
So, yes. You've kept up nicely. But now I must begin to be a brutal schoolmaster.
I need to start seeing good marks from you. So far, you've done nothing but move your eyes
around a lot. Okay, sure, you did some exceptional reading aloud earlier. Now we need some
comprehension skills here, Smotchkkiss.
*Say aloud each of the parts of speech used below.*
bq. @5.times { print "Odelay!" }@
You might want to even cover this paragraph up while you read, because your eyes might want
to sneak to the answer. We have a _number_ @5@, followed by a _method_ @.times@. Then, the first
crab pincers of a _block_. The kernel _method_ @print@ has no dot and is followed by a _string_
@"Odelay!"@. The final crab pincers close our _block_.
*Say aloud each of the parts of speech used below.*
bq. @exit unless "restaurant".include? "aura"@
Like the @print@ method, @exit@ is a kernel _method_. If you were paying attention during the big list
of keywords, you'll know that @unless@ is just such a _keyword_. The _string_ @"restaurant"@ is clung to
by the _method_ @include?@. And finally, the string @"aura"@.
*Say aloud each of the parts of speech used below.*
bq. @['toast', 'cheese', 'wine'].@
@each { |food| print( food.capitalize ) }@
This caterpillar partakes of finer delicacies. An _array_ starts this example. In the array, three
_strings_ @'toast'@, @'cheese'@, and @'wine'@. The whole array is trailed by a _method_ @each@.
Inside of a _block_, the _block argument_ @food@, travelling down its little waterslide into the
block. The _method_ @capitalize@ then capitalizes the first letter of the block argument, which
has become _variable_ @food@. This capitalized _string_ is passed to kernel _method_ @print@.
Look over these examples once again. Be sure you recognize the parts of speech used. They each
have a distinct look, don't they? Take a deep breath, press firmly on your temples. Now, let's
dissect a cow's eye worth of code.
- An Example to Help You Grow Up: >
!i/the.foxes-7.png(Gettin' cabin fever.)!
*Say aloud each of the parts of speech used below.*
<pre>
require 'net/http'
Net::HTTP.start( 'www.ruby-lang.org', 80 ) do |http|
print( http.get( '/en/LICENSE.txt' ).body )
end
</pre>
The first line is a method call. The _method_ called @require@ is used. A _string_ is passed to
the method containing @'net/http'@. Think of this first line of code as a sentence. We have
told Ruby to load some helper code, the @Net::HTTP@ library.
The next three lines all go together. The _constant_ @Net::HTTP@ refers to the library we loaded above.
We are using the _method_ @start@ from the library. Into the method, we're sending a _string_
@'www.ruby-lang.org'@ and the _number_ @80@.
The word @do@ opens a _block_. The block has one _block variable_ @http@. Inside the block, the _method_
@print@ is called. What is being printed?
From the _variable_ @http@, the _method_ @get@ is called. Into @get@, we pass a _string_ containing
the path @'/en/LICENSE.txt'@. Now, notice that another method is chained onto @get@. The _method_
@body@. Then, the block closes with @end@.
Doing okay? Just out of curiousity, can you guess what this example does? Hopefully, you're seeing
some patterns in Ruby. If not, just shake your head vigorously while you've got these examples in
your mind. The code should break apart into manageable pieces.
For example, this pattern is used a number of times:
bq. _variable_ . _method_ ( _method arguments_ )
You see it inside the block:
bq. @http.get( '/en/LICENSE.txt' )@
We're using Ruby to get a web page. You've probably used HTTP with your web browser. HTTP is
the Hypertext Transfer Protocol. HTTP is used to transfer web pages across the internet. Conceptualize
a bus driver that can drive across the internet and bring back web pages for us. On his hat are stitched
the letters HTTP.
The variable @http@ is that bus driver. The _method_ is a message to the bus driver. Go @get@
the web page called @/en/LICENSE.txt@.
So where you see the chain of methods:
bq. @http.get( '/en/LICENSE.txt' ).body@
Since we'll be getting back a web page from the @http@ bus driver, you can read this in your
brain as:
bq. _web page_ .body
And this bit of code:
bq. @print( http.get( '/en/LICENSE.txt' ).body )@
This code gets the web page. We send a @body@ message to the web page, which gives us all the
HTML in a _string_. We then @print@ that string. See how the basic dot-method pattern happens
in a chain. The next chapter will explore all these sorts of patterns in Ruby. It'll be good fun.
So, what does this code do? It prints the HTML for the Ruby home page to the screen. Using
an web-enabled bus driver.
- And So, The Quick Trip Came To An Eased, Cushioned Halt: >
!i/the.foxes-8.png(Running after the truck.)!
So now we have a problem. I get the feeling that you are enjoying this way too much.
And you haven't even hit the chapter where I use jump-roping songs to help you learn
how to parse XML!
If you're already enjoying this, then things are really going bad. Two chapters from
now you'll be writing your own Ruby programs. In fact, it's right about there that I'll
have you start writing your own role-playing game, your own file-sharing network (a la BitTorrent),
as well as a program that will pull genuine random numbers from the Internet.
!>i/graph-1.gif(Proof has been extracted from the pudding.)!
And you know (you've got to know!) that this is going to turn into an obsession. First, you'll
completely forget to take the dog out. It'll be standing by the screen door, darting its head
about, as your eyes devour the code, as your fingers slip messages to the computer.
Thanks to your neglect, things will start to break. Your mounds of printed
sheets of code will cover up your air vents. Your furnace
will choke. The trash will pile-up: take-out boxes you hurriedly ordered in, junk mail you couldn't
care to dispose of. Your own uncleanliness will pollute the air. Moss will infest the rafters, the water
will clog, animals will let themselves in, trees will come up through the foundations.
But your computer will be well-cared for. And you, Smotchkkiss, will have nourished it with your knowledge.
In the eons you will have spent with your machine, you will have become part-CPU. And it will
have become part-flesh. Your arms will flow directly into its ports. Your eyes will accept
the video directly from DVI-24 pin. Your lungs will sit just above the processor, cooling it.
And just as the room is ready to force itself shut upon you, just as all the overgrowth swallows you
and your machine, you will finish your script. You and the machine together will run this latest Ruby
script, the product of your obsession. And the script will fire up chainsaws to trim the trees, hearths
to warm and regulate the house. Builder nanites will rush from your script, reconstructing your quarters,
retiling, renovating, chroming, polishing, disinfecting. Mighty androids will force your crumbling house into
firm, rigid architecture. Great pillars will rise, statues chiseled. You will have dominion over this
palatial estate and over the encompassing mountains and islands of your stronghold.
So I guess you're going to be okay. Whatdya say? Let's get moving on this script of yours?
- Floating Little Leaves of Code:
- >
!i/the.elf-1.gif(Impossibly deep in the caverns of Ambrose... the Elf with a pet ham!)!
!i/the.elf-2.gif(...and the cat Trady Blix.)!
I've never seen the ham do anything but leak juice. Today, our
business in Ambrose Caverns is with the elf. He is a crucial part of the next lessons.
Let's all make him feel welcome. Go start warming up your listening hats!
(And please change out of those ridiculous stirrup pants.)
A prompt warning: this lesson is much slower. Stay with it. This will be a long, deep breath.
The most crucial stage of your instruction. It may seem like you're not learning much code at
first. You will be learning concepts. By the end of this chapter, you will know Ruby's
beauty. The coziness of the code will become a down sleeping bag for your own solace.
- The Leaf as a Status Symbol in Ambrose: >
Alright, Elf. Give us a quick rundown of the currency issues you've faced there in your kingdom.
!i/the.elf-3.gif(Blue Crystals got the shaft.)!
Yeah, that's not the way I remember it. This Elf was paging me constantly. When I refused to
call him back, he somehow left a message on my pager. Meaning: it beeped a couple times and
then printed out a small slip of paper. The slip said something to the effect of, "Get down here
quick!" and also, "We've got to rid the earth of this scourge of enterpreneurial caterpillars,
these twisted insect vikings are suffocating my blue crystals!"
Lately, the exchange rate has settled down between leaves and crystals. One treegrown note is
worth five crystals. So the basic money situation looks like this:
<pre>
blue_crystal = 1
leaf_tender = 5
</pre>
This example is, like, _totally_ last chapter. Still. It's a start. We're setting two _variables_.
The *equals sign* is used for _assignment_.
Now @leaf_tender@ represents the number @5@ (as in: five blue crystals.) This concept right here is
*half of Ruby*. We're _defining_. We're _creating_. This is half of the work.
Assignment is the most basic form of defining.
You can't complain though, can you Elf? You've built an empire from cashing your blue crystals
into the new free market among the forest creatures. (And even though he's an elf to us, he's a
tall monster to them.)
!i/the.elf-4.jpg(Animal Perfect, LLC)!
- !^sidebar
The Scarf Eaters: >
I hate to intrude upon your instruction, but I've already walked all over it enough to
warrant some further disregard. Can I go over my next project with you?
I've pledged to write another book. (_Trombones_.) The good news is that I won't actually
be writing any of it. You won't have to endure any more of this inane blathering.
It's over between me and words. I'd love to stick around and exploit them each, one after
another, but it's all becoming quite predictable, wouldn't you say? Eventually, they will
all be used and I'd have to come up with fake words and that would be way too cnoofy.
Now. The deal isn't cut yet, but I'm in negotiations with Anna Quindlen to do my ghost writing.
We're tag-teaming on a book that's going to blow the (Poignant) Guide right out of your hands.
To put it bluntly, the Guide will be worthless. You won't be able to pile enough
pomegranates on top of the thing.
So this new book. The Scarf Eaters. It's a coming-of-age novel. But it's also a beginner's
guide to Macromedia Flash. It's like Judy Blume crossed Praystation. It's like 0sil8 starring
Hillary Duff.
I don't want to give away the plot at all, but to tug your appetite I'll just say this:
one kid talks to his dead brother in ActionScript. More to come.
- >
Nonono. Hang on a sec. You're not ready for what the Elf here is doing in his caves.
You'll think it's all positively inhumane, naughty, sick, tweeested, yada yada.
h3. Now You're Going to Hear the Animal Perfect Mission Statement Because This Is A
Book And We Have Time And No Rush, Right?
Back, back, way back before speedboats, I owned a prize race horse who took a stumble on
the track. She did ten front flips and crashed into a guy who was carrying a full jar
of mayonnaisse. We had blood and mayonnaisse up and down the track. Needless to say,
she was a disaster.
The vet took one look at her and swore she'd never walk again. Her legs were gone and
the vet wouldn't allow a legless horse to just sit around. We'd need to put her down.
He swore his life and career on it, insisting we divide into two parallel lines. The people
who could not refute the doctor's claims on one side; those too stubborn to accept his
infallable medical reasoning on the other. The Elf, his pet ham, and I were the only
ones in that second line.
So while the others heaped up trophies and great wreaths around the horse, bidding it a
fond farewell before the bullet came to take him home, the Elf and I frantically pawed the Internet for
answers. We took matter into our own hands, cauterizing her leg wounds with live crawdads.
It worked great! We now had a horse again. Or at least: a horse body with a crustaceous abdominal
frosting.
She scurried everywhere after that and lived for years in pleasantly moist underground cavities.
Animal Perfect is now the future of animal enhancement. They build new animals and salvage
old-style animals for parts. Of course, they've come a long ways.
When Animal Perfect started, you'd see a full-grown bear walk into Animal Perfect and you'd
see a full-grown bear with sunglasses walk out. Completely cheesy.
Stick around and you'll see a crab with _his own jet pack_. That's a new 2004 model jetcrab.
But now, the whole operation is up and running. And the cleanliness of the place is astonishing.
All the equipment is so shiny. Everything is in chrome. Oh, and all the staff have concealed
weapons. They're trained to kill anyone who enters unannounced. Or, if they run out of bullets,
they're trained to pistol whip anyone who enters unannounced.
Elf, make me a starmonkey.
!i/the.elf-5a.jpg(First, the star is caught.)!
Some imaginary Ruby for you:
bq. @pipe.catch_a_star@
Variable @pipe@. Method @catch_a_star@. A lot of Rubyists like to think of methods as a message.
Whatever comes before the dot is handed the message. The above code tells the @pipe@ to
@catch_a_star@.
This is the *second half* of Ruby. Putting things in motion. These things you define and create
in the first half start to _act_ in the second half.
# Defining things.
# Putting those things into action.
So what if the star catching code works? Where does the star go?
bq. @captive_star = pipe.catch_a_star@
See, it's up to you to collect the miserable, little star. If you don't, it'll simply vanish.
Whenever you use a method, you'll always be given something back. You can ignore it or use it.
_If you can learn to use the answers that methods give you back, then you will *dominate*._
!i/the.elf-5b.jpg(Star is ratcheted to the monkey's face.)!
Quickly then.
bq. @starmonkey = ratchet.attach( captive_monkey, captive_star )@
The @ratchet@ gets an @attach@ message. What needs to be attached? The _method arguments_: the
@captive_monkey@ and the @captive_star@. We are given back a @starmonkey@, which we have
decided to hang on to.
!i/the.elf-5c.gif(Frog on the hand.)!
This is turning out to be such a short, little proggie that I'm just going to put it
all together as one statement.
bq. @starmonkey = ratchet.attach( captive_monkey, pipe.catch_a_star ) + deco_hand_frog@
See how @pipe.catch_a_star@ is right in the arguments for the method? The caught
star will get passed right to the ratchet. No need to find a place to put it. Just
let it go.
- Small and Nearly Worthless: >
!i/blix-1.gif(Law-va.)!
The hotel here in Ambrose is no good at all. The beds are all lumpy.
The elevator is tiny. One guy put all his bags in the elevator and found
out there wasn't room for him. He hit the button and chased up the stairs after
it all. But the stairwell turned out to be too narrow and his shoulders got wedged
going up.
The soap mini-bars they give you are sized down for elves, so it's impossible to
work up a lather. I hate it. I keep mistaking them for contact lenses.
I turned on the faucet and nothing came out. Thing is: Ambrose is
a place with magical properties, so I took a chance. I put my hands under
the spigot. Invisible, warm wetness. I felt the hurried
sensation of running water, darting through my fingers. When I took
my hands away, they were dry and clean.
It was an amazing nothingness to experience. It was just like @nil@.
h3. Nil
In Ruby, @nil@ represents an emptiness. It is *without value*. It isn't zero.
Zero is a number.
It's Ruby's own walking dead, a flatlined keyword. You can't add
to it, it doesn't evolve. But it's terribly popular. This skeleton's smiling
in all the pictures.
bq. @plastic_cup = nil@
The above @plastic_cup@ is *empty*. You could argue that the @plastic_cup@
contains something, a @nil@. The @nil@ represents the emptiness, though, so
go ahead and call it empty.
Some of you who have programmed before will be tempted to say the @plastic_cup@ is
*undefined*. How about let's not. When you say a variable is undefined,
you're saying that Ruby simply has no recollection of the variable, it doesn't
know the var, it's absolutely non-existent.
But Ruby is aware of the @plastic_cup@. Ruby can easily look in the @plastic_cup@.
It's *empty*, but not *undefined*.
h3. False
!<i/blix-neg.gif(Shape of a cat.)!
_The cat Trady Blix. Frozen in emptiness. Immaculate whiskers rigid. Placid
eyes of lake. Tail of warm icicle. Sponsored by a Very Powerful Pause Button._
The darkness surrounding Blix can be called *negative space*. Hang on to that phrase.
Let it suggest that the emptiness has a negative connotation. In a similar way,
@nil@ has a slightly sour note that it whistles.
Generally speaking, *everything in Ruby has a positive charge to it*. This spark
flows through strings, numbers, regexps, all of it. Only two keywords wear a
shady cloak: @nil@ and @false@ draggin us down.
You can *test that charge* with an @if@ keyword. It looks very much like the
@do@ blocks we saw in the last chapter, in that both end with an @end@.
<pre>
if plastic_cup
print "Plastic cup is on the up 'n' up!"
end
</pre>
If @plastic_cup@ contains either @nil@ or @false@, you won't see anything print
to the screen. They're not on the @if@ guest list. So @if@ isn't going to run
any of the code it's protecting.
But @nil@ and @false@ need not walk away in shame. They may be of questionable
character, but @unless@ runs a smaller establishment that caters to the bedraggled.
The @unless@ keyword has a policy of *only allowing those with a negative charge in*.
Who are: @nil@ and @false@.
<pre>
unless plastic_cup
print "Plastic cup is on the down low."
end
</pre>
You can also use @if@ and @unless@ at the *end of a single line of code*, if that's
all that is being protected.
<pre>
print "Yeah, plastic cup is up again!" if plastic_cup
print "Hardly. It's down." unless plastic_cup
</pre>
And another nice trick: stack the @if@ and @unless@.
<pre>
print "We're using plastic 'cause we don't have glass." if plastic_cup unless glass_cup
</pre>
This trick is a gorgeous way of expressing, _Do this only if *a* is true and *b* isn't
true_.
Now that you've met @false@, I'm sure you can see what's on next.
- !^sidebar
Make Your Own Starmonkey!: >
1. Turn a mug upside-down.
!i/starmonkeycrafts-1.jpg!
2. Attach an apple with a rubber band.
!i/starmonkeycrafts-2.jpg!
3. Shove car keys into the sides of the apple.
!i/starmonkeycrafts-3.jpg!
4. Glue star face.
!i/starmonkeycrafts-4.jpg!
You have two complementary star faces waiting in
your account.
p=. !i/starmonkeycrafts-5.gif!
Standard, placid.
p=. !i/starmonkeycrafts-6.gif!
Eating chalk.
- >
h3. True
bq. @approaching_guy = true@
I saw @true@ at the hotel buffet tables today. I cannot stand that guy.
His stance is way too wide. And you've never met anyone who planted his
feet so hard in the ground. He wears this corny necklace made out of
shells. His face exudes this brash confidence. (You can tell he's exerting
all of his restraint just to keep from bursting into Neo flight.)
To be honest, I can't be around someone who always has to be right.
This @true@ is always saying, "A-OK." Flashing hang ten. And seriously,
he loves that necklace. Wears it constantly.
As you'd suspect, he's backstage at everything on the @if@ event schedule.
bq. @print "Hugo Boss" if true@ acts like @print "Hugo Boss"@.
Occassionally, @if@ will haul out the velvet ropes to exercise some crowd control.
The *double equals* gives the appearance of a short link of ropes, right along the
sides of a red carpet where only @true@ can be admitted.
<pre>
if approaching_guy == true
print "That necklace is classic."
end
</pre>
The double equals is simply *an ID check*. Do the gentleman at both ends of this
rope appear to match?
In this way, you control who @if@ lets in. If you have a hard time getting along with
@true@ as I do, you can heartily welcome @false@.
<pre>
if approaching_guy == false
print "Get in here, you conniving devil."
end
</pre>
Same goes for @unless@. The gateway is yours. Take possession of it.
h3. Again, I Want You to Dominate
Now, you want a head trip? *The double equals sign is a method.* Can you guess how
it works? Here, check it out with the dot and parens:
bq. @approaching_guy.==( true )@
Ruby allows the shortcut, though. You can drop the dot and back away slowly.
Now, do you remember what you need to do to *dominate* in Ruby? _Use the answers the
methods give you._
<pre>
if nil.==( true )
print "This will never see realization."
end
</pre>
In the above, how is the method's answer being used?
Let's take the statement @nil == true@. This will fail every time. No match.
When there's no match, the double equals method answers with @false@. A shake of the head.
That answer is given to @if@, who can't accept a @false@. The @print@ never sees realization.
<pre>
at_hotel = true
email = if at_hotel
"why@hotelambrose.com"
else
"why@drnhowardcham.com"
end
</pre>
Even though @if@ isn't a method, @if@ does give a return answer. Look at the above and
wonder over what happens when @at_hotel@ is @true@.
The @if@ will return the answer given by the code it chooses to run. In the case of
@at_hotel@ being true, the first string, my e-mail address at Hotel Ambrose, will be
returned. The @else@ keyword marks code which will run, should @if@ fail. If @at_hotel@
is false, the @if@ will answer with my e-mail address at Dr. N. Howard Cham's office,
where I take my apprenticeship.
Should you have several lines of code in an @if@ or @unless@, *only the answer from the
last full statement will be used*.
<pre>
email = if at_hotel
address = "why"
address << "@hotelambrose"
address << ".com"
end
</pre>
Three lines of code inside the @if@. The first line assigns a string with my name in it to a variable.
The second and third lines add the rest of my e-mail address on to the end. The *double less-than @<<@
is the concatenation operator*. To concatenate is to *append*, or *add to the end*.
Just as we saw with the equality checker @==@, the concatenator is a method. After adding to the end
of the string, the concatenator also *answers with that very string*. So, the third line, which could be
read as @address.<<( ".com" )@, gives back @address@, which the @if@ then hands back for @email@'s
assignment.
Here's a question: what if the @if@ fails? What if @at_hotel@ is false in the above example?
Is anything returned? Nothing is assigned to @email@, right?
Yes, nothing is returned. By which I mean: @nil@ is returned. And often @nil@ is a very
useful answer.
<pre>
print( if at_hotel.nil?
"No clue if he's in the hotel."
elsif at_hotel == true
"Definitely in."
elsif at_hotel == false
"He's out."
else
"The system is on the freee-itz."
end )
</pre>
You can use the @nil?@ method on any value in Ruby. Again, think of it as a message.
To the value: "Are you nil? Are you empty?"
If @at_hotel@ is empty, Ruby doesn't have any idea if I'm in the hotel or not. So @if@
answers with the "No clue" string. In order to handle the @true@ or @false@ possibilities,
the @elsif@ keyword is used. While you can have only one @if@ and one @else@, you can fill
the inbetween with an exorbitant number of @elsif@ keywords. Each @elsif@ acts as *a further @if@
test*. Checking for a positive charge.
If you're doing okay at this point, then you're in tip-top shape for the rest of the
book. You have seen some pretty tough code in the last few examples. You strong fellow.
- Chaining Delusions Together: >
!i/the.elf-6.jpg(55,000 starmonkeys and one spirited Olympic hopeful.)!
You finish reading the above comic and retire to your daybed for reflection. It's one of
those canopy affairs which is always logjammed with pillows. You sit atop the pile,
gazing out upon the world. You see the tall smokestacks belching wide spools of fume and
haze. The tangled concourses of freeways smattered with swift, shimmering traffic is but
a gently pulsing eye muscle from your vantage point.
It is all so fantastic. How the colors of the horizon spread across the landscape as a great
mix of butter and grease with a tablespoon of vanilla extract.
Yet, for all of the beauty which beckons for your attention, the images of the Elf and
his Olympic Hopeful return. And more especially, that order for *55,000* starmonkeys.
_55,000 starmonkeys_, you think. _Fifty-five Thousand_.
You think of just the number itself. _55,000_. It's walking down a road. It might be in
a forest, you don't know for sure as your eyes are fixed right on the number itself. It's stopping
and talking to people. To tennis players, to a men's choral group. There is merriment and good
feeling. When it laughs, its lower zeros quiver with glee.
You want to talk to it. You want to skip along that forest trail with it. You want to climb
aboard a jet bound to Brazil with it. And after five days and four nights at the leisureful
Costa do Sauipe Marriott Resort & Spa, to marry it, to bear a family of 55,000 starmonkeys with it.
To take possession of Nigeria with it.
With a flying leap, you dismount your pillow tower of isolation. Scrambling with the key,
you unlock your roll top desk and pull out a sheet of paper, holding it firmly upon the
desk. You begin scribbling.
bq. _Take possession of Nigeria with my new 55,000 starmonkeys_...
_Over it, build Nigeria-sized *vegetarians only* casino and go-cart arena_...
_Wings... we could have our own special sauce on the wings that's different_...
_Mustard + codeine = Smotchkkiss' Starry Starmonkey Glow Sauce_...
_Franchise, franchise... logos_...
_Employee instructional videos_...
_When you give the customer change,
let them reach inside the frog on your hand to get it_...
_If they have no change, at least put their
reciept some place where they have to touch the frog_...
_We're leveling the playing field here_...
_Advertise cheap pizza, let's make our money off soda_...
_Collect all 4 frosted glasses_...
Wow, the ideas are really coming out. You literally had to smack yourself to stop. We need to put these
in a safe place. Actually, we should store them on your computer and mangle the words. You look out the
window and watch for FBI. I'm going to start this script.
h3. The Flipping Script
<pre>
print "Type and be diabolical: "
idea_backwards = gets.reverse
</pre>
Let this script be your confidante. It will ask for evil plans and turn their letters backwards.
The @gets@ method is *built into Ruby*. It's a *kernel method* like @print@. This method @gets@
will pause Ruby to let you type. When you hit _Enter_, @gets@ will then stop paying attention
to your keyboard punchings and answer back to Ruby with a string that contains everything you typed.
- !^sidebar
Get Ahead with The Tiger's Vest: >
!i/ad-tiger.gif!:expansion-pak-1.html
Want to start using Ruby alongside your reading? Split your attention and head off to
"Expansion Pak I: The Tiger's Vest":expansion-pak-1.html, a trite mini-chapter which
will aid you in installing Ruby. In addition, you'll learn how to use Irb and Ri, two
teaching aids that come with Ruby which will really speed you up in your learning.
- >
The @reverse@ method is then used on the string that @gets@ is giving back. The @reverse@ method
is part of the @String@ class. Which means that *anything which is a string has the @reverse@ method
available*. More on classes in the next chapter, for now just know that *a lot of methods are only
available with certain types of values*.
I don't think @reverse@ is going to cut it. The authorities only need to put a mirror to
"airegiN fo noissessop ekaT." Bust us when starmonkeys start to touch down in Lagos.
The capital letters give it away. Maybe if we uppercase all letters in the string before
we reverse it.
<pre>
idea_backwards = gets.upcase.reverse
</pre>
h3. Your Repetitiveness Pays Off
You hand me a legal pad, doused in illegible shorthand. Scanning over it, I start to
notice patterns. That you seem to use the same set of words repeatedly in your musings.
Words like _starmonkey_, _Nigeria_, _firebomb_. Some phrases even. _Put the kabosh on._
That gets said a lot.
Let us disguise these foul terms, my brother. Let us obscure them from itching eyes that
cry to know our delicate schemes and to thwart us from having great pleasure and many go-carts.
We will replace them with the most innocent language. New words with secret meaning.
I start up a word list, a Ruby @Hash@, which contains these oft seen and dangerous words of yours.
In the Hash, each dangerous word is matched up against a code word (or phrase). The code word
will be swapped in for the real word.
<pre>
code_words = {
'starmonkeys' => 'Phil and Pete, those prickly chancellors of the New Reich',
'catapult' => 'chucky go-go', 'firebomb' => 'Heat-Assisted Living',
'Nigeria' => "Ny and Jerry's Dry Cleaning (with Donuts)",
'Put the kabosh on' => 'Put the cable box on'
}
</pre>
The words which are placed before the arrow are called *keys*. The words after the
arrows, the definitions, are often just called *values*.
Notice the double quotes around @Ny and Jerry's Dry Cleaning (with Donuts)@. Since
a single quote is being used an apostrophe, we can't use single quotes around the
string. (Although, you can use single quotes if you put a backslash before the apostrophe
such as: @'Ny and Jerry\'s Dry Cleaning (with Donuts)'@.)
Should you need to look up a specific word, you can do so by using the *square brackets*
method.
bq. @code_words['catapult']@ will answer with the string @'chucky go-go'@.
Look at the square brackets as if they are a wooden pallet the word is sitting upon.
A forklift could slide its prongs into each side of the pallet and bring it down
from a shelf back in the warehouse. The word on the pallet is called the _index_.
We are asking the forklift to find the index for us and bring back its corresponding
value.
If you've never been to a warehouse, you could also look at the brackets as handles.
Imagine an industrious worker putting on his work gloves and hefting the index back to
your custody. If you've never used handles before, then I'm giving you about
thirty seconds to find a handle and use it before I blow my lid.
As with many of the other operators you've seen recently, the index brackets are
simply a shortcut for a method.
bq. @code_words.[]( 'catapult' )@ will answer with the string @'chucky go-go'@.
h3. Making the Swap
I went ahead and saved the Hash of code words to a file called *wordlist.rb*.
<pre>
require 'wordlist'
# Get evil idea and swap in code words
print "Enter your new idea: "
idea = gets
code_words.each do |real, code|
idea.gsub!( real, code )
end
# Save the jibberish to a new file
print "File encoded. Please enter a name for this idea: "
idea_name = gets.strip
File::open( "idea-" + idea_name + ".txt", "w" ) do |f|
f << idea
end
</pre>
Script starts by pulling in our word list. Like @gets@ and @print@, the @require@ method
is a kernel method, you can use it anywhere. I give it the string @'wordlist'@ and it will
look for a file named *wordlist.rb*.
After that, there are two sections. I am marking these sections with comments, the lines that
start with *pound* symbols. Comments are *useful notes* that accompany your code. Folks who
come wandering through your code will appreciate the help. When going through your own code
after some time has passed, comments will help you get back into your mindset. And there's
software out there that can take your comments and build documents from them. (RDoc and Ri --
see Expansion Pak #1!)
I like comments because I can skim a big pile of code and spot the highlights.
As the comments tell us, the first section asks you for your evil idea and swaps in
the new code words. The second section saves the encoded idea into a new text file.
<pre>
code_words.each do |real, code|
idea.gsub!( real, code )
end
</pre>
You see the @each@ method? The @each@ method is all over in Ruby.
It's available for Arrays, Hashes, even Strings.
Here, our @code_words@ dictionary is kept in a Hash. This @each@ method will hurry through
*all the pairs of the Hash*, one dangerous word matched with its code word, handing each pair to the @gsub!@ method
for the actual replacement.
In Ruby, @gsub@ is short for _global substitution_. The method is used to search and replace.
Here, we want to find all the occurences of a dangerous word and replace with its safe
code word. With @gsub@, you provide the *word to find as the first argument*, then the *word
to put in its place as the second argument*.
Why aren't we hanging on to the answer from @gsub@? Doesn't @gsub@ give us an answer back that
we should keep? You'd think the line would read:
<pre>
safe_idea = idea.gsub( real, code )
</pre>
Yes, with @gsub@ we'd need to hang on to its answer. We're using a variation of @gsub@ that is
totally hyper. Notice the *exclamation mark* on the @gsub!@ used inside the @each@ block. The
exclamation mark is a sign that @gsub!@ is a bit of a zealot. See, @gsub!@ will go ahead and
*replace the words in @idea@ directly*. When it's done @idea@ will contain the newly altered string
and you won't be able to find the old string.
Call @gsub!@ a *destructive method*. It makes its changes to the value directly. Whereas @gsub@
will leave the value intact, answering back with a new string which contains the alterations.
(Why must @gsub!@ scream when he descends upon his prey? Merciless assailant!)
h3. Text Files of a Madman
Let us now save the encoded idea to a file.
<pre>
# Save the jibberish to a new file
print "File encoded. Please enter a name for this idea: "
idea_name = gets.strip
File::open( 'idea-' + idea_name + '.txt', 'w' ) do |f|
f << idea
end
</pre>
This section starts by asking you for a name by which the idea can be called. This name is used
to build a file name when we save the idea.
The @strip@ method is for strings. This method *trims spaces and blank lines* from the *beginning
and end* of the string. This will remove the _Enter_ at the end of the string you typed.
But it'll also handle spaces if you accidentally left any.
After we have the idea's name, we open a new, blank text file. The file name is built by adding
strings together. If you typed in @'mustard-plus-codeine'@, then our math will be: @'idea-' +
'mustard-plus-codeine' + '.txt'@. Ruby presses these into a single string.
@'idea-mustard-plus-codeine.txt'@ is the file.
We're using the class method @File::open@ to create the new file. Up until now, we've used
several kernel methods to do our work. We hand the @print@ method a string and it prints the
string on your screen. One secret about kernel methods like @print@: they are actually *class
methods*.
bq. @Kernel::print( "55,000 Starmonkey Salute!" )@
What does this mean? Why does it matter? It means @Kernel@ is the center of Ruby's universe.
Wherever you are in your script, @Kernel@ is right beside you. You don't even need to spell
@Kernel@ out for Ruby. Ruby knows to check @Kernel@.
Most methods are more specialized than @print@ or @gets@. Take the @File::open@ for
example. The creator of Ruby, Matz, has given us many different methods which which
read, rename, or delete files. They are all organized inside the @File@ class.
bq. @File::read( "idea-mustard-plus-codeine.txt" )@ will answer back with a string
containing all of the text from your idea file.
bq. @File::rename( "old_file.txt", "new_file.txt" )@ will rename @old_file.txt@.
bq. @File::delete( "new_file.txt" )@ will nuke the new file.
These File methods are all *built right into Ruby*. They are all just stored in a
container called the @File@ class. So, while you can safely call kernel methods
without needing to type @Kernel@, Ruby doesn't automatically check the @File@ class.
You'll need to give the full method name.
<pre>
File::open( 'idea-' + idea_name + '.txt', 'w' ) do |f|
f << idea
end
</pre>
We pass two arguments into @File::open@. The first is the *file name to open*. The second
is a string containing our *file mode*. We use @'w'@, which means to write to a brand-new
file. (Other options are: @'r'@ to read from the file or @'a'@ to add to the end of the
file.)
The file is opened for writing and we are handed back the file in variable @f@, which can
be seen *sliding down the chute into our block*. Inside the block, we write to the file. When
the block closes with @end@, our file is closed as well.
Notice we use the *concatenator* @<<@ to write to the file. We can do this because files have
a method called @<<@ just like strings do.
h3. Settle Down, Your Ideas Aren't Trapped
Here, let's get your ideas back to their original verbage,
so you can rumminate over their brilliance.
<pre>
require 'wordlist'
# Print each idea out with the words fixed
Dir['idea-*.txt'].each do |file_name|
idea = File.read( file_name )
code_words.each do |real, code|
idea.gsub!( code, real )
end
puts idea
end
</pre>
By now, you should be up to snuff with most of this example. I won't bore you with all of the
mundane details. See if you can figure out how it works on your own.
We have an interesting class method here, though. The @Dir::[]@ method searches a directory (some
of you may call them "folders"). Just as you've seen with Hashes, the index brackets
can be class methods. (Can you start to see the shiny, glinting gorgeousness of Ruby?)
So we're using the forklift to get those files in the directory which match @'idea-*.txt'@. The
@Dir::[]@ method will use the asterisk as a wildcard. We're basically saying, "Match anything
that starts with _idea-_ and ends with _.txt_." The forklift shuffles off to the directory
and comes back with a list of all matching files.
That *list of files* will come in the form of @Array@ the Caterpillar, with a @String@ for each
file. If you are curious and want to play with with @Dir::[]@, try this:
bq. @p Dir['idea-*.txt']@ will print:
bq. @['idea-mustard-plus-codeine.txt']@ (_an Array of file names!_)
Yes, the @p@ method works like @print@. But where @print@ is designed for displaying strings,
@p@ will print _anything_. Check this out.
bq. @p File::methods@ will print:
bq. @["send", "display", "name", "exist?", "split",@ ... _a whole list of method names!_ @]@
# h3. Extra Credit: PStore
#
#
# <pre>
# require 'pstore'
# journal = PStore.new( 'journal.p' )
# journal.transaction do
# journal.
# end
# </pre>
- The Miracle of Blocks: >
!i/blix-2.gif(Flowerboyz? Heard it before.)!
- !^sidebar
Excerpt from The Scarf Eaters: >
(_from Chapter V: A Man in Uniform_.)
In April, the callow lilies came back. They stretched their baby angel wings out
and reached for the world. Gently, their tendrils caressed the sullen fence posts
until even they lilted lovelier.
From her bedroom window, Lara watched the lilies exude their staunch femininity.
She slipped the tassels of a fresh, carpathian, embroidered scarf into her mouth and
ate slowly. The long cloth slid down her throat and tickled as it snaked along her
esophagus. She giggled and burped.
Oh, how the flora drew her in. Looking at flowers went so well with being a teenage girl.
She wanted to paint them, so she opened a new Flash template. A blank movie this time.
She set her cursor loose in the garden of her movie's viewable area. Vector white lines
below shorter vector yellow lines. She selected the white lines and grouped them together.
She even moved them to a new layer entitled "Cry, Baby Angel, Cry." Then she
converted them into a graphic object and moved them to the library.
She felt a warm chill as she moved the long, white petals to her movie's library. It
felt so official. _I choose you. I name you. Dwell in the comfort of my palace forevermore._
Heh. She laughed. Colorado Springs was hardly a "palace."
Since they had moved, Dad had only been home once. He had barged through the front door in
full uniform and had given quite a start to both Lara and her mother. Her mother had even dropped
a head of lettuce -- which head she had just finished washing -- in a pitcher of Lick-M-Aid.
The pitcher was just wide enough for the lettuce and it lodged in there pretty good.
Dad came over and yanked at the moist head for sometime until declaring it FUBAR, in a
voice both bemused and then crestfallen. He tossed the clotted spout in the trash bin.
It was only later that day that Lara's mother realized that she could have simply halved the
lettuce with an electric knife. Dad laughed and slapped his forehead. He then went
around and slapped Lara's forehead, and her mother's too, affectionately.
"We just weren't thinking, were we?" is what he said. "And who dares blame us? We're
a real family today. And we shouldn't have to do anything else on the day we got
our family back."
Lara's smiled reflected across the glass of her monitor. She chose the text tool and
in 42 point serif typed: "Dad." She created a path for it and let it tween off the
right side of the screen. She cried long after it was gone.
- >
Since you and I are becoming closer friends as we share this time together, I should
probably let you in on a bit of the history going on here. It's a good time for a break
I say.
First, you should know that Blix is my cat. My second pet to Bigelow.
Granted, we hardly see each other anymore. He's completely self-sufficient.
I'm not exactly sure where he's living these days, but he no longer lives in the antechamber
to my quarters. He emptied his savings account about seven months ago.
He does have a set of keys for the house and the Seville. Should he ever
find himself stranded, I will gladly step away from our differences and entertain his
antics around the house again.
Make no mistake. I miss having him around. Can't imagine he misses my company, but
I miss his.
h3. A Siren and A Prayer
I first saw Blix on television when I was a boy. He had a starring role on a very gritty police drama
called _A Siren and A Prayer_. The show was about a god-fearing police squad that did
their jobs, did them well, and saw their share of miracles out on the beat. I mean
the officers on this show were _great_ guys, very religious, practically clergy. But,
you know, even clergymen don't have the good sense to kill a guy after he's gone too far.
These guys knew where to draw that line. They walked that line every day.
So, it was a pretty bloody show, but they always had a good moral at the end. Most times
the moral was something along the lines of, "Wow, we got out of that one quick." But there's
serious camaraderie in a statement like that.
The show basically revolved around this one officer. "Mad" Dick Robinson. People called
him Mad because he was basically insane. I can't remember if he was actually clinically
insane, but people were always questioning his decisions. Mad often blew his top and chewed
out some of the other officers, most of whom had unquestionable moral character. But we
all know it's a tough world, the stakes are high out there, and everyone who watched the
show held Mad in great regard. I think everyone on the squad grew quite a bit as people,
thanks to Mad's passion.
The officers couldn't do it all themselves though. In every single episode, they plead with a
greater force for assistance. And, in every single episode, they got their tips from a cat
named Terry (played by my cat Blix.) He was just a kitten at the time and, as a young boy
tuning into _A Siren and A Prayer_, I found myself longing for my own crime-sniffing cat.
Terry took these guys down the subway tunnels, through
the rotting stench of abandoned marinas, into backdoors of tall, industrial smokestacks.
Sometimes he was all over an episode, darting in and out, preparing traps and directing traffic.
But other times you wouldn't see him the whole episode. Then you'd rewind through the whole show
and look and look and look. You'd give up. He can't be in that episode.
Still, you can't bear
to let it go, so you go comb through the whole episode with the jog on your remote, combing, pouring
over each scene. And there he is. Way up behind the floodlight that was turned up too high. The
one that left Mad with permanent eye damage. Why? Why burn out the retinas of your own colleague,
Terry?
But the question never got answered because the series was cancelled. They started to do special
effects with the cat and it all fell apart. In the last episode of the show, there is a moment where
Terry is trapped at the top of a crane, about to fall into the searing slag in the furnace of an
iron smelt. He looks back. No going back. He looks down. Paws over eyes (_no joke!_), he
leaps from the crane and, mid-flight, snags a rope and swings to safety, coming down on a soft antelope
hide that one of the workers had presumably been tanning that afternoon.
People switched off the television set the very moment the scene aired. They tried changing the name.
First it was _God Gave Us a Squad_. _Kiss of Pain_. Then, _Kiss of Pain in Maine_, since the
entire precinct ended up relocating there. But the magic was gone. I went back to summer school that year
to make up some classes and all the kids had pretty much moved on to football pencils.
h3. Blocks
A couple years ago, I started teaching Blix about Ruby. When we got to this part in his lessons,
the part that covers blocks, he said to me, "Blocks remind me of Mad Dick Robinson."
"Oh?" I hadn't heard that name in awhile. "I can't see how that could be."
"Well, you say blocks can be difficult to understand."
"They're not difficult," I said. "A *block* is just *code that's grouped together*."
"And Mad was just an officer, sworn to uphold his duty," he said. "But he was a real miracle to
watch out in the field. Now, this first example you've shown me..." He pointed to an
example I'd written down for him.
<pre>
kitty_toys =
[:shape => 'sock', :fabric => 'cashmere'] +
[:shape => 'mouse', :fabric => 'calico'] +
[:shape => 'eggroll', :fabric => 'chenille']
kitty_toys.sort_by { |toy| toy[:fabric] }
</pre>
"This is a small miracle," he said. "I can't deny its beauty. Look, there are my kitty toys, laid
out with their characteristics. Below them, the block, sorting them by fabric."
"I apologize if your list of toys looks a bit tricky," I said. Like you, he had learned about the
Array, the caterpillar stapled into the code, with square brackets on each side
and each item separated by commas. (Ah, here is one: @['sock', 'mouse', 'eggroll']@.)
He had also been taught the Hash, which is like a dictionary, with curly braces on each end
which look like small, open books. Commas in the Hash between each pair. Each word
in the dictionary matched up with its definition by an arrow. (Be beholden: @{'blix' => 'cat',
'why' => 'human'}@.)
"Yes, vexing," he said. "It has square brackets like it's an Array, but with the arrows like it's a Hash.
I don't think you're going to get away with that."
"It does seem a bit subversive, doesn't it?" I said, tease-nudging him with a spoon. "I've done your kitty toy list
in a mix of the two. I'm using a shortcut. Which is: *If you use arrows inside of an Array,
you'll end up with a Hash inside of that Array.*"
"Oh, I see," he said. "You criss-crossed 'em. How neat!"
"Yes, yes, you're on it," I said. He was also very good with a protractor. "I have three Arrays,
each with a Hash inside. Notice the plus signs? I'm adding them into one big Array. Here's
another way of writing it..." I jotted down.
<pre>
kitty_toys = [
{:shape => 'sock', :fabric => 'cashmere'},
{:shape => 'mouse', :fabric => 'calico'},
{:shape => 'eggroll', :fabric => 'chenille'}
]
</pre>
One Array, which acts as our list of chew toys. Three Hashes in the Array to describe each toy.
h3. Sorting and Iterating to Save Lives
"Let's sort your toys by shape now," I said. "Then, we'll print them out in that order."
<pre>
kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
end
</pre>
"How does @sort_by@ work?" asked Blix. "I can tell it's a method you can use with Arrays. Because
@kitty_toys@ is an Array. But what is @toy@?"
"Okay, @toy@ is a *block argument*," I said. "Remember: the skinny pipes on each side of @toy@ make it
a *chute*."
"Sure, but it looks like you're using it like a Hash. Inside the block you have @toy[:shape]@.
That looks like a Hash."
"The @sort_by@ method is an *iterator*, Blix. It *iterates*, or *cycles*, through *a list of things*. You
remember that episode when Mad..."
"Episode?" he said. Yeah, he can't understand the concept of TV dramas.
Yeah, I've tried explaining.
"Or, yeah, remember that one _eyewitness account_ we watched where Mad was trying to talk
down that crazy spelling bee contestant from the ledge of an college library?"
"I remember it better than you because I was riding in a remote control plane." Yep, it was
one of those episodes.
"Do you remember how Mad got the guy to come down?" I asked.
"People in spelling bees love letters," said Blix. "So what Mad did was a genius move on his part.
He started with the letter A and gave reasons, for all the letters of the alphabet, why the guy
should walk back down the building and be safe on the ground."
"'A is for the Architecture of buildings like this,'" I said, in a gruff Mad voice.
"'Which give us hope in a crumbling world.'"
"'B is for Big Guys, like your friend Mad the Cop,'" said Blix. "'Guys who help people all the time
and don't know how to spell too great, but still help guys who spell really great.'"
"See, he went through all the letters, one at a time. He was _iterating_ through them." _It Err Ate Ing._
"But the guy jumped anyway, Why. He jumped off on letter Q or something."
"'Q is for Quiet Moments that help us calm down and think about all of life's little pleasures,
so we don't get all uptight and starting goofing around on tiptoes at the very edge of a big, bad building.'"
"And then he jumped," said Blix. He shook his head. "You can't blame Mad. He did his best."
"He had a big heart, that's for sure," I said, patting Blix on the shoulder.
<pre>
kitty_toys.sort_by { |toy| toy[:shape] }.each do |toy|
puts "Blixy has a #{ toy[:shape] } made of #{ toy[:fabric] }"
end
</pre>
"As for your @sort_by@, it *starts at the top* of the list and *goes through each item*, one at a time.
So @toy@ is one of those items. With each item, @sort_by@ stops and *slides that item down the
chute*, under the @toy@ name, and let's you figure out what to do with it."
"Okay, so @toy@ takes turns being each of the different toys I have."
"That's right," I said. "You know how I've really been harping on _using the answers that methods give
you_? Here, we're simply looking up the toy's shape inside the block. The block then answers to @sort_by@
with the shape string, such as @"mouse"@ or @"sock"@. Once it's done cycling through the whole list, @sort_by@
will have alphabetically compared each of the shape strings and will give back a new sorted Array."
h3. An Unfinished Lesson
"That's good enough for today," said Blix. "Can I have a fresh saucer of milk, please?"
I filled his saucer to the brim and he guzzled from it for some time while I took a poker and
jabbed at coals in the fireplace. My mind wandered and I couldn't help but think further of
blocks. I wondered what I would teach Blix next.
I probably would have taught him about @next@. When you are iterating through a list, you
may use @next@ to *skip on to the next item*. Here we're counting toys that have a non-eggroll
shape by skipping those that do with @next@.
<pre>
non_eggroll = 0
kitty_toys.each do |toy|
next if toy[:shape] == 'eggroll'
non_eggroll = non_eggroll + 1
end
</pre>
I could also have taught him about @break@, which *kicks you out of an iterating loop*.
In the code below, we'll print out (with @p@) each of the toy Hashes until we hit
the toy whose fabric is chenille. The @break@ will cause the @each@ to abruptly end.
<pre>
kitty_toys.each do |toy|
break if toy[:fabric] == 'chenille'
p toy
end
</pre>
I never got to teach him such things. I continued poking away at a particularly stubborn coal
which was caught in the iron curtain of the fireplace and threatened to drop on my antelope
skin rug.
As I hacked away ferociously at the black stone, Blix slipped away, presumably on
the bus bound for Wixl, the very bustling metropolis of the animal economies. Who knows, he may
have first stopped in Ambrose or Riathna or any of the other villages along the way. My
instinct say that Wixl was his definitely his final stop.
Without any student to instruct and coax along, I found myself quite lonely, holed up in
the estate. In the stillness of the dead corridors, I began to sketch out a biography in the
form of this guide.
I worked on it whenever I found myself bored. And when I wasn't bored, I could always
switch on _The Phantom Menace_ to get me in the mood.
!i/the.elf-7.jpg(Someone let them all out.)!
- Them What Make the Rules and Them What Live the Dream:
- >
!i/dr.cham-1.gif(Through space and time... in his bell jar... on a mission to find himself...)!
Frankly, I'm sick and tired of hearing that Dr. Cham was a madman. Yes, he tried to bury
himself alive. Yes, he electrocuted his niece. Yes, in fact, he did dynamite a retirement
home. But this was all with good cause and, in each case, I believe he took the
correct course of action.
I'm sure you'd like to side with popular opinion, but you're bound
to feel some small trickle of admiration for him once he's taken time to teach you all
about Ruby's class definitions.
And moreso when you learn about mixins. And perhaps, by the end of the chapter, we can all
start to look beyond the Doctor's grievous past and stop calling him a madman.
So if you need to call him a madman, I'd start heading down to the train tracks to
smash up some long flourescent light bulbs. Get it out of your system right now,
before we dig in.
- This One's For the Disenfranchised: >
!i/elderly-1.gif(Some people still can't get past what he did.)!
If you give me a number, which is any year from Dr. Cham's life, I'll give you a synopsis of that
time period. And I'll do it as a Ruby method, so it's an independent piece, an isolated chunk of
code which can be hooked up to the voice of a robotic volcano, when such a thing becomes the apex
of authoritative voice talents.
Okay, so I need you to notice @def@ and @case@ and @when@. You've seen the Ranges, the closed
accordions of @1895..1913@, back in chapter 3. They contain both ends and in between. And the
backslashes at the end of each line simply ignore the _Enter_ key at the end of each line, assuring
Ruby that there is _more of this line to come_.
So, please: @def@ and @case@ and @when@.
<pre>
def dr_chams_timeline( year )
case year
when 1894
"Born."
when 1895..1913
"Childhood in Lousville, Winston Co., Mississippi."
when 1914..1919
"Worked at a pecan nursery; punched a Quaker."
when 1920..1928
"Sailed in the Brotherhood of River Wisdomming, which journeyed \
the Mississippi River and engaged in thoughtful self-improvement, \
where he finished 140 credit hours from their Oarniversity."
when 1929
"Returned to Louisville to pen a novel about time-travelling pheasant hunters."
when 1930..1933
"Took up a respectable career insuring pecan nurseries. Financially stable, he \
spent time in Brazil and New Mexico, buying up rare paper-shell pecan trees. Just \
as his notariety came to a crescendo: gosh, he tried to buried himself alive."
when 1934
"Went back to writing his novel. Changed the hunters to insurance tycoons and the \
pheasants to Quakers."
when 1935..1940
"Took Arthur Cone, the Headmaster of the Brotherhood of River Wisdomming, as a \
houseguest. Together for five years, engineering and inventing."
when 1941
"And this is where things got interesting."
end
end
</pre>
The @def@ keyword. Here is our first *method definition*. A plain kernel method, which can
be used anywhere in Ruby. And how do we run it?
<pre>
puts dr_chams_timeline( 1941 )
</pre>
Which answers with "And this is where things got interesting." It's the same story again and again:
_use your answers._ I've set things up above so that the @case@ statement always answers with a string.
And since the case statement is the final (and only) statement in the method, then the method
answers with that string. Trickling water spilling down from ledge to ledge.
Let me be clear about the @case@ statement. Actually, I should call it a @case..when@ statement,
since they cannot be used separately. The @case@ keyword is followed by a value, which is compared against
each of the values which follow @when@ keywords. The first value to qualify as a match is the one the
case uses and the rest are ignored. You can do the same thing with a bunch of @if..elsif@ statements,
but it's wordier.
<pre>
case year
when 1894
"Born."
when 1895..1913
"Childhood in Lousville, Winston Co., Mississippi."
else
"No information about this year."
end
</pre>
Is identical to:
<pre>
if 1894 === year
"Born."
elsif 1895..1913 === year
"Childhood in Lousville, Winston Co., Mississippi."
else
"No information about this year."
end
</pre>
The *triple equals* is a length of velvet rope, checking values much like the double equals. It's just:
the triple equals is a longer rope and it sags a bit in the middle. It's not as strict, it's a bit
more flexible.
Take the Ranges above. @(1895..1913)@ isn't at all *equal* to @1905@. No, the Range @(1895..1913)@ is only
truly *equal* to any other Range @(1895..1913)@. In the case of a Range, the triple equals cuts you a break
and lets the Integer @1905@ in, because even though it's not *equal* to the Range, it's *included* in the
set of Integers represented by the Range. Which is good enough in some cases, such as the timeline I put
together earlier.
Which actually looked like a timeline, didn't it? I mean, sure, @dr_chams_timeline@ method is code, but
it does read like a timeline, clean and lovely.
!i/elderly-2.gif(What research revealed.)!
- !^sidebar
Caring For You. And Your Wellness.: >
I need you to be in a good mental state for the latter half of this book.
Now is the time to begin conditioning you.
Let's start with some deep breathing. Give me a good deep breath and
count to four with me.
Here we go. 1. 2. 3. 4. Now exhale. You
can feel your eyes. Good, that's exactly it.
Now let's take a deep breath and, in your mind, draw a hippopotamus
as fast as you can. Quick quick. His legs, his folds, his
marshmallow teeth. Okay, done. Now exhale.
Take another deep breath and hold it tight. As you hold it tightly in
your chest, imagine the tightness is shrinking you down into a bug.
You've held your breath so hard that you're an insect. And all the
other bugs saw you shrink and they loved the stunt. They're clapping
and rubbing their feelers together madly. But you had an apple in
your hand when you were big and it just caught up with you, crushed
the whole crowd. You're dead, too. Now exhale.
Give me a solid deep breath and imagine you live in a town where
everything is made of telephone cords. The houses are all telephone
cords, the shingles, the rafters. The doorways are a thick mass of
telephone cords which you simply thrust yourself through. When you
go to bed, the bedspread is telephone cords. And the mattress and
box springs are telephone cords, too. Like I said, everything is made
out of telephone cords. The telephone itself is made of telephone
cords. But the telephone cord going to the telephone is made out of
bread and a couple sticks. Now exhale.
Breathe in. 1. 2. 3. 4. Breathe out.
Breath in. 1. 2. Another short breath in. 3. 4. Imagine both
of your hands snapping off at the wrists and flying into your computer
screen and programming it from the inside. Exhale.
Big, big deep breath. Deep down inside you there is a submarine.
It has a tongue. Exhale.
Breathe through your nostrils. Deep breath. Filter the air through
your nostrils. Breathing through the nostrils gives you quality air.
Your nostrils flare, you are taking breaths of nature's air, the way
God intended. Imagine a floppy disk drive clogged up with orphans.
And while it chokes on orphans, you have good, wholesome God's
breath in your lungs. But that pleasurable, life-giving air will
become a powerful toxin if held too long. _Hurry, exhale God and
nature's air!_
Now, you will wake up, smoothing out the creases of this page in
your web browser. You will have full recollection of your
whole life and not forgetting any one of the many
adventures you have had in your life. You
will feel rich and renewed and expert. You will have no remembrance
of this short exercise, you will instead remember teaching a rabbit
to use scissors from a great distance.
And as you will wake up with your eyes directed to the top of this
exercise, you will begin again. But this time, try to imagine that
even _your shadow_ is a telephone cord.
- >
h3. But Was He Sick??
You know, he had such bad timing. He was scattered as a novelist, but his ventures into alchemy
were very promising. He had an elixir of goat's milk and sea salt that got rid of leg aches.
One guy even grew an inch on a thumb he'd lost. He had an organic health smoke that smelled like
foot but gave you night vision. He was working on something called Liquid Ladder, but I've never
seen or read anything else about it. It can't have been for climbing. Who knows.
One local newspaper actually visited Dr. Cham. Their book reviewer gave him four stars.
Really. She did an article on him. Gave him a rating.
Just know that Dr. N. Harold Cham felt terrible about his niece. He felt the shock treatment
would work. The polio probably would have killed her anyway, but he took the chance.
On Sept. 9, 1941, after sedating her with a dose of phenacetin in his private operating room, he attached
the conducting clips to Hannah's nose, tongue, toes, and elbows. Assisted by his apprentice, a bespeckled
undergraduate named Marvin Holyoake, they sprinkled the girl with the flakes of a substance the doctor
called _opus magnum_. A white powder gold which would carry the current and blatantly energize the girl,
forcing her blood to bloom and fight and vanquish.
But how it failed, oh, and how, when the lever was tossed, she arched and kicked -- and
*KABLAM!* -- and *BLOY-OY-OY-KKPOY!* Ringlets of hair and a wall of light, and the
bell of death rang. The experiment collapsed in a dire plume of
smoke and her innocence (_for weeks, everyone started out with, "And she will never
have the chance..."_) was a great pit in the floor and in their lungs.
To Hannah, I code.
<pre>
opus_magnum = true
def save_hannah
success = opus_magnum
end
</pre>
A method is its own island. And what goes on inside is unaffected by the simple variables around it.
Dr. Cham couldn't breach the illness of his niece, no more than an @opus_magnum@ variable can penetrate
the steely exterior of a method.
Should we run the @save_hannah@ method, Ruby will squawk at us, claiming it sees no @opus_magnum@.
I'm talking about *scope*. Microscopes narrow and magnify your vision. Telescopes extend the
range of your vision. In Ruby, *scope* refers to a field of vision inside methods and blocks.
A method's @def@ statement opens its vision. Variable names introduced there will be seen
by the method and kept meaningful until its @end@ closes its eyes. You can pass data into a
method by using arguments and data can be returned from the method, but the names used inside
the method are only good for its scope.
Some variables have wider scope. Global variables like @$LOAD_PATH@, which start with a *cash*
symbol, are available in any scope. Instance variables like @@names@, which start with an *at*
are available anywhere inside a class scope. Same goes for class variables like @@@tickets@.
Class and instance variables will be explored in a moment.
Blocks have scope, but it's a bit fuzzier. More flexible.
<pre>
verb = 'rescued'
['sedated', 'sprinkled', 'electrocuted'].
each do |verb|
puts "Dr. Cham " + verb + " his niece Hannah."
end
puts "Yes, Dr. Cham " + verb + " his niece Hannah."
</pre>
The block _iterates_ (spins, cycles) through each of the Doctor's actions. The @verb@ variable
changes with each pass. In one pass, he's sedating. In the next, he's powdering. Then, he's
electrocuting.
So, the question is: after the block's over, will he have rescued Hannah?
<pre class="result">
Dr. Cham sedated his niece Hannah.
Dr. Cham sprinkled his niece Hannah.
Dr. Cham electrocuted his niece Hannah.
Yes, Dr. Cham electrocuted his niece Hannah.
</pre>
Blocks are allowed to see variables in the vicinity. The block noticed that the @verb@ variable
existed and it overwrote its contents as it went along. When the block completed and its tiny life
ended, the @verb@ variable came out a changed creature.
If a block uses a variable which hasn't been used previously, though, then that variable vanishes at the
end of the block. The block's *scope* closes and the variable goes with it. Say that @verb@ wasn't used
before the block.
<pre>
['sedated', 'powdered', 'electrocuted'].
each do |verb|
puts "Dr. Cham " + verb + " his niece Hannah."
end
puts "Yes, Dr. Cham " + verb + " his niece Hannah."
</pre>
Pulls an error: @undefined local variable or method `verb'@. Poof.
It must be something difficult, even for a great scientist, to carry away the corpse of a young girl
whose dress is still starched and embroidered, but whose mouth is darkly clotted purple at the corners.
In Dr. Cham's journal, he writes that he was tormented by her ghost, which glistened gold and scorched
lace. His delusions grew and he ran from hellhounds and massive vengeful, angelic hands.
Only weeks later, he was gone, propelled from these regrets, vanishing in the explosion
that lifted him from the planet.
And even as you are reading this now, sometime in these moments, the bell jar craft of our
lone Dr. Cham touched down upon a distant planet after a sixty year burn.
As the new world came into view, as the curvature of the planet widened,
as the bell jar whisked through the upset heavens, tearing through sheets of aurora and
solar wind, Dr. Cham's eyes were shaken open.
!i/dr.cham-2.gif(Safe landing. Amazement.)!
What you are witnessing is the landing of Dr. Cham on the planet Endertromb. From what I
can gather, he landed during the cusp of the Desolate Season, a time when there really isn't
much happening on the planet. Most of the inhabitants find their minds locked into a listless
hum which causes them to disintegrate into just vapid ghosts of one-part-wisdom and three-parts-steam
for a time.
My modest grasp of the history and climate of Endertromb has been assembled from hanging around
my daughter's organ instructor, who grew up on the planet.
!i/elderly-3.gif(Dead husbands could destroy the Doctor.)!
I frequently drill my daughter's organ instructor in order to ensure that he can keep appointments
adequately. That he can take house calls at odd hours and promptly answer emergency calls.
When he finally revealed to me that he was an alien whose waking day consisted of five-hundred
and forty waking hours, I was incredibly elated and opened a contractual relationship with him
which will last into 2060.
For three days (by his pocket watch's account), Dr. Cham travelled the dark shafts of air,
sucking the dusty wind of the barren planet.
But on the third day, he found the Desolate Season ending and he awoke to a brilliant vista,
decorated with spontaneous apple blossoms and dewy castle tiers.
- A Castle Has Its Computers: >
!i/dr.cham-3.jpg(The panoramic vales of Sedna on Endertromb.)!
Our intrepid Doctor set off for the alien castle, dashing through the flowers. The ground belted
past his heels. The castle inched up the horizon. He desired a stallion, but no stallion
appeared. And that's how he discovered that the planet wouldn't read his mind and
answer his wishes.
As my daughter's organ instructor explained it, however, the planet *could read minds* and it *could
grant wishes*. Just not both at the same time.
One day as I quizzed the organ maestro, he sketched out the following Ruby code on a pad
of cheese-colored paper. (And queer cheese smells were coming from somewhere, I
can't say where.)
<pre>
require 'endertromb'
class WishMaker
def initialize
@energy = rand( 6 )
end
def grant( wish )
if wish.length > 10 or wish.include? ' '
raise ArgumentError, "Bad wish."
end
if @energy.zero?
raise Exception, "No energy left."
end
@energy -= 1
Endertromb::make( wish )
end
end
</pre>
This is the wish maker.
Actually, no, this is a *definition for a wish maker.* To Ruby, it's a *class definition*.
The code describes how a certain *object* will work.
Each morning, the wish maker starts out with up to five wishes available for granting.
A new @WishMaker@ is created at sun up.
<pre>
todays_wishes = WishMaker.new
</pre>
The @new@ method is a class method which creates a new, blank object. It also calls the
object's @initialize@ method automatically. In the @WishMaker@ definition, you'll see
the @initalize@ method, which contains a single line of code: @@energy = rand( 6 )@.
The @rand( 6 )@ picks a number between 0 and 5. This number will represent the number
of wishes left in the day. So, occassionally there are no wishes available
from the wish maker.
The random number is assigned to an *instance variable* which is named @@energy@.
This instance variable will be available any time throughout the class. The variable
can't be used outside the *scope* of the class.
In chapter three, we briefly looked at instance variables and decided to respect them
as *attributes*. (The *at symbol* could mean *attribute*.) Instance variables can used
to store any kind of information, but they're most often use to store bits of information
about the object represented by the class.
In the above case, each wish maker for the day has its own energy level. If the wish maker
were a machine, you might see a gauge on it that points to the energy left inside.
The @@energy@ instance variable is going to act as that gauge.
<pre>
todays_wishes = WishMaker.new
todays_wishes.grant( "antlers" )
</pre>
Okay, step back and ensure you understand the example here. The @WishMaker@ class is
an outline we've laid out for how the whole magic wish program works. It's not the _actual_ genie in
the bottle, it's the paperwork behind the scenes. It's the rules and obligations the genie
has to live by.
It's @todays_wishes@ that's the genie in the bottle. And here we're giving it a wish to grant.
Give us antlers, genie. (If you really get antlers from this example, I don't want to hear
about it. Go leap in meadows with your own kind now.)
In the last chapter, the drill was: Ruby has two halves.
# Defining things.
# Putting those things into action.
What are the actions in Ruby? Methods. And now, you're having a lick of the definition language
built-in to Ruby. Method definitions using @def@. Class definitions using @class@.
At this point in your instruction, it's easier to understand that *everything in Ruby is an object.*
<pre>
number = 5
print number.next # prints '6'
phrase = 'wishing for antlers'
print phrase.length # prints '19'
todays_wishes = WishMaker.new
todays_wishes.grant( "antlers" )
</pre>
And, consequently, each object has a class behind the scenes.
<pre>
print 5.class # prints 'Integer'
print 'wishing for antlers'.class # prints 'String'
print WishMaker.new.class # prints 'WishMaker'
</pre>
Dr. Cham never saw the wish maker as he hustled across the landspace.
It lay far beyond his landing in the valley of Sedna. Down sheer cliffs
stuffed with layers of thicket, where you might toss your wish (written
on a small 1" x 6" slip), down into the gaping void. Hopefully it will
land on a lizard's back, sticking to its spindly little horn.
And let's say your wish makes it that far. Well, then, _down the twisted wood_ goes
the skinny salamander, scurrying through the decaying churches which had been
*pushed* over that steep canyon ledge once and for all. And the expired priest inside, _who
weathered the fall_ as well, will kill the little amphibian -- strangle it
to death with a blessed gold chain -- and save it for the annual _Getting To
Know You_ breakfast. He'll step on your precious little wish and, when the
*thieves come*, that slip will still be there, stuck on his sole. Of course,
the thieves' *preferred method of torture* is to cut a priest in thin deli-shaved slices _from
top to bottom_. Who can cull evidence from that? And when they chop that last thin
slice of shoe sole, they'll have that *rubber scalp* in hand for _good luck_ and _good times_.
But they *canoe* much too hard, these thieves. They slap their paddles swiftly in the current
to get that great _outboard motor mist_ going. But the shoe sole is _on a weak chain_, tied
to one man's belt. And a *hairy old carp* _leaps, latches_ on to that minute fraction of
footwear. And the thieves _can try_, but they don't see _underwater_. If they could, they'd
see that *mighty cable*, packed with millions of _needly_ fiber optics. Indeed, *that fish is
a peripheral plugged* right into the _core workings_ of the planet Endertromb. *All it takes
is one swallow* from that fish *and your wish is home free!*
And that's how wishes come true for children in this place.
Once my daughter's organ instructor had drawn up the class for the wish maker, he then followed
with a class for the planet's mind reader.
<pre>
require 'endertromb'
class MindReader
def initialize
@minds = Endertromb::scan_for_sentience
end
def read
@minds.collect do |mind|
mind.read
end
end
end
</pre>
Much as you've seen before, the @initalize@ happens when a new @MindReader@ object is created.
This @initialize@ gathers scans the planet for mindshare. It looks like
these minds are stored in an array, since they are later iterated over using the @collect@ method.
Both the wish maker and the mind reader refer to a class named @Endertromb@. This class is
stored in a file @endertromb.rb@, which is loaded with the code: @require 'endertromb'@.
Often you'll use other classes to accomplish part of your task. Most of the latter half of this
book will explore the wide variety of helpful classes that can be loaded in Ruby.
h3. Dr. Cham Ventures Inside
But as Dr. Cham neared the castle, although the planet was aware of his thoughts, sensing his
wonderment and anticipation, all Dr. Cham felt was deadness. He tromped up the steps of its
open gate and through the entrance of the most beautiful architecture and was almost certain it
was deserted.
For a while he knocked. Which paid off.
!i/castle-1.jpg(Blocky whale greeting.)!
He watched the baby whale rise like a determined balloon. He marvelled at his first alien introduction
and felt some concern that it had passed so quickly. Well, he would wait inside.
As he stepped through the castle door, he felt fortunate that the door hadn't been answered by
a huge eagle with greedy talons, eager to play. Or a giant mouse head. Or even a man-sized hurricane.
Just a tubby little choo-choo whale.
"Not a place to sit down in this castle," he said.
At first, he had thought he had just entered a very dim hallway, but as his eyes adjusted, he saw
the entrance extended into a tunnel. The castle door had opened right into a passage made of
long, flat slabs of rock. Some parts were congruous and resembled a corridor. Other parts
narrowed, and even tilted, then finally tipped away out of view.
The passage was lit by small doorless refrigerators, big enough to hold an armful of cabbage,
down by his feet. He peered inside one, which was hollow, illuminated along all sides, and
turning out ice shards methodically.
He pawed the ice chips, which clung dryly to his fingertips, and he scrubbed his hands in the
ice. Which left some muddy streaks on his hands, but satisfied a small part of his longing to bathe.
How long had it been? Ten years? Thirty?
Along the passage, long tubes of cloth cluttered some sections. Later, bright pixel matter in porcelain
scoops and buckets.
He happened upon a room which had been burrowed out of the tunnel which had a few empty turtle
shells on the ground and a large illuminated wall. He stared into the room, bewildered. What could
this be? In one state of mind, he thought of having a seat on a shell. This could be the entrace at
last, some kind of receiving room. On the other hand, spiders could pour out of the shell's hollow when he
sat. He moved on.
h3. Meal in a Castle's Pocket
As he journeyed along the passageways (for the central tunnel forked and joined larger, vacuous caverns), he picked up
themes in some locations. Groups of rooms infested with pumping machinery. Cloth and vats of glue
dominated another area. He followed voices down a plush, pillowed cavity, which led him to a
dead end: a curved wall with a small room carved at eye-level.
He approached the wall and, right in the cubby hole, were two aardvarks eating at a table.
They gazed at him serenely, both munching on some excavated beetle twice their size, cracked
open and frozen on its back on the table.
"Hello, little puppets," he said, and they finished their bites and kept looking with their forks
held aloof.
"I wish my niece Hannah were here to meet you," he told the attentive miniature aardvarks. "She'd
think you were an intricate puppet show." He peered in at the dining area, shelves with sets of
plates, hand towels. Half of a tiny rabbit was jutting out from the top a machine,
creamy red noodles were spilling out underneath it. A door at the back of the room hung ajar. Dr. Cham could
see a flickering room with chairs and whirring motors through the door.
"Any child would want this dollhouse," he said. "Hannah, my niece, as I mentioned, she has
a wind-up doll that sits at a spindle and spins yarn. It's an illusion, of course.
The doll produces no yarn at all."
One of the aardvarks opened a trapdoor in the floor and pressed a button down inside, which lit.
Then, a small film projector slowly came up on a rod. The other aardvark sat and watched Dr. Cham.
"But Hannah still reaches down into the dollhouse and collects all the imaginary yarn into a
bundle. Which she takes to her mother, my sister, who is very good at humoring Hannah.
She sews a dress to the doll's dimensions, which Hannah takes back to the doll.
"And she tells
the doll, 'Here, look, your hard work and perserverance has resulted in this beautiful dress.
You can now accept the Chief of Police's invitation to join him tonight at the Governor's
Mansion.' And she has a doll in a policeman's uniform who plays the part of the Chief.
He's too scrawny to be an actual Chief, that would require quite a bit of plastic."
The aardvark responsible for the film projector loaded a reel and aimed the projector at the
back wall. The film spun to life and the aardvark took a seat. A green square appeared on the
wall. The attentive aardvark stared at Dr. Cham still.
"Your films are coloured," said Dr. Cham. "What a lovely, little life."
The film played on: a blue square. Then, a red circle. Then, an orange square. The attentive
aardvark turned away, watched the screen change to a pink triangle, and both aardvarks resumed eating.
A purple star. A red square. With quietness settling, Dr. Cham could hear notes droning
from the projector. Like a slow, plodding music box trying to roll its gears along the train
tracks.
"Yes, enjoy your supper," said Dr. Cham and he politely tipped his head away, marching
back up the path he'd taken.
h3. Another Dead End Where Things Began
He found himself lost in the castle's tunnels. Nothing looked familiar. He wasn't worried much,
though. He was on another planet. He would be lost regardless.
He wound through the tunnels, attempting to recall his paths, but far too interested
in exploring to keep track of his steps. He followed a single tunnel deep, down, down,
which slanted so steeply that he had to leap across ledges and carefully watch his footholds.
The gravity here seemed no different than Earth. His legs were pulled into slides just
as easily.
Although he had no absolute way of knowing where he was, he felt certain that he had left
the castle's boundaries. This deep, this long of a walk. It had been an hour since he'd
entered through the door. And, as the tunnel wound back up, he was sure that he would
emerge into a new dwelling, perhaps even a manhole which he could peek out from and see
the castle. Perhaps he shouldn't have come so far down this route. He hoped nothing was
hibernating down here.
The tunnel came to a stop. A dark, dead end.
!i/dr.cham-4.gif(At the end of the tunnels: a computer and a book.)!
He had time. So he read the book. He read of the foxes and their pursuit of the porcupine who stole their
pickup truck. He read of the elf and the ham. He saw the pictographs of himself and found he
could really relate to his own struggles. He even learned Ruby. He saw how it all ended.
Were I him, I couldn't have stomached it. But he did. And he pledged in his bosom to see
things out just as they happened.
On the computer monitor, Dr. Cham saw the flashing @irb@ prompt. Like Dr. Cham, you might
recognize the @irb@ prompt from "The Tiger's Vest":expansion-pak-1.html
(the first expansion pak to this book, which includes a basic introduction to Interactive Ruby.)
Whereas he had just been exploring tunnels by foot, he now explored the machine's setup with
the prompt. He set the book back where he had found it. He didn't need it anymore. This was
all going to happen whether he used it or not.
He started with:
<pre>
irb> Object::constants
=> ["Marshal", "String", "Dir", "LoadError", "Float", ... and so on ]
</pre>
This command lists all the top-level constants. Classes are also listed as constants, so
this list can be great to see what's loaded into Ruby at any time.
He scanned the list for anything unfamiliar. Any classes which didn't come with Ruby.
@Marshal@, @String@, @Dir@, @LoadError@, @Float@. Each of those came with Ruby.
But further down the list:
<pre>
... "Struct", "Values", "Time", "Elevator", "Range" ...
</pre>
_Elevator?_ Exactly the kind of class to poke around with. He had a go.
<pre>
irb> Elevator::methods
=> ["method", "freeze", "allocate", ... another long list ... ]
irb> Elevator::class_variables
=> ['@@diagnostic_report', '@@power_circuit_active', '@@maintenance_password']
irb> Elevator::constants
=> []
</pre>
Looks like the @Elevator@ class had plenty of methods. Most of these looked
like they were the same methods every object has in Ruby. For example,
@method@, @freeze@ and @allocate@ come with every class in Ruby. (@Elevator::freeze@
would keep the @Elevator@ class from being changed. @Elevator::allocate@ would
make a new @Elevator@ object without calling the @initialize@ method.)
The @class_variables@ were interesting to Dr. Cham. This elevator appeared genuine.
But no available @constants@. This tells us there are no classes nested inside
the @Elevator@ class.
He tried to create an @Elevator@ object.
<pre>
irb> e = Elevator::new
ArgumentError: wrong number of arguments (0 for 1), requires a password
from (irb):2:in `initialize'
from (irb):2:in `new'
from (irb):2
from :0
</pre>
He tried a few passwords.
<pre>
irb> e = Elevator::new( "going up" )
AccessDeniedError: bad password
irb> e = Elevator::new( "going_up" )
AccessDeniedError: bad password
irb> e = Elevator::new( "stairs_are_bad" )
AccessDeniedError: bad password
irb> e = Elevator::new( "StairsAreBad" )
AccessDeniedError: bad password
</pre>
That was useless. _Oh, wait!_ The maintenance password. Listed in the @class_variables@.
<pre>
irb> Elevator::maintenance_password
NoMethodError: undefined method `maintenance_password' for Elevator:Class
from (irb):1
from :0
</pre>
Hmm. Instance variables are only available inside an object. And class variables are only
available inside a class. How to get at that password?
<pre>
irb> class Elevator
irb> def Elevator.maintenance_password
irb> @@maintenance_password
irb> end
irb> end
=> nil
irb> Elevator::maintenance_password
=> "stairs_are_history!"
</pre>
Alright! He got the password. Did you see that?
He added a class method to the @Elevator@ class. Isn't that great how you can start a new class
definition for @Elevator@ and Ruby just adds your changes to the existing class definition?
Class methods are usually called with the *double colon*. But, a period is fine as well.
Since @Elevator@ is a class itself, Ruby will figure that if you call @Elevator.maintenance_password@,
you're calling a class method. The double colon simply helps make class methods obvious
to the reader.
And justly so. Class methods are a bit unusual. Normally you won't want to store information
directly inside of a class. However, if you have a bit of information that you need to
share among all objects of a class, then you have a good reason to use the class for storage.
It's understandable that the @@@maintenance_password@ would be stored in the class, instead of
in each separate object. This way, the objects can simply reach up into the class and see the
shared password.
Here's probably how the password protection works.
<pre>
class Elevator
def initialize( pass )
raise AccessDeniedError, "bad password" \
unless pass.equals? @@maintenance_password
end
end
</pre>
Passwording a class like this is pointless, since anything in Ruby can be altered and
overwritten and remolded. Dr. Cham had the password and ownership of the elevator is his.
<pre>
irb> e = Elevator.new( "stairs_are_history!" )
#<Elevator:0x81f12f4 @level=4>
irb> e.level = 1
</pre>
Dr. Cham was standing right there when the elevator doors, off behind the computer terminal,
opened for him. With an exasperated sense of accomplishment and a good deal of excitement
surrounding all of the events that lie ahead, he stepped into the elevator and pressed 4.
- !^sidebar
An Evening of Unobstructed Voltage: >
I dug up this article from _The Consistent Reminder_, a Connecticut newspaper
which ran the four star review of Dr. Cham. Midgie Dare, the book reviewer
who suddenly opened her critical eye to anything tangible, praised the Doctor for his
manners and innovations in the very same daily edition that she defamed cantaloupe and docked
Manitoba for having crackly telephone service.
I got a kick out of the end of her article. Here you go.
bq. He dismounted his horse with unquestionable care for anyone who
might be in the vicinity. Attentive of all sides, he lowered himself
from the saddle gently, slowing to a pace which must be measured in
micrometers per second to be appreciated.
bq. Those of us in his company
found ourselves with maws agape, watching his boot touch down upon
the ground. So precise and clean a step that it seemed it would never
meet the earth, only hover slight above it. Then, before the landing
had actually registered with any of us, we were off to the cuisine, whisked
away in the shroud of gaiety that was always right in front of Harold Cham,
always just behind him, and most especially concentrate directly
in his own luminary self.
bq. He also carried loosely at his side a capitally ignorant statesman's
daughter, who spared us no leave from her constant criticisms of atheists
and railway routes.
bq. "At home, my efforts to light a candle were trounced upon
by further train rumblings, which thrusted the match in my hand nearer the curtains!"
She derided Dr. Cham for his waning grip
on her forearm and became jealous when he was able to tune into a pleasurable
woman's voice on the radio once we returned to the residence.
bq. The dusk did settle, however, and we found ourselves in a communal daze beneath
the thick particles of cotton drift that wafted through the polished piano room,
quite entertained by the _Afternoon Nap Program_, which played their phonograph
so quietly at the station that we could only hear the scratching of dead Napoleon's
sleeves across the bedsheets. I felt a great shriek inside me at the thought!
Still, on yonder chairs, the two lovers kept an abrupt distance between themselves and
I felt encompassed by Dr. Cham's warm gaze and his playful tip of the sherry glass.
- The Continued Story of My Daughter's Organ Instructor: >
I know you may be alarmed to hear that I have a daughter. You think my writing is indicative
of a palsied or infantile mind. Well, please rest. I don't have a daughter.
But I can't let that stop me from sorting out her musical training.
As I was related these elaborate histories of the planet Endertromb, I found myself
wandering through hallways, running my fingertips along the tightly buttoned sofas
and soaking myself in the saturated bellowings of the pipes, as played by my daughter's
organ instructor. His notes resounded so deep and hollow in the walls of his manor
that I began to casually mistake them for an ominous silence, and found it even
easier to retreat into deep space with my thoughts. To think upon the ancient
planet and its darker philosophies: its flesh temples, tanned from the dermal remains
of its martyrs; its whale cartels, ingesting their enemies and holding them within for
decades, dragging them up and down the staircases of ribs; its poison fogs and its
painful doorways; and, the atrocious dynasties
of The Originals, the species which claims fathership to all of the intellegent beings
across the universe.
But, eventually, I'd hear those pipes of a higher octave sing and I'd be back in the very same
breezy afternoon where I'd left.
How interesting that even the breeze of our planet is quite a strange thing to some
outsiders. For he had also told me of the travellers from Rath-d, who ventured to Earth
five centuries ago, but quickly dissipated in our air currents since they and their crafts
and their armor were all composed of charcoal.
I had sat at the organ, listening to his faint tales of his colony, while he punctuated his
symphonies to greater volumes and the story would disappear for awhile, until the coda came back around.
He spoke of he and his brothers piling into the hollow of his mother's tail and tearing the
waxy crescent tissue from the inner wall. Juicy and spongy and syrupy soap which bleached
their mouths and purged their esophagus as it went down. They chewed and chomped the stuff
and it foamed. After they ate, they blew bubbles at each other, each bubble filled with a dense
foam, which they slept upon. And early in the morning, when mother opened her tail again, she watched
serenely as her babies lay cradled in the stew of dark meatballs and sweet, sticky froth.
He spelled out all the tastes of Endertromb. Of their salmon's starchy organs, which cooked
into a pasta, and its eyes which melted into rich cream. Of their buttermelon with tentacles.
And he was just beginning to appreciate the delicacies as a child, only to be lifted from a
schoolyard by a pair of upright pygmy elephants who reached at him, through the heavens, and
snatched upon his collar with a vast length of crane.
They transplanted him on Earth, led him from their craft, trumpeting their snouts loudly for the city of
Grand Rapids to hear, then left, weeping and embracing each other.
"But, strangely (em-pithy-dah), I learned upon, played upon (pon-shoo) the organs on my
home (oth-rea) planet," he said.
My daughter's organ instructor speaks these extra words you see in parentheses. Who knows
if they are from his native tongue or if they are his own soundful hiccups. He keeps
another relic from Endertromb as well: he has twelve names.
"No, (wen-is-wen)," he said. "I have one name (im-apalla) which is said (iff)
many-many different ways."
I call him Paij-ree in the morning and Paij-plo in the later evening.
Since it is day as I write, I will call him Paij-ree here.
h3. Mumble-Free Earplugs
!>i/my.daughters.organ-1.gif(Alien at the keys.)!
So I told Paij-ree, "Paij-ree, I am writing a book. To teach the world Ruby."
"Oh, (pill-nog-pill-yacht) nice," he said. He's known Ruby longer than I have, but still:
_I_ will be my daughter's Ruby instructor.
And I said, "Paij-ree, you are in the book. And the stories of your planet." I talk to
him like he's E.T. I don't know why. Just like how I said next, "And then maybe someday
you can go home to your mom and dad!"
To which he said, "(pon-shoo) (pon-shoo) (em-pithy-dah)." Which is his way of speaking
out loud his silence and awe.
He wanted to see what I'd written, so I showed him this short method I've written for you.
<pre>
def wipe_mutterings_from( sentence )
while sentence.include? '('
open = sentence.index( '(' )
close = sentence.index( ')', open )
sentence[open..close] = '' if close
end
end
</pre>
"Can you see what this does, Paij-ree? Any old Smotchkkiss can use this method
to take all the incoherent babblings out of your speaking," I said.
And I fed something he said earlier into the method.
<pre>
what_he_said = "But, strangely (em-pithy-dah),
I learned upon, played upon (pon-shoo) the
organs on my home (oth-rea) planet."
wipe_mutterings_from( what_he_said )
print what_he_said
</pre>
And it came out as a rather plain sentence.
<pre>
But, strangely ,
I learned upon, played upon the
organs on my home planet.
</pre>
"You shouldn't use that (wary-to) while loop," he said. "There are lovelier, (thopt-er),
gentler ways."
In the @wipe_mutterings_from@ method, I'm basically searching for opening parentheses. When I
find one, I scan for a closing paren which follows it. Once I've found both, I replace them and
their contents with an empty string. The @while@ loop continues until all parentheses are gone.
The mutterings are removed and the method ends.
"Now that I look at this method," I said. "I see that there are some confusing aspects
and some ways I could do this better." Please don't look down on me as your teacher for
writing some of this code. I figure that it's okay to show you some sloppy techniques
to help you work through them with me. So let's.
Okay, *Confusing Aspect No. 1*: This method cleans a string. But what if we accidentally
give it a @File@? Or a number? What happens? What if we run @wipe_mutterings_from( 1 )@?
If we give @wipe_mutterings_from@ the number 1, Ruby will print the following and exit.
<pre>
NoMethodError: undefined method `include?' for 1:Fixnum
from (irb):2:in `wipe_mutterings_from'
from (irb):8
</pre>
What you see here is a rather twisted and verbose (but at times very helpful) little fellow
called the *backtrace*. He's a wound-up policeman who, at the slightest sign of trouble,
immediately apprehends any and all suspects, pinning them against the wall and spelling out their
rights so quickly that none can quite hear it all. But it's plain that there's
a problem. And, of course, it's all a big misunderstanding, right?
When Ruby reads you these Miranda rights, listen hardest to the beginning. The first line
is often all you need. In this first line is contained the essential message. And in
the above, the first line is telling us that there is no @include?@ method for the number 1.
Remember, when we were talking about the @reverse@ method in the last chapter? Back then, I said,
"*a lot of methods are only available with certain types of values*." Both @reverse@ and
@include?@ are methods which work with strings but are meaningless and unavailable for numbers.
To be clear: the method tries to use to the number. The method will start with @sentence@ set to 1.
Then, it hits the second line: @while sentence.include? '('@. Numbers have no @include?@
method. Great, the backtrace has shown us where the problem is. I didn't expect anyone to pass in
a number, so I'm using methods that don't work with numbers.
*See, this is just it.* Our method is its own little pocket tool, right? It acts as its own
widget independent of anything else. To anyone out there using the @wipe_mutterings_from@
method, should they pass in a number, they'll be tossed this panic message that doesn't
make sense to them. They'll be asked to poke around inside the method, which really isn't
their business. They don't know their way around in there.
Fortunately, we can throw our own errors, our own *exceptions*, which may make more sense
to someone who inadvertantly hands the wrong object in for cleaning.
<pre>
def wipe_mutterings_from( sentence )
unless sentence.respond_to? :include?
raise ArgumentError,
"cannot wipe mutterings from a #{ sentence.class }"
end
while sentence.include? '('
open = sentence.index( '(' )
close = sentence.index( ')', open )
sentence[open..close] = '' if close
end
end
</pre>
This time, if we pass in a number (again, the number 1), we'll get something more sensible.
<pre>
ArgumentError: cannot wipe mutterings from a Fixnum
from (irb):3:in `wipe_mutterings_from'
from (irb):12
</pre>
The @respond_to?@ method is really nice and I plead that you never forget it's there. The
@respond_to?@ checks any object to be sure that it has a certain method. It then gives
back a @true@ or @false@. In the above case, the incoming @sentence@ object is checked for an
@include?@ method. If no @include?@ method is found, then we raise the error.
You might be wondering why I used a symbol with @respond_to?@. I used a symbol @:include?@
instead of a string @'include?'@. Actually, either will work with @respond_to?@.
Usually symbols are used when you are passing around the name of a method or any other Ruby construct.
It's more efficient, but it also catches the eye. The @respond_to?@ asks Ruby to look inside itself
and see if a method is available. We're talking to Ruby, so the symbol helps denote that. It's not
a big deal, Ruby just recognizes symbols quicker than strings.
Now, *Confusing Aspect No. 2*: Have you noticed how our method changes the sentence?
<pre>
something_said = "A (gith) spaceship."
wipe_mutterings_from( something_said )
print something_said
</pre>
Did you notice this? In the first line of the above code, the @something_said@ variable
contains the string @"A (gith) spaceship."@. But, after the method invocation, on the
third line, we print the @something_said@ variable and by then it contains the cleaned
string @"A spaceship."@.
How does this work? How does the method change the string? Shouldn't it make a copy
of the string before changing it?
Yes, absolutely, it should! *It's bad manners to change strings like that.* We've used @gsub@ and
@gsub!@ in the last chapter. Do you remember which of those two methods is a *destructive
method*, which changes strings directly?
Either we need to call this method @wipe_mutterings_from!@ (as a courtesy to all the other
good folks out there that might use this method) or change the method to work on a copy of
the string rather than the real thing. Which is an easy change! We just need to @dup@
the string.
<pre>
def wipe_mutterings_from( sentence )
unless sentence.respond_to? :include?
raise ArgumentError,
"cannot wipe mutterings from a #{ sentence.class }"
end
sentence = sentence.dup
while sentence.include? '('
open = sentence.index( '(' )
close = sentence.index( ')', open )
sentence[open..close] = '' if close
end
sentence
end
</pre>
The @dup@ method makes a copy of any object. Look at that line we added again on its own:
<pre>
sentence = sentence.dup
</pre>
What a peculiar line of code. How does @sentence@ become a copy of @sentence@?
Does it erase itself? What happens to the original @sentence@? Does it disappear?
Remember that variables are just nicknames. When you see @sentence = "A (gith) spaceship."@,
you see Ruby creating a string and then giving that string a nickname.
Likewise, when you see @sentence = sentence.dup@, you see Ruby creating a new string and then
giving that string a nickname. This is handy inside your method because now @sentence@ is
a nickname for a new copy of the string that you can safely use *without changing the
string that was passed into the method*.
You'll see plenty of examples of variable names being reused.
<pre>
x = 5
x = x + 1
# x now equals 6
y = "Endertromb"
y = y.length
# y now equals 10
z = :include?
z = "a string".respond_to? z
# z now equals true
</pre>
And, yes, sometimes objects disappear. *If you can't get to an object through a variable, then
Ruby will figure you are done with it and will get rid of it.* Periodically, Ruby sends out its
*garbage collector* to set these objects free. Every object is kept in your computer's memory
until the garbage collector gets rid of it.
- !^sidebar
An Excerpt from The Scarf Eaters: >
(_from Chapter VII: When Push Comes to Shove -- or Love_.)
"Never say my name again!" screamed Chester, and with the same gusto, he turned back
to the *File > Publish Settings...* dialog to further optimize his movie down to a
measley 15k.
# Mrs. Cothway appeared in the vaulted entry way with three elegant Arabian horses,
# but Woody refused to look away from his Vaio and shaved another 6k off his movie.
#
#
# And so she cantered with them to the laundry room entrace, which was fatal for each of the mares, as
# young Annabelle fed them too much 7-Up and Earmuffs.
- >
Oh, and one more thing about @dup@. Some things can't be dup'd. Numbers, for instance.
Symbols (which look like @:death@) are identical when spelled the same. Like numbers.
Also, some of the special variables: @nil@, @true@, @false@. These are things that Ruby won't let
you alter, so there's so point making a copy anyway. I mean, imagine if you could change @false@
to be @true@. The whole thing becomes a lie.
Perhaps *Confusing Aspect No. 3* is a simple one. I'm using those square brackets on the
string. I'm treating the string like it's an Array or Hash. I can do that. Because strings
have a @[]@ method.
When used on a string, the square brackets will extract part of the string. Again,
slots for a forklift's prongs. The string is a long shelf and the forklift is pulling out a slab
of the string.
Inside the brackets, we pass the _index_. It's the label we've placed right between the prongs
where the worker can see it. When it comes to strings, we can use a variety of objects as our
index.
<pre>
str = "A string is a long shelf of letters and spaces."
puts str[0] # prints 65 (the character code for an 'A')
puts str[0..-1] # prints 'A string is a long shelf of letters and spaces.'
puts str[1..-2] # prints ' string is a long shelf of letters and spaces'
puts str[1, 3] # prints 'A s'
puts str['shelf'] # prints 'shelf'
</pre>
Alright, the last *Confusing Aspect No. 4*: this method can be sent into an endless loop.
You can give this method a string which will cause the method to hang and never come back.
Take a look at the method. Can you throw in a muddy stick to clog the loop?
<pre>
def wipe_mutterings_from( sentence )
unless sentence.respond_to? :include?
raise ArgumentError,
"cannot wipe mutterings from a #{ sentence.class }"
end
sentence = sentence.dup
while sentence.include? '('
open = sentence.index( '(' )
close = sentence.index( ')', open )
sentence[open..close] = '' if close
end
sentence
end
</pre>
Here, give the muddy stick a curve before you jam it.
<pre>
muddy_stick = "Here's a ( curve."
wipe_mutterings_from( muddy_stick )
</pre>
Why does the method hang? Well, the @while@ loop waits until all the open parentheses
are gone before it stops looping. And it only replaces open parentheses that have a matching
closing parentheses. So, if no closing paren is found, the open paren won't be replaced
and the @while@ will never be satisfied.
How would you rewrite this method? Me, I know my way around Ruby, so I'd use a regular
expression.
<pre>
def wipe_mutterings_from( sentence )
unless sentence.respond_to? :gsub
raise ArgumentError,
"cannot wipe mutterings from a #{ sentence.class }"
end