Ruby wizardry, p.22
Ruby Wizardry, page 22
“Right again,” said the Queen. “Because I used puts to add the first line, ONE KAT-MAN-BLEU BURGER, PLEASE was added on its own line. The readlines method just goes through and creates an array from the file, where each item in the array is a single line of text. So we have an array with two elements here.”
“Astounding!” said the King, peering over his wife’s shoulder.
“Isn’t it?” she asked. “There’s also a readline method, which just gives us back one line at a time. See?” She typed some more:
>> file.rewind => 0 >> file.readline => "THE MELTIEST OF GRILLED CHEESESn" >> file.readline => "ONE KAT-MAN-BLEU BURGER, PLEASE"
“We can even use readlines with each to print out all the lines at once!” the Queen said, typing even more quickly:
>> file.rewind => 0 >> file.readlines.each { |line| puts line } THE MELTIEST OF GRILLED CHEESES ONE KAT-MAN-BLEU BURGER, PLEASE => ["THE MELTIEST OF GRILLED CHEESESn", "ONE KAT-MAN-BLEU BURGER, PLEASE"]
“That’s amazing!” said Ruben.
Avoiding Errors While Working with Files
“I think I’m starting to understand file input and output now. But what happens if I try to use a file that doesn’t exist?” Ruben asked as he reached over to the Computing Contraption’s keyboard and typed:
>> File.open('imaginary.txt', 'r') Errno::ENOENT: No such file or directory - imaginary.txt
“An error!” Scarlet said. “That makes sense. Is there any way to find out if a file exists before we try to use it?”
“Good question!” said the Queen. “If we’re not sure whether a file exists, we can use Ruby’s built-in File.exist? method to check.” She typed:
>> File.exist? 'lunch.txt' => true >> File.exist? 'imaginary.txt' => false
“Wonderful, wonderful!” said the King, clapping his hands together. “With all these magnificent Ruby tools, I have no doubt we can capture these crooks quite quickly.”
“You’re right!” said the Queen. She turned to Rusty. “Is there anything in the Ruby program that represents all the loading docks?” she asked.
Rusty nodded. “There’s an array, loading_docks, which is an array of files. Each file represents a loading dock door, so if you open and read all the files, all the doors should open!”
The Queen thought for a moment, her fingers hovering above the keyboard. Then she typed into the Computing Contraption:
loading_docks.each do |dock| current_dock = File.open(dock, 'r') puts current_dock.read current_dock.close end
One by one, the doors to each loading dock rolled open, hung ajar for a moment, then slid shut. Descriptions of each dock’s contents began to fill the Computing Contraption’s screen.
“Ruby code . . . Ruby code . . . shipment of Key-a-ma-Jiggers . . . there!” shouted Rusty, pointing to a door in the center of the far wall.
Four shadowy figures leapt from the loading dock near the lower-left corner of the wall just as the doors began to slide shut again.
“Freeze!” shouted the King. “We’ve got you surrounded!”
The four figures moved with surprising speed, knocking over several Refactory workers as they tried to make their way to the nearest exit.
“Stop them!” Rusty yelled as the five of them ran down the metal walkway to the loading dock floor.
Several Refactory workers struggled with the intruders, but they were too fast and too slippery. In just a few seconds, they’d made it all the way to the exit!
“Make way, make way!” cried the Queen, and the five of them reached the Refactory exit just as the shadowy villains escaped through the door. Without breaking stride, the King, the Queen, Ruby, Scarlet, and Rusty barreled through the doorway and into the narrow corridor leading back the way they’d come in.
“Are they headed for the freight elevator?” Ruben panted as they ran.
“Much worse!” Rusty said. “They’re headed straight for the WEBrick road!”
The King and Queen gasped together. “The WEBrick road!” said the Queen. “That leads straight out of the kingdom! If they get out through the kingdom gates, we’ll never catch them!”
“Then we’ll just have to be sure that doesn’t happen,” Rusty said. He turned and called over his shoulder: “Everyone, after them!” And with that, every single person in the Refactory ran toward the small bright exit sign, with the King, the Queen, Scarlet, Ruben, and Rusty leading the pack.
All Loading Docks, Report for Duty!
We’ve nearly caught our crooks red-handed! Oh man, the suspense is killing me. Who are they? Will the King, the Queen, Ruben, Scarlet, and Rusty catch them in time? What’s on the Refactory cafeteria lunch menu for tomorrow? Questions worth pondering until the end of time, for sure—or at least, until the end of this chapter. In the meantime, let’s get in just a bit more practice reading from and writing to a file.
Let’s start out by making a new file called loading_docks.rb and typing the following code. This is a simple little program that will create a text file for each of our loading docks, write some text into it, and then read it back to us.
loading_docks.rb
def create_loading_docks(➌docks=3) ➊ loading_docks = [] ➋ (1..docks).each do |number| ➍ file_name = "dock_#{number}.txt" loading_docks << file_name ➎ file = File.open(file_name, 'w+') file.write("Loading dock no. #{number}, reporting for duty!") file.close end loading_docks end ➏ def open_loading_docks(docks) ➐ docks.each do |dock| file = File.open(dock, 'r') puts file.read file.close end end ➑ all_docks = create_loading_docks(5) ➒ open_loading_docks(all_docks)
While there are a few bits of code that are making appearances from earlier chapters, there’s nothing brand-new here for you to worry about. Let’s walk through the code line by line.
First, we set up an empty array called loading_docks ➊, which we’ll use to store the names of all the loading dock files we’ll create (so we can read them later). Next, we use the (1..docks) range to create as many loading docks as the create_loading_docks method requires ➋ (it defaults to 3 if no number is passed in ➌).
For each number in the range, we call a block that creates a file with that number (such as dock_1.txt) and adds that filename to the loading_docks array ➍. We then open the file, write a string of text into it, and close it ➎.
Finally, in the open_loading_docks method ➏, we simply take our array of loading dock names (it looks something like ["dock_1.txt", "dock_2.txt"...], and so on), and for each filename, we open the file for reading, read its contents, and close it ➐. So when we run this script with all_docks = create_loading_docks(5) ➑ and open_loading_docks(all_docks) ➒ at the bottom, we end up creating dock_1.txt through dock_5.txt, each of which has its individual number and the "reporting for duty!" string in it.
Pretty great, right?
As always, you can run the finished script by typing ruby loading_docks.rb at the command line. When you run it, you’ll see this:
Loading dock no. 1, reporting for duty! Loading dock no. 2, reporting for duty! Loading dock no. 3, reporting for duty! Loading dock no. 4, reporting for duty! Loading dock no. 5, reporting for duty!
If you look in the directory where you ran loading_docks.rb, you’ll also see a .txt file for each dock, containing the very text our script printed out!
But I’m sure your head is already spinning with ways to improve this humble little script. For instance, we could change the number of files we create from 5 to 1, 3, 10, or any other number we choose! Just be careful—creating too many files will not only fill up your folder, but it could even crash your computer. (That’s why we defaulted to 3 and only did 5 in the example.)
You probably noticed that we wrote to the files with the 'w+' mode, meaning that if we run the script again, it will overwrite the files with the new content. What if we want to add to the file instead, though? (Hint: The 'a+' mode might be involved.)
For that matter, what if we want to write something fancier than just a plain old text file? What if we want to write a file that writes another Ruby file? This is not only possible, but it’s a big part of what professional programmers do every day. Try to write a file with a small bit of Ruby in it—something as simple as puts 'Written by Ruby!'. (Make sure you write the file with .rb at the end instead of .txt so Ruby can run it.)
Finally, how might you work in some of the file methods we saw, like exist?, rewind, or puts? Are there other file methods in the Ruby documentation at http://ruby-doc.org/core-1.9.3/File.html that might be cool to use? Remember to ask your local adult before going online!
You Know This!
You can read! You can write! Well, okay, you already knew how to do those things, but now you know how to do them with Ruby. I don’t doubt that you’re a full-fledged Ruby sorcerer by now, but just to make sure there’s nothing unclear about this new Ruby wizardry we’ve covered, let’s take a second to review it.
You saw that Ruby can create, read, write, and understand files, which are exactly like the computer files you already know about: text documents, pictures, Ruby scripts, and more. Ruby can open a file that already exists with the open method:
>> file = File.open('alien_greeting.txt', 'r') => #
It can read a file with the read method:
>> file.read => "GREETINGS HUMAN!"
And when we’re finished using a file, we should close it using the close method:
>> file.close => nil
It turns out we can accidentally crash our computer by keeping too many files open at once, so it’s always a good idea to close any file we’ve opened. Luckily, if we open a file with a block, Ruby automatically closes the file for us:
>> File.open('alien_greeting.txt', 'r') { |file| file.read } => "GREETINGS HUMAN!"
Ruby is pretty picky about being told what to do, so we have to use different modes to tell Ruby which input and output mode it should use. When we use 'r', we tell Ruby that we expect it only to read files, and when we use 'w', we tell it we expect it only to write files. To tell Ruby it should both read and write a file, we can give it the 'w+' mode:
>> new_file = File.new('brand_new.txt', 'w+') => #
You found out that 'w+' will overwrite a file—that is, it will replace everything in the existing file with whatever string we tell Ruby to put in there. If we just want to add to a file instead of replacing it completely, we can use the 'a' mode ('a+' if we want to add to the file and read from it):
>> file = File.open('breakfast.txt', 'a+') => #
Speaking of our friend rewind, you saw we could use it to back up to the start of the file and read the whole file:
>> file = File.open('dinner.txt', 'a+') => #
In that first file.read, the string is empty because we’re at the end of the file. After we rewind, though, we go back to the start, and when we file.read again, our text is there.
You discovered that if we want to add a blank line after a line of text, we can use a file’s puts method instead of write. When we read the file back, Ruby shows us the blank line as a backslash and the letter n (n):
>> file.puts('A sprig of fresh parsley!') => nil >> file.rewind => 0 >> file.read => "A festive ham!A sprig of fresh parsley!n"
In fact, you saw that we could use the readline and readlines methods to read out lines of a file one by one. readline reads one line from the file at a time, and calling it a bunch of times reads each line, one after another:
>> file = File.new('dessert.txt', 'a+') => #
If we want to read the lines of our file all at once, we can use file.readlines with a call to the each method and a block:
>> file.rewind => 0 >> file.readlines.each { |line| puts line } A gooseberry pie A small sack of muffins => ["A gooseberry pien", "A small sack of muffinsn"]
Finally, you saw that we could check whether a file exists by using the exist? method:
>> File.exist? 'breakfast.txt' => true >> File.exist? 'fancy_snack.txt' => false
Files and file input/output probably don’t seem like a big deal to you now (especially since you know a lot about how they work), but they’re a major part of how computers get work done. Don’t hesitate to mess around with creating and changing your files on your computer, and—with permission—hunt around the Internet for more information on files, how they work, and any interesting bits of Ruby code you can run to improve your understanding. But enough out of me: our heroes are hot on the tails of the tricksters who have been mucking things up in the kingdom all day, and we’re about to find out who they are, what they want, and whether the King, the Queen, Ruben, Scarlet, and the crew of the Refactory can stop them once and for all!
Chapter 13. Follow the WEBrick Road
Ruby and the Internet
The King and Queen burst through the Refactory exit and into the bright late-afternoon sunshine, Ruben and Scarlet following hot on their heels. Ahead of them, the WEBrick road stretched far away, its dark red bricks glowing softly. In the distance, they could make out the hunched shapes of the four mysterious villains retreating, and farther still, the wall that marked the outer edge of the kingdom.
“They’re so fast!” Ruben gasped, his hands on his knees. “We’ll never be able to catch them!”
“Never say never,” Rusty said, jogging up behind them. “There must be something we can do.”
The Queen turned to him. “Is there any way to shut down the WEBrick road?” she asked.
Rusty thought for a moment. “I’m not sure,” he said, “but I have an idea.” He flipped up all the pages of his clipboard and pulled out a thin piece of metal with a familiar-looking screen. “This is my portable Computing Contraption,” he said. “My workers and I will go on ahead and try to catch these snakes. In the meantime, if there’s anything you can do to shut down the road and keep them from escaping, you can do it on this little computer.”
“Right!” said Scarlet, taking the hand-held computer from the Foreman. Rusty gave her a wink, then motioned to the men and women of the Refactory who were rapidly pouring out of the exit. “This way, everyone! Let’s try to head these villains off! Move, move, move!”
As the Refactory workers ran down the deep red road, the King, the Queen, Scarlet, and Ruben huddled around the Foreman’s portable Computing Contraption.
“Okay, first things first,” said Scarlet. “How can we shut down the road?”
The King tugged on his fluffy white beard. “It seems to me,” he said, “that we should first check to be sure the road is working properly! If it’s already off for some reason, we can join Rusty’s team up ahead and apprehend these goons.”
Scarlet and Ruben looked at each other. “That’s . . . actually a great idea,” Ruben said. “How do we test to see if the road is open?”
“Well,” said the King, “the WEBrick road, like all things in the kingdom, runs on Ruby, and it’s the main connection between the kingdom and the rest of the world. If we can use Ruby to check whether we can get information from outside the kingdom, we’ll know if the road is working.”
“That’s it!” said Ruben. “If we can connect to the Internet with Ruby, then we’ll know the road is open!”
“But how do we do that?” said Scarlet. “I feel like I know Ruby pretty well now, but I don’t even know where to begin with connecting to the Internet.”
“I think I know a way,” said the Queen. “You see, connecting to the Internet is just like connecting to a file—you just have to tell Ruby the right way to do it! May I?” she asked. Scarlet nodded and handed the Queen the portable Computing Contraption. The Queen began to type.
“Remember how we could write our code in separate Ruby files, then use require to pull one script’s code into another?” the Queen asked. Scarlet and Ruben nodded. “Well,” the Queen continued, “it turns out there are little bundles of files that come with Ruby that we can require, too!”
“There are?” asked the King, incredulous. “Absolutely!” said the Queen. “You can think of these bundles of files as tiny libraries of code that we can use in our own projects. They’re called gems.”
“That’s right! I think I’ve heard of Ruby gems before,” Ruben said. “Is there a gem for connecting to the Internet?”
Using the open-uri Ruby Gem
The Queen nodded. “The open-uri gem,” she said. “It lets your Ruby code open Internet sites the same way it can open files! Once we require it in our IRB session, we’ll be able to do all sorts of wonderful things.” She typed into the Computing Contraption:
>> require 'open-uri' => true
She handed the little machine back to Scarlet. “Loaded up and ready to go!” she said.
Scarlet looked at the screen. “Don’t I need './open-uri', not just 'open-uri'?” she asked.
“Not for gems!” said the Queen. “That’s true for files you create, but if you’re requiring a gem, you can just type the name as a string. One of the many things Ruby does for you is keep track of where gems are installed on your computer. Since it already knows where to find them, you don’t need to look in the current directory with ./. Just type the name of the gem, and Ruby does the rest.”
“Perfect!” said Scarlet. “Now we just need a website to test whether the WEBrick road is working.”
