Inheriting the Magic of Ruby - Ruby Wizardry: An Introduction to Programming for Kids (2014)

Ruby Wizardry: An Introduction to Programming for Kids (2014)

Chapter 9. Inheriting the Magic of Ruby

Her Majesty’s Menagerie

The King, Ruben, and Scarlet raced westward along the underground passage toward the castle.

“How much farther?” panted Ruben.

“I’m not sure,” said the King, “but Wherefore told us to go as far as we could, and then we’d be at the Mysterious Pipe.” He thought for a moment. “It can’t be too far, though,” he said at last. “These are the Ambrose Caverns, and while they stretch beneath the entire kingdom, I know Haldo has managed to get between the castle and places farther than the Pines in a matter of minutes.”

“That’s right! Haldo knows these tunnels,” said Scarlet. She ran in silence for a minute or two. “What if . . .” she began.

“What if what?” asked Ruben.

“Well, what if whoever’s causing all this trouble is someone Haldo let in? Or what if Haldo’s one of them?”

“Bite your tongue!” said the King. “Haldo’s been in my service since he was a boy. He’d never do anything to hurt us or this kingdom!”

“We should consider every possibility,” Scarlet said.

“Even so,” said the King, “all suspects are innocent until we prove them guilty. If we’re lucky, we’ll catch these scoundrels red-handed when we arrive at the castle!”

“Which will be any minute,” Ruben said. “Look!”

Up ahead, the narrow tunnel opened up into a wide cavern. The King, Scarlet, and Ruben jogged into the open space, then stood for a moment, catching their breath.

“This is it,” said the King, “the basement below the basement below the castle! Now we just need to find the Mysterious Pipe and climb back up into my Royal Study.”

“And there it is!” said Scarlet. In the darkness, they could just make out the outline of the Mysterious Pipe in the far corner.

The trio walked up to the base of the pipe, which was gurgling softly.

“Now what?” asked Ruben. “The Mysterious Pipe is full of water! How are we supposed to climb up it?”

“Well, we turned the Flowmatic Something-or-Other on before,” Scarlet said. “We can turn it back off!” She felt around the bottom of the pipe until she found the familiar boxy shape of a Computing Contraption, then flipped open its lid. The glow of the IRB prompt illuminated their faces.

“Right!” said the King. “What variable did we change before?”

“flowmatic_on!” replied Ruben.

Scarlet quickly typed into the Computing Contraption:

>> flowmatic_on = false

=> false

With a slow booooop and a glug-glug sound, the Mysterious Pipe shut down and emptied.

“Well done, Scarlet!” said the King as he walked to the far side of the Pipe. He grabbed a large metal wheel jutting out of the side and gave it a spin. The wheel turned several times, and with a hollow clank, the door to which it was attached eased open.

“Into the Mysterious Pipe!” cried the King, and the three climbed inside.

Ruben looked straight up and squinted. “I can’t even see the light at the top!” he said. “This Pipe is huge! Hellooo!” he called, and the Mysterious Pipe echoed: Hello! Ello! Lo!

“It’ll take forever to climb, if we can even do it at all!” said Scarlet. She thought for a moment. “I might have an idea.” She turned to Ruben and the King. “Do you trust me?” she asked.

“With my life!” said the King.

“To the end!” said Ruben.

“All right, then,” said Scarlet. “Hold your breath!” She reached around to the Computing Contraption on the side of the Mysterious Pipe and typed:

>> flowmatic_on = true

She slammed the metal door shut, and in an instant, the pipe filled with water.

For a moment, the three floated at the bottom of the Mysterious Pipe, holding their breath. Then the entire pipe began to tremble slightly, and with a deep whoosh, the force of the water propelled the King, Scarlet, and Ruben straight up!

image with no caption

In just a few seconds, the three of them began to slow down, and they found themselves floating just inches from the narrow top of the Mysterious Pipe. The King reached down and pushed on the latch of the pipe’s door, and in a flood of water, the trio tumbled out onto the floor of the King’s Royal Study.

“Genius! Absolutely genius!” the King sputtered.

“Thank you,” said Scarlet, taking a small bow. “But we’ve got to get to the Queen! Do you know where she is?”

“She should be back from Her Royal Majesty’s Hacktastic Ruby Conference,” said the King, “so I imagine she’s up in her Royal Office. Let’s go!” And with that, he dashed out of the room.

Ruby and Scarlet followed. “The Queen was at a Ruby conference?” Scarlet asked as they raced up the stairs.

“Indeed!” said the King. “It might surprise you to learn that although I’m rather new to all of this Ruby business, my wife is quite the hacker.”

“That’s amazing!” said Ruben. “Maybe she’ll be able to help us fix all these Ruby malfunctions and catch the bad guys causing them.”

“I hope so. Ah! Here we are,” said the King, and he skidded to a halt in front of an enormous wooden door with golden handles.

He pulled down on both handles at once, swung the doors open, and rushed inside.

The Queen was seated at her desk in a high-backed chair, furiously typing at a Computing Contraption.

“They tried to break into my Computing Contraption!” she said. “The very idea!”

“They who?” asked Scarlet and Ruben together as they followed the King into the Queen’s Royal Office.

“I don’t know!” said the Queen, still typing. “There were four of them, and I caught them at my Computing Contraption, trying to figure out my password. Luckily, I’m a stickler for security.”

“That she is,” said the King, wringing out his fluffy white beard. “She won’t even let me buy gummy bears on the Internet!”

“For good reason,” said the Queen, and she stopped typing. “The last time I let you do that, you sent a small fortune to someone claiming to be the Gummy Bear King!” She paused, looking at Scarlet and Ruben. “I don’t believe we’ve met,” she said. “Who might you be?” She looked the three of them up and down. “And why are you all so wet?”

“I’m Ruben,” said Ruben, “and this is Scarlet. We’re helping the King find whoever’s responsible for all the Ruby malfunctions going on, and we ran all the way from the Carmine Pines and swam up through the Mysterious Pipe to do it!”

“Well, you’ve come to the right place!” said the Queen. “I’m going to track down those ne’er-do-wells if it’s the last thing I do.” She began typing again.

image with no caption

“Did you get a good look at any of them?” asked Scarlet. “Were there any clues? Did you see or hear anything that might help us catch them?”

“I didn’t see their faces,” the Queen said, “but I overheard them trying to figure out my Computing Contraption’s password. There were four of them—it sounded like two boys and two girls. I got home early from the Hacktastic Ruby Conference and thought I’d try out some of the Ruby tricks I learned, and when I came up to my office, I caught them in the act! They ran when I shouted at them to surrender, and I sent the palace guards after them. In the meantime, I’ve been working to increase the security on my Computing Contraption to ensure it’s completely immune to attack.”

“Did they get anything from your Computing Contraption?” Scarlet asked.

“Thank heavens, no,” said the Queen. “They didn’t get my password, but we’d be in terrible trouble if they had. With that, they could access any system in the kingdom with no restrictions!”

“These villains are escalating!” said the King, pacing around the Queen’s office. “We’ve got to catch them as quickly as possible, before they strike again. We might not be so lucky next time.”

The Queen nodded. “I’ve instructed the guards to bring any suspects they catch directly to us for questioning,” she said. “In the meantime, I’ve upgraded all the security on my Computing Contraption. The only thing left to do is update some of my Ruby programs to make them more secure, too!”

“Ruby!” said Ruben. “That happens to be our specialty. Can we help?” he asked.

The Queen smiled. “That would be lovely,” she said. “While I’m something of an expert in more than a few programming languages, I’m pretty new to Ruby.” She slid over in her chair, and Ruben and Scarlet clambered up beside her.

“First things first,” said the Queen. “Do you know how to create a Ruby class?”

A Brush-up on Classes

“I think so,” said Ruben. “May I create any class I want?” The Queen nodded, and Ruben typed into her Computing Contraption:

>> class Animal

>> attr_accessor :name

>>

>> def initialize(name, legs=4)

>> @name = name

>> @legs = legs

>> end

>> end

=> nil

“I see!” said the Queen. “You’ve defined an Animal class. You’re using attr_accessor to automatically make a method to access the names of your animals, and the initialize method sets the animal’s @name and number of @legs whenever a new animal is created.”

“Yup!” said Ruben. “And if we create an animal with Animal.new but don’t give it a number of legs, it’ll be 4 by default.”

The Queen nodded. “That makes sense to me. Why don’t you go ahead and create a couple of animals?”

Ruben typed some more:

➊ >> monkey = Animal.new('George', 2)

=> #<Animal:0x00000104953940 @name="George", @legs=2>

➋ >> monkey.name = 'Kong'

=> "Kong"

➌ >> dog = Animal.new('Bigelow')

=> #<Animal:0x00000104950178 @name="Bigelow", ➍@legs=4>

“Wonderful! At ➊, we’ve created monkey, an instance of the Animal class, and created it with the name 'George' and 2 legs. Next, at ➋, we’ve changed our monkey’s name to 'Kong' to show that our attr_accessor lets us both read and change the name.

“Finally, at ➌, we’ve created a second instance of the Animal class, dog, with the name 'Bigelow'. Since we didn’t create our dog with a number of legs, it gets four by default, as you can see by the return value at ➍.”

A Couple of Classes

The Queen thought for a moment. “Yes, this will do nicely. Now then,” she continued, “imagine if instead of just having monkey and dog as instances of Animal, we decided we wanted to have Monkey and Dog as separate classes instead. How could we do it?”

“Well, we could do something like this,” Ruben said, and he typed:

>> class Monkey

>> attr_accessor :name

>>

>> def initialize(name)

>> @name = name

>> @legs = 2

>> end

>> end

=> nil

“Precisely,” said the Queen. “That defines a Monkey class, and the monkeys it creates will have @names and two @legs. The attr_accessor also automatically creates a name method for each monkey so we can get its name. To create a new monkey from the class, we use Monkey.newand pass its name as a string value. Like this!” She typed into the Computing Contraption:

>> monkey = Monkey.new('George')

=> #<Monkey:0x00000104bdf3a8 @name="George", @legs=2>

>> monkey.name

=> "George"

“We can do the same thing for our Dog class,” Ruben continued. “We know that pretty much every dog has four legs, so it’ll look just like the Monkey class, only the class name will be different and the number of @legs will be 4.” He typed into the Computing Contraption:

>> class Dog

>> attr_accessor :name

>>

>> def initialize(name)

>> @name = name

>> @legs = 4

>> end

>> end

=> nil

“Just like we can create new monkeys with Monkey.new and pass in a string for the monkey’s name, we can create new dogs with Dog.new and pass in a string for the dog’s name!” Ruben said.

>> dog = Dog.new('Bigelow')

=> #<Dog:0x00000104be3d18 @name="Bigelow", @legs=4>

>> dog.name

=> "Bigelow"

“That’s certainly one way to create a couple of classes,” said the Queen, “but it looks like you had to write a lot of the same code for both your Monkey and your Dog class.”

“That’s true,” said Ruben. “Is that okay?”

Inheritance and DRY Code

“Well,” said the Queen, “any time you find yourself writing something more than once, you should ask yourself whether you have to. Good code—unlike my husband here,” she said, stifling a laugh as the King poured water out of the sleeves of his kingly robe, “—should be DRY.”

“I know that one!” said the King, shaking drops of water off his string and replacing it in his pocket. “It stands for Don’t Repeat Yourself.”

“He knows that one because he repeats himself all the time,” the Queen whispered to Scarlet and Ruben. “But yes,” she said, “if you avoid repeating yourself in your code, you save lots of time! Also, if you ever have to change something, you only need to change it one place, not several.”

“I like the sound of that!” said Ruben, “but how can we make our class code more DRY?”

“With inheritance,” said the Queen.

“Inheritance!” said Scarlet. “I think I’ve heard it mentioned before, but I’m not sure what it is.”

“I’ll show you,” said the Queen, explaining as she typed into the Computing Contraption. “We’ve already created a class called Animal. What if we could use that class as a way to create both our Monkey and Dog classes?”

>> class Dog < Animal

>> def bark

>> puts 'Arf!'

>> end

>> end

=> nil

“The class Dog < Animal part means that the Dog class inherits from the Animal class. It says to Ruby: ‘Make a new class called Dog that knows how to do everything Animal does,’” the Queen said. “Then we just add a method, like we’d normally do. Here, I’m adding a bark method for Dogs, since dogs know how to bark.” She rolled up her sleeves.

“Here’s the amazing part: because Dog inherits from Animal, new dogs can do anything animals can do and anything dogs can do. They’ll have a name method and a default of four legs, and know how to bark!”

>> dog = Dog.new('Bigelow')

=> #<Dog:0x00000104c89218 @name="Bigelow", @legs=4>

>> dog.name

=> "Bigelow"

>> dog.bark

Arf!

=> nil

“Astounding!” said the King.

“Isn’t it?” said the Queen. “It also means that instead of typing all that class definition business again for our Monkey class, we can just inherit from Animal again. Because we inherit from Animal, we get our name method and a default @legs value of 4, plus we’ll get this neat newmake_sounds method I added just for monkeys.”

>> class Monkey < Animal

>> def make_sounds

>> puts 'Eeh ooh ooh!'

>> end

>> end

=> nil

“Now we can create a new monkey with a name and two legs. Not only can we change its name with the name= method we inherited from Animal, but we can also make_sounds!”

“We can get the name and change it?” Ruben asked.

The Queen nodded. “Remember, we inherited from Animal, and Animal has attr_accessor :name. That automatically creates a name method for getting the name and a name= method for setting the name. See?”

>> monkey = Monkey.new('George', 2)

=> #<Monkey:0x00000102deaed8 @name="George", @legs=2>

>> monkey.name = 'Oxnard'

=> "Oxnard"

>> monkey.make_sounds

Eeh ooh ooh!

=> nil

“Wow!” said Scarlet, “That’s amazing—the monkey and the dog have their own methods, but they also can do anything an Animal can do!”

image with no caption

“That’s what makes inheritance so wonderful!” said the Queen. “Given our Animal class from before, which had an attr_accessor for :name and an initialize method that set the @name and @legs instance variables, we can make two new classes that inherit that information and add some new things—like a bark method for Dog instances and a make_sounds method for Monkey instances.

“Inheritance in Ruby works exactly as it does in real life,” the Queen continued. “Just like you might have inherited your father’s eye color or your mother’s math smarts, objects in Ruby can inherit information and methods from other objects.”

“Oh!” said Ruben. “So not only can we use classes to create lots of similar objects and avoid writing extra code, but we can even write classes that borrow code from other classes?”

“On the nose,” said the Queen. “We might want to use inheritance in our code if two classes have what I like to call an ‘is-a’ relationship, as in ‘a monkey is a kind of animal’ or ‘a dog is a kind of animal.’”

“But a Dog would never inherit from a Monkey,” said Scarlet, “because a dog isn’t a kind of monkey.”

“Exactly,” said the Queen.

“Could you show us the syntax again?” said Scarlet. “This is a good trick and I want to remember it.”

Subclass and Superclass

“Of course,” said the Queen. “When you have one class that inherits from another, you use the class keyword, just like always. Then you write the name of the class that will inherit, which we call the subclass or the child class, and then a <. You can think of that little < as the tip of an arrow that says, ‘Put all the powers and abilities of the class on the right into the class on the left!’ To finish up, you then write the name of the class you’re inheriting from to the right of the <, which we call the superclass or parent class. Finally, you just define any new methods as you normally would. It looks like this,” she said, and typed:

super_and_subclass.rb

➊ class MySuperclass

def say_hello

puts 'Hello!'

end

end

➋ class MySubclass < MySuperclass

def say_goodbye

puts 'Goodbye!'

end

end

“Here we have two classes, MySuperclass and MySubclass,” the Queen explained. “MySubclass inherits from MySuperclass on line ➋, so instances of MySubclass not only have the say_goodbye method defined in the MySubclass class, but they can also use the say_hellomethod they inherit from MySuperclass! Let’s see what happens when we create a new instance of MySubclass.”

>> load 'super_and_subclass.rb'

=> true

>> subby = MySubclass.new

=> #<MySubclass:0x00000104a4c478>

>> subby.say_hello

Hello!

=> nil

>> subby.say_goodbye

Goodbye!

=> nil

“I created an instance of MySubclass called subby using MySubclass.new,” said the Queen. “Just like I promised, subby can use both the say_goodbye method defined in MySubclass as well as the say_hello method defined in MySuperclass, because MySubclass inherits fromMySuperclass and can therefore do anything MySuperclass knows how to do.”

“Thanks!” said Scarlet, “I think I’ve got it now.” She studied the screen for a few seconds. “Is it possible for a class to inherit from more than one other class?” she asked.

“Alas, no,” said the Queen. “You can have only one class name on the left side of the < and one class name on the right side. However!” she continued, “there is a Ruby trick that lets you mix the behavior of several classes into one, which we’ll get to in a little while.”

“Okay,” said Ruben, “but what if you want your subclass to have a different version of a method than the superclass has?”

Overriding Methods: Pirates are People, Too

“Now that, we can do,” said the Queen. “Any subclass can override a method it inherits from its superclass at any time. Let’s have a look. We’ll create a superclass called Person and subclass called Pirate, with a speak method for both. Of course, pirates and regular people speak pretty differently, don’t they?” Scarlet and Ruben nodded. “So,” the Queen continued, “the two speak methods will be different.” She typed into the Computing Contraption:

pirates_and_people.rb

➊ class Person

attr_reader :name

def initialize(name)

@name = name

end

def speak

puts 'Hello!'

end

end

➋ class Pirate < Person

def speak

puts 'Arr!'

end

end

“Starting at ➊, I’ve defined the Person class with an attr_reader :name, so we’ll be able to get and change the name of any Person instances,” the Queen said. “The initialize method sets the name to the string we’ll pass in when we call Person.new, and the speak method just prints out 'Hello!'”

“With you so far!” said the King.

“Next, at ➋, I’ve defined the Pirate class to inherit from Person, so Pirate instances will be able to do anything a Person instance can do,” said the Queen. “But! I’ve given Pirate its very own speak method that prints 'Arr!'. We’ll see how that works in a moment. First, let’s go ahead and create an instance of each class to make sure we can create it and get its name without any trouble.”

>> load 'pirates_and_people.rb'

=> true

>> esmeralda = Person.new('Esmeralda')

=> #<Person:0x00000104bfaa90 @name="Esmeralda">

>> rubybeard = Pirate.new('RubyBeard')

=> #<Pirate:0x00000104bfedc0 @name="RubyBeard">

>> esmeralda.name

=> "Esmeralda"

>> rubybeard.name

=> "RubyBeard"

“Now, let’s test our speak method,” the Queen said. “Because Pirate created its own speak method, instances of Pirate will use that one instead of the one inherited from Person,” she explained. “But since we didn’t change the name and name= methods given to Pirate byattr_reader :name, which it inherited, we can get and change names the same way for both people and pirates!”

>> esmeralda.speak

Hello!

=> nil

>> rubybeard.speak

Arr!

=> nil

“That’s really cool,” Ruben said, “but when would we decide to override a method?”

“Any time one class inherits from another and you want most of the same behavior, but not all,” said the Queen. “In this case, we want a Pirate to be a Person and to be created like one, but we want to make sure our Pirates sound like pirates. So we simply override the methods we want to be different between regular old people and pirates!”

“That makes sense,” said Scarlet, “but what if we want a little of both? That is, what if we want to modify a method we inherit, but not completely replace it?”

Using super

“I’m so glad you asked,” said the Queen. “That’s absolutely something Ruby lets us do—all we need is the super keyword. Using the Animal class we created earlier, we’ll create a new version of a method that already exists, just like we did with the speak method, and add our new code. Then, we’ll use super to tell Ruby: ‘Okay, I’m done adding new things to this method! Now have it do all the things the superclass’s version of the method does.’ It works like this,” she said, and typed:

super_dog.rb

class Dog < Animal

def initialize(name)

puts 'Just made a new dog!'

super

end

end

“Now we can create a Dog class that inherits from Animal, just like before,” said the Queen.

>> load 'super_dog.rb’

=> true

>> dog = Dog.new('Bigelow')

Just made a new dog!

=> #<Dog:0x00000104c6f020 @name="Bigelow", @legs=4>

“Here, though, we’ve given Dog its own initialize method, which Dog instances will use instead of the one inherited from Animal,” the Queen continued.

“Just like pirates used their own speak method instead of the one from Person,” Ruben said.

“Exactly!” said the Queen. “We added our own puts statement to the Dog initialize method to print out a message, but then we used super to tell Ruby: ‘Okay! Now, use Animal’s initialize method.’ All super does is call the version of the method from the superclass! SinceAnimal’s initialize method sets the @name and the @legs instance variables for us, you see not only @name="Bigelow" but @legs=4!”

“Gracious me, that’s astounding,” said the King, who had finally dried himself off. “Is there anything Ruby can’t do?”

“That’s nothing,” said the Queen. “Now the real fun begins. We’ll use inheritance, method overriding, and super to create some trusty friends to defend us against the intruders in our kingdom!”

Protecting the Kingdom with GuardDogs and FlyingMonkeys

“But before we do that,” said the Queen, “let’s get back to our Dogs and Monkeys. First, I’ll redefine a Dog class, since it’s been a bit since we looked at it.” She typed into her Computing Contraption:

guard_dog.rb

class Dog < Animal

attr_accessor :name

def initialize(name)

@name = name

end

def bark

puts 'Arf!'

end

end

“Our Dog class inherits from Animal and will be initialized with a name, and it will have a bark method to let it bark whenever it likes,” said the Queen. “Next, I’ll create a brand-new class that inherits from Dog. Let’s keep adding to guard_dog.rb!” She typed into the Computing Contraption:

guard_dog.rb

class GuardDog < Dog

➊ attr_accessor :strength

➋ def initialize(name, strength)

@strength = strength

super(name)

end

➌ def bark

puts 'Stop, in the name of the law!'

end

➍ def attack

puts "Did #{rand(strength)} damage!"

end

end

“Here, I’ve created a GuardDog class that inherits from Dog. At ➊, we have an attr_accessor for :strength, so we’ll be able to set and get the strength of our new guard dog. Next, I added an initialize method at➋ that partly overrides the one from Dog: it sets the GuardDog’s@strength, then calls super with just the name to use Dog’s initialize method, which sets the @name. At ➌, I completely overrode the bark method from Dog and gave GuardDog its own phrase to say.

Finally at ➍, I added a brand-new attack method that prints out a string saying how much damage the dog did. That method uses Ruby’s built-in rand method to choose a random number between zero and whatever the GuardDog’s strength is.”

“Wow!” said Ruben. “That’s amazing! And I didn’t know you could call super with arguments.”

“Oh, yes,” said the Queen. “If you call super by itself, it calls the superclass’s initialize method with all of the arguments the subclass’s initialize method got. GuardDog takes one more argument than Dog—it takes strength as well as name—and that would cause an error if we tried to give both of those to Dog, which is created only with a name. So we call super with just name to make sure that Dog’s initialize method gets the number of arguments it expects.”

Every GuardDog Has His Day

“Now then,” the Queen continued, “let’s create a new GuardDog and test it out!”

>> load('guard_dog.rb')

=> true

>> rex = GuardDog.new('Rex', 7)

=> #<GuardDog:0x0000010334e168 @strength=7, @name="Rex">

>> rex.strength

=> 7

>> rex.bark

Stop, in the name of the law!

=> nil

>> rex.attack

Did 1 damage!

=> nil

>> rex.attack

Did 4 damage!

=> nil

“Now we’ve got a special kind of dog—a GuardDog—with its own set of methods!” The Queen said. “We partly overrode its initialize method because we wanted it to have strength, but then we used super to finish creating it like a regular dog. We overrode bark because we wanted our GuardDog to have the bark method, then finished up by adding a completely new attack method that GuardDogs have but Dogs don’t.”

“I’m starting to get it now,” said Ruben. “We use inheritance to minimize the amount of code we have to retype, and we override methods when we want to make exceptions and give our subclasses special behavior!” The Queen nodded.

“Don’t forget super,” Scarlet said. “We use that when we want to partly change the behavior of a method in a subclass, but not completely replace it.”

The King furrowed his brow. “This makes sense, but could we see a bit more?” he asked. “It’s an awful lot to keep in my head all at once.”

Once More, with Feeling!

Ruben nodded. “Could we have one more example, just to be sure we understand?” he asked.

“Of course,” said the Queen. “Here’s another example of inheritance, method overriding, and super, this time using our trusty Monkey class. Let’s make Monkey look like this,” she said, and typed:

flying_monkey.rb

class Monkey < Animal

attr_reader :name, :arms

def initialize(name, arms = 2)

@name = name

@arms = arms

end

def make_sounds

puts 'Eeh ooh ooh!'

end

end

“Here, we have a Monkey class. Using attr_reader, we can get (but not change) our monkey’s name and number of arms, which defaults to 2. We also have a make_sounds method that prints out a string.”

“Looks pretty standard,” said the King.

“Next,” the Queen continued, “we’ll create a FlyingMonkey class that inherits from Monkey. We’ll keep adding to flying_monkey.rb!” She typed into her Computing Contraption:

flying_monkey.rb

➊ class FlyingMonkey < Monkey

➋ attr_reader :wings

➌ def initialize(name, wings, arms = 2)

@wings = wings

super(name, arms)

end

➍ def throw_coconuts

coconuts = rand(arms)

damage = coconuts * wings

puts "Threw #{coconuts} coconuts! It did #{damage} damage."

end

end

“For our FlyingMonkey class,” said the Queen, “we first inherit from Monkey ➊. Next, we add an attr_reader for :wings so we know how many wings our FlyingMonkey has ➋. We initialize the flying monkey with a certain number of @wings, but then call super to have theMonkey class take care of setting the @name and number of @arms ➌. We then define a brand-new throw_coconuts method ➍ that uses Ruby’s built-in rand method to calculate how much damage the flying monkey can do by throwing coconuts. The number of coconuts is a random number between zero and the flying monkey’s number of arms, and the damage is that number multiplied by the number of wings the monkey has, because monkeys with more wings can fly higher.”

“Okay!” said the Queen. “Let’s create a flying monkey and test out his methods.”

>> load 'flying_monkey.rb'

=> true

>> oswald = FlyingMonkey.new('Oswald', 6, 4)

=> #<FlyingMonkey:0x000001013d1718 @wings=6, @name="Oswald", @arms=4>

>> oswald.make_sounds

Eeh ooh ooh!

=> nil

>> oswald.throw_coconuts

Threw 3 coconuts! It did 18 damage.

=> nil

“Amazing!” said Scarlet. “We create the FlyingMonkey by using its own initialize method for wings, then letting Monkey finish up by setting the name and number of arms. And because FlyingMonkey inherits from Monkey, a flying monkey can not only throw_coconuts but can also use Monkey’s make_sounds method!”

“Huzzah!” said the King. “I’ll bet that monkey is excellent at throwing coconuts, too. Which I suppose is only natural for a flying monkey.”

“And I’ll bet he makes very good monkey sounds,” added Ruben.

“I guess that makes sense,” said Scarlet, “but something’s been bothering me: why does our GuardDog know how to talk?”

“He’s a very smart dog,” said the Queen.

“Very,” said the King.

“Speaking of,” said the Queen, “I think it’s high time we put our guard dogs and flying monkeys to work!” She pressed a button on the arm of her chair, and her Computing Contraption began to hum. In a matter of seconds, doors slid open on all sides of her office, and dozens of guard dogs and flying monkeys emerged!

image with no caption

“Taco Tuesdays!” said the King. “And I thought all these gadgets and hacking conferences were a waste of time and money.”

“On the contrary,” said the Queen. “I think they just might save the kingdom!”

The King opened his mouth to speak, but at that very moment, a bright red telephone began ringing madly on the Queen’s desk. She picked it up. “Hello?” she said. She waited a moment, and then her eyes went wide. “Stay right where you are! We’re on our way!” She hung up and jumped from her chair. “The guards have news!” she said. “They’re down in the Royal Stables. Quickly now, let’s go!”

image with no caption

And with that, the four of them dashed from the Queen’s office and headed for the stables out back, the guard dogs charging ahead and the flying monkeys following close behind.

The Queen’s Machine

This is getting exciting! While the King, the Queen, Ruben, and Scarlet go catch the bad guys, let’s jump in and help the Queen create a Ruby class to help keep all her royal business secret; after all, there’s only so much GuardDogs and FlyingMonkeys can do! I’m thinking some kind of login account for her Computing Contraption that’s a bit more secure than what she’s been using so far might be just what we need; we don’t want anyone breaking in again anytime soon. So, we’ll set up an Account class with a password for the Queen to use to log in to her computer.

Let’s begin by making a new file called secrecy.rb and typing the following code.

secrecy.rb

➊ class Account

attr_accessor :username, :password

➋ def initialize(username, password)

@username = username

@password = password

end

end

➌ class SuperSecretAccount < Account

➍ def initialize(username, password)

@reset_attempts = 0

super(username, password)

end

➎ def password=(new_password)

while @reset_attempts < 3

print 'Current password?: '

current_password = gets.chomp

if @password == current_password

@password = new_password

puts "Password changed to: #{new_password}"

break

else

@reset_attempts += 1

puts "That's not the right password."

puts "Attempt #{@reset_attempts} of 3 used up!"

end

end

end

➏ def password

'The password is secret!'

end

end

➐ regular = Account.new('Your name', 'your password')

super_safe = SuperSecretAccount.new('Your name', 'your password')

➑ regular = Account.new('Your name', 'your password')

super_safe = SuperSecretAccount.new('Your name', 'your password')

puts "Your regular account password is: #{regular.password}"

regular.password = 'Something else!'

puts "Your regular account password is now: #{regular.password}"

puts "If we try to see the secret account password, we get: #{super_

safe.password}"

changed_password = 'Something else!'

puts "Trying to change your secret account password to: #{changed_

password}..."

super_safe.password = changed_password

This is a long one, so let’s go through it step-by-step.

First, we create a basic Account class at ➊ that sets up some instance variables (check them out in the initialize method at ➋). Instances of the Account class can have their @username and @password read and changed by any Ruby code that happens to want to, thanks to theattr_accessor for both :username and :password.

We’re off to a pretty good start! This code lets us create an account for someone and lets that person set her password, just as you might do for a website or your email. The problem, though, is that this code lets any Ruby code change the user’s password, which we definitely don’t want.

To fix that, we create our SuperSecretAccount class at ➌ that inherits from Account, and here’s where things get interesting. First, SuperSecretAccount’s initialize method also takes a username and password, and it passes these to super to let Account take care of setting those instance variables ➍. The SuperSecretAccount also creates a new instance variable, @reset_attempts, to keep track of how many times a user tries to log in.

Next, the SuperSecretAccount class overrides the password= method ➎ (one of the two created by Account’s attr_accessor :password), so it requires a user to enter her old password in order to change it. If she enters the correct password, the program updates the password and immediately breaks out of the while loop; if she tries unsuccessfully three times, the program exits without changing the password.

After that, the SuperSecretAccount class overrides the password method at ➏ (the other one created by Account’s attr_accessor :password) and makes it print the string The password is secret! instead of giving up the password as it normally would. Finally, we create a couple of accounts ➐ and try getting and setting the passwords ➑.

You can run the code in your file by typing ruby secrecy.rb from the command line. Make sure you’re in the same folder as your secrecy.rb file and type:

$ ruby secrecy.rb

Here’s the output I get (yours might be a little different, depending on what you enter when you run the script):

➊ Your regular account password is: your password

Your regular account password is now: Something else!

➋ If we try to see the secret account password, we get: The password

is secret!

➌ Trying to change your secret account password to Something else!...

Current password?: lasers

That's not the right password.

Attempt 1 of 3 used up!

Current password?: ninjas

That's not the right password.

Attempt 2 of 3 used up!

Current password?: your password

Password changed to: Something else!

First, we see our program print out our regular account’s password, followed by the new password after we change it ➊. That was too easy!

Next, at ➋, we see that our secret account correctly hides the password from prying eyes, printing out only The password is secret! if we try to look at it.

Finally, we try to change our secret account password at ➌. We put in two wrong passwords (lasers and ninja) before finally entering the correct password, your password, and our Ruby program prints out that we successfully updated our password to Something else!.

Feel free to play around with the code. What happens when you try to get and set the password on the regular account in secrecy.rb? What about when you try to change the super_safe one?

What happens if we try to set the password on our super_safe account and pass in the correct current password? The wrong one? Try passing in the wrong password a bunch of times. What happens?

Once you’re done exploring the code, you can try thinking about all the cool stuff we could do to make it even better. For example, what methods could we add to the Account or SuperSecretAccount to make them even more useful? (Maybe a reset_password method, in case you’ve completely forgotten your password?) What methods might SuperSecretAccount override from Account? Are there any that might use some of the functionality of Account but not all of it? How could we go about doing that? (Hint: super would be involved.)

Lastly, Ruby does have some built-in methods that can help make your code more secure (or at least control which methods can be called). If you like, you can read all about it in the Ruby docs: http://ruby-doc.org/core-2.0.0/Module.html#method-i-private.

You Know This!

You learned some tricky stuff in this chapter, but I’m confident you’ve got a good handle on it. Just to be sure, let’s go through it all once more.

First, we reviewed how to create a Ruby class using the class keyword.

class Greeting

def say_hello

puts 'Hello!'

end

end

Next, you found out that Ruby classes can share information and methods with each other through inheritance: just as a person can inherit traits from her parents, one Ruby class can inherit information and behavior from another. The class that does the inheriting is called a subclass or child class, and the class it inherits from is called the superclass or parent class.

Inheritance syntax looks like this:

class Dog < Animal

def bark

puts 'Arf!'

end

end

In this example, because Dog inherits from Animal, instances of the Dog class (made with Dog.new) can use any of the methods defined in the Animal class.

We also learned about method overriding and the super keyword. Method overriding is just writing a method in a subclass that has the same name as a method in the superclass; when we create an instance of the subclass, it will use the subclass’s version of the method instead of the superclass’s. You’d want to override the superclass’s method any time you want different or more specific behavior in the subclass. For example, say you’re writing a game where your wizard is a hero (class Wizard < Hero), and wizards use magic in their attack method instead of the game’s default sword.

You can override a method like so:

class Hero

def initialize(name)

@name = name

end

def attack

puts "Swung sword for #{rand(5)} damage!"

end

end

class Wizard < Hero

def attack

puts "Cast spell for #{rand(20)} damage!"

end

end

We can see this in the following example: the hero’s pretty good with a sword, but the wizard knows how to cast spells!

>> aragorn = Hero.new('Aragorn')

=> #<Hero:0x0000010334e398 @name="Aragorn">

>> aragorn.attack

Swung sword for 4 damage!

=> nil

>> gandalf = Wizard.new('Gandalf')

=> #<Wizard:0x000001033627f8 @name="Gandalf">

>> gandalf.attack

Cast spell for 17 damage!

=> nil

If we want to change only part of a method, we use super; we add whatever extra functionality we want, then call super to call the superclass’s version of the method, like so:

class Wizard < Hero

def attack

super # This calls Hero's attack method

puts 'But I also know magic! You shall not pass!'

end

end

The attack method is doing two things. First, it calls the superclass’s version of attack using super (that is, the attack defined in Hero that just prints the Swung sword message). Then it prints an additional message (But I also know magic! You shall not pass!). You’d do this when you want to modify the behavior of the superclass’s method, but not replace it completely.

>> gandalf = Wizard.new('Gandalf')

=> #<Wizard:0x000001032d4278 @name="Gandalf">

>> gandalf.attack

Swung sword for 2 damage!

But I also know magic! You shall not pass!

=> nil

Last but not least, you saw that you can call super with arguments in order to send the right arguments to the superclass’s method:

class Dog

attr_accessor :name

def initialize(name)

@name = name

end

end

class GuardDog < Dog

attr_reader :strength

def initialize(name, strength)

@strength = strength

super(name)

end

end

Now when we create a GuardDog, it adds its own @strength and lets Dog take care of adding the @name:

>> mook = GuardDog.new('Mook', 2)

=> #<GuardDog:0x00000102fcfca8 @strength=2, @name="Mook">

>> mook.name

=> "Mook"

>> mook.strength

=> 2

All right! At this point, you’re a class master. Well, almost—just as there are ways to update and change your methods, there are ways to update and change your classes; you can even mix behaviors from a bunch of different classes into the Ruby classes you create! The last piece of the Ruby class puzzle is modules, and if we hurry, we can get down to the Royal Stables just in time to learn all about them.