NAME
Acme::Beamerang::ArgParser - A partial commandlet oriented args parser
SYNOPSIS
use Acme::Beamerang::ArgParser;
use My::Sub::Dispatcher::Thing;
my $state = {};
my $opts = {
output => sub { defined $_[0] ? $state->{output} = $_[0] : die "--output expects a parameter" },
quiet => sub { defined $_[0] ? $state->{quiet} = $_[0] : $state->{quiet}++ },
};
$opts->{o} = $opts->{output};
$opts->{q} = $opts->{quiet};
# Note: Only defines which pre-command "args" are "ours" for filtering.
# and ropes the rest off for children to parse themselves.
my $cfg = Acme::Beamerang::ArgParser->new( filter => [keys %opts] )->parse( @ARGV );
for my $argument ( @{$cfg->args} ) {
my ( $name, $value ) = @{ $argument };
$opts->{$name}->( $value );
}
my $worker = My::Sub::Dispatcher::Thing->new( %$state );
$worker->exec( @{ $cfg->extra_args } );
EXPERIMENTAL
This code is reasonably simple, but its really an experimental prototype for a bunch of things I'm working on, and is subject to change.
Any finalized results of said project may use other parts, or be implemented differently.
This module is subsequently prone to radical changes until a way forward ( and even a name ) is decided upon.
DESCRIPTION
Acme::Beamerang::ArgParser
is an experimental argument parsing utility to simplify some of the essential flow control needed when building a multi-level -cmd
style argument parser.
The general priniciple is to not actually implement anything specific to argument validation and dispatch, and to leave that up to the user, while focusing on determining the associations of arguments and the ownership of arguments, in order to allow composed command lines where the root node is blind to what their child nodes expect.
ANATOMY OF MULTI LEVEL COMMANDS
app [APP AND APP FACET ARGS] command [COMMAND ARGS]
app
Reads its arguments up to the first non-argument, command
.
Elements of [APP AND APP FACET ARGS]
that it recognizes due to the filter rule it takes posession of, and hides them from any composed in facets.
Any elements of [APP AND APP FACET ARGS]
that app
does not recognize it assumes will be handled by child facets, or will otherwise be handled to be "excess" by the consuming code.
command
and COMMAND ARGS
are captured verbatim and not processed in any way.
APP AND FACET ARGS
All app
level and facet
level arguments need to be in one of the following forms in order to work as intended.
-u
--unary
-o=argument
--option=argument
Split style parameters are unsupported:
# NOT POSSIBLE as it would think the command to be "argument"
app --option argument command --commandarg
This is required to be able to parse out facet arguments without knowing what that facet expects those arguments to be.
For example, imagine an application as follows:
app --verbose --dispatcher legacy --size 10 command
Where size
and verbose
are arguments that only the dispatcher called legacy
understood, and that dispatcher is loaded lazily.
As far as app
is concerned, it only knows about the dispatcher
argument.
Without the tight-binding of =
, the logic would have to assume strange things, eventually culminating in --verbose
having an argument of --dispatcher
and the command being legacy
(LOL!), or --size
not supporting arguments, and the command being 10
(LOL!)
And this design choice is considered an important axiom of this approach.
METHODS
new
my $instance = Acme::Beamerang::ArgParser->new( filter => ['h','help','v','version' ]);
Creates a new instance of an ArgParser
new.filter
An ArrayRef
of arguments to filter and deem as "ours"
Single character arguments map to single-dashed extractors
Multi-Character arguments map to double-dashed extractors.
parse
my $cfg = $instance->parse( @ARGV );
Processes input @ARGV
and filters its content into categories based on filter
, halting processing at the first token that doesn't start with a -
.
Returns a ::Cfg
instance describing the arguments.
Cfg.args
Returns the arguments deemed "ours" as an ArrayRef
of decoded args.
for my $arg (@{ $cfg->args }) {
my ( $argname, $argvalue ) = @{ $arg };
do_help and last if $argname eq 'h' or $argname eq 'help';
if ( $argname eq 'output' or $argname eq 'o' ) {
die "output/o takes an argument" unless defined $argvalue and length $argvalue;
set_output( $argvalue ) and next;
}
}
Each arg is an ArrayRef
as follows:
[ 'arg' ] # --arg
[ 'arg', '' ] # --arg=
[ 'arg', 'value' ] # --arg=value
Where arg
can only be values passed in via filter
Cfg.command
my $command = $cfg->command
Contains the value of the first non-parameter argument, or undef
if none was visited during parse.
Cfg.orig_args
Contains an ArrayRef
of the original unmodified arguments that were passed in.
my (@orig_args) = @{ ::ArgParser->new( filter => [ qw/ hello / ])->parse( @ARGV )->orig_args }
This is mostly for debugging purposes.
Cfg.unknown_args
Contains an ArrayRef
of all hyphen prefixed arguments that were seen prior to the command
that were not defined in the filter list.
my (@unknown_args) = @{ $cfg->unknown_args };
The presence of any items in this list can be used to cause validation errors, or they can be assumed to be transparent arguments to pass down to the next level.
Cfg.unparsed_args
Contains an ArrayRef
of all tokens that succeeded the command
( excluding the command itself ).
my (@unparsed_args) = @{ $cfg->unparsed_args };
These are to be considered "Property of the command
"
Cfg.extra_args
Returns an ArrayRef
equivalent to the input arguments to parse
, minus the contents of .args
.
my (@extra_args) = @{ $cfg->extra_args };
ENVIRONMENT
This module consumes Acme::Beamerang::Logger
and subsequently will respond to all respective flags it supports.
BEAMERANG_ARGPARSER_$LEVEL
BEAMERANG_$LEVEL
BEAMERANG_ARGPARSER_UPTO
BEAMERANG_UPTO
AUTHOR
Kent Fredric <kentnl@cpan.org>
LICENSE
This software is copyright (c) 2016 by Kent Fredric.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.