Gem Survey

(Sat Feb 10, 2007) [/Ruby#

Just for fun, here are the RubyGems I currently have installed:

actionmailer (1.3.2)
actionpack (1.13.2)
actionwebservice (1.2.2)
activerecord (1.15.2)
activesupport (1.4.1)
aws-s3 (0.3.0)
builder (2.0.0)
capistrano (1.4.0)
cgi_multipart_eof_fix (1.0.0)
cheat (1.2.1)
daemons (1.0.3)
fastthread (0.4)
flexmock (0.4.5)
gem_plugin (0.2.1)
gruff (0.2.8)
hoe (1.1.7, 1.1.4)
hpricot (0.5)
mime-types (1.15)
mocha (0.4.0)
mongrel (0.3.13.2)
mongrel_cluster (0.2.0)
net-sftp (1.1.0)
net-ssh (1.0.10)
piston (1.3.0)
rails (1.2.2)
rake (0.7.1)
rb-appscript (0.3.0)
rcov (0.7.0.1)
RedCloth (3.0.4)
rmagick (1.14.1)
ruby-prof (0.4.1)
rubyforge (0.4.0, 0.3.1)
rubygems-update (0.9.2, 0.9.1)
safariwatir (0.2.2)
sources (0.0.1)
test-spec (0.3.0)
xml-simple (1.0.10)

This list may be interesting to look back on, say, 2 years from now. (Don't count on it.) Who knows which gems will stand the test of time, and if so, what versions we'll be using by then.

Which RubyGems are you sporting?

Getting Started with the Amazon S3 Library for Ruby

(Thu Feb 01, 2007) [/Ruby#

Every once in a while you run across a library that makes your day. Everything just works as you'd expect and you can focus on getting something done quickly. Today, for example, I needed to share a relatively large file with a significant number of people. Linking it up on our host just didn't make sense. Added to which, just last week Marcel Molina Jr. had given a talk at The Rails Edge about his new AWS::S3 library. Coincidence? No, opportunity!

Getting started was so doggone easy that I just had to share...

1. Sign Up

Signing up is free, then you pay as you go. It's currently $0.20 per gigabyte of data transferred and $0.15 per gigabyte stored each month.

Using your existing Amazon account, simply create an Amazon Web Services (AWS) account. Then just activate the S3 Web Service. You'll receive an email that includes a link for fetching two keys: an access key id and a secret access key. You need these to make requests against the S3 service.

2. Download the AWS::S3 Library
gem install aws-s3
3. Play Around

Before you start shuttling bits around from inside an application, I recommend playing around with the API a bit. It's fun, and made easier with an interactive shell called s3sh that comes with the AWS::S3 library. It's your irb for Amazon S3.

$ s3sh

The first thing you'll need to do is make a connection to your S3 account:

AWS::S3::Base.establish_connection!(
  :access_key_id     => 'your access key id goes here',
  :secret_access_key => 'your secret key goes here'
)

Then create a unique container, called a bucket, for your objects:

Bucket.create('intergalactic')

The bucket also serves as the global namespace for your objects. Here's where the magic happens. Add an object to your bucket:

AWS::S3::S3Object.store('flight_manual.pdf', 
                        open('/path/to/local/flight_manual.pdf'), 
                        'intergalatic',
                        :access => :public_read)

Once the object has been stored in the big hard-drive in the sky, you can access it at http://s3.amazonaws.com/intergalactic/flight_manual.pdf.

That's really all there is to it! It's a convenient service fronted by an elegant Ruby library that adds up to a whole lot of fun and productivity. The API supports a boatload more options and operations, and it's extremely well designed. Having polished off the task in a couple minutes, I found the short and sweet documentation to be just what I needed for diving deeper.

4. Integrate

For my one-off task today, using s3sh was super convenient. But at some point you'll probably want to manage buckets and objects from within a larger program, such as a Rails application. That's pretty easy given the AWS::S3 Ruby library. But just to get you started, I'll leave you with this handy s3.rb file for storing an object in one fell swoop:

#!/usr/bin/env ruby
require 'rubygems'
require 'aws/s3'

AWS::S3::Base.establish_connection!(
  :access_key_id => ENV['AMAZON_ACCESS_KEY_ID'],
  :secret_access_key => ENV['AMAZON_SECRET_ACCESS_KEY']
)

file = ARGV.first
bucket = "intergalactic"

AWS::S3::S3Object.store(File.basename(file), 
                        open(file), 
                        bucket, 
                        :access => :public_read)

puts AWS::S3::S3Object.url_for(File.basename(file), bucket)[/[^?]+/]

To store the same object we did in the previous step, you'd type:

s3.rb /path/to/local/flight_manual.pdf

When all the bits have been safely tucked away, the public URL is printed. Share your objects at will, but remember that the meter's running.

5. Enjoy

Thanks Marcel!

RubyCocoa Sneak Preview

(Wed Jan 17, 2007) [/Ruby#

It's great to see a flurry of activity around RubyCocoa: the bridge for writing Cocoa apps using Ruby. Word on the street is that it'll ship with Leopard...

RubyOSA: The End of AppleScript?

(Tue Oct 24, 2006) [/Ruby#

At RubyConf this weekend, Laurent Sansonetti from Apple gave a talk and demonstration of RubyOSA. It's a bridge that connects Ruby to the AppleEvent infrastructure. Translated, that means you can do in Ruby what before you could only do in the insufferable language called AppleScript.

RubyOSA is still under heavy development, so watch your head. The official release is rumored to be out Any Day Now (TM). Until then, here's a quick way to start playing:

1. Install libxml and its Ruby bindings:

sudo port install libxml
sudo gem install libxml-ruby

2. Checkout and build RubyOSA:

svn co svn://rubyforge.org/var/svn/rubyosa/trunk rubyosa
cd rubyosa/
ruby extconf.rb
make
ln -s src/lib/rbosa.rb rbosa.rb

3. Fire it up in irb:

irb -r osa.bundle -r rbosa.rb

4. Start controlling applications, such as iTunes:

>> require 'rbosa'
=> false
>> itunes = OSA.app_with_name('iTunes')
>> track = itunes.current_track
>> p track.name
=> "Blue Dress"
>> track.rating = 20
=> 20
>> itunes.next_track
=> nil
>> itunes.previous_track
=> nil
>> itunes.sound_volume = 50
=> 50
>> itunes.pause
=> nil
>> itunes.play
=> nil
>> itunes.current_playlist.tracks.size
=> 2110
>> itunes.public_methods(false).size
=> 68

Or iChat:

>> ichat = OSA.app('ichat')
>> ichat.status_message = "Live from RubyOSA"
=> "Live from RubyOSA"
>> windows = ichat.windows.to_a
>> windows.first.name
=> "Chat with Marcel Molina"
>> ichat.public_methods(false).size
=> 20

Taking that a step further, you can hold yourself accountable with Rake Shame. Let your friends motivate you!

In theory, you can use Ruby to control any application that has a scriptable definition. We're still poking and prodding on the boundaries. For example, OSA.app('finder') works, but it's uncharted territory. And yet the way forward looks a lot clearer than it used to.

Major props to Marcel for helping me get started. :-)

Have fun!

Overheard at RubyConf

(Mon Oct 23, 2006) [/Ruby#

Herewith, a sparse collection of completely out of context and unattributed quotes I overheard at RubyConf this weekend. Names aren't attached either because I didn't know the person's name or I felt the accuracy of the quote was sketchy. Corrections and additions welcome.

"They're flipping one of the ladies rooms into a mens room for us"
"We decided Ruby wasn't niche enough (queue Erlang movie)"
"I'm not Matz"
"Rubinius has evil built in"
"I hope your minds are sufficiently blown"
"It's ugly, we'll fix it"
"First come, first served, but not right now"
"No cussing; that's so 2005"
"Can you write that code, please?"
"But seriously, who doesn't have this book? (holds up AWDWR book)"
"How many people here understand Unicode (hands go up) to a fairly deep level? (hands go down)"
"YARV will find you a new girl friend"
"We have room for the next 3 alien races in the Unicode plane"
"As with most Java APIs, things aren't necessarily built for comfort"

Ruby Learning Test #2: Active Reading

(Fri Apr 08, 2005) [/Ruby#

I had no idea this test-driven learning approach would resonate with so many folks. Thanks for your kind words and encouragement!

What Happens Next?

I'm sorry to have made you wait so long for the second installment of this series. To be honest, I was waiting to get some input from you, dear reader, about how best to proceed. Although I've been using this technique to learn new languages and APIs for several years now, I've just started thinking about how to share it with others. You see, I don't want this to turn into another Ruby language reference. You probably already have Programming Ruby pressed against your bosom.

Since posting the first test, many of you have suggested great ideas about what you'd like to see covered in future tests. I'm delighted to hear that you're not expecting a language reference, and I won't need to hire a ghost writer. Instead, I'll be using this space to explore how to use the learning test technique to supplement other resources. That is, I'll show you how I wrote learning tests to internalize the core concepts I learned in the Ruby book. Things like classes, blocks, exceptions, modules, and threads. As well, please do keep the comments coming. Your input will influence how this plays out, and I'll be picking up the pace now that I know you're on the edge of your seat to learn more Ruby. So welcome aboard!

Pulled Up By Your Bootstraps

So you've installed Ruby and learned enough about the Test::Unit module to bootstrap some learning. It may not feel like much, but given this simple (and safe) testing environment, you can learn useful things about Ruby without having to learn much more about Test::Unit just yet.

Hopefully you felt motivated to write a few more tests to explore how String works---it has over 75 standard methods just waiting to serve you. You don't need a main() method or a debugger to try them out. Just a simple assert_equal will do, and you already know how that works. Do you need to write a test for each method right away? Certainly not. Instead, I wait until I learn about a new method, then I write a learning test in order to learn how the method really works.

Trying What You Read

So how does this technique work in practice? Last time we were focused on just feeling our way around writing tests and getting them to pass. We didn't exactly discuss how this changes the way you read the Ruby book.

Say, for example, you're reading through Chapter 2 of Programming Ruby, and Dave starts explaining how to send messages to objects. Then he shows you some code.

"Rick".index("c") --> 2

To the left of the arrow you see the Ruby expression. The resulting value of the expression is to the right of the arrow. To really learn how to program, I have to program alongside the book. Learning tests give me a way to incrementally try out and, well, test what I've learned. So when you see that format in the book, think of it as an assertion and write a test.

def test_index
  assert_equal(2, "Rick".index('c'))
end

Notice that the test method is named test_index. The test prefix is required by Test::Unit. The _ is the Ruby convention for separating words in method names. Finally, index describes what the test is testing.

I like to keep my test methods nice and short. They test just one thing, and I describe what they test using an expressive method name. That way, if I forget how the index method works, I can easily find an example in my learning test repository. In this case, a search would find the use of index in the assertion, regardless of the method name. But as you write more learning tests, expressive test method names become invaluable. I often find that if I'm unable to find a test quickly, it means the method name needs to be changed. So expect the names to get refined over time.

Coloring Outside the Lines

OK, so the learning test passes. Not surprisingly, the index method returns the index of the first occurrence of the given character. It's at this point in my reading that I'm generally not satisfied. I want to do a bit of exploration, and perhaps even try to break something. The learning test environment allows me to answer questions where the book doesn't.

What happens if the given character isn't in the string, for example? Knowing very little about the language at this point, you can make a guess and ask Ruby for the real answer.

def test_index_character_not_found
  assert_equal(-1, "Rick".index('t'))
end

(That test doesn't pass, by the way. Go ahead and run it to find out what the actual answer is.)

This is active reading. While you're reading, pay attention to things that pique your curiosity. If you keep reading, you risk losing that moment when your brain really wants to learn by doing. Writing a learning test gives you a way to immediately apply the material and, perhaps more importantly, seek answers to questions the material raises for you.

Your Next Mission

Use ri to find out what methods the Fixnum class responds to. Then create a test case in a file called number_test.rb and write a few tests. A Fixnum represents an integer value within a certain range. Here's a learning test demonstrating how to create one:

def test_create_fixnum
  f = 123
  assert_equal(Fixnum, f.class)
end

Notice the use of the class method to test the class of the object referenced by the f variable. If you create a number outside the range of a Fixnum, what is the class of the variable f?

Everything in Ruby is an object. Yes, even numbers. Try sending some messages to numbers. Here's a starter test:

def test_abs
  assert_equal(1, -1.abs)
end

What other tests can you think of for numbers? Chapter 5, Standard Types, of Programming Ruby shows example uses of numbers and the reference library in the back documents all of the methods. As you read about numbers in the book, what questions do you have? Answer the questions by writing learning tests. I'm holding back writing a bunch of assert_equal tests here because that won't help you learn. But next time we'll dig into how to write learning tests for facets of Ruby that require more than a simple assertion of an expression's value.

Ruby Learning Test #1: Are You There, World?

(Fri Mar 18, 2005) [/Ruby#

I truly have no idea where this is going or if it will scale. But I've been meaning to try it, and I'll let you judge whether it's worth it. Here's what I do know: It doesn't cost you anything to come along for the ride.

How I Learned Ruby

A couple years back I flipped open the first edition of Dave and Andy's wonderful book Programming Ruby. A few pages in I'm introduced to my first Ruby code. It looks somewhat foreign... maybe it's just my strained eyes. Ah, it's so clean and elegant! I want to try it. Oh, but will I have the time and opportunity to keep learning Ruby? Just when I'm at the tipping point, Dave beckons me closer: "Go ahead, run the code and you'll see the following output." Those words are intoxicating to a programmer, and so I proceeded to write my first Ruby program.

But it was at that very moment when I asked Ruby a question and it responded with the answer that I learned more than Ruby. I knew right then that I didn't want to run the example just once. No, I wanted to preserve the example and ultimately build a Ruby knowledge base that I could draw from later. That meant the examples had to be executable and check their own results. That sounded familiar. I needed to write tests.

There was just one problem: I didn't yet know enough about Ruby to write a test. Oh, I could print stuff to the screen, but that sort of manual inspection breaks down quickly. Using JUnit had taught me the value of writing automated tests that put the computer in charge of remembering what outcome is expected. Thankfully, when you install Ruby you automatically get the Test::Unit unit testing module. And so I had to learn enough about Test::Unit to learn Ruby itself. The result, years later, is the following:

[~/work/code/ruby/learn] $ ruby all_learning_tests.rb 
ruby all_learning_tests.rb
Loaded suite all_learning_tests
Started
.......................... (a lot more dots)
Finished in 1.467719 seconds.

215 tests, 452 assertions, 0 failures, 0 errors

That's pretty much everything I know about Ruby, give or take. It's a living repository of knowledge; the test suite grows each time I learn something new. Promptly after installing a new version of Ruby, I run the test suite. I do that not necessarily because I think the tests will find a bug, but rather to get a heads-up when something has changed and I need to reset my expectations. It's also a good way to identify when something has been deprecated.

But the real value of writing these tests was less about testing, and more about learning. Through trial and error they taught me how Ruby and its rich set of libraries really work. Not surprisingly, typing in code and running it makes you remember. Indeed, writing learning tests is a fun way to poke and prod any new language or API. And with every test you write you're investing in an executable knowledge base.

So You Want To Learn Ruby?

Perhaps you've been meaning to learn Ruby for fun or profit, but you just don't know where to start. I'd like to help by trying a bit of an experiment. No, I'm not going to send you a copy of my Ruby learning tests. The learning comes through doing.

Rather, I'll start by showing you how I wrote my first Ruby learning test. Then, over the coming weeks and months, I'll spoonfeed you more tests as a starting point for exploring new facets of Ruby. (Submissions are appreciated, too.) Of course, if I get the sense that nobody's listening, I'll stop.

After we get the fundamentals down, I won't try to explain in depth what the Ruby code in each test does. If the test is written well, you won't need an explanation. And frankly, you really do need a copy of Programming Ruby for a definitive guide and tutorial to Ruby. (Disclaimer: I don't get royalties from sales of the book, but it's quite excellent and it saves me typing here.) When I introduce a major new topic, I'll try to call out page numbers from that book. Then you can go to those pages, read up on how and why something works, and codify your understanding with learning tests.

One caution before we go any further: Ruby has spoiled many programmers.

Write a Learning Test

Starting is always the difficult part, so let's get right to it. Download and install Ruby using the one-click Windows installer, tarball, or any other option.

Now we get to write our first learning test. Strings are always fun and easy pickings, plus they usually have a lot of interesting methods to play with. So change to a directory where your learning tests will live (mine's called learn) and create a file called string_test.rb that contains the following Ruby code:

require 'test/unit'

class StringTest < Test::Unit::TestCase

  def test_length
    s = "Hello, World!"
    assert_equal(13, s.length)
  end

end

You just wrote your first Ruby class! The require statement at the top of the file loads the Test::Unit module: Ruby's built-in unit testing framework. The next line declares a class called StringTest. The < symbol on the class declaration line means that StringTest is a subclass of Test::Unit::TestCase. Classes that are test cases must extend Test::Unit::TestCase to enjoy the set of computer-checked assertion methods that we'll use later.

The StringTest class has one method: test_length. Test methods take no parameters and they must be named with a "test" prefix so that Test::Unit knows that they're tests we want to run. After defining the method, we create a variable called s that references an object of class String. We don't have to declare the type of the variable because Ruby figures out its type based on what the variable can do. (More on this exciting topic later.) We then call the assert_equal method (inherited from TestCase) with two parameters. What we're saying here is we expect the length of the string s to be 13. Then the method definition ends with the end keyword, as does the class definition.

Now, let's run the test.

> ruby string_test.rb
Loaded suite string_test
Started
.
Finished in 0.0051 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

OK, so we can count! Let's kick it up a notch. Double-quoted strings can also contain any Ruby expression of the form #{expression}. So let's add another test.

def test_expression_substitution
  assert_equal("", "#{'Hello! ' * 3}")
end

Hmm, that's not going to pass. We're asserting that the result of the expression passed as the second parameter will equal an empty string. We may not be sure what the expression will do, but we're pretty sure it will evaluate to a non-empty string. So why use "" as the expected value?

Sometimes when we're trying to learn something new, we just don't know the answer. We might have a guess, but if we trust the source then we can do one better: we can ask. By leaving the expected outcome empty, we're asking Ruby to tell us what the answer is when we run the test.

> ruby string_test.rb 
Loaded suite string_test
Started
F.
Finished in 0.093141 seconds.

  1) Failure:
test_expression_substitution(StringTest) [string_test.rb:13]:
<""> expected but was
<"Hello! Hello! Hello! ">.

2 tests, 2 assertions, 1 failures, 0 errors

Ah, so that's how it works! Thanks, Ruby! It's pretty much what we expected: Print the phrase "Hello!" three times. Now we plug that answer in, happy to have learned something new.

def test_expression_substitution
  assert_equal("Hello! Hello! Hello! ", "#{'Hello! ' * 3}")
end

With the answer recorded, we can re-run just that test method.

> ruby string_test.rb --name test_expression_substitution
Loaded suite string_test
Started
.
Finished in 0.005031 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

Wait just a doggone minute! What did we just do? We ran a test, knowing it would fail, and then we picked out the answer from the error and plugged it into the test so that it now passes. You wouldn't dare try this with code you were really testing!

We have no idea if Ruby did what it should have done: we just know what it did. That is, we used the language as a tool to explore itself. In the same way that a test is better than a specification, the language is better than a description of the language. The test is definitive—when we ask Ruby what the answer to 'Hello! ' * 3 is, we're going to the horse's mouth. It doesn't matter what the documentation says; what we're testing is what actually happens. And that's learning. So the test is both a learning test and a regression test.

Now It's Your Turn

We've barely scratched the surface of what the Ruby String class can do. See pages 61 and 606 of the book for a full listing of all the methods and explanations for each. You can also get a list of all the messages that String responds to by typing:

> ri String

Indeed, there's a lot to learn, but these first tests give you a framework to explore the many wonders of strings. Better yet, you have a safe learning environment and a language that makes it easy to write and run tests. So go ahead, write a few more string tests, then run them and Ruby will tell you the answers. Give gsub and squeeze a poke. It's just a start, but it may be more Ruby code than you've written before. Next time we'll look at another facet of Ruby through the eyes of learning tests.

Upgrading With Impunity

(Thu Oct 09, 2003) [/Ruby#

I just upgraded from Ruby 1.6.7 (ships with Mac OS X) to Ruby 1.8.0. Upgrading software in general can be a risky game, but in this case I was actually looking forward to it. Indeed, I wanted something to break.

When I first started looking into Ruby, I decided that I wanted to learn it by writing tests. That is, I wanted to explore the language and its libraries by poking and prodding it with chunks of example client code. But instead of using the equivalent of a main method containing example code, I wanted the examples to check their own results. In other words, I'd use tests to ask Ruby a question, learn from its response, then codify the answer in a test assertion. Then when I wanted to refresh my memory of a particular usage of Ruby, I'd refer back to the test. So over time I've grown a fairly comprehensive suite of learning tests -- things I know to be true about Ruby.

Flash-forward to today just after I'd upgraded to Ruby 1.8.0. What's the next step? Pull the test trigger:

[enoch:~/projects/ruby/learn] > ruby AllTests.rb 
Loaded suite AllTests
Started
..............................................................................FF...........................
Finished in 0.667704 seconds.

  1) Failure!!!
testObjectSpace(ReflectionTest) [./ReflectionTest.rb:12]:
<45> expected but was
<34>

  2) Failure!!!
testRangeIntrospection(ReflectionTest) [./ReflectionTest.rb:21]:
<["extend", "equal?"]> expected but was
<["to_a", "instance_eval"]>

107 tests, 243 assertions, 2 failures, 0 errors
Interesting. Two tests failed. 105 tests passed. What gives?

Here's the first test that fails, with the failed assertion highlighted:

def testObjectSpace
  a = 102.7   
  b = 95.1
  living = Array.new

  ObjectSpace.each_object(Numeric) { |x| living.push(x) }             

  assert_equal(45, living.length)
  assert(living.include?(a))
  assert(living.include?(b))
end
I'm learning introspection (reflection) here. The omniscient ObjectSpace class knows about all living objects. I'm asking it to traverse its universe of objects of type Numeric and then I rack 'em up on a stack. Then, for a reason I can't quite recall now, I assert that 45 Numeric objects are alive. Finally I assert that two numbers I created are among the land of the living.

So why is the first assertion failing? Well, apparently in Ruby 1.8.0 there are only 34 Numeric objects alive at the point this test is run. It's an implementation detail really. In hindsight, I probably could have just tested that my two numbers are alive. Testing for 45 objects is a triviality.

Here's the second test that fails, again with the failed assertion highlighted:

def testRangeIntrospection
  range = 1..10   
  methods = range.methods

  assert_equal(68, methods.length)
  assert_equal(["extend", "equal?"], methods[1..2])
  assert(range.respond_to?("equal?"))
  assert(!range.respond_to?("hasKey"))
end
I'm still learning introspection here. This time I create a Range object, assert that it has 68 methods at my disposal, test the name of the methods at the index 1 and 2, and then demonstrate how to use respond_to? to see if the object will respond to selected method invocations. It worked in Ruby 1.6.7. Seems that in Ruby 1.8.0 the returned method names have been reordered. Does it matter? Probably not. Again, the first two assertions are trivialities that test implementation details. I can't remember why I wrote those assertions now. The last two assertions are probably sufficient, but I was in a learning mode at the time.

So should I just clean up these tests to be version independent? Doing so would mean removing the trivialities -- making the tests more generic. More to the point, do I care that version 1.8.0 has a few less Numeric instances and might be using a different data structure to hold method names? Well, I probably shouldn't care because I don't want to become dependent on these sort of implementation details. But I think the tests are trying to tell me something. I just don't get it (yet). And if I remove the assertions I might miss it later.

So for now I'm just going to update the assertions -- trivialities and all -- to pass for Ruby 1.8.0, then see what happens the next time I upgrade. After all, running the tests is free. In the meantime, 105 passing tests tells me that my expectations about Ruby still hold true despite the upgrade. That is, while there may be new things in version 1.8.0 that I've yet to learn, upgrading hasn't invalidated what I already know. These learning tests have payed back once again, this time by giving me a lot of confidence going forward.

A real gem

(Fri Oct 04, 2002) [/Ruby#

I dig Java. Besides being the language that rescued me from five long years coding in C++, Java has paid the bills over the last six years. I remember keying in my first Java program, ironically from the pages of a Microsoft Press book a colleague loaned me. Since then the number of Java APIs has grown to staggering proportions. It seems you can do just about anything in Java these days, and I continue to have good success with it, especially building enterprise apps.

But I don't want to be a one-trick pony. After attending a conference session by Pragmatic Dave earlier this year, I started to try my hand at Ruby. Now when I program in my sleep (all programmers do that, right?), I see Ruby code.

Ruby is a top shelf object-oriented language that started in Japan, but has created a groundswell in the U.S. over the last couple years. It's a dynamically-typed language, with types bound to objects rather than classes. An object's type is determined by which messages (methods for us Java/C++ hacks) the object responds to. Yes, I know what they taught you about the goodness of strongly-typed languages: Write code with lots of syntactic sugar and let the compiler sort it out. Trouble is, we mere mortals have to read and write the code.

The beauty of Ruby is its simplicity and consistency. With Ruby, I find myself writing code to get the job done rather than to appease the compiler. Using test-driven development gives me all the confidence I need that the code is reliable. Will Ruby supplant Java? Nah. But there are many general purpose programs and utilities that could be written quite effectively in Ruby. In my limited experience, programming in Ruby is just good, clean fun!

If you want to learn Ruby, I recommend that you run, don't walk, to your nearest bookstore for a copy of Programming Ruby. I promise that looking into Ruby will give you a different perspective on whatever language you're currently using. You'll be sharpening the saw.