A Horse of a Different Color - Ruby Wizardry: An Introduction to Programming for Kids (2014)

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

Chapter 10. A Horse of a Different Color

Utter Panda-monium

The King, the Queen, Ruben, and Scarlet spiraled down staircase after staircase toward the Royal Stables. Just when Ruben and Scarlet thought there would be no end to the twisty maze of stairs, the Queen reached a huge set of heavy oak doors and threw them open. They all ran blinking into the sudden light of the fields behind the palace, and only a stone’s throw away stood the entrance to the Royal Stables.

“Over here!” said the Queen. “Quickly now!”

They ran to the front gate of the stables, where two of the Queen’s guards were waiting. Each held a very familiar-looking Senior Apprentice to the Royal Plumber by the arm.

image with no caption

“Haldo!” gasped Ruben.

“Now, now,” said the King. “I’m sure there’s a reasonable explanation for all this.” Despite his words, the King looked worried. He turned to Haldo. “Haldo, what in the name of the Hashery’s glorious breakfast hash is the explanation for all this?”

Before Haldo could respond, the Queen approached the guards. “Haldo isn’t who I saw in my Royal Office,” she said. “There were four of them, and they were much shorter. Please release him.”

The guards nodded and dropped Haldo’s arms.

“Thank you, Your Highness,” Haldo said, brushing himself off.

“Why are you down here at the stables?” asked the King.

“That’s what we were just asking him,” said the guard with the crooked nose.

“I was trying to explain,” said Haldo. “You see, after searching through the Ambrose Caverns and finding nothing, I returned to my work as the Senior Apprentice to the Royal Plumber. I’d learned so much from Scarlet and Ruben about Ruby, though, that I could do a day’s work in just a few hours. I had a bit of spare time on my hands, so I took on the job of Part-Time Apprentice to the Royal Stableman as well.”

“Marvelous,” said the King, visibly relieved that Haldo was not the villain they’d been chasing. “A veritable jack-of-all-trades!”

“I’m not sure I’d go that far,” Haldo said, blushing slightly.

“Your Majesties,” said the guard without the crooked nose, “we chased a group of hooded figures from the Queen’s office, but lost track of them once they got out here to the stables. When we saw what had happened, Haldo was the only one around. We thought he might be involved, so we called the Queen.” He shrugged. “Turns out Haldo had come out to see what the trouble was and try to help.”

“One moment,” said the Queen. “When you saw what had happened in the stables?”

The guards exchanged an uneasy look. “You’d better come see,” said the guard with the crooked nose.

The group hurried into the stables. The two guards pointed to the first stall, and the King, the Queen, Ruben, and Scarlet peered inside.

“Strangest horse I’ve ever seen,” said the King.

image with no caption

“That . . . is a panda,” said Ruben.

“And it’s red!” moaned the Queen. “Good heavens, what’s happened here?”

“It’s not supposed to be red?” Scarlet asked.

“Not at all!” said the Queen. “All royal pandas are supposed to be purple!” She ran to the next stall, then the next, then the next. “This one’s blue!” she cried. “And this one’s yellow! Not a single panda is purple!” She threw up her hands. “Whoever heard of a Purple Panda-monium Parade with pandas of every color except purple?”

“Wait, the pandas were purple, but now they’re not?” said Ruben. “Aren’t they born purple?”

“And what, exactly, is the Purple Panda-monium Parade?” Scarlet asked.

“One at a time,” said the Queen. She turned to Ruben “No, the pandas aren’t born purple. They’re born white, but we feed them special extra-nutritious food that turns them purple. As for the parade,” she said to Scarlet, “We hold it once a month to celebrate the peace and prosperity of the kingdom. We figure if there’s going to be a little craziness in our lives, we should at least be in control of it.” She sighed. “Of course, given all the chaos today, we won’t be able to hold the parade.”

“Not so fast,” said Scarlet. “I’ll bet we can fix this! It sounds like someone must have tampered with the pandas’ food. Where is it?”

“Over here,” said Haldo. “The food is prepared by the Panda Provisionator 3000.”

They all walked past the rows of stalls to the far side of the stable, where a huge round machine covered in dials and switches hummed away. A familiar-looking screen glowed in its center.

image with no caption

“A Computing Contraption!” said Ruben. “Does the Panda Provisionator 3000 run on Ruby?”

“Absolutely,” said Haldo. “Ever since you kids helped me fix the Mysterious Pipe, I’ve been learning as much Ruby as I can. I daresay I’ve gotten pretty good,” he said, hooking his thumbs behind the straps of his overalls. “I’ve even gotten the hang of the Panda Provisionator here.”

“Could you tell us if someone’s messed with the pandas’ food?” Ruben asked.

“And can you fix it?” asked the Queen, looking anxious.

Creating Modules

“I think so,” said Haldo. “Let’s have a look.” He opened a file called colorize.rb on the Computing Contraption, and this is what the group saw:

module Colorize

def color

[:red, :blue, :green, :yellow].sample

end

end

“Aha!” said Haldo. “I see the trouble here. Someone’s changed the color method to return a random color as a symbol—either red, blue, green, or yellow. That’s what the sample method does,” he explained. “It picks a random item from an array.”

“That’s why the pandas are all different colors except purple!” Ruben said. “But wait a minute—there’s nothing about panda food in this file. And what does the first line mean?”

“That? That means this code is a module,” said Haldo, scratching his heavy black beard. “You can think of a Ruby module as a bucket of handy information and methods that we can use whenever we need it.”

“It looks kind of like a class,” said Scarlet.

“It’s very much like a class!” said Haldo. “Like classes, modules have their own methods. In fact, that’s all modules really are: just collections of methods!”

“Then what’s the difference between classes and modules?” Ruben asked.

“Modules are actually exactly like classes, only we can’t make new modules with the new method,” Haldo explained. “First, let’s do a lightning-quick review of classes.” He started up IRB and typed:

>> class FancyClass; end

=> nil

“That just creates a new, empty class called FancyClass,” Haldo explained.

“What’s that semicolon for?” Scarlet asked.

“It’s just a way of telling Ruby you’re done with a line of code,” Haldo said. “Normally in IRB you do that by pressing RETURN or ENTER and starting a new line, but since our class and module definitions are empty, we can just use the semicolon to tell Ruby we’re done with one line and we’re starting a new one.” He shrugged. “Some people don’t like to use semicolons. To each her own! Now, let’s create an instance of our FancyClass.”

>> FancyClass.new

=> #<FancyClass:0x000001044d80c8>

“You’ve created instances of classes before, right?” Haldo asked. Scarlet and Ruben nodded. “Good!” he said. “Now, let’s create a module and try to create an instance of it.”

>> module ImportantThings; end

=> nil

>> ImportantThings.new

NoMethodError: undefined method `new' for ImportantThings:Module

“Trying to create an instance of a module causes an error because modules don’t have the new method that classes do,” Haldo said.

“So if you can’t create instances of a module,” Ruben said, “what can you do with it?”

“I’ll show you!” said Haldo. “Let’s create a module of our own.” He typed:

>> module Bucket

>> MAX_BITS_AND_TRINKETS = 100

>> def announcing_bits_and_trinkets

>> puts 'Step right up! Bits and trinkets available now!'

>> end

>> end

=> nil

“What’s MAX_BITS_AND_TRINKETS,” Scarlet asked, “and why is it in all caps?”

Constants

“That’s a constant,” said Haldo. “Constants are like variables, only their values don’t change once you set them. They start with a capital letter—for example, class and module names are constants—and while you technically can reassign them during your Ruby program, Ruby will warn you if you do. See?” He typed:

>> RUBY = 'Wonderful!'

=> "Wonderful!"

>> RUBY = 'Stupendous!'

(irb):2: warning: already initialized constant RUBY =>

"Stupendous!"

“When you create your own constant that isn’t a class or module—that is, just a name for a value that won’t change—you usually write it in ALL CAPS,” Haldo said.

“Can you use constants only inside modules?” Ruben asked.

“Nope!” Haldo said. “You can use them anywhere in your Ruby program. I just bring them up now because class names and module names are technically constants, since they start with a capital letter.”

“That’s pretty cool,” said Scarlet, “but how do we get to our ALL CAPS constants and methods if they’re stuck inside a module?”

“I’m glad you asked,” Haldo said, smiling. “Let’s have a look!” He typed some more:

>> class Announcer

>> include Bucket

>> end

=> Announcer

“Here, I’ve made an Announcer class that includes the Bucket module. Our Bucket module contains a constant, MAX_BITS_AND_TRINKETS, which is set to 100, and a method, announcing_bits_and_trinkets, that prints some text on the screen.

When we include a module in a class, the constants and methods in that module can be used by any instance of the class. Because we’ve included Bucket in Announcer, an Announcer can now use any of the constants and methods defined in Bucket! Let’s create an instance ofAnnouncer and see what happens when we use a method we defined in Bucket.”

>> loud_lucy = Announcer.new

=> #<Announcer:0x00000103f0c5b8>

>> loud_lucy.announcing_bits_and_trinkets

Step right up! Bits and trinkets available now!

=> nil

“Wow!” said Ruben. “loud_lucy knows how to use the announcing_bits_and_trinkets method, even though it’s defined in the Bucket module!”

Extending Your Knowledge

“Exactly!” said Haldo. “But include isn’t the only way to get constants and methods defined in modules into other classes. Have a look at this.” He typed some more:

>> class Announcer

>> extend Bucket

>> end

=> Announcer

>> Announcer.announcing_bits_and_trinkets

Step right up! Bits and trinkets available now!

=> nil

“If we extend the module Bucket into the class, then those constants and methods can be used by the class itself,” Haldo explained. “In this case, the class Announcer—instead of its instance, loud_lucy—can use the method. You usually end up wanting your instances to have the method rather than your classes, so in my experience, you tend to include more often than you extend.”

“Remember when I said there was a Ruby trick that lets you mix the behavior of several classes into one?” asked the Queen. “This is how you do it!”

Mixins and Inheritance

“Wait,” said Ruben. “So you can have a class that inherits from another class and includes modules to add extra methods?”

“See for yourself!” replied Haldo, and he typed:

>> module Enchanted

>> def speak

>> puts 'Hello there!'

>> end

>> end

=> nil

“First, I’ve just created an Enchanted module with a single speak method.”

>> class Animal

>> def initialize(name)

>> @name = name

>> end

>> end

=> nil

“Next, I’ve created an Animal class that takes care of setting the names of the Animal instances we create.”

>> class Dog < Animal

>> include Enchanted

>> def bark

>> puts 'Arf!'

>> end

>> end

=> nil

“In the next step, I’ve created a Dog class that inherits from Animal and includes Enchanted. If we’ve done everything right, our Dog instances should be able to use the Dog bark method and the Enchanted speak method. Let’s try it now!”

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

=> #<Dog:0x000001049df148 @name="Bigelow">

>> bigelow.bark

Arf!

=> nil

>> bigelow.speak

Hello there!

=> nil

“When we use a module this way, we call it a mixin,” Haldo said, “because you’re mixing new constants and methods into an existing class. Basically, Dog now gets the powers of Animal and Enchanted, even though it only directly inherits from Animal. We can include as many classes as we like! Assuming we defined all these modules somewhere, we could use them all in a row:

class Dog

include Enchanted

include Magical

include AnythingWeLike

# ...and so on and so forth

end

“So if you had a Dog class and the modules Enchanted, Magical, and AnythingWeLike,” said the King, “if you were to make a dog with the Dog class, that dog could use any of the methods defined in Enchanted, Magical, or AnythingWeLike.”

“Exactly,” Haldo said. “We could also extend our class with as many modules as we wanted.” He continued typing:

class Dog

extend Enchanted

extend Magical

extend AnythingWeLike

# ...and so on and so forth

end

“That’s amazing!” said Scarlet.

“But hang on just a second,” Ruben said. “That means that somewhere on the Computing Contraption, there’s a file for the panda food that includes the Colorize module?”

Requiring Another File

“Absolutely correct,” said Haldo. “It happens to be called panda_food.rb. Take a look!” And he opened the file for them all to see. “This is the code that controls the pandas’ food.”

NOTE

The next few examples are just for you to follow along and read for now—running this code as is will cause an error! We’ll run this example ourselves later in the chapter.

require './colorize'

class PandaFood < Food

include Colorize

attr_reader :calories

CALORIES_PER_SERVING = 1000

def initialize

@calories = CALORIES_PER_SERVING

end

end

“Here’s how it works,” Haldo said. “Let’s pick one of the pandas—Hogarth’s my favorite—and see if we can figure out what’s going on with his food.” He opened up IRB and typed:

>> hogarths_food = PandaFood.new

=> #<PandaFood:0x00000104480850 @calories=1000>

>> hogarths_food.calories

=> 1000

“attr_accessor gives us access to the @calories instance variable, which is 1000,” Haldo explained. “Now let’s take a look at the color!”

>> hogarths_food.color

=> :yellow

“Hmm,” said Haldo. “Can that be right? Let’s try it again.”

>> hogarths_food.color

=> :blue

“There you have it!” Haldo said. “You see? That’s our trouble. Other Ruby programs running in the Panda Provisionator 3000 check the color of the panda food when they give instructions to the machine to make it, and they’re getting colors like yellow and blue, but not purple!”

“Then the pandas ate the food and changed color!” said Ruben. “Wow, that must happen pretty quickly.”

Haldo nodded. “The pandas were just fed. It actually takes a while for them to change from white to any other color, but once they’ve taken on a color, eating different-colored food will make their color change instantly.”

“So switching them back should be a piece of cake!” said Scarlet. “We just need to change the color back to purple.” She studied the screen for a minute. “Hey Haldo,” she said, “what’s this require bit do?”

“I’m glad you caught that,” said Haldo. “The require method pulls in Ruby code from a file outside the file you’re currently working in! So you don’t need it for IRB when you’re just messing around, but if you’ve written out a Ruby file, you can use require to pull in code from a separate file. You don’t even need to type the .rb file extension; you just type require, then the name of your file as a string, and you can use that code immediately.”

He created a file called test_colors.rb and began typing:

➊ require './colorize'

➋ class TestColors

➌ include Colorize

end

test = TestColors.new

➍ puts test.color

Haldo closed the file. When he ran it with ruby test_colors.rb, this is what they saw:

$ ruby test_color.rb

blue

$ ruby test_color.rb

yellow

“See?” Haldo said. “We can create our own file called test_colors.rb, then require the colorize.rb file inside it ➊. Once we do that, we can create our own TestColors class ➋, include the Colorize module from the colorize.rb file we saw earlier ➌, and then use the color method ➍!”

“Nice!” said Ruben. “But why do we need the ./ in front of colorize?”

“That’s a little complicated,” said Haldo, “but the short answer is that when you want to require a Ruby file, you need to tell Ruby where to look for it. ./ says, ‘Look in this folder right here!’ If we needed to require something from a folder outside the one we’re in, we’d use two dots to tell Ruby to go up one folder. This can be confusing,” Haldo finished, “so I drew a couple of pictures to help myself remember. I think I still have them!” He rummaged around in his pocket for a moment, then pulled out a piece of paper, unfolded it, and showed it to the King, the Queen, Scarlet, and Ruben.

image with no caption

“I get it!” said Scarlet. “One dot and a slash means ‘look in the current folder,’ two dots and a slash means ‘go up one folder and look there,’ and anytime we need to go into folders within folders, we just use folder names separated by slashes.”

“Exactly right,” said Haldo.

“But is there ever a time when you don’t need to use dots or slashes?” she asked.

“That’s also a bit complicated,” said Haldo, “but the short answer is yes. I can show you sometime, but there’s a way to use the Internet to download collections of Ruby files other people have written, called gems, to use in your own code!”

“That sounds amazing!” said Scarlet.

“It is!” said Haldo. “When we get to the bottom of this mystery, I’ll be glad to show you.”

“I think I’ve got a handle on all this,” interrupted the King, “but I’ve been wondering about constants since you brought them up. Is including a module in a class the only way to get to its constants?”

Looking Up Constants

“Not at all!” said Haldo. “Take a look.” He quickly typed into the Computing Contraption:

>> module APocketFullofMethods

>> NUMBER_OF_METHODS = 42

>> end

=> nil

>> NUMBER_OF_METHODS

NameError: uninitialized constant NUMBER_OF_METHODS

>> APocketFullofMethods::NUMBER_OF_METHODS

=> 42

NOTE

These examples will work if you try them out, so go ahead!

“Here, I’ve defined a module called APocketFullofMethods,” Haldo said. “Inside it, I’ve put a constant, NUMBER_OF_METHODS, which equals 42. You see that if I try to get to NUMBER_OF_METHODS from outside the module, I get a NameError, but if I typeAPocketFullofMethods::NUMBER_OF_METHODS, I get 42!”

“Wonderful!” said the King.

“But what are those two colons in a row for?” asked Scarlet.

“Ah, I’ve seen this before,” said the Queen. “That’s the scope resolution operator, right, Haldo?”

“Oh, yes,” said Haldo, “but I find that name a bit confusing. Really, you can think of it as a way of looking things up: the four dots look like two little sets of eyes. It’s how we specify which module to look in to find something we’ve created.”

“That’s cool!” said Ruben.

“Isn’t it?” said Haldo, “Ruby modules are mostly good for two things. The first, as I showed you, is mixing new behavior into a Ruby class. The second is called namespacing. You can think of it as making individual spaces for the things you name—mostly methods and constants—to live in.” He pushed his sunglasses up on his nose. “You see, if you define a method with a certain name, and then define it again, Ruby replaces the old version of the method with the new one. But if you put a method inside a module with the same name as a method or constant outside the module, you can use them both!”

“Modules must create a new scope!” said Ruben. “So having two methods with the same name and putting one in a module is like having two identical sodas, only one’s in the fridge and the other’s not. With methods, one’s in the module (the fridge) and one isn’t, so you know which one’s which based on where it is.”

“Exactly,” said Haldo.

“And everything we just said about methods works for constants, too, right?” asked Scarlet.

“It does!” answered Haldo.

“What happens if you put one module inside another?” asked Ruben.

“You just need to keep using those :: dots,” said Haldo. “For instance, if you had a module Pastel inside the Colorize module and you wanted to get to the NUMBER_OF_PASTEL_COLORS constant inside the Pastel module, you’d typeColorize::Pastel::NUMBER_OF_PASTEL_COLORS.”

“If things inside a module are namespaced, like you said,” Scarlet asked, “does that mean you can have two things with the same name, only one’s inside the module and one’s outside?”

“Absolutely!” Haldo said. He typed:

>> module Namespace

>> GREETING = 'Hello from INSIDE the module!'

>> end

=> nil

>> GREETING = 'Hello from OUTSIDE the module!'

=> "Hello from OUTSIDE the module!"

“Here, I’ve defined two constants with the same name: GREETING. The first one is inside the Namespace module, and the other is in the main scope, outside any module. Here’s how we tell Ruby which one to get.” He typed some more:

>> GREETING

=> "Hello from OUTSIDE the module!"

>> Namespace::GREETING

=> "Hello from INSIDE the module!"

“I get it!” said Ruben. “The two colons tell Ruby which scope to use!” He thought for a moment. “Can we do all of this for class methods, too? I mean, if a module can contain methods created with def, can’t it have methods that get added to the including class with self.def?”

Haldo nodded. “You can use the scope resolution operator to get class methods as well as constants, but in Ruby, we usually get class methods using the dot, and constants using the two colons. Since the method is a class method,” he continued, “it’s just like calling a method on a regular old object. Remember, classes are objects! Here’s an example—we haven’t defined any of these methods, so the code won’t run, but it would look something like this:

MyClass.fancy_class_method

MyClass::CLASS_CONSTANT

“Whew!” said the King, sitting down on a bale of hay. “I think I’ve got all this—surprisingly.” Scarlet and Ruben grinned at each other.

“What I don’t understand,” the King continued, “is how these scoundrels broke the Colorize module so quickly. They were only in the stables for a few seconds! How fast do they type?”

“I think I might have just found the answer,” said the Queen, who had been inspecting the Panda Provisionator 3000. She reached around to the side of the machine and pulled out a small bit of scuffed metal.

“What’s that?” asked Haldo.

“This,” said the Queen, “is a Key-a-ma-Jigger. It’s a little device you can preload code onto. Our mischief-makers must have known something about how the Provisionator works and preloaded some code onto this little machine to break it. They just had to plug it in and run!”

“Sweet corn muffins!” said the King. “We’re up against professionals.”

“I’ll say,” said Ruben, frowning. “How are we going to catch them? They’ve got to be a mile away by now.”

The Queen had been studying the Key-a-ma-Jigger, and her mouth curled into a small smile. “I think I know that, too,” she said. “Have a look! Key-a-ma-Jiggers are sold on little rings of five, and this one still has the ring on it. That means this was probably their last one!” She closed her fist around the tiny machine. “My guess is that they need more, and there’s only one place in the whole kingdom that makes Key-a-ma-Jiggers.”

“Where?” asked Scarlet, Ruben, and Haldo at the same time.

“Yes, dear, where?” asked the King.

“The Refactory!” replied the Queen.

“The Refactory!” said Haldo. “That’s in the center of the kingdom. The Loop can take you there in just a few minutes!”

“Let’s go!” said the King. “We’ll take the express straight to the center of the kingdom. Let’s catch these poisonous perpetrators purple-handed!”

“To the Loop!” said the Queen. She turned to Haldo. “Haldo, do you mind staying behind and fixing the Provisionator?”

“Not at all, Your Highness,” he said. “It shouldn’t take long.”

“Thank you,” said the Queen. She turned to the others and said, “Quickly, now!” And with that, they ran out of the stables toward the Loop platform on the hill next to the palace.

A Horse of a Different Color

Now that you know how modules work, you can help Haldo fix up the Panda Provisionator and get all the pandas back to the right color! With any luck, you’ll have them all fixed up in time for the Purple Panda-monium Parade.

Let’s begin by making a new file called colorize.rb and typing the following code. We’ll actually be making two files this time around: one for the module and one for the class that includes it.

colorize.rb

module Colorize

def color

:purple

end

end

First, we set up the Colorize module and created a very simple color method that just returns the color we want (:purple).

In another file in the same folder on your computer, create the panda_food.rb file and type the following code into it. It might be a little weird writing two files instead of just one, but there’s nothing here you don’t already know how to do!

panda_food.rb

➊ require './colorize'

➋ class Food

def serve

puts 'Food is ready!'

end

end

➌ class PandaFood < Food

➍ include Colorize

attr_accessor :calories

➎ CALORIES_PER_SERVING = 1000

def initialize

@calories = CALORIES_PER_SERVING

end

def serve

puts 'One piping hot serving of panda food, coming up!'

end

➏ def analyze

puts "This food contains #{@calories} calories and is #{color}."

end

end

➐ hogarths_food = PandaFood.new

puts hogarths_food.analyze

First, we require colorize.rb in our panda_food.rb file at ➊. Next, we define a very simple Food ➋ class that our PandaFood class inherits from ➌, and we include the Colorize module in our PandaFood class at ➍. We round it all out with a constant to tell us how many calories are in each serving ➎ and an analyze method to tell us about the food’s calorie content and color ➏. (You can’t be too careful when it comes to your food!) Finally, we create an instance of PandaFood and call the analyze method on it ➐.

As always, try running the code in your file by typing ruby panda_food.rb from the command line. Make sure you’re in the same folder as your panda_food.rb file and type:

$ ruby panda_food.rb

You should see this:

This food contains 1000 calories and is purple.

Purple panda food! Our pandas are saved!

This should work for Haldo’s purposes nicely, but you can make this code even better with a little elbow grease (which you can purchase directly from the Refactory for the low, low price of nine ninety-nine ninety-nine ninety-nine ninety-five). For example, our Colorize module has only one method, and all it does is return the color purple. How might we change the color method to set whatever color we wanted? What other methods might we want to add to Colorize?

We also don’t do a whole lot with our Food class—PandaFood overrides the only method Food has! What else could we add to Food to make it even better? (Hint: The possibilities are endless!)

Finally, remember the code Haldo saw in Creating Modules that had been tampered with? It looked like this:

module Colorize

def color

[:red, :blue, :green, :yellow].sample

end

end

If you’re feeling adventurous, try changing the code in your colorize.rb file to this code and then rerun ruby panda_food.rb. See how the color changes each time, just like our heroes saw?

You Know This!

I can tell you’ve got a great grasp on all this module business. (I’m an excellent judge of many a character.) Let’s go over it one more time, though, just to make sure I know it. Haldo did a lot of explaining and I didn’t do any, so I want to be sure this is all sealed up tight in my noggin.

First, we learned about modules and how they’re basically just like classes, except you can’t create instances of them with the new method. We saw that we could use modules as namespaces, which is just a fancy way of saying they let us organize our code nicely, like so:

module Bucket

MAX_BITS_AND_TRINKETS = 100

def announcing_bits_and_trinkets

puts 'Step right up! Bits and trinkets available now!'

end

end

We also learned about constants (like MAX_BITS_AND_TRINKETS), which are just like Ruby variables, only their values aren’t supposed to change. (You can change them, but Ruby will issue a stern warning.) Constants are always CAPITALIZED.

We saw that we could also use modules as mixins by using include or extend. When we use include, it adds all the methods in the module to instances of whatever class is doing the including; when we use extend, those module methods are added to the class itself:

module Greetings

def sailor

puts 'Ahoy there!'

end

def pirate

puts 'Avast, ye salty dog!'

end

def robot

puts 'BEEP BOOP WHAT IS UP'

end

end

There, we’ve just created a Greetings module with a few methods. Next, we’ll create a Message class and include the Greetings module:

class Message

include Greetings

end

Then we’ll see that any instance of Message can use the methods defined in Greetings!

>> message = Message.new

=> #<Message:0x007fd6022c7948>

>> message.pirate

Avast, ye salty dog!

=> nil

If we extend Message with Greetings instead, then the Greetings methods can be used by the Message class itself:

>> class Message

>> extend Greetings

>> end

=> nil

>> Message.robot

BEEP BOOP WHAT IS UP

=> nil

Remember, it’s the Message class itself that now has the robot method, not an instance of Message! If we try to create an instance of Message and call the robot method on it, we’ll get an error:

>> my_message = Message.new

=> #<Message:0x000001030cdf88>

>> my_message.robot

NoMethodError: undefined method `robot' for

#<Message:0x000001030cdf88>

But if a class includes Greetings, then instances of that class have the method instead:

>> class Message

>> include Greetings

>> end

>> my_message = Message.new

=> #<Message:0x00000103108d18>

>> my_message.robot

BEEP BOOP WHAT IS UP

=> nil

By including modules into classes that already inherit from other classes, we can get all the benefits of inheriting from multiple classes with the simplicity of having just one superclass:

module Enchanted

def speak

puts 'Hello there!'

end

end

There, we’ve got our Enchanted module again with its tried-and-true speak method.

class Animal

def initialize(name)

@name = name

end

end

class Dog < Animal

include Enchanted

def bark

puts 'Arf!'

end

end

We’ve seen this before: we just define an Animal class and a Dog class that inherits from it. Dog has one method: bark.

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

=> #<Dog:0x000001049df148 @name="Bigelow">

>> bigelow.bark

Arf!

=> nil

>> bigelow.speak

Hello there!

=> nil

Finally, we see that Dog instances like bigelow can use bark (which it got from Dog) and speak (which it got from Enchanted)!

This is all fine and dandy when our modules and classes are in the same file, but what happens when they’re not? That’s right: we can use require! To pull a file we wrote into another file, we just use the require method and give it a string with the name of the file we want (no .rb file extension necessary). Remember, we need to use dots and slashes to tell Ruby where to look: ./ means “look in the current folder” and ../ means “go outside the current folder and look around.” If we want to go two folders up, we’d use ../../; if we wanted to get at a file calledgenius_idea_3.rb in the current folder but nested inside the folders fancy_things and genius_ideas, we’d type ./fancy_things/genius_ideas/genius_idea_3.

So for example, if we had colorize.rb in the same folder as the following Ruby script, we’d write it like this:

require './colorize'

class Food < PandaFood

include Colorize

# ...and so on and so forth

end

Finally, you saw that we could use the scope resolution operator to get at particular constants located in modules (even deeply nested ones!), and we can simply use the dot syntax we’re used to for getting ahold of class methods:

MyClass::AModuleInsideThat::YetAnotherModule::MY_CONSTANT

MyClass.some_method

With that, you now officially know everything there is to know about Ruby classes and modules! (Okay, okay, there’s always more to learn, but you know all the stuff you’d use to write everyday Ruby programs.) You know so much Ruby, in fact, that we’re going to take a short break from learning new stuff to focus on rewriting some of the code we already know. Rewriting your code so it still does the same thing but looks nicer or runs faster is called refactoring, and—as luck would have it!—that’s exactly what the Refactory is all about.