App-Cmdline

 view release on metacpan or  search on metacpan

lib/App/Cmdline.pm  view on Meta::CPAN

#-----------------------------------------------------------------
# App::Cmdline
# Author: Martin Senger <martin.senger@gmail.com>
# For copyright and disclaimer see the POD.
#
# ABSTRACT: helper for writing command-line applications
# PODNAME: App::Cmdline
#-----------------------------------------------------------------
use warnings;
use strict;

package App::Cmdline;
use parent 'App::Cmd::Simple';

our $VERSION = '0.1.2'; # VERSION

BEGIN {
    # we need to say no_auto_version early
    use Getopt::Long qw(:config no_auto_version);
}
use Sub::Install;

# ----------------------------------------------------------------
# Return the command-line script usage (the 1st line of the
# Usage). The content of the usage slightly differs depending on the
# configuration options used.
# ----------------------------------------------------------------
sub usage_desc {
    my $self = shift;
    my $config = { map { $_ => 1 } @{ $self->getopt_conf() } };
    if (exists $config->{'no_bundling'}) {
        return "%c [short or long options, not bundled]";
    } else {
        return "%c %o";
    }
}

# ----------------------------------------------------------------
# Create (and return) option definitions from wanted option sets
# (given as class names). Also install the validate_args() subroutine
# that will call validate_opts() on all wanted option sets.
# ----------------------------------------------------------------
sub composed_of {
    my $self = shift;
    my @option_classes = @_;  # list of class names with wanted options sets

    # create option definitions
    my @opt_spec = ();
    foreach my $set (@option_classes) {
        push (@opt_spec, $set) and next if ref ($set);
        ## no critic
        eval "require $set";
        if ($set->can ('get_opt_spec')) {
            push (@opt_spec, $set->get_opt_spec());
        } else {
            warn "Cannot find the set of options $set. The set is, therefore, ignored.\n";
        }
    }

    # install a dispatcher of all validating methods
    Sub::Install::reinstall_sub ({
        code => sub {
            foreach my $set (@option_classes) {
                next if ref ($set);
                if ($set->can ('validate_opts')) {
                    $set->validate_opts ($self, @_);
                }
            }
        },
        as   => 'validate_args',
                               });
    # add the configuration options
    return (@opt_spec, { getopt_conf => $self->getopt_conf() } );
}

# ----------------------------------------------------------------
# Check if the given set of options has duplications. Warn if yes.
# ----------------------------------------------------------------
sub check_for_duplicates {
    my ($self, @opt_spec) = @_;
    my $already_defined = {};
    foreach my $opt (@opt_spec) {
        # e.g. $opt: [ 'check|c' => "only check the configuration"  ]
        #      or:   []
        next unless ref ($opt) eq 'ARRAY';
        next if @$opt == 0;
        my ($opt_name) = split (m{\|}, $opt->[0]);
        next unless defined $opt_name;
        if (exists $already_defined->{$opt_name}) {
            warn
                "Found duplicated definition of the option '$opt_name': [" .
                join (' => ', @$opt) . "].\n";
        } else {
            $already_defined->{$opt_name} = 1;
        }
    }
    return @opt_spec;
}

# ----------------------------------------------------------------
# Return a refarray of the Getopt configuration options.
# ----------------------------------------------------------------
sub getopt_conf {
    return [
        'no_bundling',
        'no_ignore_case',
        'auto_abbrev',
        ];
}

# ----------------------------------------------------------------
# Die with a given $error message and with the full Usage.
# ----------------------------------------------------------------
sub usage_error {
    my ( $self, $error ) = @_;
    die "Error: $error\nUsage: " . $self->usage->text;
}

1;


=pod

=head1 NAME

App::Cmdline - helper for writing command-line applications

=head1 VERSION

version 0.1.2

lib/App/Cmdline.pm  view on Meta::CPAN

   }

   # The main job is implemented here
   use Data::Dumper;
   sub execute {
       my ($self, $opt, $args) = @_;

       print STDERR "Started...\n" unless $opt->quiet;
       print STDOUT 'Options ($opt):    ' . Dumper ($opt);
       print STDOUT 'Arguments ($args): ' . Dumper ($args);
       ...
   }

=head1 DESCRIPTION

This module helps to write command-line applications, especially if
they need to be fed by some command-line options and arguments. It
extends the L<App::Cmd::Simple> module by adding the ability to use
several predefined sets of options that many real command-line
applications use and need anyway. For example, in most applications
you need a way how to print its version or how to provide a decent
help text. Once (or if) you agree with the way how it is done here,
you can spend much less time with the almost-always-repeating options.

Your module (representing the application you are writing) should
inherit from this module and implement, at least, the method
L<opt_spec|"opt_spec"> (optionally) and the method L<execute|"execute"> (mandatory).

=for :stopwords d'E<234>tre

=head1 METHODS

In order to use the ability of composing list of options from the
existing sets of predefined options (which is, after all, the main
I<raison d'E<234>tre> of this module) use the method
L<composed_of|"composed_of">. And to find out that various predefined
sets of options do not step on each other toes, use the method
L<check_for_duplicates|"check_for_duplicates">.

When writing a subclass of App::Cmdline, there are only a few methods
that you might want to overwrite (except for L<execute|"execute"> that you
B<must> overwrite). Below are those that may be of your interest, or
those that are implemented here slightly differently from the
L<App::Cmd::Simple>.

=head3 Summary of methods

=over

=item Methods that you must overwrite

   execute()

=item Methods that you should overwrite

   opt_spec()

=item Methods that you may overwrite

   usage_desc()
   validate_args()
   usage_error()
   getopt_conf()
   ...

=item Methods that you just call

   composed_of()
   check_for_duplicates()
   usage_error()

=back

=head2 B<opt_spec>

This method returns a list with option definitions, each element being
an arrayref. This returned list is passed (starting as its second
argument) to C<describe_options> from
L<Getopt::Long::Descriptive>. You need to check the documentation on
how to specify options, but mainly each element is a pair of I<option
specification> and the I<help text for this option>. For example:

   sub opt_spec {
       my $self = shift;
       return
           [ 'latitude|y=s'  => "geographical latitude"  ],
           [ 'longitude|x=s' => "geographical longitude" ],
       ;
   }

The I<option specification> (the first part of each pair) is how the
option can appear on the command-line, in its short or long version, if
it takes a value, how/if can be repeated, etc.

The option elements can be richer. Another useful piece of the option
definition is its default value - see an example of it in
L<App::Cmdline::Options::DB/OPTIONS>.

The example above, however, does not add anything new to the
L<App::Cmd::Simple>. Specifying the options this way, you could (and
probably should) inherit directly from the L<App::Cmd::Simple> without
using C<App::Cmdline>. Therefore, let's have another example:

   sub opt_spec {
       my $self = shift;
       return
           [ 'latitude|y=s'  => "geographical latitude"  ],
           [ 'longitude|x=s' => "geographical longitude" ],
           $self->composed_of (
               'App::Cmdline::Options::Basic',
               'App::Cmdline::Options::DB',
           );
   }

In this example, your command-line application will recognize the same
options (latitude and longitude) as before and, additionally, all
options that were predefined in the I<role> classes
L<App::Cmdline::Options::Basic> and L<App::Cmdline::Options::DB>. See
more about these classes in L<"PREDEFINED SETS OF OPTIONS">;

If not overridden, it returns an empty list.

lib/App/Cmdline.pm  view on Meta::CPAN

But if I change the configuration by implementing:

   sub getopt_conf {
       return [ 'bundling' ];
   }

the bundled options are now recognized as options (and no argument
reminded):

   senger@ShereKhan2:myapp -xy
   Executing...
   Options ($opt):    $VAR1 = bless( {
      'xpoint' => 1,
      'ypoint' => 1
       }, 'Getopt::Long::Descriptive::Opts::__OPT__::2' );
   Arguments ($args): $VAR1 = [];

=head2 B<usage_desc>

The returned value from this method will be used as the first line of
the usage message. The full usage is returned by another method,
C<usage>, that you usually do not overwrite because its default
behaviour is to create a reasonable summary from the help texts you
provided in the L<opt_spec|"opt_spec"> method and, possibly, by this
C<usage_desc> method.

Behind the scene, the returned string is interpreted by the
L<Getopt::Long::Descriptive> which accepts also few special
constructs:

=over

=item

%c will be replaced with what C<Getopt::Long::Descriptive> thinks is
the program name (it is computed from $0).

=item

%o will be replaced with a list of the short options, as well as the
text "[long options...]" if any have been defined.

=item

Literal % characters will need to be written as %%, just like with
sprintf.

=back

By default, the C<App::Cmdline> returns slightly different usage
description depending on the bundling configuration option (see
L<getopt_conf|"getopt_conf">): if the bundling is disabled, the bundle
of all short options is not shown. Often, you want to use whatever
C<App::Cmdline> returns plus what you wish to add on the first line of
the usage. For example:

   sub usage_desc {
       return shift->SUPER::usage_desc() . ' ...and anything else';
   }

=head2 B<validate_args>

Originally, this method was meant to check (validate) the command-line
arguments (remember that arguments are whatever remains on the
command-line after options defined in the L<opt_spec|"opt_spec">
method have been processed). The options themselves could be already
validated by various subroutines and attributes given in the option
specifications (as described, sometimes only vaguely, in the
L<Getopt::Long::Descriptive>). But sometimes, it is useful to have all
validation, of options and of arguments, in one place - so we have
this method.

The method gets two parameters, C<$opt> and C<$args>. The first one is
an instance of L<Getopt::Long::Descriptive::Opts> giving you access to
all existing options, using their names (as were defined in
L<opt_spec|"opt_spec">) as the access methods. The second parameter is
an arrayref containing all remaining arguments on the command-line.

I<Important:> Some predefined sets of options (see the L<"PREDEFINED
SETS OF OPTIONS">) do also some checking (or other actions, like
printing the version and exiting) and this checking is invoked from
the C<App::Cmdline>'s validate_args method. Therefore, it is strongly
recommended that if you overwrite this method, you also call the SUPER:

   sub validate_args {
       my ($self, $opt, $args) = @_;
       $self->SUPER::validate_args ($opt, $args);
       if ($opt->number and scalar @$args != $opt->number) {
          $self->usage_error ("Option --number does not correspond with the number of arguments");
       }
   }

   senger@ShereKhan2:myapp -n 2 a b c
   Error: Option --number does not correspond with the number of arguments
   Usage: myapp [short or long options, not bundled] <some arguments...>
        -n --number     expected number of args
        -h              display a short usage message
        -v --version    display a version

The example also shows calling the method C<usage_error>. Unless you
overwrite also this method, it prints the given error message together
with the usage and dies.

=head2 B<execute>

Last but definitely not least. You B<have> to implement this method
and put here whatever your command-line application is supposed to do.

The method gets two parameters, C<$opt> and C<$args>. The first one is
an instance of L<Getopt::Long::Descriptive::Opts> giving you access to
all existing options, using their names (as were defined in
L<opt_spec|"opt_spec">) as the access methods. The second parameter is
an arrayref containing all remaining arguments on the command-line.

   sub execute {
       my ($self, $opt, $args) = @_;
       if ($opt->crystal eq 'ball') {
          print ask_ball ($args->[0]);
       } else {
          die "All is vanity...\n"
             unless $opt->godess;
       }
   }

=head1 PREDEFINED SETS OF OPTIONS

The predefined sets of options are represented by classes that are
considered rather C<roles>. You do not extend them (inherit from them)
but you just use them (by naming them in the method
L<composed_of|"composed_of">).

This distribution bundles several of such classes. See their own
documentation to find out what options they provide. Here is just a
quick summary:

=over

=item L<App::Cmdline::Options::Basic>

Provides basic options (help and version).

=item L<App::Cmdline::Options::ExtBasic>

Provides the same options as in L<App::Cmdline::Options::Basic> and
adds options for richer documentation.

=item L<App::Cmdline::Options::DB>

Provides options for accessing a database (user authentication, host and
port name, etc.).

=item L<App::Cmdline::Options::ExtDB>

Provides the same options as in L<App::Cmdline::Options::DB> and adds
an option for showing what values were given by the database-related
options.

=back

=head3 How to create a new predefined set

You may wish to create a new set of options if you want to re-use
them. For application-specific options, used only once, you do not
need to have a predefined set, you just specify them directly in the
L<opt_spec|"opt_spec"> method.

The classes that can be used as the predefined sets of options do not
inherit from any common class (so far, there was no need for it) -
unless one extends another one (as is the case of
L<App::Cmdline::Options::ExtBasic>). It is, however, recommended, to
use the namespace I<App::Cmdline::Options::> - just to find them
easier on CPAN.

Each of these classes should implement up to two methods:

=over

=item B<get_opt_spec>

Strictly speaking, it is not mandatory, but without this method the
class can hardly predefine any new options. The method should return
a list of arrayrefs, suitable to be consumed by the
L<opt_spec|"opt_spec"> method. For example (taken from the
L<App::Cmdline::Options::Basic>):

   sub get_opt_spec {
       return
           [ 'h'         => "display a short usage message"  ],
           [ 'version|v' => "display a version"              ];
   }

=item B<validate_opts>

This method, if exists, will be called from the
L<validate_args|"validate_args"> method. Its purpose is to do
something with the options belonging to (predefined by) this class.

It gets four parameters, C<$app> (the class name of your application),
C<$caller> (who is calling), C<$opts> (an object allowing to access
all options) and C<$args> (an arrayref with the remaining arguments
from the command-line).

If it finds an error, it usually dies by calling
$caller->C<usage_error>.

=back

=head1 AUTHOR

Martin Senger <martin.senger@gmail.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2013 by Martin Senger, CBRC - KAUST (Computational Biology Research Center - King Abdullah University of Science and Technology) All Rights Reserved.

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

=cut


__END__

I<raison d'E<234>tre>



( run in 1.790 second using v1.01-cache-2.11-cpan-140bd7fdf52 )