Wednesday, January 27, 2010

Try::Tiny - 'Lightweight' try{} catch{} finally{}

As a lot of people will know error handling in Perl normally amounts to

my $val = eval { do_something_evil()};
die "Something evil happened: $@" if $@;

Not too much of a problem here except this (in some very odd cases) will not correctly trap the error and it means writing cleanup code can be hard (whilst trying not to clobber the value of $@ at the same time). This is not an easy thing to do so enter Try::Tiny which is a fantastic module giving you full try catch & finally support in Perl. Meaning the above code looks more like

my $val = try {
do_something_evil();
}
catch {
die "Something evil happened: $_";
};

Since I am a Java programmer first this syntax looks more natural to me. What is really impressive though is that finally support has now been added allowing us to do:

my $val = try {
do_something_evil();
}
catch {
die "Something evil happened: $_";
}
finally {
warn 'But first maybe some cleanup';
};

This is a perfect tool for those situations where local just does not cut it (say resetting a value in an object no matter what happens to the code).

Anyway next time you want to do error handling in Perl have a look at Try::Tiny; you won't regret it (and if you're wondering why I'm recommending this have a look at Try::Tiny's commit history on github & you may see why).

Thursday, January 14, 2010

TAP That!

TAP (Test Anything Protcol) is Perl's simple test output format for reporting (amongst other things) success & failure of test assertions. This output is what you see printed to screen

ok 1 - Hello
not ok 2 - Nothing right

One thing I wanted to do was to build an object which can be run in two modes
  • As a normal test script (using Test::More)
  • As an object which can assert all tests are good (and confess if anything goes wrong)
So how can I do this? Thankfully Test::More delegates to Test::Builder::new (which is a singleton instance) for its assertion code. The builder has an output method which when passed a scalar reference Test::Builder will write the TAP output to that scalar. Couple this with TAP::Parser we can scan the TAP output and confess accordingly like so.

use Test::Builder;
use Test::More;
use Carp;

my $tap;
my $t = Test::Builder->new->output(\$tap);
ok(1, 'Hello');
fail('FAIL');
done_testing();

my $tap_parser = TAP::Parser->new({
tap => $tap
});

while ( my $result = $tap_parser->next() ) {
if(!$result->is_ok()) {
confess('Error during tests: '.$result->as_string());
}
print $result->as_string(), "\n";
}

Bingo! Now I'm sure there's a module somewhere which already does this but I like how easy this is.