Actions: | Security

AllGoodBits.org

Navigation: Home | Services | Tools | Articles | Other

Perl - Convenient and Well Written

Often, particularly in its early days before the various movements for better perl, perl code was written badly. Here is an example, do-nothing script/module that serves me as an example of how to write small scripts and modules using some of the better approaches.

With the exception of some POD requirements and minor perltidy(1)ing that I usually don't care about, this code passes Perl::Critic using PBP policies all the way, that is down to Severity: 1 (aka brutal).

It shows perl code as both executable script and module, good pragmas, exception handling, error handling, option processing, argument processing, passing variables by reference and pod.

Clearly, this is not perfect, both in that it is non-comprehensive even for my own purposes and because there are controversies about what actually is the best approach. But I'm not expecting perfection. I wrote it to refer to when I come back to perl after having spent a lot of time programming in other languages.

#!/usr/bin/env perl

# $Id$
# $Author$
# $Date$

package Foo;

use strict;
use warnings;
use version; our $VERSION = qv('0.0.1');

use Readonly;

use Carp;
use Try::Tiny;

my %options;

Readonly my $USAGE => <<'ENDUSAGE';

  Do something

  Usage: perl foo.pm

  --bar               optionally takes a string value
  --baz               non-zero length (comma-delimited) list of strings

  --force             overwrite destination directory
  -h, --help          print this usage
  -v, --verbose       report activity
  --debug             lots of detail, implies verbose

ENDUSAGE

if (!(caller()) {
    __PACKAGE__->run(\@ARGV);
}

sub run {
    #takes an arrayref which deferences to @ARGV
    my $arg_arrayref = $_;
    require Getopt::Long;

    GetOptions(\%options,
        "bar:s",
        "baz=s@",
        "force",
        "debug",
        "help|h",
        "verbose|v+",
    );
    if ($help) { croak $USAGE; }

    twiddle(\%options,$arg_arrayref);
    return;
}

sub twiddle {
    # takes a hashref of options and an arrayref of arguments

    # unpack the options
    my($opt_hashref,$arg_arrayref) = @_;
    my $verbose = ${$opt_hashref{'verbose'}};
    my $debug = ${$opt_hashref{'debug'}};

    # params::validate? (or carp/croak)

    for my $arg (@{$arg_arrayref}) {
        try {
            do_it($arg);
        } catch {
            carp "failed to twiddle $arg: $_";
        };
    }
    return;
}

1;

__END__

=pod

=head1 NAME

foo - Attempting to demonstrate exemplary code in the sense of being an example of various good practices and techniques

=head1 SYNOPSIS

=head1 DESCRIPTION

Showing perl code as both executable script and module, good pragmas, exception handling, error handling, option processing, argument processing, passing variables by reference, pod.

=head1 USAGE

use Foo;
Foo::twiddle( (baz => ['foo','bar'], verbose => 1 ), ['arg1','arg2']);

=head1 REQUIRED ARGUMENTS

None

=head1 OPTIONS

=head1 BUGS AND LIMITATIONS

=head1 DEPENDENCIES

=head1 TODO

Possible improvements

=over

=item Demonstrate testing

=item Demonstrate logging

=item Use Params::Validate

=item Use pod2usage (look in the docs for pod2usage and L<http://www.vromans.org/johan/articles/getopt.html>)

=item More explanation of what's happening, why it's good practice (and what it avoids and why).

=back

=head1 AUTHOR

Duncan Hutty - L<http://www.allgoodbits.org>

=head1 LICENSE AND COPYRIGHT

This software is Copyright (c) 2012 by Duncan Hutty.

This is free software, licensed under:

     The Artistic License 2.0 (GPL Compatible)


=cut