ground group growth hands

This past year of blogging has introduced me to a wide variety of people in the Perl community. Some I’ve admired from afar for years due to their published work, and even more I’ve met” interacting on social media and other forums. So this will be the first in an occasional series highlighting not just the code, but the people that make up the Perl family.

Paul LeoNerd” Evans

I first came across Paul’s work during his series last year on writing a core Perl feature; he’s responsible for Perl v5.32’s isa operator and v5.34’s experimental try/​catch exception handling syntax. I interviewed him about the latter for Perl.com in March 2021. He’s been active on CPAN for so much longer, though, and joined the Perl Steering Council in July. He’s also often a helpful voice on IRC.

Elliot Holden

Renowned author and trainer Randal L. merlyn” Schwartz linked over the weekend in a private Facebook group to Elliot’s impassioned YouTube video about his day job as a Perl web application developer. Through his alter ego Urban Guitar Legend Elliot is also a passionate musician; besides gigging and recording he’s been posting videos for nine years. (I’m a bit envious since I took a break from music almost twenty years ago and haven’t managed to recapture it.) Elliot seems like the quintessential needs-​to-​get-​shit-​done developer, and Perl is perfect for that.

Gábor Szabó

Gábor is a polyglot (both in human and computer languages) trainer, consultant, and author, writing about programming and devops on his Code Maven and Perl Maven websites. He’s also the founder and co-​editor of Perl Weekly and recipient of a Perl White Camel award in 2008 thanks to his organizational and support contributions. Last year he introduced me to the world of live pair programming, working on a web application using the Mojolicious framework.


If you’re on Twitter and looking to connect with other Perl developers, please consider participating in the Perl community I’ve set up there. Twitter Communities are topic-​specific moderated discussion groups, unlike the freewheeling #hashtags system that can be diluted by spam or topics that share the same name. Unfortunately, they’re still read-​only on the Twitter Android app, but you can participate fully on iOS/​iPadOS and the website.

chocolate bar and sugar cubes on a hand
What about My::Favorite::Module?

I mentioned at the Ephemeral Miniconf last month that as soon as I write about one Perl module (or five), someone inevitably brings up another (or seven) I’ve missed. And of course, it happened again last week: no sooner had I written in passing that I was using Exception::Class than the denizens of the Libera Chat IRC #perl channel insisted I should use Throwable instead for defining my exceptions. (I’ve already blogged about various ways of catching exceptions.)

Why Throwable? Aside from Exception::Class’s author recommending it over his own work due to a nicer, more modern interface,” Throwable is a Moo role, so it’s composable into classes along with other roles instead of mucking about with multiple inheritance. This means that if your exceptions need to do something reusable in your application like logging, you can also consume a role that does that and not have so much duplicate code. (No, I’m not going to pick a favorite logging module; I’ll probably get that wrong too.)

However, since Throwable is a role instead of a class, I would have to define several additional packages in my tiny modulino script from last week, one for each exception class I want. The beauty of Exception::Class is its simple declarative nature: just use it and pass a list of desired class names along with options for attributes and whatnot. What’s needed for simple use cases like mine is a declarative syntax for defining several exception classes without the noise of multiple packages.

Enter Throwable::SugarFactory, a module that enables you to do just that by adding an exception function for declaring exception classes. (There’s also the similarly-​named Throwable::Factory; see the above discussion about never being able to cover everybody’s favorites.) The exception function takes three arguments: the name of the desired exception class as a string, a description, and an optional list of instructions Moo uses to build the class. It might look something like this:

package Local::My::Exceptions;
use Throwable::SugarFactory;

exception GenericError  => 'something bad happened';
exception DetailedError => 'something specific happened' =>
  ( has => [ message => ( is => 'ro' ) ] );

1;

Throwable::SugarFactory takes care of creating constructor functions in Perl-​style snake_case as well as functions for detecting what kind of exception is being caught, so you can use your new exception library like this:

#!/usr/bin/env perl

use experimental qw(isa);
use Feature::Compat::Try;
use JSON::MaybeXS;
use Local::My::Exceptions;

try {
    die generic_error();
}
catch ($e) {
    warn 'whoops!';
}

try {
    die detailed_error( message => 'you got me' );
}
catch ($e) {
    die encode_json( $e->to_hash )
      if $e isa DetailedError and defined $e->message;
    $e->throw if $e->does('Throwable');
    die $e;
}

The above also demonstrates a couple of other Throwable::SugarFactory features. First, you get a to_hash method that returns a hash reference of all exception data, suitable for serializing to JSON. Second, you get all of Throwable’s methods, including throw for re-​throwing exceptions. 

So where does this leave last week’s FOAAS.com modulino client demonstration of object mocking tests? With a little bit of rewriting to define and then use our sweeter exception library, it looks like this. You can review for a description of the rest of its workings.

#!/usr/bin/env perl

package Local::CallFOAAS::Exceptions;
use Throwable::SugarFactory;

BEGIN {
    exception NoMethodError =>
      'no matching WebService::FOAAS method' =>
      ( has => [ method => ( is => 'ro' ) ] );
    exception ServiceError =>
      'error from WebService::FOAAS' =>
      ( has => [ message => ( is => 'ro' ) ] );
}

package Local::CallFOAAS;  # this is a modulino
use Test2::V0;             # enables strict, warnings, utf8

# declare all the new stuff we're using
use feature qw(say state);
use experimental qw(isa postderef signatures);
use Feature::Compat::Try;
use Syntax::Construct qw(non-destructive-substitution);

use WebService::FOAAS ();
use Package::Stash;
BEGIN { Local::CallFOAAS::Exceptions->import() }

my $foaas = Package::Stash->new('WebService::FOAAS');

my $run_as =
    !!$ENV{CPANTEST}       ? 'test'
  : !defined scalar caller ? 'run'
  :                          undef;
__PACKAGE__->$run_as(@ARGV) if defined $run_as;

sub run ( $class, @args ) {
    try { say $class->call_method(@args) }
    catch ($e) {
        die 'No method ', $e->method, "\n"
          if $e isa NoMethodError;
        die 'Service error: ', $e->message, "\n"
          if $e isa ServiceError;
        die "$e\n";
    }
    return;
}

# Utilities

sub methods ($) {
    state @methods = sort map s/^foaas_(.+)/$1/r,
      grep /^foaas_/, $foaas->list_all_symbols('CODE');
    return @methods;
}

sub call_method ( $class, $method = '', @args ) {
    state %methods = map { $_ => 1 } $class->methods();
    die no_method_error( method => $method )
      unless $methods{$method};
    return do {
        try { $foaas->get_symbol("&$method")->(@args) }
        catch ($e) { die service_error( message => $e ) }
    };
}

# Testing

sub test ( $class, @ ) {
    state $stash = Package::Stash->new($class);
    state @tests = sort grep /^_test_/,
      $stash->list_all_symbols('CODE');

    for my $test (@tests) {
        subtest $test => sub {
            try { $class->$test() }
            catch ($e) { diag $e }
        };
    }
    done_testing();
    return;
}

sub _test_can ($class) {
    state @subs = qw(run call_method methods test);
    can_ok $class, \@subs, "can do: @subs";
    return;
}

sub _test_methods ($class) {
    my $mock = mock 'WebService::FOAAS' => ( track => 1 );

    for my $method ( $class->methods() ) {
        $mock->override( $method => 1 );

        ok lives { $class->call_method($method) },
          "$method lives";
        ok scalar $mock->sub_tracking->{$method}->@*,
          "$method called";
    }
    return;
}

sub _test_service_failure ($class) {
    my $mock = mock 'WebService::FOAAS';

    for my $method ( $class->methods() ) {
        $mock->override( $method => sub { die 'mocked' } );

        my $exception =
          dies { $class->call_method($method) };
        isa_ok $exception, [ServiceError],
          "$method throws ServiceError on failure";
        like $exception->message, qr/^mocked/,
          "correct error in $method exception";
    }
    return;
}

1;

[Updated, thanks to Dan Book, Karen Etheridge, and Bob Kleemann] The only goofy bit above is the need to put the exception calls in a BEGIN block and then explicitly call BEGIN { Local::CallFOAAS::Exceptions->import() }. Since the two packages are in the same file, I can’t do a use statement since the implied require would look for a corresponding file or entry in %INC. (You can get around this by messing with %INC directly or through a module like me::inlined that does that messing for you, but for a single-​purpose modulino like this it’s fine.)


The perlcritic tool is often your first defense against awkward, hard to read, error-​prone, or unconventional constructs in your code,” per its description. It’s part of a class of programs historically known as linters, so-​called because like a clothes dryer machine’s lint trap, they detect small errors with big effects.” (Another such linter is perltidy, which I’ve referenced in the past.)

You can use perlcritic at the command line, integrated with your editor, as a git pre-​commit hook, or (my preference) as part of your author tests. It’s driven by policies, individual modules that check your code against a particular recommendation, many of them from Damian Conway’s Perl Best Practices (2005). Those policies, in turn, are enabled by PPI, a library that transforms Perl code into documents that can be programmatically examined and manipulated much like the Document Object Model (DOM) is used to programmatically access web pages.

perlcritic enables the following policies by default unless you customize its configuration or install more. These are just the gentle” (severity level 5) policies, so consider them the bare minimum in detecting bad practices. The full set of included policies goes much deeper, ratcheting up the severity to stern,” harsh,” cruel,” and brutal.” They’re further organized according to themes so that you might selectively review your code against issues like security, maintenance, complexity, and bug prevention.

My favorite above is probably ProhibitEvilModules. Aside from the colorful name, a development team can use it to steer people towards an organization’s favored solutions rather than deprecated, buggy, unsupported, or insecure” ones. By default, it prohibits Class::ISA, Pod::Plainer, Shell, and Switch, but you should curate and configure a list within your team.

Speaking of working within a team, although perlcritic is meant to be a vital tool to ensure good practices, it’s no substitute for manual peer code review. Those reviews can lead to the creation or adoption of new automated policies to save time and settle arguments, but such work should be done collaboratively after achieving some kind of consensus. This is true whether you’re a team of employees working on proprietary software or a group of volunteers developing open source.

Of course, reasonable people can and do disagree over any of the included policies, but as a reasonable person, you should have good reasons to disagree before you either configure perlcritic appropriately or selectively and knowingly bend the rules where required. Other CPAN authors have even provided their own additions to perlcritic, so it’s worth searching CPAN under Perl::Critic::Policy::” for more examples. In particular, these community-​inspired policies group a number of recommendations from Perl developers on Internet Relay Chat (IRC).

Personally, although I adhere to my employer’s standardized configuration when testing and reviewing code, I like to run perlcritic on the brutal” setting before committing my own. What do you prefer? Let me know in the comments below.