Channels ▼
RSS

Web Development

Programming in Ruby

Source Code Accompanies This Article. Download It Now.


Some Real Examples

At this point, we'll present two larger Ruby programs. The first is a basic web server that echoes back the headers it receives. It's written as two classes; see Listing One. WebSession is a convenience class that provides two methods for writing to a TCP connection. The standardPage method is interesting. At a minimum, it writes a standard page header and footer. If called with a block, however, it inserts the value returned by that block as the page body. This kind of wrapping functionality is a natural use for Ruby's blocks.

Listing One
require "socket"

class WebSession
  def initialize(connection)
    @connection = connection
  end
  def write(string)
    @connection.write string
  end
  def standardPage(title)
    write "HTTP/1.1 200 OK\r\n"
    write "Content-Type: text/html\r\n\r\n"
    write "<html><head> <title>#{title}</title> </head>\n"
    write yield if block_given?
    write "</body></html>"
  end
end

class WebServer
  def initialize(port)
    @listen = TCPServer.new('localhost', port || 8080);
  end
  def run
    loop do
      Thread.start(@listen.accept) do |aConnection|
        begin
          session = WebSession.new(aConnection)
          
          request = []
          loop do 
            line = aConnection.gets.chomp("\r\n")
            break if line.length == 0
            request << line
          end

          session.standardPage("Your Request") {
            "<h1>Your request was:</h1>\n" +
            request.join('<br>') +
            "<p>Thank you for testing our system."
          }
        ensure
          aConnection.close
        end  # begin
      end    # Thread
    end      # loop
  end        
end

WebServer.new(ARGV[0]).run

The WebServer class uses Ruby's TCP library to accept incoming connections on a given port. For each connection, it spawns a Ruby thread that reads the header and writes the contents back to the client. The code in the thread is wrapped in a begin/end block, used in Ruby to handle exceptions. In this case, we use an ensure clause to make sure that the connection to the client is closed, even if we encounter errors while handling the request.

The second program packs a number of features into a small space. At its core, it represents the list of songs in an MP3 collection as an array, providing all the existing array functionality plus the ability to shuffle the entries randomly. If the array is sorted, then the entries will be ordered by song title. Each entry in the array is an object of class Song. As well as providing a container for the song title, album, and artist, this class implements the general comparison operator, <=>. This operator is used when sorting containers containing songs: In this case, we compare song titles. There are two common approaches to making our MP3List act as if it were an array: delegation or subclassing. Listing Two shows the approach using delegation. The library module delegate provides a class SimpleDelegator, which handles all the details of forwarding method calls from class MP3List to the delegate. We create the array containing the songs, then invoke SimpleDelegator's initialize method (using super(songlist)) to set up the delegation. From that point on, MP3List will act as if it were an array. When users shuffle a song list, we create a new array containing the entries of the original in a random order, and use SimpleDelegator's __setobj__ method to delegate to that new array.

Listing Two
require 'delegate'
require 'find'

class Song
  attr_reader :title, :album, :artist
  def initialize(filename)
    @artist, @album, @title = filename.split("/")[-3..-1]
    @title.chomp!(".mp3")
  end
  def <=>(anOther)
    title <=> anOther.title
  end
  def to_s
    "'#{@title}'  #{@artist}--'#{@album}'\n"
  end
end

class MP3List < SimpleDelegator
  def initialize(base)
    songlist = Array.new
    Find.find(base) do |entry|
      if entry =~ /\.mp3$/
        if !block_given? or yield entry
            songlist << Song.new(entry)
        end
      end
    end
    super(songlist)
  end
  def shuffle!
    newlist = Array.new
    length.times do
      newlist << delete_at(rand(length))
    end
    __setobj__(newlist)
    self
  end
end

base = ARGV[0] || "/mp3"

list = MP3List.new(base + "/Hatfield And The North")

puts "Original:  ", list.sort
puts "Shuffled:  ", list.shuffle!
puts "5 entries: ", list[0..4]

puts "Filtered: "
list = MP3List.new(base) { |x| x =~ /Woke Up This Morning/ }
puts list

Listing Three shows an alternative implementation of MP3List in which we subclass the built-in Array class and add our own shuffle! method. Why the exclamation mark? Ruby convention is to append a "!" to methods that change their object (or are otherwise dangerous), and to append a question mark to predicate method names.

Listing Three
class MP3List < Array
  def initialize(base)
    super()
    Find.find(base) do |entry|
      if entry =~ /\.mp3$/
        if !block_given? or yield entry
            self << Song.new(entry)
        end
      end
    end
  end
  def shuffle!
    newlist = Array.new
    length.times do
      newlist << delete_at(rand(length))
    end
    replace(newlist)
  end
end

Conclusion

Programming in Ruby is an immensely satisfying experience — the language is able to represent high-level concepts concisely, efficiently, and readably. It's easy to learn, and at the same time it is deep enough to excite even the most jaded language collector. Download a copy and try it for yourself.


Dave and Andy are consultants and coauthors of Programming Ruby and The Pragmatic Programmer, both from Addison-Wesley. They can be contacted at http:// www.pragmaticprogrammer.com/.


Ruby Resources

The official Ruby Home Page is http://www.ruby-lang.org/. You can also find Ruby information at http://www.rubycentral.com/. In particular, you'll find complete online references to Ruby's built-in classes and modules at http://www.rubycentral.com/ref/, and to the Ruby FAQ at http://www.rubycentral.com/faq/. The latest version of Ruby can be downloaded from: http://www.ruby-lang.org/en/download.html. Ruby also has its own newsgroup (comp.lang.ruby). Traffic on this group is archived and mirrored to the ruby-talk mailing list.

For information on subscribing to ruby-talk, the English-language mailing list, see http://www.ruby-lang.org/en/ml.html.

— D.T. and A.H.


Designing Ruby

By Yukihiro Matsumoto

Designing the ideal language has been a dream of mine since my student days. It wasn't until 1993, however, that I realized that as computers increased in power, new opportunities were opening up for object-oriented programming (OOP) and interpretive (scripting) languages. Over time, I narrowed my interest to Perl and Python, but they still weren't what I wanted. Consequently, I designed Ruby.

When I first read Programming Perl, by Larry Wall, et al. (O'Reilly & Associates, 2000), I was impressed not only by Perl's features and syntax, but by Larry Wall's basic tenants, in particular: "Easy things should be easy, hard things should be possible" and "There's more than one way to do it."

When I started designing what ended up being Ruby, I borrowed the best from languages such C, Perl, Smalltalk, Lisp, CLU, Eiffel, and the like. In the process, I soon realized that computer languages should follow the principles of human-machine interface design, specifically:

  • Consistency. I'm not a minimalist, but I do believe that programming languages should be consistent. A small set of rules should cover the entire language. For instance, in Ruby, all data is an instance of some class without exception. Everything is treated equally.

  • Conciseness. I want my computer to be a servant, not a master, so I'd like to tell it what to do quickly and efficiently. I removed extraneous language elements, such as declarations, static type specifications, and the like. They are all good, but aren't necessary for concise programming. A rich set of literals (as in Perl) helps concise programming, too. In general, Ruby scripts are as concise as Perl scripts, yet far more readable.

  • Flexibility. A language should not restrict expression, but should enhance it. It should be general purpose. "Easy things should be easy, hard things should be possible." But, you know, even hard things can become easy using proper abstraction, thanks to Ruby's deep object-oriented nature.

I also wanted Ruby to be natural to use, and I wanted programmers to feel at ease when coding in it so they can enjoy the act of programming itself. To me, this is the ultimate goal of language design. I have to admit, I don't believe I can satisfy everyone — every programmer has different needs. But I still believe I can satisfy many, including myself.

Programming in Ruby makes me happy, and I've been told that many others like programming in Ruby as much as I do. I want you to enjoy programming and hope Ruby helps you to this end.


Yukihiro Matsumoto is the creator of Ruby. He can be contacted at matz@ zetabits.com.


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.
 
Dr. Dobb's TV