Ruby wizardry, p.10
Ruby Wizardry, page 10
“You were right!” said Ruben. “But what do we do now that we’re here?”
“I imagine we’ll ask for a bit of help from someone who knows the area,” the King said.
“How?” asked Scarlet and Ruben together.
“With a Computing Contraption, of course!” said the King.
Scarlet looked around. “But we’re in the middle of the forest!” she said. “There are no Computing Contraptions between here and the Hashery.”
“Computing Contraptions are everywhere in the kingdom,” the King said. “You just have to know where to look.” He reached up and pulled on the lowest branch of a nearby tree, and a cleverly hidden Computing Contraption swung out from inside the tree’s trunk.
“Wow!” said Ruben. “Now what?”
“Well, I imagine we’ll need to use Ruby to figure out who lives here in the Pines,” the King said. “Even if we don’t find the owner of our shiny green scale, we might at least find someone to help us out.”
“Perfect!” said Scarlet. “So there’s a directory of people who live in the Kingdom stored in each Computing Contraption?”
“Well, yes,” said the King, rubbing his head. “But here’s the rub. I don’t know much about Ruby, but I do recall hearing once that there’s actually no built-in method to get a list of all those people.”
Ruben sat down on a flat rock. “No method!” he said. “How are we supposed to find someone to help us if Ruby doesn’t have a built-in method for it?”
Scarlet thought for a minute. “Well,” she said, “I think it’s possible to write our very own Ruby methods, but I’ve never actually seen it done before.”
“Write our own Ruby methods?” asked the King. “That would be marvelous! Are you sure it’s possible?”
“Of course it’s possible!” cried a nearby voice. The King and Scarlet both jumped, and Ruben nearly fell off his rock. They all turned in the direction of the voice to see, standing only a few yards away from them . . . a knight, with sword drawn!
“Agh!” Ruben shouted, and tried to hide behind his rock.
“What in the name of midnight snack marzipan is the meaning of this?” demanded the King.
The knight froze, then hastily pushed her visor up on her helmet.
“Your Majesty!” she cried, and bowed deeply. “A bajillion apologies! I didn’t recognize you with my visor down.” She quickly slid her sword back into its scabbard.
“A lady knight!” said Scarlet.
“No, just a knight,” said the King. “After all, if she were a man, you wouldn’t say, ‘A man knight,’ would you?”
“I suppose not,” admitted Scarlet.
“Who are you?” Ruben asked.
The knight stood tall and proudly put her hands on her hips. “I’m the Off-White Knight!” she replied.
“Off-white?” asked the King. “Your armor is more of an eggshell color, I think.”
“Maybe an ecru,” Scarlet said, squinting.
“I think that’s a large bird,” said the King.
“Enough tomfoolery!” said the knight. “I am the Off-White Knight, and now it’s time for you to DEFEND yourselves!”
“Agh!” shouted Ruben again, covering his head with his hands.
The knight tried to scratch her head, but ended up scratching the outside of her helmet. “Why are you cowering like that?” she asked.
“Aren’t you going to slay us?” Ruben asked.
The Off-White Knight laughed. “Heavens, no!” she said. “In fact, it’s my knightly duty to help anyone in the Carmine Pines who needs assistance, so I’ll show you how to write your own Ruby methods.”
“But it’s daytime,” said the King.
Ruben and Scarlet gave each other a knowing look.
Defining Your Own Methods
The Off-White Knight cleared her throat. “Yes. Well,” she said, “what I was trying to say was that you certainly can define your own Ruby methods. You simply need to use the special words def and end.” She walked up to the cleverly disguised Computing Contraption and began typing.
>> def simon_says(phrase) >> return phrase >> end
“You start by typing def, which is short for define, because you’re defining a brand-new method. Next, you type the name of your method, which is simon_says in this case. Then you put the parameters next, in between parentheses. For this method, we have just one parameter: phrase.”
“The what now?” asked the King, rubbing his head with both hands.
“The parameters,” said the Off-White Knight. “They’re sort of like placeholders or nicknames for the information you’ll give your method when you call it.”
“Let me get this straight,” said the King. “When you write out what a method does using def and end, that’s called defining the method.”
“That’s right,” said the knight.
“And when you actually use the method somewhere, that’s calling the method.”
“Indeed!” said the knight. “Sometimes we say invoke instead of call, but they mean exactly the same thing. You define a method so Ruby knows what it does, and you call the method when you want to use it. Calling a method looks like this,” she continued, and typed some more:
>> simon_says('Prepare for battle!') => "Prepare for battle!"
“I’m a bit fuzzy right now,” said the King.
“You’re a bit fuzzy all the time,” said the Off-White Knight, eyeing the King’s fluffy beard.
“Yes, yes,” said the King, “but I’m also still confused. Could you go over calling the method a bit?”
“Of course!” said the knight. “When we defined the simon_says method earlier, we just told Ruby what code to run whenever we use the name simon_says. We can then use that code by writing the method name and putting in our own bit of information—the string 'Prepare for battle!'—where we had the phrase parameter before. Like I said, phrase is just like a placeholder that sits between the parentheses until we’re ready to use the method with 'Prepare for battle!'.
“What about the parentheses around 'Prepare for battle!'?” Ruben asked. “I’ve seen Ruby methods get called without parentheses before.”
“You’re right!” said the knight. “The parentheses are optional; you usually use them when you define the method, but you can use them or skip them when you call the method. It’s all the same to Ruby!”
return Versus puts
“All right, I understand defining and calling now,” said the King. “But what’s this return business, and how is it different from puts? Don’t they both print things on the screen?”
“Aha!” said the Off-White Knight. “A lot of people find this very confusing, but I think I can show you the difference between return and puts with just a couple of examples.”
“Here we define a method called print_sum that prints the sum of two numbers with puts,” she said:
>> def print_sum(a, b) >> puts a + b >> end
“Next, we’ll define a second method that returns the sum.”
>> def return_sum(a, b) >> return a + b >> end
“Do you see the difference between the print_sum and return_sum methods we defined?” the knight asked. “One puts, the other returns.” Scarlet, Ruben, and the King all nodded.
“Perfect!” said the Off-White Knight. “Let’s see what that really means for what our Ruby code does. First, we’ll call our print_sum method.”
>> sum = print_sum(2, 3) 5 => nil >> sum => nil
“See that?” said the knight. “puts will print something on the screen for you—in this case, it added 2 and 3 and printed the result 5 to the screen—but it won’t do anything with the value of 5: it produces nil after doing the printing! When we check out the value of sum, we see that it’s nil.”
“Now let’s call our return_sum method.” She typed some more:
>> sum = return_sum(2, 3) => 5 >> sum => 5
“Now I understand,” said the King. “Printing something just makes that value appear on the screen, but returning it lets you store that value in a variable, as we did with sum.”
“You’ve got it!” said the knight. “A method is just like a little machine. Things go into it and things come out. The things that go into a method when you call it are its arguments, and the thing that comes out is its return value.
“If a method doesn’t have a specific return value, it returns nil. You know how you always see => nil when you puts or print something? That’s because although the puts and print methods write text on the screen, they don’t have a return value, so they return nil.”
“Hang on a moment,” said the King. “If a method can automatically return nil when it has no other value to return, why can’t we automatically return other values?”
Understanding Method Arguments
“We can!” said the Off-White Knight. “Whenever you’re in a method definition, Ruby automatically returns the last bit of Ruby code that gets run. If you want to save some typing, you can leave off the return keyword if the last thing in your method is the return value, and Ruby will automatically return it for you.”
“Awesome!” said Ruben “Anything that saves us some typing is good. Now, just to back up a second to the difference between parameters and arguments: parameters are the handy names you put between parentheses in your method definition to let your method know what kinds of information it will get, and the arguments are the information you actually give to your method when you call it,” Ruben said.
“Quite right!” said the Off-White Knight. “Hang on, let me give you another example.” She typed furiously into the Computing Contraption, narrating all the while. “Let’s define a method called add_things with the parameters thing_one and thing_two and return their sum. That’d look like this:
>> def add_things(thing_one, thing_two) >> thing_one + thing_two >> end
“Next, we’ll call the method with the arguments 3 and 7. The return value is 10.”
>> add_things(3, 7) => 10
“That’s great,” said Scarlet, “but what happens if you want to sometimes pass an argument to a method, and sometimes not? If you don’t pass the right number of arguments, Ruby throws an error!” She typed into the Computing Contraption:
>> def plus_one(number) >> number + 1 >> end >> plus_one 2 => 3 >> plus_one() ArgumentError: wrong number of arguments (0 for 1)
“Yeah!” said Ruben. “Here, Ruby’s saying that it got zero arguments, but it expected one.”
“Great point!” said the Off-White Knight. “In that case, you can use optional or default parameters. Those are special parameters that come with a placeholder value, and if you don’t give Ruby arguments for those parameters when you call the method, Ruby inserts the placeholders instead. Let me define a method like that for you,” she said, and began typing at the Computing Contraption once more:
>> def declare_name(name='The Off-White Knight!') >> puts name >> end
“See the equal sign?” she said. “That tells the method to use that string if it’s not told otherwise. Now, without any arguments, the method will use the default name,” she said. “Let’s try calling it!” She typed some more:
>> declare_name() The Off-White Knight! => nil
“Wow!” said Ruben. “You didn’t pass any arguments at all, so the default one was used automatically.”
“That’s right,” said the knight. “And again, because Ruby is super flexible, you don’t even need the parentheses to show that you’re calling a method!” She typed even more:
>> declare_name The Off-White Knight! => nil
“That looks a little too magical to me,” said the King. “If there were lots of code floating around, how would I immediately know the difference between a method with no parentheses and a plain old variable?”
“That’s a good point,” said the Off-White Knight. She tried to wipe the sweat from her brow, but accidentally knocked her visor down instead. “I often use the parentheses, because they make it clear I’m using a method and not something else, like a variable.”
“Now, let’s say you do want to use your own name,” she continued, struggling to push her visor back up. “You just pass it in—with or without parentheses—like this.” She typed a few more lines:
>> declare_name('Lady Scarlet the Bold!') Lady Scarlet the Bold! => nil >> declare_name 'Sir Ruben the Fearless!' Sir Ruben the Fearless! => nil
“Whew!” said the Off-White Knight. “Let me take a break for just a second. My gauntlets are tired.”
What Is nil?
“Of course,” said the King. “I’m still a bit hung up on nil, though,” he said. “What is it?”
“I think I know the answer to that one,” Ruben said. “nil is Ruby’s way of saying ‘nothing at all.’ When Ruby wants to express the idea of ‘nothing’ or ‘no value,’ it uses nil.”
“Is nil true or false?” asked the King.
“Neither!” said Ruben. “It’s its very own thing. But it is one of two falsey values in Ruby—the other is false.”
“What do you mean by ‘falsey’?” asked the King.
“I mean that if you use nil in an if statement, it will be the opposite of true,” Ruben said. “This should look familiar.” He typed into the Computing Contraption:
>> if nil >> puts "This text won't be printed!" >> end
“Your code didn’t print anything on the screen!” said the King.
Ruben nodded. “That’s because Ruby will never treat if nil as a true condition. To Ruby, saying ‘if nothing’ and ‘if false’ are the same, so it will never run the code between if nil and end.” He thought for a moment. “Remember the if statement?” he asked.
The King nodded vigorously. “As if it were only yesterday!” he said.
“It was today,” said Ruben.
“Tomato, tomato,” said the King, pronouncing the word the same way both times. Ruben and Scarlet looked at each other and shrugged.
“Anyway,” said Ruben, “The if statement takes a bit of Ruby code and does one thing if that code is true and something else if it’s false. nil is always treated like false in if statements, so to make something happen if a value is nil, you might think you have to do this:
>> if !nil >> puts "But I will get printed!" >> end But I will get printed! => nil
“But there’s a piece of built-in Ruby code we’ve already seen that means the same thing as ‘if not’: unless!” Ruben typed some more:
>> unless nil >> puts "But I will get printed!" >> end But I will get printed! => nil
“unless has the exact same meaning as ‘if not.’ When we say ‘Stay up late if you’re not sleepy,’ that means the same thing as ‘Stay up late unless you’re sleepy,’” Ruben explained.
“I’ve seen this before!” said the King. “We use unless in Ruby anytime we’d otherwise use if and !.”
“Right!” said Ruben. “Both false and nil will behave the same way when used in an if or unless statement, but it’s important to remember that nil’s not the exact same thing as false.” He continued typing:
>> nil == false => false
“Did you see that => nil at the end of the unless example?” the Off-White Knight asked. “That’s what I was talking about. nil is the return value of puts. Check it out!” She reached over Ruben’s head and typed some more:
>> puts 'Prepare for nil!' Prepare for nil! => nil
“One last thing about nil,” said the knight. “Not only is it not the same thing as false, it’s not the same thing as zero! Zero is a number; nil is simply nothing at all.”
“I think I’ve got it now,” said the King.
Splat Parameters
“Right,” said the knight. “Then we’re on to more method magic! I showed you how to make a method take an optional argument, but Ruby also lets you tell a method to take any number of arguments. Splat parameters are the way to tell a Ruby method, ‘Hey, I’m going to pass you a whole list of things to use. I don’t know how many, so just deal with whatever number I send!’” The knight flexed her fingers a few times. “They work like this,” she said, and began typing:
>> def declare_knights(*knights) >> puts knights >> end >> declare_knights('Lady Scarlet', 'Sir Ruben', 'The Off-White Knight') Lady Scarlet Sir Ruben The Off-White Knight => nil
“You can think of the asterisk (*) in our first line as a little splat mark that tells the method to take the whole messy bucket of arguments, no matter how many, and do something with them,” the Off-White Knight said.
“I see the little splat next to the parameter name,” said the King, “but not in the method body and not when you call the method. Is the * only used when you define the method and only between the parentheses?”
“Exactly,” said the knight. “Ruby is very smart—you only have to tell it something once! As I mentioned,” the Off-White Knight continued, “Ruby realizes that the last thing that appears in your method body is probably the thing you want to return. So, as I mentioned earlier, if you want to save some typing, you can leave off the return keyword if the last thing in your method is the return value. Ruby does it automatically! That means that this:
>> def add(a, b) >> return a + b >> end >> add(1, 3) => 4
is exactly the same as this!
>> def add(a, b) >> a + b >> end >> add(1, 3) => 4
“Wonderful!” said Scarlet. “I’ll definitely remember that trick when writing my own methods.”
“And now,” said the Off-White Knight, pulling her sword out of its scabbard, “it’s time for you to YIELD!”
“Agh!” shouted Ruben, covering his head with his hands again.
Block Methods
The Off-White Knight struggled to get her sword back into its scabbard. “You really should do something about that cowering habit,” she said. “What I was saying is, you need to use the yield keyword when you write your own Ruby methods that take blocks.”
