Sun, 26 Nov 2006

When Open Classes Attack

(... or "Why Cut-and-paste coding is a bad idea", part $bignum)

I've just spent an embarrassingly long time debugging a problem in an app that I'm converting from single-threaded to multi-threaded operation.

As a nice first step, I wrote a new test suite, and put it in a new file threading_test.rb. This file was created by copying another, similar test suite from another file.

I've since been having a lot of trouble with the new functionality, as it was throwing a lot of spurious errors. I've only just realised that this is because I defined a teardown method for my new test suite, which kinda depended on the corresponding setup method I defined. Except that I screwed up the file copy a bit, and didn't rename the test suite class name, so I was defining a teardown which was running alongside a completely unrelated setup method (the one from the original class). Kaboom!

Of course, a large part of the reason it took me so long to track the problem down is that the reported exception was vaguely threading related, and since I'm no threading guru (or even wet-eared novice, truth be told) I'd spent a long time faffing around in my threading code wondering where the problem was. Then I read the exception information really closely and suddenly all was revealed...



posted at: 18:05 | category: /general/ruby | permalink


Thu, 24 Aug 2006

That's one way to put it...

This pretty much accurately expresses my gut feeling of what Ruby is:

... pretty much a Perlized dialect of Smalltalk...

from http://c2.com/cgi/wiki?SmalltalkLanguage



posted at: 08:19 | category: /general/ruby | permalink


Thu, 08 Jun 2006

[Ruby] Blocks as Resource Management

One of the really neat things about Ruby is the extensive use of blocks (closures, I think the rest of the world calls them) in the standard library to control the allocation and deallocation of resources. Take the following snippet:

 File.open('something.txt') do |fd|
   # Manipulate the file through 'fd'
 end
 # File handle is now closed

There's no way that you can escape from that block without the file being closed (well, in theory, you could lose the file handle if there was an exception thrown in the block, but the File::open method can take care of that internally by catching, closing, then rethrowing).

I've loved this feature since I first saw it -- "holy crap!" I thought to myself, "I never have to call close again!". And I was pretty well right -- I think I can count (in unary, not binary!) on one hand (well, I'd use grep | wc) the number of times I've used IO#close in my Ruby code.

The funny thing is, though, that I don't think I've used this pattern in my own code enough. But I just had an epiphany whilst waiting for a train home -- and I think I'm cured. Take this hideous piece of test code I had laying around in an app I'm working on at the moment:

 def test_something_funny
   faux_path_on
   d = Domain.new('127.0.0.1', 'somethingfunny.com')
   faux_path_off
   
   # assert writ large
 end

Domain::new calls out to a shell command (dig(1) to be precise -- take a guess at what I'm writing <grin>, and yes, it will be released shortly) but for test purposes I can't run dig (because I'm not guaranteed to have all the infrastructure available for a real dig to succeed). So I have a flimsy mock of dig in a directory in my test suite, which is where faux_path_on sets ENV['PATH'] to look at.

This is, of course, pretty untidy, because when (not if!) I forget to call faux_path_off, everything goes to complete poop, and my children will have two heads or something.

The much cleaner version is like this:

 def test_something_funny
   d = faux_path { Domain.new }
 
   # Assert ahoy!
 end

And my faux_path method (I've even saved a method!) is as simple as simple can be:

 def faux_path
   realpath = ENV['PATH']
   ENV['PATH'] = my_fake_path
   rv = yield
   ENV['PATH'] = realpath
   return rv
 end

I also have a trickier faux_* method, which sets a couple of environment variables which tell my mock nsupdate(8) where to write the data it gets sent from my application -- again, it's a block-taking method that sets the environment, yields, reads in the data from the temporary file, cleans it up, and returns the contents of the aforementioned file.

The power of the Ruby is growing within me, I can feel it. And I like it.



posted at: 20:40 | category: /general/ruby | permalink