Perl Moo Throwable Example
While working on a project with Moo (the first time for me) I discovered Try::Tiny. It's a great little helper and I highly recommend it. However, on my quest to make better error handling in Perl I thought it would be a good idea to have some kind of exception system.
Turns out, this already exists. In fact it is quite simple: die() accepts a reference as parameter, which could very well be an object. A couple of modules on CPAN make use of this, one of the simplest with the fewest dependencies is Throwable.
Since I could not find a good example of how to use it correctly (which I understood), I'm going to post such an example here for anyone who might be interested.
So, you need to have Moo and Throwable installed first. I'm doing these kind of experiments with perbrew, which I recommend as well! Here's my "Exception" class. It's a Moo role with two attributes (type and msg) and a class method e().
package Ex; use Moo; with 'Throwable';has type => (is=>‘ro’); has msg => (is=>‘ro’);
sub e { if (ref($) eq ‘Ex’) { return $; } else { chomp; return Ex->new(msg => $_, type=>0); } }
This Exception class will act as our wrapper to throw exceptions or convert old school perl die()'s into exceptions.
Here is a simple Moo class without any attributes which implements two methods X::Nix::tu() and X::Nix::ta(). Both of them just die. The method tu() throws an exception of type Ex - our exception class from above comes into use here. The ta() method on the other hand just dies with a good old croak() call.
package X::Nix; use Carp;
use Ex;
use Moo;sub tu { Ex->throw({type => 1, msg => ‘dont die while root’}); }
sub ta { croak “death by disruptor”; }
Now let's take a look at our main script, which uses the X::Nix class and tries to catch both exceptions:
package main; use Try::Tiny; use X::Nix;my $x = X::Nix->new();
try { $x->tu; } catch { printf “died with %d and said: <%s>\n”, Ex::e->type, Ex::e->msg; };
try { $x->ta; } catch { printf “died with %d and said: <%s>\n”, Ex::e->type, Ex::e->msg; };
It calls both methods and tries to catch them. Note how it calls Ex::e->type, though: Try::Tiny puts the exception object/message into $_. The class method Ex::e() uses $_ directly, therefore we are able to leave it in our catch{} code. This is the same thing as Ex::e($_)->type. Here's the output:
died with 1 and said: <dont die while root> died with 0 and said: <death by disruptor at role.pl line 49.>
So, as you can see, with the help of our Ex::e() class method we can handle real exceptions and old school die()'s the same way, wo don't need to distinguish between the two. This way you can use foreign perl modules which use croak() and die() and catch those errors as if they were exceptions.