NAME
CXC::Form::Tiny::Plugin::OptArgs2 - A Plugin to interface Form::Tiny with OptArgs2
VERSION
version 0.12
SYNOPSIS
package My::Form {
use Form::Tiny plugins => ['+CXC::Form::Tiny::Plugin::OptArgs2'];
use Types::Standard qw( ArrayRef HashRef Str );
use Types::Common::String qw( NonEmptyStr );
use Types::Path::Tiny qw( Path );
# configure plugin; these are the default values
optargs_opts( inherit_required => !!1, inherit_optargs => !!0 );
form_field 'file' => ( type => NonEmptyStr, default => sub { 'file.ext' } );
# the 'option' keyword immediately follows the field definition
option(
isa => 'Str', # optional, can usually guess from the field
comment => 'Query in a file',
isa_name => 'ADQL in a file',
);
# arguments can appear in any order; use the 'order' attribute to
# specify their order on the command line
form_field 'arg2' => ( type => ArrayRef, );
argument(
isa => 'ArrayRef', # optional, can guess from the field
comment => 'every thing else',
greedy => 1,
order => 2, # this is the second argument on the command line
);
form_field 'arg1' => ( type => Path, coerce => 1 );
argument(
isa => 'Str', # not optional, can't guess from the field
comment => 'first argument',
order => 1, # this is the second argument on the command line
);
}
use OptArgs2;
use Data::Dumper;
# create form
my $form = My::Form->new;
# parse command line arguments and validate them with the form
@ARGV = qw( --file x.y.z val1 val2 val3 );
$form->set_input_from_optargs(
optargs( comment => 'this program is cool', optargs => $form->optargs ) );
die Dumper( $form->errors_hash ) unless $form->valid;
say $form->fields->{file}; # x.y.z
say $form->fields->{arg1}; # val1
say $form->fields->{arg2}[0]; # val2
say $form->fields->{arg2}[1]; # val3
results in
x.y.z
val1
val2
val3
DESCRIPTION
CXC::Form::Tiny::Plugin::OptArgs2
is a Form::Tiny plugin which make it easier to use Form::Tiny to validate command line options and arguments parsed by OptArgs2.
It provides new keywords to be used alongside Form::Tiny's form_field
keyword to specify OptArgs2 options and arguments and link them to Form::Tiny fields.
It adds a method to the form's class which returns the OptArgs2 compatible optargs
specification and another which sets the form's input from the output of OptArgs2's optargs
and class_optargs
functions.
USAGE
The typical steps for handling command lines with this plugin are:
Create Form::Tiny forms.
Optionally configure the plugin with "optargs_opts"
Use an "option" or "argument" keyword after each form field to define the OptArgs2 specification (but not after fields which contain nested forms).
Extract OptArgs2 specification
Parse the command line with OptArgs2
Validate the parsed values with with the Form::Tiny forms.
Interfacing with OptArgs2
First, the OptArgs2 compatible optargs
specifications are extracted from the form object via the optargs
method:
my $form = My::Form->new;
my \@optargs = $form->optargs;
These are then passed on to OptArgs2 via either the optargs
, cmd
functions, or subcmd
functions, e.g.
my $opts = optargs( comment => 'a comment', optargs => \@optargs );
Finally, the parsed results are passed back to the form for validation:
$form->set_input_from_optargs( $opts );
die unless $form->valid;
Nested Forms
Consider this structure:
package My::SubForm {
...
form_field 'upload' => ( type => HashRef[Str] );
option ( isa => 'HashRef', ... )
}
package My::Form {
...
form_field 'stuff' => ( type => My::SubForm->new );
form_field 'nonsense => ( type => My::SubForm->new );
}
The stuff
and nonsense
form fields refer to nested forms; they are not added as OptArgs2 options.
Note that the option
element My::SubForm
does not specify a name (it can; see below). If not specified it will be generated based upon its parent field, so in this case the following options will be generated for OptArgs2:
--stuff_upload
--stuff_nonsense
The path separator defaults to _
, but may be changed via the "optargs_opts" configuration keyword.
When the validated form data is retrieved via $form-
fields>, it will look like:
{ stuff => { upload => { ... } },
nonsense => { upload => { ... } }
}
If the option
element did specify a name, then the automatic creation of the hierarchical names would not be done, and the repeated use of the subform would result in duplicate option names, and an exception would (hopefully) be thrown.
Configuration Files
The typical steps for handling command lines with this plugin are:
For required options, the default behavior is for the OptArgs2 specifications to inherit the required
attribute from their associated form field, and thus the initial completeness check for required options is performed by OptArgs2.
However, when a configuration file is used to provide values which the command line will augment or override, this no longer works. OptArgs2 is passed only what is on the command line, and typically the configuration file is specified by a command line option, so the integration of the values in the configuration file has to be performed after OptArgs2 parses the command line, and thus the completeness check has to happen after that.
Now the sequence looks like this:
- 3.
-
Parse command line with OptArgs2. Do *not* check for completeness
- 3.1
-
Read configuration file and merge the values with those obtained from OptArgs2
- 4.
-
Validate with Forms, checking for completeness.
In this scenario, the "inherit_required" plugin flag must be false, otherwise OptArgs2 will make that decision on possibly incomplete data.
[For a complete solution to this issue, check out App::Easer]
Multi-level Commands
OptArgs2 requires separate specifications for each level of command. On the command line, sub-commands can accept options for all of their higher level commands. For example, if command cmd accepts --version
, so will its sub command subcmd. When OptArgs2 parses the command line it returns the combined options as a single structure, regardless of where the option is specified, e.g.
cmd --version subcmd ...
cmd subcmd --version
results in the same structure.
Thus, there are more forms required to create the option specifications for OptArgs2 than are required to validate the results it retrieves from the command line.
The most straightforward way to handle this is to create a series of layered forms, one per command layer, with the form at one layer inheriting from the form for the next higher layer.
For example, given a command structure of cmd, subcmd, subsubcmd, the form structure would look like
package Form::Cmd { ... }
package Form::SubCmd { ...; extends 'Form::Cmd'; }
package Form::SubSubCmd { ...; extends 'Form::SubCmd'; }
OptArgs2 specifications extracted from each form should only provide options for the corresponding command; this requires that the "inherit_optargs" flag be false so that only the specifications for the options in that form be extracted and passed to OptArgs2.
Options and arguments returned by OptArgs2 are collected from a command and its parents, so are validated by the form at that command's level, which will inherit the fields from its parent forms, similar to how OptArgs2 operates.
"inherit_optargs" defaults to false so this behavior is the default.
"optargs_opts" v.s. a Plugin.
As an alternative to specifying "optargs_opts" at the top of the form definition, a Form::Tiny plugin can be used:
package My::Form::Plugin;
use parent 'Form::Tiny::Plugin';
sub plugin ( $self, $caller, $context ) {
return {
meta_roles => [ 'My::Form::Plugin::Meta', ],
};
}
package My::Form::Plugin::Meta {
use Moo::Role;
around inherit_optargs => sub { return !!0 };
}
package My::Form;
use Form::Tiny plugins => ['+CXC::Form::Tiny::Plugin::OptArgs2','+My::Form::Plugin'];
...
Because this plugin wraps the attribute accessor, a form using this plugin cannot override the value via the "optargs_opts" keyword.
To change an attribute's default value, wrap its builder, rather than its accessor, e.g.
package My::Form::Plugin::Meta {
use Moo::Role;
around _build_inherit_required => sub { return !!0 };
}
This allows a form to use the "optargs_opts" keyword and override the plugin's "inherit_required" default.
KEYWORDS
Plugin configuration
Plugin behavior for a specific form is performed via the "optargs_opts" keyword, which must appear before any "option" or "argument" keywords.
optargs_opts
This takes a list of named attributes, which may include the following:
- inherit_required => Optional [Bool],
-
This flag determines whether
options
inherit therequired
flag from their associated form field. It defaults totrue
. See "Configuration Files" for a discussion of where this may not be useful. - inherit_optargs => Optional [Bool],
-
This flag determines whether a child form inherits the options and arguments of its parent. (This differs from nested forms, which appear as the values of form fields). If a inheritance hierarchy is used to model command / sub-command configuration options, then it is best to not inherit the options, as OptArgs2 will automatically create that hierarchy, and having this plugin do so will cause options to appear duplicated. See "Multilevel Commands".
- inherit_optargs_match => Optional [ArrayRef],
-
This parameter provides some flexibility in specifying which parent forms to include or exclude when inheriting options and arguments.
It is takes an array of regular expressions and include/exclude flags (
-
,+
) which are matched against the classes in the form's@ISA
array. The first regular expression which matches determines whether the parent class is included or excluded.For example, if the match list is
[ qr/Form0/, '-', qr/Form1/ ]
each class is first compared to
qr/Form0/
; if that succeeds the process stops and the class is included (the+
is implicit). Otherwise the process continues withqr/Form1/
; if that matches it is excluded (because of the immediately preceding-
flag).What happens if a class doesn't match anything in the list? If the list is composed only of included regexps, then it will be excluded. If at least one excluded regexp is in the list, it will be included.
Match lists can be nested, and each match list can be preceded by an include/exclude flag to specify the default behavior for matches. For example,
[ qr/Form1/, '-', [ qr/Form2/, qr/Form3/ ] ]
results in one included regexp and two excluded ones.
Specifying options and arguments.
Option and argument specifications use the option
and argument
DSL keywords. They must immediately follow the definition of their associated Form::Tiny fields (see "SYNOPSIS").
option
The option
keyword takes a list of named attributes with the following accepted names and value types (refer to OptArgs2 for their meaning):
name => Optional [NonEmptySimpleStr],
alias => Optional [NonEmptySimpleStr],
comment => NonEmptySimpleStr,
default => Optional [Value|CodeRef],
required => Optional [Bool],
hidden => Optional [Bool],
isa => Optional [Enum[ qw( ArrayRef Flag Bool Counter HashRef Int Num Str ) ] ],
isa_name => Optional [NonEmptySimpleStr],
show_default => Optional [Bool],
trigger => Optional [CodeRef],
Unlike in OptArgs2, a leading prefix of --
in the value of the isa
attribute is optional.
argument
The argument
keyword takes a list of named attributes with the following accepted names and value types (refer to OptArgs2 for their meaning, except for order
):
name => Optional [NonEmptySimpleStr],
comment => NonEmptySimpleStr,
default => Optional [Value|CodeRef],
greedy => Optional [Bool],
fallthru => Optional [Bool],
isa => Optional [Enum [qw( ArrayRef HashRef Int Num Str SubCmd)]],
isa_name => Optional [NonEmptySimpleStr],
required => Optional [Bool],
show_default => Optional [Bool],
order => Int,
The order
attribute specifies the relative order the argument should appear on the command line. This allows the form fields to be specified in an arbitrary order.
Automatic determination of OptArgs2 attributes
name
name
will be taken from the immediately preceding form_field
specification if not provided. The form field and option names need not be the same.
isa
The isa
option can be omitted in the list of option and argument attributes if the OptArgs2 type can be deduced from the Form::Tiny type. The types which require explicit specification are the OptArgs2 Flag
, Counter
, and SubCmd
types.
Here are the existing mappings from Type::Tiny to OptArgs2 types:
ArrayRef Flag Bool Counter HashRef Int Num Str
=> eponymous OptArgs2 type
BoolLike => Bool
Any Path File Dir => Str
required
(This behavior is regulated the "inherit_required" attribute passed to the "optargs_opts" keyword.)
OptArgs2 has a simpler concept of a required element than does Form::Tiny. If the field's required attribute is false, the option or argument's value is set to false, otherwise it is set to true.
SUPPORT
Bugs
Please report any bugs or feature requests to bug-cxc-form-tiny-plugin-optargs2@rt.cpan.org or through the web interface at: https://rt.cpan.org/Public/Dist/Display.html?Name=CXC-Form-Tiny-Plugin-OptArgs2
Source
Source is available at
https://gitlab.com/djerius/cxc-form-tiny-plugin-optargs2
and may be cloned from
https://gitlab.com/djerius/cxc-form-tiny-plugin-optargs2.git
AUTHOR
Diab Jerius <djerius@cpan.org>
COPYRIGHT AND LICENSE
This software is Copyright (c) 2023 by Smithsonian Astrophysical Observatory.
This is free software, licensed under:
The GNU General Public License, Version 3, June 2007