Everything Is an Object (Almost) - Ruby Wizardry: An Introduction to Programming for Kids (2014)

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

Chapter 8. Everything Is an Object (Almost)

The Subject of Our Story Is an Object

Scarlet ran to the Computing Contraption. “Do you know the name of that directory of everyone in the kingdom?” she called to the King. “It’s a hash that associates everyone’s name with his or her address.”

“Let’s see,” said the King. “Ah, yes! I’m pretty sure it’s called citizens.”

Scarlet nodded and began typing into IRB. When she pressed ENTER, this is what she saw:

>> citizens

=> {

:aaron_a_aardvark => 'A van down by the river',

:alice_b_abracadabra => 'The green house with two chimneys',

:trady_blix => 'Mal Abochny',

# ...and so on and so forth

The King peered over her shoulder. “That’s it!” he said. “But whoa! There must be a bajillion people in the kingdom! How will we find Wherefore?”

Scarlet typed some more:

>> citizens.size

=> 24042

“Yeah, the hash is definitely too big to go through by hand,” Scarlet said, “but I bet we can write our own method to find him!”

Ruben studied the citizens hash for a minute. “Remember how we could get the value of a hash key by typing the hash name, then the key in square brackets?” he asked.

“Yep,” Scarlet said.

“Well,” Ruben said, “what if we just write a method that takes a person’s name and the citizens hash, then tries to find that name in the hash?”

“Ruben, you’re a genius!” Scarlet said. She quickly typed:

>> def find_person(name, people)

>> if people[name]

>> puts people[name]

>> else

>> puts 'Not found!'

>> end

>> end

“Hang on, hang on. What’s all this?” the King asked. “Just a quick method I whipped up,” Scarlet said. “See? It’s called find_person, and it takes a person’s name as a symbol and a hash of people as parameters. If it finds the name in the hash, it prints it out; otherwise, it says the name’s not found!” She typed:

>> find_person(:wherefore, citizens)

=> One half mile due east!

“There it is!” Scarlet said. “It found the :wherefore key in the citizens hash.”

“One half mile due east!” said the Off-White Knight. “That should only take a few minutes, and east is that way. Let’s go!”

The Dagron rose to her full height, blotting out the sun for an instant. “I’ll come as well,” she said. “Wherefore and I are old friends, and we haven’t spoken in some time. It will be good to see him again.”

“Well, then,” said the King, “lead the way!”

The Off-White Knight and the Dagron turned and headed toward the late morning sun, and the King, Scarlet, and Ruben followed. The trees became taller and closer together as they walked, and after a few minutes, the sun could only be seen as a warm red light through the tops of the Carmine Pines.

“Hold on a second,” Ruben said, stopping and turning his head to one side. “Do you hear that?”

They all paused. The King cupped his hand to his ear, shook his head, wiggled his little finger around in his ear, then cupped his hand to it again. “I don’t hear anything,” he said.

“I think I hear it too,” said the Dagron. “It’s—”

“Music!” cried Ruben. “It’s coming from over there!” He pointed a little to the right of where they’d been headed.

“Let’s go!” said Scarlet, and the group pressed on into the Pines.

The music grew louder, and after walking through a particularly dense thicket of trees, the group found themselves on the edge of a small meadow. In the very center, perched on a wide tree stump, was a man in a scarlet tunic and archer’s cap with a long white feather in it. He was playing a pink mandolin and occasionally stopping to furiously scribble on a long roll of parchment with a quill pen, which sported the same type of long white feather as his cap.

“Wherefore!” boomed the Dagron.

The man on the stump stopped scribbling and looked up. A wide grin broke out across his face. “The Dagron!” he called. “Wonderful to see you! Come in, come in, come in.”

image with no caption

Led by the Dagron, the group crossed the meadow and circled around Wherefore. Wherefore leapt nimbly from his stump, removed his cap, and bowed deeply.

“Friends,” he said, “welcome to my forest stronghold!” He gestured to the stump. “It’s not much to look at now, but I’ve always had a weak spot for a fixer-upper. And I,” he said, “am your humble host, Wherefore the Wand’ring Minstrel.” Wherefore replaced his cap on his head. “I know, of course, the Dagron, and I’ve met the Off-White Knight before.” He looked at the King and pressed his palms together. “Your Majesty,” he said. “I’ve not had the pleasure of meeting you before, but it is a pleasure.”

“Likewise,” said the King. “We’ve heard much about you!”

Wherefore turned to Ruben and Scarlet. “Which leaves you fine rapscallions. What do you call yourselves?”

“I’m Scarlet,” said Scarlet, “and this is Ruben.”

“Hi!” said Ruben.

“Hello and hello!” said Wherefore. “Wonderful to meet you. I’m afraid you’ve caught me at a bit of a bad time, though.” He sighed. “I’ve spent all morning writing a ballad, and it’s hardly half done. I’ve got to get back to it immediately if I’m going to finish it before nightfall.”

“A ballad?” said Scarlet.

“Oh, yes,” Wherefore said, “you see, I’m something of a businessman. I operate a small-time ballad delivery service with dozens of customers. The only catch is,” he said, “that this means I do in fact have dozens of customers, and each ballad takes me hours to write. I can hardly keep up!” He pulled a handkerchief from his tunic pocket and mopped his brow.

The Dagron hummed thoughtfully, exhaling little puffs of smoke. “You know,” she said, “I think I can be of service.” She looked around the nearly empty meadow. “But I’ll need a little Ruby magic. Do you happen to have a Computing Contraption nearby?”

Wherefore laughed. “Do I have a Computing Contraption!” he said, and he stepped on the largest root of the tree stump. The stump shuddered, then rose a few feet out of the ground. It rotated slowly as it emerged, revealing the familiar glow of a Computing Contraption screen!

Classes and Objects

“Wonderful,” said the Dagron, coiling herself around the stump and leaning in close to the screen. “Now then! Every object in your Ruby program has a unique ID number,” she said. “You’ll find that objects you create usually have much higher numbers than objects Ruby creates. See?” She touched the screen of the Computing Contraption with her claw and said, “Ruby has a few objects that are very familiar, like 0 or true. Each object in Ruby has its very own ID number, and that’s how Ruby keeps track of them all. Have a look!”

>> 0.object_id

=> 1

>> true.object_id

=> 20

“Built-in Ruby objects like these get ID numbers from Ruby automatically when IRB starts or the script loads,” the Dagron continued. “Ruby also gives IDs to Ruby objects we create in our programs, but they’re very high numbers. This is because Ruby comes with so many built-in objects for us!” She touched the Computing Contraption again, and more text appeared:

>> :baloney.object_id

=> 1238088

>> "The Ballad of Wherefore the Wand'ring Minstrel".object_id

=> 2174481360

“How did she do that?” Ruben whispered to the Off-White Knight. “She didn’t even type anything!”

“She doesn’t need to,” the knight whispered back. “Dragons are magical creatures, and the Dagron is one of the most magical of all.”

“But where do all these objects come from?” the Dagron asked. Wherefore sat cross-legged on the ground and looked up expectantly at her.

“From classes,” the Dagron said, answering her own question. “You can think of Ruby classes as little machines that make objects of a certain type, and each object in Ruby knows what class it belongs to. We can use the class method to ask objects what their classes are. To start, Ruby numbers are from the Fixnum class. Behold!” she said, and more code appeared on the screen:

>> 7.class

=> Fixnum

“A string’s class is naturally . . . String!” she continued:

>> 'Odelay!'.class

=> String

“That’s nice to know,” interrupted the King, “but what good does it do us?”

“I was just getting to that,” said the Dagron. “When you know what class a Ruby object is, you can use the new method to make a new object of that class. You’ve seen this before, yes?” She gestured at new code on the screen:

>> greeting = 'Hello!'

“Yup!” said Ruben.

“Well, now you can do this!” said the Dagron, touching the Computing Contraption once more.

>> greeting = String.new('Hello!')

=> "Hello!"

>> greeting

=> "Hello!"

>> greeting.class

=> String

“You see?” said the Dagron, folding her claws. “Every object in Ruby has a class, which we can find with the class method. Not only that, but every object is created by a class with the new method, and it’s the class’s job to produce objects of a particular type!”

“So the class is like a cookie cutter, stamping out particular kinds of cookies,” said Wherefore, hitting his palm with his closed fist in a stamping motion. “Gingerbread men, chocolate chip cookies, sugar cookies shaped like snowflakes. And the objects are the cookies!”

“That’s a very good way of thinking about it,” said the Dagron.

“When’s lunch?” asked Wherefore.

image with no caption

“I’m afraid I still don’t quite understand,” the King interrupted. “And I’m still a bit mystified as to what makes classes so important.”

“I think I can help with that one,” said Scarlet. “When we’re dealing with numbers or strings, the helpful things classes do might not be obvious. But if we’re creating our own objects with their own new classes, classes become a way of creating a bunch of objects from a template. For example, if we have a Minstrel class, we can make a bunch of minstrels!”

“How?” asked the King.

Creating Our First Class, Minstrel

“I’m glad you asked! Let’s give it a try,” said the Dagron. She touched the Computing Contraption, and more code filled the screen.

NOTE

For some of these longer code examples, we’ll write Ruby scripts! Whenever you see a filename in italics above the code, like minstrel.rb for the next example, that means you can type the code into your text editor and save it as a file with the given name.

minstrel.rb

class Minstrel

def initialize(name)

@name = name

end

def introduce

puts "My name is #{@name}!"

end

def sing

puts 'Tralala!'

end

end

“Now then,” the Dagron said, clearing her throat, “let’s have a look. The class keyword tells Ruby you’d like to make a new class,” she said. “Just like you use def and end to tell Ruby you’re defining a new method, you use class and end to tell Ruby you’d like to create a new class.

“After class, you type the name of the class, which can be whatever you like,” the Dagron explained. “Class names, however, always begin with a capital letter, like Minstrel.” Wherefore had turned his parchment over and was taking notes as quickly as he could on the back of his ballad. “We’re creating the Minstrel class, so we can make lots of new minstrels.”

“Between class and that final end, you can add any methods you wish, just as you would define them outside a class,” the Dagron continued. “In the Minstrel class, I defined three: initialize, introduce, and sing.”

Ruben leaned in close to the Computing Contraption’s screen. “Why does that @name variable have an @ in front of it?” he asked.

“All in good time,” said the Dagron.

NOTE

To follow along with the Dagron, we’ll need to load her script into IRB. When we want to use code in IRB from a file we’ve written, we just start IRB while we’re in the folder that contains our Ruby script, then use the load command to load the file. Load minstrel.rb like this:

>> load 'minstrel.rb'

=> true

Now let’s give the Dagron’s code a try!

“First, let’s look at the Minstrel class’s initialize method. This gets called whenever we make a new instance of the class with the new method. Have a look!” The Dagron added more code to the screen.

>> wherefore = Minstrel.new('Wherefore')

=> #<Minstrel:0x000001052c77b0 @name="Wherefore">

“When we call Minstrel.new, we make a new minstrel. Because initialize takes a single parameter, name, we pass in a name when we call the new method. See the @name="Wherefore" bit there? It means wherefore’s name is 'Wherefore'!” The Dagron puffed thoughtfully for a second. “So if there’s any code you want to run as soon as a new instance of your class is created, you put it in the definition of your class’s initialize method.”

“Got it,” said the King.

“Now all that Proc.new stuff makes way more sense!” said Ruben. “We were just making new procs whenever we called new!”

“That’s right!” said the Off-White Knight. “Proc is a built-in Ruby class, and whenever we call new, we create a new one. We basically have a little factory that generates new procs whenever we want. And that’s all classes are: little factories that make objects!”

“Precisely,” puffed the Dagron, and she almost seemed to smile.

“What about the other two methods you added?” Scarlet asked.

“Ah, yes,” said the Dagron. “Our wherefore is a Minstrel, so he has access to those methods automatically.”

>> wherefore.introduce

My name is Wherefore!

=> nil

“You see?” she said. “The introduce method prints a string with the minstrel’s name in it, which in this case is Wherefore. And not only can he introduce himself, he can also sing!”

>> wherefore.sing

Tralala!

=> nil

“We’ve talked about how classes make objects of a certain type,” said the Dagron, “but we haven’t really mentioned what an object is. It’s quite simple: objects are just little collections of values! You can think of them as buckets of information—a thing that might have a name, or a size, or a color. Each object gets methods from its class that let us talk about its name or size or color, and that’s what makes up our Ruby code.”

“All right,” said the King, “I think I understand why classes are so important now: they let you reuse your code for many objects without having to rewrite all the information and methods each time, and Ruby code is made up of objects. But let’s go back to Ruben’s question—what was that kooky spiral we saw on wherefore’s @name?”

“The at sign (@) just shows Ruby that it’s a special kind of variable—the kind that talks about an object’s value, like its name or size or color! I’ll explain that a little more. Let’s try out an example using weezards,” said Wherefore.

“You mean wizards,” said Scarlet.

“No, weezards,” said Wherefore. “Short wizards. Wee things. Weezards.”

image with no caption

“Very well,” said the Dagron. “But to get there, I’ll need to explain the four different kinds of Ruby variables.”

“Four kinds!” exclaimed the King. “I thought there was only one!”

“The variables you’re used to seeing are called local variables,” said the Dagron. “They’re very good for creating a variable that you’re going to use quickly. But once we start writing our own methods and classes, we’ll need to create variables that can be defined inside those method and class definitions but are used much later—for example, when we finally call a method or create an instance of a class.”

“The other three kinds of variables,” the Dagron continued, “are global variables, class variables, and instance variables. It may seem confusing that we use different kinds of variables in different places, but once you get the hang of it, it’s very easy.”

“What do you mean by different places?” Scarlet asked.

Scopes,” said the Dagron.

Variable Scope

Oh man, this is getting good. We’re getting into the real meat of the language! Scope is a very important idea in Ruby, and I got so excited, well, I just couldn’t contain myself. I hope it’s okay if I take a second to explain scope to you while the Dagron explains it to our intrepid heroes. It’ll take but a minute.

This might come as a surprise to you, but not all variables are available for you to use willy-nilly at any point in a Ruby program. There are times in your program where even though you’ve defined a variable, if you try to use it, Ruby will complain and say it doesn’t exist! What could this mean?

What it means is this: at any given point in your program, only some of the variables and methods you’ve defined can be seen. The collection of variables and methods that can be seen at any given time in your program defines the current scope; you can use anything that’s in scope, and you can’t use anything that’s out of scope.

What determines your variable’s scope in Ruby? For now, here’s a good rule of thumb: new scopes are created inside a method definition, inside a class definition, and inside a block. So if you’re using the run-of-the-mill local variables we’ve been using, this will work perfectly:

>> regular_old_variable = 'Hello!'

=> "Hello!"

We’re just setting a regular_old_variable to the string 'Hello!'. Pretty standard stuff.

Next, we’ll define a variable within a method:

>> def fancy_greeting

>> greeting = 'Salutations!'

>> end

=> nil

Here, we’re defining the variable named greeting inside a method named fancy_greeting. You’ve seen method definitions before, so there’s nothing new here, either!

Next, we’ll revisit blocks:

>> 3.times { |number| puts number }

0

1

2

=> 3

You’re a block expert by this point, so you’ve got this too: we’re calling the times method on the number 3 and passing it a block. Inside the block, we use the variable number to keep track of which number we’re on, and we print out each number from 0 to 2 in turn. (Don’t forget that computers start counting things at 0, not 1.)

These Variable Errors Will Shock and Surprise You!

What might surprise you, though, is that some of this stuff will cause Ruby to throw an error! Let’s look at these one by one. In the following code, we start by defining a variable. But that regular_old_variable exists outside the class definition of FancyThings (in the outer scope), so it doesn’t exist inside the class definition!

>> regular_old_variable = 'Hello!'

=> "Hello!"

>> class FancyThings

>> puts regular_old_variable

>> end

NameError: undefined local variable or method `regular_old_variable'

for FancyThings:Class

Inside class definitions, you get a brand-new set of local variables (the kinds of variables you’ve seen all along so far), so Ruby rightfully tells you that inside the class, you don’t have anything called regular_old_variable yet.

The same goes for method definitions: they get their own sets of local variables, too, so when you define regular_old_variable within a method, it doesn’t exist outside the method definition:

>> def fancy_greeting

>> puts regular_old_variable

>> end

>> fancy_greeting

NameError: undefined local variable or method `regular_old_variable'

for main:Object

Another error!

And, as you might have already guessed, the number variable in our block example is local to the block. It stops existing as soon as the block is over, so if we try to use it again after the block is finished, we get an error!

>> 3.times { |number| puts number }

0

1

2

=> 3

>> puts number

NameError: undefined local variable or method `number' for

main:Object

Here, for each number from 0 to 3, Ruby puts the number passed into the block. Now, here’s where blocks get interesting: just as with methods or classes, a variable defined in a block stops existing when the block is finished. Unlike methods and classes, though, blocks can access variables and information that are outside them! In this case, our block knows about the number 3 and so knows that the variable number should take on each number between 0 and 3. Once the block is finished, though, Ruby no longer cares about number, so it causes an error if we try to use it again.

When I first learned that Ruby could see variables in some parts of a program and not others, I scratched my head for a good while, and I’m sure you’re asking yourself the exact same thing I asked myself then: “If that’s true, how on Earth can I use variables that I make inside classes or methods elsewhere in my program?” Well, as luck would have it, the Dagron’s about to tell us!

Global Variables

“Let’s start with global variables, which can be seen from anywhere in the program. An example might help,” said the Dagron, and she touched the Computing Contraption’s screen with her claw:

>> $location = 'The Carmine Pines!'

>> def where_are_we?

>> puts $location

>> end

>> where_are_we?

The Carmine Pines!

=> nil

“Here,” said the Dagron, “we create a variable called $location that’s equal to the string 'The Carmine Pines!'. Then we create a method, where_are_we?, that tries to access $location. Normally, this wouldn’t work, but because $location is a global variable, we get 'The Carmine Pines!' when we call the where_are_we? method!”

“Aha! I’ve seen this kind of variable before,” said the Off-White Knight. “I recognize it by the dollar sign it starts with! Global variables can be useful, since they can be seen anywhere in a Ruby program. You can define a global variable outside a method, inside a method, in a class, anywhere you want, and if you try to use it anywhere else in your program, it will just work. But,” she said, holding up one finger, “if the variable can be seen anywhere in the program, it can also be changed anywhere in the program, and it’s not always clear when or how that change happened.”

Scarlet nodded. “That’s right!” she said. “Remember when we found out that something was altering the variables in the Flowmatic Something-or-Other? Imagine how bad it would be if all our variables could be changed anywhere in our programs at any time!”

“Perish the thought!” said the King, shuddering. “We certainly don’t want that. All right, so we won’t use global variables if we can help it! What are the other sorts of variables we can use?”

Class Variables

“A wise choice, Your Majesty,” said the Dagron. “Another type of variable we can use is a class variable, which is useful if we want a class to save some information about itself. Just as all global variables start with $, all class variables start with @@, and a class can have as many class variables as it wants. A class variable can be seen from inside the class and by any instances of the class; all instances share the same class variable. Now, Wherefore, we’ll use your weezard example.” She blew a smoke ring at the Computing Contraption, and this code filled the screen:

weezard.rb

class Weezard

@@spells = 5

def initialize(name, power='Flight')

@name = name

@power = power

end

def cast_spell(name)

if @@spells > 0

@@spells -= 1

puts "Cast #{name}! Spells left: #{@@spells}."

else

puts 'No more spells!'

end

end

end

“We’ve defined a Weezard class with a class variable called @@spells,” said the Dagron, “as well as two methods: initialize, which sets up the name and power for a particular weezard, and cast_spell, which any weezard can use. Let’s go ahead and use new to create two new weezards with some special powers. Don’t forget to load the code you just wrote first!”

>> load 'weezard.rb'

=> true

>> merlin = Weezard.new('Merlin', 'Sees the future')

=> #<Weezard:0x00000104949260 @name="Merlin", @power="Sees the

future">

>> fumblesnore = Weezard.new('Fumblesnore', 'Naps')

=> #<Weezard:0x0000010494c500 @name="Fumblesnore", @power="Naps">

“Here’s the interesting thing about our weezards,” the Dagron continued. “Even though Merlin and Fumblesnore have different powers, they’re interacting with the same variable, @@spells! Each time they use cast_spell, the spell variable decreases by one. Take a look.”

>> merlin.cast_spell('Prophecy')

Cast Prophecy! Spells left: 4.

=> nil

>> fumblesnore.cast_spell('Nap')

Cast Nap! Spells left: 3.

=> nil

“So when you create a class variable, there’s just one copy for the whole class, and any instances you create all share that one class variable?” Ruben asked.

“That’s right,” said the Dagron.

“It’s kind of weird that all weezards share a fixed group of spells, isn’t it?” asked Wherefore. “Wouldn’t it make sense for each weezard to have his own set of spells?”

Instance Variables

The Dagron nodded. “Sometimes it makes sense for the class that creates objects to keep track of certain information, but not all that often,” she said. “For that reason, we don’t end up using a lot of class variables in Ruby; it’s mostly instance and local variables. In fact, with instance variables, we can give each weezard her own set of spells,” the Dagron continued, and more code appeared on the screen. “An instance variable can be seen from inside the class and by any instance of the class, just like class variables. The big difference is that each instance gets its very own copy of the variable!”

weezard_2.rb

class Weezard

def initialize(name, power='Flight')

@name = name

@power = power

@spells = 5

end

def cast_spell(name)

if @spells > 0

@spells -= 1

puts "Cast #{name}! Spells left: #{@spells}."

else

puts 'No more spells!'

end

end

end

“See how we’ve moved the @@spells variable from a variable that belongs to the class to a @spells instance variable inside the initialize method?” asked the Dagron. “Variables that start with @ are instance variables. They’re called instance variables because each instance, which is what Ruby calls an object created by a class, has its own copy.”

“So when we create instances of the Weezard class with the new method, each instance will get assigned its own @spells variable?” Scarlet asked.

“Precisely,” said the Dagron. “In fact, let’s do that now. We’ll create our weezards, just as we did before.”

>> load 'weezard_2.rb'

=> true

>> merlin = Weezard.new('Merlin', 'Sees the future')

=> #<Weezard:0x0000010459e160 @name="Merlin", @power="Sees the

future", @spells=5>

>> fumblesnore = Weezard.new('Fumblesnore', 'Naps')

=> #<Weezard:0x000001045a13d8 @name="Fumblesnore", @power="Naps",

@spells=5>

“This looks just like it did the last time we created weezards!” grumped the King.

“It’s very similar,” admitted the Dagron, “but there is one important difference. Look what happens when each weezard casts a spell!”

>> merlin.cast_spell('Prophecy')

Cast Prophecy! Spells left: 4.

=> nil

>> fumblesnore.cast_spell('Nap')

Cast Nap! Spells left: 4.

=> nil

“They each have their own @spells variable!” said Scarlet. “That’s why fumblesnore’s spell count wasn’t affected when merlin cast a spell.”

“Exactly right,” said the Dagron. “Even though their @spells variables have the same names, each instance gets its own copy, so they don’t conflict with each other. Not only that, but because instances of classes can always access their instance variables, any instance variables we define in our classes’ initialize method can be used by the newly created objects.”

“That’s why we do things like @name = name in our initialize method definitions,” said the Off-White Knight. “It makes sure that when we pass in the name argument, each instance saves a copy in @name.”

Local Variables

“Speaking of local variables,” said the Dagron, “let’s have a look at those, shall we? They should be quite familiar, but they’re worth a second look. A local variable can be seen only in its current scope, which means it can be seen only in the method or class where it’s defined.”

New code appeared on the Computing Contraption’s screen:

>> class YeOldeClass

>> local_variable = 'I only exist inside the class!'

>> end

>> puts local_variable

NameError: undefined local variable or method `local_variable' for

main:Object

>> def yet_another_method

>> another_local = 'I only exist inside this method!'

>> end

>> puts another_local

NameError: undefined local variable or method `another_local' for

main:Object

“So really, local variables can be seen only inside the methods or classes where they’re defined, or we can use them outside all class and method definitions,” Scarlet said.

“That’s right,” said the Dagron. “There is a special scope in Ruby called the top-level scope, so if you define local variables outside any method or class definition, Ruby can see them. Have a look!”

>> local_variable = "I'm the top-level local variable!"

>> def local_in_method

>> local_variable = "I'm the local variable in the method!"

>> puts local_variable

>> end

>> puts local_variable

I'm the top-level local variable!

=> nil

>> local_in_method

I'm the local variable in the method!

=> nil

“You see?” said the Dagron. “Local variables can even have the exact same variable names, as long as they’re in different scopes! Ruby knows that the method definition gets its own set of local variables, so it doesn’t complain that there are two with the same name.”

“So local variables can be seen only in the classes or methods where we define them, or in this special top-level scope that’s outside any class or method,” said the King. “But global variables can be seen anywhere, and if we create an instance of a class, the instance can see any instance variables we created when we defined the class.”

“Precisely,” said the Dagron.

“And the class can see its own class variables,” the King continued.

“Correct!” said the Dagron. “In fact, it’s not just instances that can have methods like initialize, introduce, and sing; even classes can have their own methods!”

“Just when I was starting to get this!” moaned the King. “How is that possible?”

“Because,” replied the Dagron, “Ruby classes are also objects!”

“I need to sit down,” said the King.

“You are sitting down,” said Wherefore.

“So I am,” said the King, who was sitting cross-legged between the Off-White Knight and the Wand’ring Minstrel. “Go on, Madame Dagron,” he said. “How can we add a method directly to a class itself, and not just an instance of a class?”

Objects and self

“Well,” said the Dagron, “Ruby keeps a special built-in variable named self around at all times, and self refers to whatever Ruby object we’re currently talking about.” She was talking quickly now, and small sparks leapt from her mouth as she continued. “So all we need to do is use selfto define a method inside our class, and instead of adding that method to the instance, we add it to the class itself.”

“Perhaps an example would make things clearer,” said the Off-White Knight. She reached over and typed into the Computing Contraption:

monkey.rb

class Monkey

@@number_of_monkeys = 0

def initialize

@@number_of_monkeys += 1

end

def self.number_of_monkeys

@@number_of_monkeys

end

end

“Here I’ve created a Monkey class,” said the knight. “It has a @@number_of_monkeys class variable that will keep track of how many monkey instances we create, as well as the initialize method we’ve seen in classes before. When we call new on Monkey to create a new monkey, it will add 1 to the @@number_of_monkeys.”

“What about that self.number_of_monkeys method?” asked Ruben.

“That’s a class method!” said the knight. “It’s a method of the Monkey class itself, and when we call it, it will return the @@number_of_monkeys. Let’s have a look! First, we’ll load that script, and then we’ll create a few monkeys.”

>> load 'monkey.rb'

=> true

>> monkey_1 = Monkey.new

=> #<Monkey:0x000001048fccf8>

>> monkey_2 = Monkey.new

=> #<Monkey:0x00000104902310>

>> monkey_3 = Monkey.new

=> #<Monkey:0x00000104907900>

“Good!” said the Off-White Knight. “Now that we have some monkeys, let’s ask the Monkey class how many monkeys there are.” She typed into the Computing Contraption:

>> Monkey.number_of_monkeys

=> 3

“Amazing!” said Wherefore. “But why not ask an individual monkey how many monkeys there are?”

“Well,” said the knight, “first, it doesn’t quite make sense to ask a monkey instance how many other instances there are—that’s the class’s business, not the instance’s! But more importantly, because we used self when we defined the number_of_monkeys method, it’s only a method of the class, not its instances! See?” She typed some more:

>> monkey_1.number_of_monkeys

NoMethodError: undefined method `number_of_monkeys' for

#<Monkey:0x000001048fccf8>

“There we are,” said the Dagron. “The Monkey class has its very own number_of_monkeys method now, but it’s only on the class itself; the monkey instances themselves don’t have that method.”

image with no caption

“In fact,” said the knight, “adding methods onto classes is common enough that Ruby has its own shorter syntax for it. It looks like this!” And she typed some more:

monkey_2.rb

class Monkey

@@number_of_monkeys = 0

def initialize

@@number_of_monkeys += 1

end

class << self

def number_of_monkeys

@@number_of_monkeys

end

end

end

“See that?” she asked. “Instead of defining the number_of_monkeys method on the class with self.number_of_monkeys, I used class << self to tell Ruby: ‘Hey! Every method I define until I say end is a method for the class, not its instances.’ Look what happens when I call the method on Monkey without creating any instances.”

>> load 'monkey_2.rb'

=> true

>> Monkey.number_of_monkeys

=> 0

“Now look what happens if I create an instance and call the method again,” said the knight.

>> monkey = Monkey.new

=> #<Monkey:0x0000010490af60>

>> Monkey.number_of_monkeys

=> 1

“See? It’s just like using self.number_of_monkeys,” the Off-White Knight said, beaming.

“Very interesting,” said the Dagron. “I’d never seen class << self before.”

“Really?” asked Wherefore.

“No one knows everything,” said the Dagron. “Not even me!”

“Many people find the def self.method_name syntax easier to understand,” said the knight, “so it’s perfectly fine to use that whenever you need to add a method to a class.”

“Of course,” said Scarlet, “and now self makes so much more sense to me! It just refers to whatever the Ruby program is ‘talking about.’ And in this case, self is the class we’re inside!”

Methods and Instance Variables

“Precisely,” said the Dagron. “And with that, I have but one more trick to show you. You see, while it’s very easy to create instance variables for our instances, it’s not always so easy to get at them. See what I mean?” she said, and as she spoke, new code began to fill the screen:

>> class Minstrel

>> def initialize(name)

>> @name = name

>> end

>> end

“I’ve re-created our Minstrel class from before, but with only an initialize method,” said the Dagron. “No introduce or sing methods! Let’s create an instance, like we did earlier.”

>> wherefore = Minstrel.new('Wherefore')

=> #<Minstrel:0x000001049637c8 @name="Wherefore">

“Now,” said the Dagron, “see how our minstrel instance has the name ‘Wherefore’? (You can tell by the @name="Wherefore" bit.) Let’s try to get to it.”

>> wherefore.name

NoMethodError: undefined method `name' for

#<Minstrel:0x000001049637c8 @name="Wherefore">

“You see,” said the Dagron, “while wherefore has a @name instance variable, it doesn’t have a name method. And when it comes to Ruby, all that matters are methods. In order to make wherefore.name actually work, we need to write a method to reach the @name instance variable.”

“Does that mean we’ll need to define a method in the Minstrel class called name?” Scarlet asked.

“That’s exactly right,” said the Dagron, and the code on the screen changed under her claw:

another_minstrel.rb

class Minstrel

def initialize(name)

@name = name

end

def name

@name

end

end

“Now we have a name method that returns the @name instance variable,” said the Dagron. “Let’s see what happens when we create a new minstrel with this name method and try to use it!”

>> load 'another_minstrel.rb'

=> true

>> wherefore = Minstrel.new('Wherefore')

=> #<Minstrel:0x000001049637c8 @name="Wherefore">

>> wherefore.name

=> "Wherefore"

“Huzzah!” cried the King. “We’ve done it! We’ve changed the minstrel’s name with the name method.”

“Truly wonderful,” said Wherefore, “but what if we want to change the minstrel’s name to something else?”

“Well,” said the Dagron, “let’s see if we can do that with the code we have now.” She added more code to the Computing Contraption’s glowing screen:

>> wherefore.name = 'Stinky Pete'

NoMethodError: undefined method `name=' for

#<Minstrel:0x000001049637c8 @name="Wherefore">

“We can get the name,” said the Dagron, “but we can’t change it; Ruby’s complaining that our instance has no method that changes names. It’s looking for a method we haven’t written yet!”

Ruben studied the screen. “It’s that NoMethodError again,” he said. “It looks like Ruby wants the Minstrel class to have a method called name=!”

The Dagron nodded. “If we want to change the @name, we have to write a special method called name= to do it,” she said. “If you write the name of a method with an equal sign at the end, Ruby understands it to mean: ‘I want this method to change the value of something.’ So to change the@name,” she finished, “we’d add a bit more code.”

She added the name= method to the rest of the code for all of them to see:

another_minstrel_2.rb

class Minstrel

def initialize(name)

@name = name

end

def name

@name

end

def name=(new_name)

@name = new_name

end

end

“Now we have a new method, name=, that takes a single parameter, the new_name,” said the Dagron. “This should tell Ruby to let us change the name simply by calling wherefore.name = 'some new name'! Let’s give it a try. First, we’ll create a new minstrel.”

>> load 'another_minstrel_2.rb'

=> true

>> wherefore = Minstrel.new('Wherefore')

=> #<Minstrel:0x000001049637c8 @name="Wherefore">

>> wherefore.name

=> "Wherefore"

“Next, we’ll try to change its name.”

>> wherefore.name = 'Stinky Pete'

=> "Stinky Pete"

>> wherefore.name

=> "Stinky Pete"

“That’s amazing!” said Ruben. “But writing all these methods to get and set instance variables sure is hard work. Is there any faster way to do it?”

The Dagron nodded. “As it turns out, there is,” she said. “There are three built-in shortcut methods for reading and writing instance variables: attr_reader, attr_writer, and attr_accessor. Here’s how they work.” She touched the Computing Contraption with her claw, and these words appeared:

another_minstrel_3.rb

class Minstrel

attr_accessor :name

attr_reader :ballad

def initialize(name)

@name = name

@ballad = 'The Ballad of Chucky Jim'

end

end

“For example, if you pass the symbol :name to attr_reader, it will automatically create a method called name that will read the instance variable @name. attr_writer will automatically create a method called name= that will change the value of @name, and attr_accessor will create both the name and name= methods.” The Dagron clicked her claws. “In this case, I’ve called attr_accessor with :name and attr_reader with :ballad, which should mean I can both get and change the minstrel’s name, but can only read his ballad without changing it. Let’s create a new minstrel to test out.”

>> load 'another_minstrel_3.rb'

=> true

>> wherefore = Minstrel.new('Wherefore')

=> #<Minstrel:0x0000010413c0e0 @name="Wherefore", @ballad="The

Ballad of Chucky Jim">

“Perfect,” said the Dagron. “Let’s see if attr_accessor lets us get and change the minstrel’s name, like we could before.”

>> wherefore.name

=> "Wherefore"

>> wherefore.name = 'Wherefive'

=> "Wherefive"

>> wherefore

=> #<Minstrel:0x0000010413c0e0 @name="Wherefive", @ballad="The

Ballad of Chucky Jim">

“Now let’s see if we can read the minstrel’s ballad, but not change it; that’s what attr_reader is supposed to do,” said the Dagron. She filled in more code on the Computing Contraption:

>> wherefore.ballad

=> "The Ballad of Chucky Jim"

>> wherefore.ballad = 'A Song of Mice and Friars'

NoMethodError: undefined method `ballad=' for

#<Minstrel:0x0000010413c0e0>

Wherefore shook his head in amazement. “Extraordinary!” he said. “With these Ruby tools, I’ll be able to write ballads in no time at all.”

“This is one of the most amazing parts of Ruby,” said the Off-White Knight. “When we design programs around objects, we’re doing something called object-oriented programming, and it lets us write programs that describe real-life things like minstrels and ballads. Everything becomes a thousand times easier!”

“This is marvelous, truly marvelous,” said Wherefore. “I can’t thank you enough. How can I possibly repay you?”

“Well,” Scarlet said, “actually, we were looking for you to ask whether you’d seen anything unusual happening in the kingdom. Ruby systems all over the kingdom have been breaking all day, and we’re starting to think none of the problems is an accident.”

“Show him the scale!” Ruben said.

“Oh, yeah!” Scarlet said, and she pulled the glittering green scale from her pocket. “Have you ever seen anything like this? We thought at first it might have belonged to the Dagron, but we checked and she isn’t missing a one.”

“Hmm,” said Wherefore. “Quite the quandary. No, I don’t think I’ve seen any creature with scales like this, but I did see something strange out here in the Pines not an hour ago.”

The King, Ruben, and Scarlet exchanged startled looks.

“What was it?” Scarlet asked.

“Well,” said Wherefore, “I only caught a snippet of conversation, but it was a few voices, talking in low tones behind that thicket yonder. I went to see what was going on, but they ran when I got near—three, maybe four of them,” he said.

image with no caption

“Who were they?” asked the King.

“I didn’t see,” said Wherefore, “but the part I overheard was dastardly indeed. They said something about not having made a big enough impact, and how they were going to see the Queen about whatever it is they were doing. I’ll bet my hat that when they fled, they were heading straight for the castle!”

“The castle! The Queen!” cried the King. “Oh my biscuits, oh my gravy! If these are our saboteurs, the Queen could be in terrible danger!”

“We’ve got to get back there, and fast!” said Scarlet. “Off-White Knight, Dagron, can you help us?”

The knight frowned thoughtfully. “I’m honor bound to stay in the Pines and to help anyone who wanders through,” she said. “But my duties are also to the King and Queen. I can spread the word as quickly as possible that trouble’s afoot and send as many friends to the castle as I can!”

“Please do!” said Scarlet. “What about you, Dagron?”

The Dagron shook her head. “Magic and wisdom come at a price,” she said. “I’m not able to leave the Carmine Pines. However, there is a shortcut back to the castle.”

“Where?” asked Ruben.

“The underground passage!” said Wherefore. “Yes, I know it. Follow me, I’ll take you there!”

The King, Scarlet, and Ruben thanked the Off-White Knight and the Dagron and, waving goodbye, hurried to catch up with Wherefore, who was already halfway across the meadow. They all plunged headlong along a twisty path of roots and knotted tree trunks, and after a few minutes of breathless running, they arrived at an enormous red pine tree, bigger than all the ones around it as far as they could see.

“Going down!” cried Wherefore, and knocked on the trunk three times. With a pleasant ding, a door slid open in the side of the trunk, revealing a cramped elevator car.

“Take the elevator to the sub-sub-basement,” he said, stuffing the three of them inside. “You’ll find a long, narrow passage that heads off to the west. Take that as far as you can, then look for a large black pipe. There’ll be words written on it that say—”

“—the Mysterious Pipe!” cried Ruben and Scarlet together. “We saw Haldo disappear down into the lower levels of the castle earlier today; this passage must connect to the same place!”

“Then you know your way!” said Wherefore. “Goodbye and good luck—in the meantime, I’ll help the Off-White Knight and the Dagron send help your way.” And with that, the elevator doors slid shut, and the King, Scarlet, and Ruben began to descend deep into the earth.

Dial-a-Ballad, or the Minstrel’s Delivery Service

Now that we’ve taught Wherefore the Wand’ring Minstrel all about Ruby objects and classes, it’s time to help him create his very own Ballad! Otherwise, he won’t be much of a minstrel. Don’t worry, though—now that you know all about classes and how they work, creating a simple class to help Wherefore write faster, better ballads will be a breeze.

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

ballad.rb

class Ballad

➊ attr_accessor :title

attr_accessor :lyrics

➋ @@number_of_ballads = 0

➌ def initialize(title, lyrics='Tralala!')

@title = title

@lyrics = lyrics

@@number_of_ballads += 1

end

➍ def self.number_of_ballads

@@number_of_ballads

end

end

➎ ballad = Ballad.new('The Ballad of Chucky Jim')

➏ puts "Number of ballads: #{Ballad.number_of_ballads}"

puts "Ballad object ID: #{ballad.object_id}"

puts "Ballad title: #{ballad.title}"

puts "Ballad object ID again!: #{ballad.object_id}"

puts "Ballad lyrics: #{ballad.lyrics}"

It’s hard to believe, but you now know so much Ruby that there’s actually nothing new here! You’ve seen all this stuff before: creating classes and instances of classes, using attr_accessor, using class and instance variables, adding methods to classes and instances, the whole shebang. Let’s step through it line by line and have a look at the output.

First, we create a Ballad class at ➊ with a title and lyrics we can both read and change (thanks to attr_accessor).

Next, at ➋, we set up a class variable, @@number_of_ballads, to track the number of ballads our class creates, and our initialize method at ➌ both sets the name and lyrics of the ballad and adds 1 to the @@number_of_ballads.

For the last part of our class definition, we add a number_of_ballads method at ➍ on the Ballad class itself; that will let us get the @@number_of_ballads later.

Finally, we create a new ballad with Ballad.new at ➎, then print out some interesting facts about our ballad at ➏.

You can run the code in your file by using the terminal to go to the folder where you saved ballad.rb, then typing ruby ballad.rb on the command line.

Your object IDs will be slightly different from mine, but you should see something like this:

Number of ballads: 1

Ballad object ID: #<Ballad:0x0000010413e0e0>

Ballad title: The Ballad of Chucky Jim

Ballad object ID again!: #<Ballad:0x0000010413e0e0>

Ballad lyrics: Tralala!

=> nil

We’ve just proved that our self.number_of_ballads method works, that our object ID doesn’t change once we create an object, and that we can get to all of the information we’ve stored in our ballad with the magic of attr_accessor.

All that’s well and good, but the really interesting part is how you take it further! For example, you can start small by writing code to change the title of a ballad you create or to update its lyrics after it’s created. (Do you think this will change the object ID?)

You could also add more attr_readers, attr_writers, or attr_accessors. You could add more methods (what about a playing_time method to return how many minutes long the ballad is?). You could add class methods or create additional ballads.

You could even take on the greatest challenge of all: actually writing the lyrics to “The Ballad of Chucky Jim”! The world is your oyster. (If you don’t like oysters, then the world is your cupcake.)

You Know This!

You learned a fair amount in this chapter, but nowhere near as much as you crammed into your noggin with methods! Learning about objects and classes was practically a vacation! Even so, let’s take the time to go over it all one more time, just to make sure you’ve got it all.

Objects and Classes

You already knew that just about everything in Ruby is an object, but in this chapter, you learned more about objects and took a closer look at object IDs. An object’s ID number is like a fingerprint: every object has its very own, and no two objects have exactly the same one. As a general rule, objects created by Ruby have lower object ID numbers than objects you create:

>> 0.object_id

=> 1

>> :minstrel.object_id

=> 465608

We also saw that classes are how we can create a whole bunch of objects with similar characteristics. We create classes with the class keyword, like so:

>> class Monkey

>> # Class magicks go here!

>> end

Creating classes is all well and good, but classes don’t really do much for us until we instantiate (create) an object from that class. You can think of classes as cookie cutters and the objects they create as cookies: the cookie cutter (class) makes a bunch of things that are all very similar, but the thing we’re most interested in is the cookie itself (the object).

For instance, we can define a Monkey class with our class keyword, and we instantiate it—that is, we make a particular monkey from our Monkey class cookie cutter—by calling Monkey.new:

monkey_review.rb

class Monkey

@@bananas = 5

def initialize(name)

@name = name

end

def eat_banana

@@bananas -= 1

puts "Ate a banana! #{@@bananas} left."

end

end

Great! So far we’ve got a Monkey class with two methods and a class variable. The class variable @@bananas keeps track of how many bananas there are for all monkey instances, the initialize method sets the monkey’s name when Monkey.new is called, and eat_banana decreases@@bananas by 1.

Next, let’s create a couple of monkeys:

>> load 'monkey_review.rb'

=> true

>> socks = Monkey.new('Socks')

=> #<Monkey:0x000001052c77b0 @name="Socks">

>> stevie = Monkey.new('Stevie')

=> #<Monkey:0x00000104ca38e8 @name="Stevie">

Now we can have each monkey eat a banana and see what happens:

>> socks.eat_banana

Ate a banana! 4 left.

=> nil

>> stevie.eat_banana

Ate a banana! 3 left.

=> nil

Did you notice how our Monkey class’s @@bananas class variable was reduced every time any monkey instance ate a banana? Remember, that’s because class variables are shared by all instances of that class.

We can assign any combination of local, instance, class, and global variables in combination with our classes, as shown here:

monkey_review_2.rb

class Monkey

$home = 'the jungle'

@@number_of_monkeys = 0

def initialize(type)

@type = type

@@number_of_monkeys += 1

puts "Made a new monkey! Now

end

end

Here, we’ve changed our Monkey class to have a global $home variable ('the jungle'), a @@number_of_monkeys class variable that keeps track of how many instances the Monkey class has created, and a @type instance variable that lets each individual monkey be of a different type.

>> load 'monkey_review_2.rb'

=> true

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

Made a new monkey! Now there's 1.

=> #<Monkey:0x00000104aafb40 @type="blue monkey">

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

Made a new monkey! Now there's 2.

=> #<Monkey:0x00000104ab3b28 @type="silver monkey">

>> gold = Monkey.new('golden monkey')

Made a new monkey! Now there's 3.

=> #<Monkey:0x00000104ab7c00 @type="golden monkey">

See how each @type is unique to each monkey, but they all change the same @@number_of_monkeys variable?

Finally, every part of the program also has access to the $home variable, since it’s global:

>> puts "Our monkeys live in #{$home}."

Our monkeys live in the jungle.

=> nil

Variables and Scope

This can all be a bit tricky to keep straight, so I’ve created the following handy-dandy table to help you remember the differences between local, global, instance, and class variables.

Variable type

Looks like

Where can it be seen?

Local

odelay

Inside the top-level scope, method, or class where it’s defined.

Global

$odelay

Anywhere!

Instance

@odelay

Inside the class where it’s defined or in any instance of the class. Each instance gets its own copy.

Class

@@odelay

Inside the class where it’s defined or in any instance of the class. Each instance shares the same class variable with all other instances.

Remember, it’s usually not a good idea to use global variables, because not only are they visible everywhere in our program but they can also be changed from anywhere in our program. When things can be changed from a bunch of places, it can be hard to figure out what part of our program made the change if something unexpected happens. I’m showing you global variables so you know what they are and how they work, but in almost every situation, they’re much more trouble than they’re worth.

As you saw in the last example, we could reach the $home variable from outside the class definition because it was defined as global (global variables start with a $). We can only ever get at a variable if it’s in the proper scope. Let’s review some examples from earlier in the chapter:

>> local_variable = 'Local here!'

=> "Local here!"

Our local_variable exists in this outer scope, but it doesn’t exist inside the class definition:

>> class OutOfTowner

>> puts local_variable

>> end

NameError: undefined local variable or method `local_variable' for

OutOfTowner:Class

The local_variable doesn’t exist inside the method definition, either!

>> def tourist

>> puts "Can you take our picture, #{local_variable}?"

>> end

>> tourist

NameError: undefined local variable or method `local_variable' for

main:Object

Our variable number exists inside the block, but it’s gone as soon as the block’s code is finished:

>> 3.times { |number| puts number }

0

1

2

=> 3

>> puts number

NameError: undefined local variable or method `number' for

main:Object

We discovered that Ruby has a built-in variable, self, that refers to whatever object the method will be called on, and we can use self to add methods directly to classes (instead of just the objects they create), as shown here:

monkey_review_3.rb

class Monkey

@@number_of_monkeys = 0

def initialize

@@number_of_monkeys += 1

end

def self.number_of_monkeys

@@number_of_monkeys

end

end

You’ve seen this before! It’s our Monkey class with a @@number_of_monkeys class variable, an initialize method that increases that variable every time we make a new monkey, and a self.number_of_monkeys method that means that we can call Monkey.number_of_monkeysto find out how many we’ve created so far:

>> load 'monkey_review_3.rb'

=> true

>> Monkey.number_of_monkeys

=> 0

It’s 0 right now, but if we create a monkey, we’ll see that number go up!

>> monkey = Monkey.new

=> #<Monkey:0x0000010490af60>

>> Monkey.number_of_monkeys

=> 1

If you’re ever unsure of the value of self in a particular part of your program, you can always puts self to see what it is.

We also learned that if an object has an instance variable that we want to see or change, we have to write methods to do it. We can write these methods ourselves as follows:

minstrel_review.rb

class Minstrel

def initialize(name)

@name = name

end

def name

@name

end

def name=(new_name)

@name = new_name

end

end

Here, we’ve set a @name in our initialize method, which means that any time we call Minstrel.new, we pass in a name for that minstrel. The name method gets that @name variable for us, and the name= method allows us to assign a new_name to the minstrel . . .

. . . but we can also use the shortcuts attr_reader (to read an instance variable), attr_writer (to change an instance variable), and attr_accessor (to do both). All we do is pass the instance variable name as a symbol, like so:

minstrel_review_2.rb

class Minstrel

attr_accessor :name

attr_reader :ballad

def initialize(name)

@name = name

@ballad = 'The Ballad of Chucky Jim'

end

end

Here, we’ve used attr_accessor and passed it a :name symbol to have it automatically create name and name= methods for us; we called attr_reader with :ballad, so we only get a ballad method to read the @ballad instance variable. Check out what happens if we try to change our ballad!

>> load 'minstrel_review_2.rb'

=> true

>> wherefore = Minstrel.new('Wherefore')

=> #<Minstrel:0x0000010413c0e0 @name="Wherefore", @ballad="The

Ballad of Chucky Jim">

>> wherefore.ballad

=> "The Ballad of Chucky Jim"

>> wherefore.name

=> "Wherefore"

>> wherefore.name = 'Wherefive'

=> "Wherefive"

>> wherefore.ballad = 'A Song of Mice and Friars'

NoMethodError: undefined method `ballad=' for

#<Minstrel:0x0000010413c0e0>

Object-Oriented Programming

Finally, we learned that writing programs that revolve around classes and objects is called object-oriented programming (OOP). Our minstrel is a good example of an object: a piece of code that acts just like something in the real world! It has attributes (facts about itself) as well as behavior, which is just a way of talking about the methods the object knows how to use. We can define the behavior of any minstrel with a Minstrel class, as follows.

minstrel_review_3.rb

class Minstrel

attr_reader :name

@@number_of_minstrels = 0

def initialize(name)

@name = name

@@number_of_minstrels += 1

end

def sing(song_name)

puts "Time to sing a song called: #{song_name}!"

puts 'Tralala!'

end

def self.number_of_minstrels

@@number_of_minstrels

end

end

Our class has an attr_reader for :name (meaning we can read the name, but not change it), as well as a @@number_of_minstrels class variable that keeps track of how many instances we’ve created and an initialize method that gives our minstrel a name and increases the@@number_of_minstrels.

It also has two methods: one, sing, is a method of minstrel instances and sings a little song, and the other, self.number_of_minstrels, is a method of the Minstrel class and tells us how many minstrels we’ve created so far.

Let’s see them in action!

>> load 'minstrel_review_3.rb'

=> true

>> wherefore = Minstrel.new('Wherefore')

=> #<Minstrel:0x000001031eac68 @name="Wherefore">

>> Minstrel.number_of_minstrels

=> 1

wherefore.sing('A Tail of Two Foxes')

Time to sing a song called: A Tail of Two Foxes!

Tralala!

=> nil

Voilà! We can create a new minstrel, call Minstrel.number_of_minstrels to see that we’ve created one, and then call our minstrel instance (wherefore)’s sing method to hear his ballad “A Tail of Two Foxes.”

Things are starting to get a bit suspenseful around here, so I’m gonna go grab a bag of popcorn—be right with you. In the meantime, go on ahead to see what the King, Scarlet, and Ruben find when they get back to the castle, and get ready for even more object-oriented Ruby magic!