One of the greatest things about Ruby is its never-ending ability to momentarily bewilder you before exploding your brain all over your face. Whether it’s a seemingly supurfluous bit of syntactic sugar (point for alliteration to me, in case you’re counting) or a questionable method call where one doesn’t seem necessary, Ruby constantly has me scratching my head mere seconds before blowing me away.
My most recent moment of ermahgerd came when discovering the
.send method. Here’s what it looks like when called on an
(The parenthesis aren’t necessary, but I like them for clarity. Up in the code. Not surrounding this sentence, silly.)
So what exactly is this doing? It looks wacky, fo sho. To break it down, let’s look at its equilavent in the way we normal human beings would most likely write it:
So yeah, that’s different. To understand what’s going on with
.send, which, by the way, ‘sends a message to a receiver’, we have to remember that
array[whatever] is really
array.=(index, value). Just because brackets are involved doesn’t mean a method isn’t being called (or that a message isn’t being sent).
.send then, basically passes a method to an object, along with a couple of arguments, and then invokes that method.
Mini-break: What does this whole “sends a message to a receiver” or “passes a method to an object” stuff mean? At first, it’s kind of annoy…erm…I mean difficult to understand the oft-repeated refrain that “everything in Ruby is an object.” First of all, what the heck is an object? And second of all, so what? Here’s the quick and dirty deets, supremely over-simplified—when we say “object” we really just mean “thing”. And now I’m guilty of saying it myself: everything really is an object. Like, _all the things_are ojects. Every last one of them. And they’re just things that chill and wait for you to tell them to do stuff. Which is really cool, because all of these things can respond to a zillion different methods. And methods are little messages you “send” to these objects that tell them to do stuff. An object either understands a method or it doesn’t. So if we think about it really abstractly, let’s say I’m a Ruby object. (Sweet!) You could send me a method like
.karate and I’d look at you like you’re crazy because my parents never let me do martial arts when I was younger and that was sad and I’ll stop rambling about that right now. But if you sent me a method like
.drink_water I’d know exactly what to do—I can drink a glass of water like a boss—and I’d do it. This, in a nutshell, is what objects and methods are all about in Ruby. (Well, kinda. Maybe. You know what? Just keep moving…)
Now, back to your regular programming (hah!)
Wait, why is it cool? Doesn’t it seem like an unnecessary step? After all, as we just decided, the following code would look just as weird, get the same thing done, and not use the
.send method at all:
(Ignore the weird syntax highlighting on that first bracket on line two—I think I broke the internet or something.)
So why bring
.send into the mix at all? It just seems like extra…stuff, right? Well, what happens if we think about dynamically calling methods based on stuff that happens in our code? Or, even better, what if we want to call methods depending on user input? Then we can do some really cool stuff.
Well, I mean, somebody could do really cool stuff. I’m just going to show you how to make a silly, over-simplified, bot-type-thingy that sorta-kinda responds to user input. Ish.
Boom…done! Ok, kidding. But we’ve gotten started at least. Here we’re just defining a
ChatterBox that we’ll be using to demonstrate a neat little use of
.send that comes in handy. Maybe not brain-explode-all-over-your-face handy, but huh-would-you-look-at-that handy. The
COMMANDS = [blah, blah, blah] bit is going to be useful in just a second. Actually, it’s going to be useful right now.
Let’s pretend for a second that we have some fancy pants command line interface that we can wrap around this sucker and that everytime a user types something in, that sentence gets passed to a
.call method on our
ChatterBox. Cool? Cool.
Sweet. Now let’s make it do something. I’m going to throw a bunch of code in there, and then we’ll chat (hah again!) about it:
The first line is pretty basic: it takes the passed in argument, sentence, splits it up (by default that happens at the spaces) using the
.split method, and iterates through that newly-minted
array, while keeping track of the index.
And now we move on to the dopeness that is
COMMANDS is just our array of possible commands we want to accept from the user. We first check to see if any word in the sentence that was passed in, when converted to a
sybmol matches one of our possible commands. If it does, we get to break out the magic.
.send, remember, works like this:
So in this case, our
object is our instance of the
ChatterBox class, and we send it, as a method, the word that matched one of our possible commands. Then, we send the rest of the sentence along as arguments.
.send takes care of all of this for us. It converts the
string version of the command into a symbol and uses that to call the method of the same name that we’ve written into the class. To that method, it passes the other non-matching part of the sentence.
In other words, say we do something like this:
In the sentence we passed to the
.call method, there is one of our commands: ‘What’.
.send then gets a hold of this in our
.call method and turns it into this:
And remembering how the
.send method works, we know that this is going to call a method,
.what and send it
[“are”, “you”, “doing”, “tonight”] as an argument.
So it’s essentially doing this:
But we didn’t actually have to hard-code in a call to that
Pretty neat, right? Based on a passed in sentence, we can fire off a specific method and then use that method to parse a sentence in any way that we want.
One of these (reallyverymessymethodsthatyoushouldonlylookatforliketwosecondssoyoureyesdon’tmelt) methods might look something like this:
I’ll let you go through that and see how poorly a person can actually write a bunch of
if statements, but it’s pretty fun to run if you add similar methods for the other possible commands, if I do say so myself. Here’s some sample output from this (ignore the syntax highlighting again):
Hey, look! We made a really pathetic AIM bot, minus AIM and the internet and other stuff. Ok, really we just made a totally useless program to create a contrived example with which to demonstrate
.send. Still, though, it’s really awesome.
.sendis really awesome, to clarify, not this program. And supremely useful if you want to dynamically call methods based on user input (or in other situations where you don’t really know what method is going to be called).
Not too shabby, Ruby. Not too shabby.