Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Web Development

Programming in Ruby


Take the pure object orientation of Smalltalk, but remove the quirky syntax and reliance on a workspace. Add in the convenience and power of Perl, but without all the special cases and magic conversions. Wrap it up in a clean syntax based in part on Eiffel, and add a few concepts from Scheme, CLU, Sather, and Common Lisp. You end up with Ruby.

Thanks in part to the energy of its creator, Yukihiro Matsumoto (Matz), Ruby is already more popular than Python in its native Japan. Ruby is a pure, untyped, object-oriented language — just about everything in Ruby is an object, and object references are not typed. People who enjoy exploring different OO programming paradigms will enjoy experimenting with Ruby: It has a full metaclass model, iterators, closures, reflection, and supports the run-time extension of both classes and individual objects.

The freely available Ruby (http://www.ruby-lang.org/) is being used worldwide for text processing, XML and web applications, GUI building, in middle-tier servers, and general system administration. Ruby is used in artificial intelligence and machine-learning research, and as an engine for exploratory mathematics.

Ruby's simple syntax and transparent semantics make it easy to learn. Its direct execution model and dynamic typing let you develop code incrementally: You can typically add a feature and then try it immediately, with no need for scaffolding code. Ruby programs are typically more concise than their Perl, Python, or C++ counterparts, and their simplicity makes them easier to understand and maintain. When you bump up against some facility that Ruby is lacking, you'll find it easy to write Ruby extensions using both Ruby and low-level C code that adds new features to the language.

We came across Ruby when we were looking for a language to use as a prototyping and specification tool. We've used it on all of our projects since. We have Ruby code performing distributed logging, executing within an X Windows window manager, precompiling the text of a book, and generating indexes. Ruby has become our language of choice.

Everything's an Object

Everything you manipulate in Ruby is an object, and all methods are invoked in the context of an object. (In our examples here, we'll sometimes show the result of evaluating an expression to the right of an arrow (->). This is not part of the Ruby syntax.)



"gin joint".length	-> 9
"Rick".index("c")	-> 2
-1942.abs 	-> 1942
sam.play(aSong)  -> "duh dum, da dum de  			       dum ..."

In Ruby and Smalltalk jargon, all method calls are actually messages sent to an object. Here, the thing before the period is called the "receiver," and the name after the period is the method to be invoked.

The first example asks a string for its length, and the second asks a different string to find the index of the letter "c." The third line has a number calculate its absolute value. Finally, we ask the object "sam" to play us a song. It's worth noting a major difference between Ruby and most other languages. In Java, for example, you'd find the absolute value of some number by calling a separate function and passing in that number. In Ruby, the ability to determine absolute values is built into numbers — they take care of the details internally. You simply send the message abs to a number object and let it do the work.



number = Math.abs(number) // Java
number = number.abs // Ruby

The same applies to all Ruby objects: In C, you'd write strlen(name); while in Ruby, it's name.length. This is part of what we mean when we say that Ruby is a genuine OO language.

The parentheses on method calls are optional unless the result would be ambiguous. This is a big win for parameterless methods, as it cuts down on the clutter generated by all those () pairs.

Classes and Methods

As Example 1 shows, Ruby class definitions are remarkably simple: The keyword class is followed by a class name, the class body, and the keyword end to finish it all off. Ruby features single inheritance: Every class has exactly one superclass, which can be specified as in Example 2. A class with no explicit parent is made a child of class Object — the root of the class hierarchy and the only class with no superclass. If you're worried that a single inheritance model just isn't enough, never fear. We'll be talking about Ruby's mix-in capabilities shortly.


class Song
  def initialize(title)
    @title = title
  end
  def to_s
    @title
  end
end
aSong = Song.new("My Way")

Example 1: A simple class definition.


class KaraokeSong < Song
  def initialize(title, lyric)
    super(title)
    @lyric = lyric
  end
  def to_s
    super + " [#@lyric]"
  end
end

Example 2: A subclass of class Song.

Returning to the definition of class Song in Example 1, the class contains two method definitions, initialize and to_s. The initialize method participates in object construction. To create a Ruby object, you send the message new to the object's class, as in the last line of Example 1. This new message allocates an empty, uninitialized object, and then sends the message initialize to that object, passing along any parameters that were originally given to new. This makes initialize roughly equivalent to constructors in C++ and Java.

Class Song also contains the definition of the method to_s. This is a convenience method; Ruby sends to_s to an object whenever it needs to represent that object as a string. By overriding the default implementation of to_s (which is in class Object), you get to control how your objects are printed (for example, by tracing statements and the debugger), and when they are interpolated in strings. In Example 2, we create a subclass of class Song, overriding both the initialize and to_s methods. In both of the new methods we use the super keyword to invoke the equivalent method in our parent class. In Ruby, super is not a reference to a parent class; instead, it is an executable statement that reinvokes the current method, skipping any definition in the class of the current object. By default, all methods (apart from initialize) are publicly accessible; they can be invoked by anyone. Ruby also supports private and protected access modifiers, which can be used to restrict the visibility of methods to a particular object or a particular class, respectively. Ruby's implementation of "private" is interesting: You cannot invoke a private method with an explicit receiver, so it may only be called with a receiver of self, the current object.

Attributes, Instance Variables, and Bertrand Meyer

The initialize method in class Song contains the line @title = title. Names that start with single "at" signs (@) are instance variables — variables that are specific to a particular instance or object of a class. In our case, each Song object has its own title, so it makes sense to have that title be an instance variable. Unlike languages such as Java and C++, you don't have to declare your instance variables in Ruby; they spring into existence the first time you reference them. Another difference between Ruby and Java/C++ is that you may not export an object's instance variables; they are available to subclasses, but are otherwise inaccessible. (This is roughly equivalent to Java's "protected" concept.) Instead, Ruby has attributes: methods that get and set the state of an object. You can either write these attribute methods yourself, as in Example 3, or use the Ruby shortcuts in Example 4.


class Song
  # ...
  def title         # attribute reader
    @title          # returns instance variable
  end
  def title=(title) # attribute setter
    @title = title 
  end
end

Example 3: Writing your own attribute methods.


class Song
  # ...
  attr_accessor :title
end

Example 4: Ruby shortcut for attribute methods.

It's interesting to note the method called title= in Example 3. The equals sign tells Ruby that this method can be assigned to — it can appear on the left side of an assignment statement. If you were to write aSong.title = "Chicago," Ruby translates it into a call to the title= method, passing "Chicago" as a parameter. This may seem like some trivial syntactic sugar, but it's actually a fairly profound feature. You can now write classes with attributes that act as if they were variables, but are actually method calls. This decouples users of your class from its implementation — you're free to change an attribute back and forth between some algorithmic implementation and a simple instance variable. In Object-Oriented Software Construction (Prentice Hall, 2000), Bertrand Meyer calls this the "Uniform Access Principle."

Blocks and Iterators

Have you ever wanted to write your own control structures, or package up lumps of code within objects? Ruby's block construct lets you do just that. A block is simply a chunk of code between braces, or between do and end keywords. When Ruby comes across a block, it stores the block's code away for later; the block is not executed. In this way, a block is similar to an anonymous method. Blocks can only appear in Ruby source alongside method calls.

A block associated with a method call can be invoked from within that method. This sounds innocuous, but this single facility lets you write callbacks and adaptors, handle transactions, and implement your own iterators. Blocks are also true closures, remembering the context in which they were defined, even if that context has gone out of scope.

The method in Example 5 implements an iterator that returns successive Fibonacci numbers (the series that starts with two 1s, where each term is the sum of the two preceding terms). The main body of the method is a loop that calculates the terms of the series. The first line in the loop contains the keyword yield, which invokes the block associated with the method, in this case passing as a parameter the next Fibonacci number. When the block returns, the method containing the yield resumes. Thus, in our Fibonacci example, the block will be invoked once for each number in the series until some maximum is reached.


def fibUpTo(max)
  n1, n2 = 1, 1
  while n1 <= max
    yield n1              # invoke block value
    n1, n2 = n2, n1+n2    # and calculate next
  end
end

Example 5: Iterator for Fibonacci numbers.

Example 6 shows this in action. The call to fibUpTo has a block associated with it (the code between the braces). This block takes a single parameter — the name between the vertical bars at the start of the block is like a method's parameter list. The body of the block simply prints this value.


fibUpTo(1000) {|term| print term, " "} 
   produces:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987

Example 6: Using the fibUpTo iterator.

If you write your own collection classes (or any classes that implement a stream of values), you can benefit from the real beauty of Ruby's iterators. Say you've produced a class that stores objects in a singly linked list. The method each in Example 7 traverses this list, invoking a block for each node. This is a Visitor Pattern in three lines of code. The choice of the name, each, was not arbitrary. If your class implements an each method, then you can get a whole set of other collection-oriented methods for free, thanks to the Enumerable mix-in.


class LinkedList
  # ...
  def each
    node = head
    while node
      yield node
      node = node.next
    end
  end
end

Example 7: Iterator for a linked list.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.