NAME

P5U::Tutorial::Development - so, you want to write your own p5u command...

GENERAL DESIGN PRINCIPLES

Keep command modules (P5U::Command::Foo) lean. Each time p5u is executed, every command module is loaded; not just the one being used. One technique is to split your code into two modules, a workhorse (typically called something like P5U::Lib::Foo), and the command module. The command module only loads the workhorse when it's actually needed.

There are some modules that you can use without worrying about performance (because App::Cmd or P5U has already loaded them). These include Class::Load, JSON and Path::Tiny.

If you need OO, then use Moo (but do it in a workhorse module that's loaded on demand), Types::Standard and namespace::clean. If you need the web, then use LWP::UserAgent or LWP::Simple. If you need to do stuff with the file system, use Path::Tiny and Path::Iterator::Rule. These are good modules, and they're already listed as P5U dependencies, so there's no reason to avoid them.

TEMPLATE

package P5U::Command::Foo;

use 5.010;
use strict;
use utf8;
use P5U-command;  # important!

# Metadata
BEGIN {
   $P5U::Command::Foo::AUTHORITY = 'cpan:JOEBLOGGS'; 
   $P5U::Command::Foo::VERSION   = '0.001';
};

# These are used when generating help information
use constant {
   abstract    => q[do some foo],
   usage_desc  => q[%c foo %o],
};

# The first command name is the preferred name.
# Subsequent ones are aliases.
sub command_names
{
   qw( foo fu );
}

# See Getopt::Long::Descriptive
sub opt_spec
{
   return (
      [ "bar|b"  => "foobar" ],
      [ "baz"    => "foobaz" ],
   );
}

# This is where the magic happens!
sub execute
{
   require P5U::Lib::Foo;  # load workhorse
   
   my ($self, $opt, $args) = @_;
   # $opt is a hashref of options
   # $args is an arrayref of non-option arguments
   
   # do stuff
}

1;

Note that P5U is based on App::Cmd, so all P5U command modules are subclasses of App::Cmd::Command. Thus there are a bunch of other useful methods available for your subclassing pleasure. One in particular that you should be aware of is $self->usage_error($msg) to die with an error message. (But not all errors are usage errors. For example, an error communicating with a server is not a usage error, but a failure for the user to specify which server to communicate with is.)

UTILITY METHODS

P5U command modules are also subclasses of P5U::Command which provides some utility methods for loading and saving config files, and so on. These are:

$self->get_tempdir

Each time this is called returns a new empty directory as a Path::Tiny object. When this object goes out of scope, the directory and its contents will be deleted.

$self->get_cachedir

Returns a Path::Tiny object representing a directory where the command can cache data. In the current version, this cache is not automatically purged, but in future versions it will be.

$self->get_datadir

Returns a Path::Tiny object representing a directory where the command can keep long-lived data.

$self->get_config

Returns a hashref of configuration data.

$self->save_config($hashref)

Save a hashref of configuration data.

BUGS

Please report any bugs to http://rt.cpan.org/Dist/Display.html?Queue=P5U.

SEE ALSO

P5U, App::Cmd, Getopt::Long::Descriptive.

AUTHOR

Toby Inkster <tobyink@cpan.org>.

COPYRIGHT AND LICENCE

This software is copyright (c) 2012-2013 by Toby Inkster.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.