Where Ruby Really Sparkles

Ruby is a concise, fully object-oriented, and powerful programming language. It's also ideal for building networked applications. Find out why Ruby really connects.


If you’re looking for a simple, elegant way to script access to the Internet, you should probably take a look at Ruby (for an introduction to Ruby see “A Ruby Refresher”). Ruby is concise (like Python), fully object-oriented (like SmallTalk), and powerful (like Perl). In addition, Ruby is a remarkably capable language for building Internet applications. Ruby’s libraries and built-in networking support make networked applications (such as email clients, SOAP servers, and distributed processing) easy to write and easy to maintain and extend. Yeah, take a look at Ruby and learn what its many fans already know: Ruby really connects.

Sending Email via SMTP

Every week, Holden Glova, Pat Eyler, and Phil Thomson submit a Ruby Weekly News (RWN) article to the Ruby Garden Web site (http://www.rubygarden.org). A Ruby script receives the article through email, transforms it from the original XML into HTML and plain text, posts the HTML to the site, and emails the plain text to a mailing list. If there’s a problem (perhaps because the XML is malformed), the script sends the submitter an email with a cryptic summary of the problem.

The script uses the Net::SMTP (Simple Mail Transfer Protocol) library to send email. Listing One shows a part of this script containing the method that sends the email. The method takes three parameters: an email address, a subject, and the body of the message. Because this particular script runs in a very controlled context, things such as the name of the sender and the host used to relay email to the net are defined as global constants instead of parameters.

Listing One: Sending e-mail via SMTP

1 FROM_ADDRESS = “dave@pragprog.com
2 SMTP_HOST = “localhost”
4 def reply(to, subject, msg)
5 mail = “To: #{to}\r\n” +
6 “From: #{FROM_ADDRESS}\r\n” +
7 “Subject: #{subject}\r\n” +
8 “\r\n” +
9 msg
11 Net::SMTP.start(SMTP_HOST) do |smtp|
12 smtp.send_mail(mail, FROM_ADDRESS,
13 [ to, 'rurl_archive@zip.local.pragprog.com' ])
14 end
15 end

There are two parts to an email message: the envelope and the content. The envelope tells SMTP agents (such as sendmail and postfix) how to deliver the message. The content contains the human-readable message itself and a number of headers (such as the message subject). Some of these headers potentially duplicate information in the envelope (such as the “To” address). The duplicated headers are used for display purposes — it’s the information in the envelope that’s used to deliver the message. (This is why you can receive spam that contains a “To” header that isn’t your email address).

You can see the division between content and envelope in the reply method. Lines 5 through 9 build the content. This consists of three headers (“To,” “From”, and “Subject”) followed by a blank line and the message body. Note that the lines before the body must end with both a carriage return (\r) and a linefeed (\n).

The call to Net::SMTP.start initiates the connection to the mail transfer agent (MTA), the software that’s responsible for routing email through the net. The call takes a parameter identifying the host computer running the MTA and the port on which it’s listening. (If your local machine does not run an MTA, you might need to specify your ISP as the SMTP_HOST). Net::SMTP.start returns an object that lets us communicate with the MTA, passing that object as a parameter to the block (lines 11 through 13). Using a block in this way ensures that the connection will get closed when our block exits.

In our case, the interaction with the SMTP object is simple: the statement starting on line 12 sends the content we just created.

The second parameter specifies the “From” address to be used, while the third is an array containing a list of recipients. We arrange for the mail to be sent to two places, the intended recipient and a local mailbox that achives all messages.

POP In and Read Mail

It’s equally easy to receive email from POP mailboxes using Ruby. Let’s say you want to run a quick survey on programming languages. Participants vote for their their favorite language by sending an email with the subject line, I like xxxx to a given email address, replacing xxxx with a name. The Ruby script in Listing Three could be used to fetch these emails from your POP server and calculate results. This example saves the counts for each language in flat files, one file per language.

Listing Three: Fetching email with POP

1 require ‘net/pop’
3 Net::POP3.delete_all(‘pop3.server.address’, 110,
4 ‘YourAccount’, ‘YourPassword’ ) do |email|
5 hdr = email.header
6 if hdr =~ /Subject:\s+I like\s+(\w+)/
7 language = $1.upcase
8 else
9 language = “INVALID”
10 end
12 count = (File.read(language) rescue “0″)
13 File.open(language, “w”) {|f| f.puts old_count.succ}
14 end

POP servers store email messages. When you read one of these messages, you can chose to leave the message on the server or delete it. In our case, we want to read and delete messages. Fortunately, Ruby has a convenient iterator, delete_all, which fetches messages one at a time, deleting them from the server after they are processed. delete_all needs the name of the POP server and a port number (110 is the standard port). It also takes a user name and password.

Given those parameters, the script connects to the POP server and starts fetching all the messages for that user. Each message is passed in turn to the associated block (as an object of class Net::POPMail). When the block finishes execution, the message is deleted on the server.

Within the block, line 5 fetches all the header lines from the message into a string. Line 6 then uses a regular expression to look for the language name in the I like xxxx subject field, and lines 12 and 13 update the count in a flat file with the same name as the language.

Line 12 contains an interesting construct. Once we’ve had a vote for a language, there’ll be a flat file for that language containing a count. We can read that count, increment it, and write it back out. However, what do we do the first time we get a vote for a language? Because the file doesn’t exist, we’ll get an exception when we try to read its contents. Fortunately with Ruby we can catch this exception (using the rescue keyword) and return a default value for the file’s contents (0).

There’s also a subtlety on line 13. When we increment the count using old_count.succ, we’re actually incrementing a string. However, Ruby strings are smart enough that incrementing a string containing an integer returns the next integer.

Ruby and SOAP

The Simple Object Access Protocol (SOAP) is quickly becoming the standard protocol for remote procedure calls across the Internet. (For more information about SOAP, see the October 2001 and August 2002 issues of the magazine, or online at http://www.linux-mag.com/2001-10/soap_01.html and http://www.linux-mag.com/2002-08/webs_01.html, respectively.)

Ruby offers great support for SOAP, both as a client and as a server, using Nakamura Hiroshi’s SOAP4R module. There are basically four things needed to make a SOAP request:

  1. An endpoint, or the net address that handles incoming SOAP requests. An endpoint is typically code running in a Web server environment, but other SOAP transports (even email) are possible.
  2. A namespace, defining the context in which the names of the methods we call are resolved.
  3. A method name. This is the name of the remote procedure to call.
  4. A set of parameters.

With SOAP4R, we specify the first two when creating the SOAP driver, the third when we bind a method to that driver, and the parameters when we call the method.

For example, we might have a SOAP server running on http://my.server.com that handles sales orders. We access this from the client side by creating a new SOAP::Driver object, passing in a namespace and the address of the server. (The first two parameters in the constructor are to do with logging, which we don’t use).

NS = “urn:ordersService”
SVR = “http://my.server.com/orders
drv = SOAP::Driver.new(nil, nil, NS, SVR)

Once we have a driver, we specify the methods we want to call on the server using the addMethod call. The first parameter is the name of the method, the rest are the names of the parameters the method takes. In this case we’ll be accessing a method called orders_for_product, passing it the customer’s account number and a product code. (Ruby does not use WSDL to define SOAP interfaces.)

drv.addMethod(“orders_for”, “cust_acct”,

Once that’s all set up, we can use this method to query the server as many times as we want:

customers.each do |cust_acct|
products.each do |prod_code|
orders = drv.orders_for(cust_acct,

Notice how the dynamic nature of Ruby has made this simple. The orders_for method has been dynamically added to the driver object, so we can use it just like any other method call. And we never had to specify the form of the return value — the driver constructs a Ruby object on the fly to match whatever it receives from the server.

To make this more concrete, let’s write a real SOAP client. Listing Four shows a simple client that uses SOAP to fetch search results from the Google search engine. It uses code based on Ian Macdonald’s google.rb. Before using this program, you’ll need to register with Google (http://www.google.com/accounts) and obtain a key. Once you have a key, set the KEY constant (on line 3) appropriately.

Listing Four: Running a Google search using SOAP

1 require “soap/driver”
2 ENDPOINT = ‘http://api.google.com/search/beta2
3 NS = ‘urn:GoogleSearch’
4 KEY = “get_a_key_from_google”
6 fail “Missing query args” if ARGV.empty?
8 query = ARGV.join(” “)
10 soap = SOAP::Driver.new(nil, nil, NS, ENDPOINT)
11 soap.addMethodWithSOAPAction(
12 ‘doGoogleSearch’, NS, ‘key’, ‘q’, ‘start’, ‘maxResults’, ‘filter’,
13 ‘restrict’, ‘safeSearch’, ‘lr’, ‘ie’, ‘oe’)
14 res = soap.doGoogleSearch(
15 KEY, query, 0, 10, false, nil, false, nil, ‘latin1′, ‘latin1′)
17 puts “Estimated result count: ” + res.estimatedTotalResultsCount
19 res.resultElements.each do |entry|
20 puts
21 puts “#{entry.URL}: #{entry.title}”
22 puts entry.snippet
23 end

The actual Google query is performed by invoking the doGoogleSearch method on Google’s servers. This method takes a whopping ten parameters (to get a full description of these, see the documentation that comes with Google’s Web API download), but in our simple program, we’re only interested in specifying the query. We set all of the other parameters to reasonable defaults. On line 11, we add the doGoogleSearch method to the SOAP driver connected to Google. On line 14 we invoke this method, performing the actual Google search.

What comes back from Google is a fairly complex object. At the top level, the object contains information about the search itself (the start and end indices, the query used, the time taken and so on). It also contains an array of result elements. These correspond to the numbered results that you see on a Google page when you perform a search manually. Each of these result elements is in turn a fairly complex object: in our code we simply extract the result title, url, and text snippet.

Note here how the SOAP interface makes this simple: as well as defining accessor methods for all the attributes in the returned object, it automatically creates iterators over arrays of objects, allowing us to write:

res.resultElements.each do |element|


If you run the program in Listing Four from a command prompt, specifying the search parameters (in this case “ruby soap”) as arguments, you might see something like the output in Figure One.

Figure One: Results of a Google search

$ ruby google_search.rb ruby language

Estimated result count: 206000

http://www.ruby-lang.org/en/: <b>Ruby</b> Home Page
<b>…</b> Japanese Page If you can read this oriental <b>language</b>,
<br> you can get more information about <b>Ruby</b>. Site <b>…</b>

http://slashdot.org/developers/01/08/11/2211254.shtml: Slashdot | Progra….
<b>…</b> Programming in the <b>Ruby</b> <b>Language</b>. <b>…</b>
This discussion has<br> been archived. No new comments can be posted.

http://dev.rubycentral.com/faq/rubyfaq.html: The <b>Ruby Language</b> FAQ
The <b>Ruby</b> <b>Language</b> FAQ. Originally by: Shugo
Maeda.<br> Now maintained by Dave Thomas with help from Andy Hunt. <b>…</b>

Writing a SOAP server in Ruby turns out to be even easier. All you have to do is write the objects whose interface you want to publish, and then arrange for these objects to be accessible via a SOAP servlet. The objects you write don’t even have to be SOAP aware. As an example, Listing Five shows a simple Ruby class that implements a single method, double, which doubles its argument.

Listing Five: The file doubler.rb, the Ruby SOAP doubling class

class Doubler
def double(arg)
arg + arg

To access this functionality using SOAP, we need to connect it to a SOAP server and namespace. In Ruby, the easiest way to do this is to use the WEBRick, a Web server toolkit. Combining this with the soaplet.rb servlet code (distributed in the samples/webrick directory of the SOAP4R package), and you can implement the entire SOAP server using the few lines of code in Listing Six.

Listing Six: A Ruby SOAP server

1 require ‘webrick’
2 require ‘soaplet’
3 require ‘doubler’
5 server = WEBrick::HTTPServer.new(:Port => 2001)
7 soaplet = SOAP::WEBrickSOAPlet.new
8 soaplet.addServant(‘urn:doublerService’, Doubler.new)
9 server.mount(“/doubler”, soaplet)
11 trap(“INT”) { server.shutdown }
12 server.start

The first three lines simply include the WEBRick library, the soaplet class, and our Doubler class. Line 5 is all that’s required to create a new Web server (in this case on port 2001). Lines 7 creates a soaplet (a servlet that passes SOAP requests on to an object). Line 8 binds this soaplet to a new instance of our Doubler object, and line 9 then mounts this soaplet on the path /doubler on our Web server. Line 12 obviously starts the Web server, but what’s going on with line 11? When you start a WEBRick server, it takes control, handling requests and sending responses. However, we’d like to be able to shut our program down tidily. Line 11 achieves this by registering a handler for SIGINT signals, invoking the server’s shutdown method when one is received. Under most operating systems, typing control-C generates a SIGINT, so we can control our Web server from the command prompt.

We can test this out with the SOAP client in Listing Seven. Line 10 shows another example of using rescue: the statement attempts to convert the argument (which is passed in as a string) into an integer. If this fails, the rescue catches the exception and returns the original argument instead. The result of this is that the double method might be passed either an Integer or a String. Let’s see if it makes a difference when we run the program.

$ ruby soap_client.rb 12 wiki

Pass in ’12′, and we receive back ’24′. However, given the string parameter wiki it returns wikiwiki — Ruby’s type polymorphism was propagated through the SOAP interface. Because the double method is written as arg + arg, passing it an Integer results in arithmetic doubling, while passing in a String results in string concatenation.

Listing Seven: The doubler SOAP client

1 require “soap/driver”
3 SVR = ‘http://localhost:2001/doubler
4 NS = ‘urn:doublerService’
6 soap = SOAP::Driver.new(nil, nil, NS, SVR)
7 soap.addMethod(‘double’, ‘arg’)
9 ARGV.each do |arg|
10 arg = (Integer(arg) rescue arg)
11 puts soap.double(arg)
12 end

Distributed Objects

Let’s finish with a brief diversion into distributed Ruby. Now that networking is pervasive, it seems natural to want to start flinging objects around the world. Unfortunately, protocols such as CORBA and RMI take a good deal of effort to use: they require special coding, exception handling, and interface definitions before you can send your first bit over the wire.

Ruby has an elegant solution to this complexity. Distributed Ruby (known as drb or druby) is a separate library, written entirely in Ruby, that lets you send objects between Ruby processes over TCP. There’s very little ceremony involved.

Listing Eight shows a simple server that shares an object that returns the local time. Lines 3 through 7 declare the class to be shared, line 9 associates an object of this class with a drb server (in this case on port 2222). As the server runs in a separate thread, line 10 stops the main program from terminating before this thread exits.

Listing Eight: A simple distributed Ruby server

1 require ‘drb’
3 class Info
4 def get_time
5 “It is now #{Time.now}”
6 end
7 end
9 DRb.start_service / (“druby://your.host.name:2222″, Info.new)
10 DRb.thread.join

The client, shown in Listing Nine, is equally simple. The DRbObject call on line 4 establishes a connection with the remote server and returns a proxy for the remote object. After that, you call the proxy as if it were local.

Listing Nine: A simple distributed Ruby client

1 require ‘drb’
3 DRb.start_service
4 info = DRbObject.new(nil, “druby://your.host.name:2222″)
6 3.times do
7 puts info.get_time
8 sleep 2
9 end

Kicking the abstraction up a notch is the concept of tuplespaces. Pioneered by David Gelernter in the Linda system, tuplespaces are like having a shared bulletin board — processes can stick messages on it and take messages off it, where messages are arrays of values. The out method outputs a value to the tuplespace, so you could create a tuplespace containing four entries using the following code.

require ‘tuplespace’
ts = TupleSpace.new
ts.out [ "dave", "car", "blazer" ]
ts.out [ "dave", "computer", "dell" ]
ts.out [ "andy", "car", "explorer" ]
ts.out [ "andy", "os", "linux" ]

What makes the tuplespace interesting is that data is fetched not by address, but by matching content. Effectively, every read says “fetch me a chunk of data that matches this pattern.”

The Ruby tuplespace implementation is particularly powerful: matches can be done based on values, classes of objects, regular expressions, ranges, and so on. A pattern of nil can be used to indicate that you don’t care about the value of a particular element. This pattern matching is done by the in method. If there is an entry that matches the pattern passed to in, that entry is removed from the tuplespace and returned to the caller, otherwise in waits for an entry to become available. If multiple entries match the pattern, a random one is returned.

Continuing the previous example, the following code reads three entries from the tuplespace. Note the last statement, which illustrates using a regular expression in the pattern match.

# read one of Dave’s possessions
res1 = ts.in ["dave", nil, nil]
# someone owning a car
res2 = ts.in [nil, "car", nil]
# a possession containing the “x” or “z”
res3 = ts.in [nil, nil, /[xz]/]

From this simple abstraction you can build complex, cooperating, parallel systems with interesting properties.

For example, you can use a tuplespace to solve complex AI problems. One process can pose a problem by writing some combination of values into the tuplespace. Some other processes can wait for problems that it can solve by reading entries that match a particular pattern. When a process gets a problem, it might break the original problem down into a number of subproblems and posts those on the tuplespace. Different processes pick these problems up, and either post solutions or additional subproblems.

This process continues until finally all the little answers ripple up to a final solution to the original problem.

To finish off this article, we’ll write a simple person-to-person chat system using distributed Ruby and tuplespaces. The system stores three-element tuples for each message: the name of the sender, the name of the recipient, and the text of the message itself.

The client, shown in Listing Ten, runs two threads. The sender thread operates in the program’s main thread, and the receiver thread is created explicitly. The sender thread (lines 16 through 22) reads lines from the users console. These should be of the form

to: message text

where to is the name of the person to receive the message. The split method on line 17 returns the two parts of the line. An entry containing our name, the receiver’s name, and the text is then written to the tuplespace on line 19.

Listing Ten: A chat client based on tuplespaces

1 require ‘drb’
2 require ‘tuplespace’
4 DRb.start_service
5 ts = DRbObject.new(nil, / “druby://server.host:12321″)
7 my_name = ARGV[0]
9 Thread.new do
10 loop do
11 from, unused, line = ts.in / [ nil, my_name, nil ]
12 puts “#{from} says: #{line}”
13 end
14 end
16 while line = gets
17 to, text = line.split(/:/, 2)
18 if text
19 ts.out [ my_name, to, text ]
20 else
21 puts ‘** use “to: message”‘
22 end
23 end

The receiver thread is even simpler. Lines 9 to 14 run a simple loop in a thread, reading messages from the tuplespace that have our name as a receiver and writing the contents of these entries to the console.

Tuplespaces need a server where the tuples are stored. In Ruby that’s just a drb server containing a TupleSpace object. The four Ruby statements needed to implement this are shown below.

require ‘drb’
require ‘tuplespace’

DRb.start_service(“druby://server.host:12321″, /

To run this example, run the server, and then run the clients on any computers that can access the server port (12321) over a network. When you run the client, specify the name you want to be known as as a parameter.

As you play with it you may notice an interesting effect: the server will store messages for you if you’re not currently connected. Our application may not displace Yahoo Instant Messenger, Jabber, or iRC, but it isn’t bad for a handful of lines of code.

Net Benefits

This article has just been a sampler of some of the ways that Ruby plays well with networking. We didn’t cover the low level Ruby TCP libraries, and a wealth of higher-level protocol support, including FTP, HTTP, NNTP, telnet, CORBA, and XML-RPC. We also didn’t cover the whole area of server-side scripting using Ruby. Ruby supports all of those things, too.

Ruby is a remarkably good language for programming applications for the net. The library support is both broad and deep, and the code you end up writing tends to be clear, concise, and easy to maintain.

Write Ruby and you’ll be happy.

A Ruby Refresher

Ruby (http://www.ruby-lang.org/) is a true object-oriented scripting language, a cross between Smalltalk and Perl. Everything in Ruby is an object, and virtually everything in a Ruby program is expressed as an operation on some object.

As a quick review of Ruby, let’s look at the simple anagram finder shown in Listing Two. (If you’ve never seen Ruby before, you can read an introduction to the language in “Write Ruby, Be Happy” in the April 2002 issue of Linux Magazine, or online at http://www.linuxmagazine.com/2002-04/ruby_01.html.)

Listing Two: A simple anagram finder in Ruby

1 # Read in a dictionary of words (one per line).
2 # Let the user enter individual words; for each list
3 # the word’s anagrams from the dictionary
6 class AnagramFinder
7 DICTIONARY = “/usr/dict/words”
9 def initialize
10 @anagrams = Hash.new
12 File.foreach(DICTIONARY) do |line|
13 word = line.chomp
14 characters = characters_of(word)
15 @anagrams[characters] ||= Array.new
16 @anagrams[characters] << word
17 end
18 end
20 def characters_of(word)
21 word.split(”).sort.join
22 end
24 def each_match(word)
25 words = @anagrams[characters_of(word)]
26 if words
27 words.each {|word| yield word}
28 end
29 end
30 end
32 anagrams = AnagramFinder.new
34 loop do
35 print “Enter a word: ”
36 word = gets
37 break unless word
38 anagrams.each_match(word.chomp) do |anagram|
39 puts “#{anagram} is an anagram of #{word}”
40 end
41 end

Two words are anagrams if the letters they contain are the same. A simple way to check for anagrams is to sort the letters in both words and compare the results for equality. For example, the characters in the words “swing” and “wings” both sort to “ginsw,” so they’re anagrams.

In our program, we read in a dictionary of words and sort the characters in each word. We then build a hash (sometimes called an associative array), which maps the sorted characters to all of the words that contain those characters. Once the hash is constructed, finding a word’s anagrams is simple: we sort the letters in the new word and use the sorted letters to index our hash. The result will either be an array of all the anagrams of the input word, or nil if there are no matching words.

The first thing you’ll notice is that Ruby source is fairly clean. There are no semicolons at the ends of statements and few extraneous characters. Definitions (such as the class definition starting at line 6 and the method definitions on lines 9, 20, and 24) are terminated by the keyword end, as are control structures (such as the if statement on lines 26-28). Comments (like those at the top of the listing) start with a hash character (#) and run to the end of the line.

Objects of class AnagramFinder (lines 6 through 30) are responsible for building the hash of anagrams and subsequently looking up words to find their anagrams. Line 7 defines a constant containing the name of the dictionary file. Constants in Ruby start with capital letters. (Interestingly, this means that class names are also constants. Classes can be manipulated just like other objects, but that’ll have to be the subject of another article). Lines 9 though 18 define the AnagramFinder‘s initialize method.

initialize methods in Ruby can be thought of as the equivalent of constructors in Java and C++. In the case of AnagramFinder, the method is responsible for building the hash of anagrams. Line 10 constructs the empty hash, assigning it to the instance variable @anagrams. Instance variables hold the data specific to a particular object, and always start with an “at” sign (@). Note that we didn’t have to declare the variable: it springs into existence when it is first referenced. Also note that we don’t specify a type for @anagrams: all Ruby variables can hold references to objects of any type. (This can be a religious issue for some developers, but if you haven’t yet tried a language with dynamic typing, spend some time with Ruby: you’ll be surprised how few errors arise because of the lack of type declarations, and you’ll be amazed at how much more flexible your programs can be.)

Lines 12 to 17 are responsible for reading in the dictionary file and building the hash from the words it contains. File.foreach is a method that reads a file line-by-line. It passes each line it finds to a block. In our case the block starts do |line| and ends (not surprisingly) with end. Blocks are a pervasive concept in Ruby, but they’re different than the blocks you’ll encounter in languages such as C, Java, and Perl. Ruby blocks are a way of passing a chunk of code to a method. The method can then invoke this code, passing it parameters.

In our example, we pass the code block to the File. foreach method, and the method will, in turn, call this block for each line in the file, passing the line to the block as a parameter. The block parameters are declared between vertical bars, so the net effect of all this magic will be to invoke the block body (lines 13 through 16) once for each line in the file, passing the line in as the parameter line.

For each line, we strip the trailing newlines (using chomp), then call the characters_of method to return the sorted list of characters in the word (just how this works is described shortly). Note how we call methods in Ruby: obj.name is a request for object obj to execute method name. At times you’ll hear people describing this as sending the message name to obj.

Our application needs to build a list (an array in Ruby terms) of all words that contain the same sorted letters. This means that the first time we encounter a particular set of letters we need to initialize the corresponding hash entry with an Array object (line 15). Line 16 then appends the current word to this array.

The method characters_of on lines 20-22 is responsible for taking a word such as “wombat” and returning its sorted characters (“abmotw”). It shows the convenience of Ruby’s built-in library. The split method uses the passed-in delimiter to divide a string into chunks, returning an array. With an empty delimiter, it returns an array containing a separate entry for each character in the string. The sort method then returns a new array containing these characters in order, and join, a method of arrays, concatenates these back into a single string. Note that there’s no explicit return statement. By default, a Ruby method returns the value of the last statement executed.

The last method in our class, each_match, allows its caller to iterate over all the anagrams of a particular word. Line 25 indexes the hash with the sorted set of characters from the word. If there are no matches for this word, this operation returns nil, otherwise it returns an array of anagrams. Because nil is false in boolean contexts, the body of the if statement that follows will only be executed if an array is returned. In this case, the each method iterates over the elements of the array, and the yield statement passes each element to the block associated with the call to each_ match. We can see this in action in lines 38 to 40.

Because the code starting at line 32 appears outside a class or method definition, it’s executed directly when Ruby interprets the source file.

Line 32 creates a new AnagramFinder object. The body of the loop (lines 34 to 41) prompts the user to enter a word and reads the response. If the user ends the program by entering the end-of-file character (normally ^D), gets returns nil, and line 37 terminates the loop. Otherwise we call the AnagramFinder object’s each_match method to iterate over all the angrams for the entered word.

The puts statement on line 39 illustrates one final feature: within a string the sequence #{…} interpolates arbitrary expressions.

Assuming the code is in a file anagram.rb, you can run it from the command line:

$ ruby listing1.rb
Enter a word: cat
act is an anagram of cat
cat is an anagram of cat
Enter a word:^D

Dave Thomas is an author of Programming Ruby and the best-selling The Pragmatic Programmer, both from Addison-Wesley. Along with Andy Hunt he runs an independent consultancy from offices in Dallas, TX and Raleigh, NC. Contact them via www.pragmaticprogrammer.com.

Comments are closed.