Ruby wizardry, p.12

Ruby Wizardry, page 12

 

Ruby Wizardry
Select Voice:
Brian (uk)
Emma (uk)  
Amy (uk)
Eric (us)
Ivy (us)
Joey (us)
Salli (us)  
Justin (us)
Jennifer (us)  
Kimberly (us)  
Kendra (us)
Russell (au)
Nicole (au)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

Larger Font   Reset Font Size   Smaller Font  

  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.

  “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') => #

  “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.”

  “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') => # >> fumblesnore = Weezard.new('Fumblesnore', '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.”

 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Add Fast Bookmark
Load Fast Bookmark
Turn Navi On
Turn Navi On
Turn Navi On
Scroll Up
Turn Navi On
Scroll
Turn Navi On
183