App-Easer
view release on metacpan or search on metacpan
docs/docs/30-commands-hierarchy.md view on Meta::CPAN
- the definition for sub-command `MyApp::Foo` can be found in module
`MyApp::Foo`, calling its function `spec` that is supposed to return a
hash reference with the specification of the command. The name `spec`
is a default, conventional value;
- the definition for sub-command `MyApp::Bar#specification` can be found
in module `MyApp::Bar`, calling its function `specification`. In this
case we have an explicit indication of which function should be called
to get the hash reference with the sub-command specification.
When `specfetch` is set to `+SpecFromHashOrModule`, the child name is
expanded as described above only if the specific string is missing as a
key inside the `commands` sub hash. In other terms, in the following
example no external module will be loaded to figure out the
specification for child `Some::Foo#whatever`, because the specification
is already available in `commands`:
```perl
my $app = {
configuration => { specfetch => '+SpecFromHashOrModule' },
commands => {
MAIN => {
docs/docs/40-command-options.md view on Meta::CPAN
When the `validate` key is set in a command's specification, it can
either be an *executable* like described above, or a hash reference. In
this latter case, [Params::Validate][] is used to validate the
configuration against that hash reference. This particular approach can
be considered stable and not subject to changes in the future.
## Configuration file from another option
It's possible to expand the options values gathering with loading them
from a JSON file, whose path is provided among the available options.
This allows e.g. to add a command-line option `-c|--config` to point to
a file with further values.
The indication to also look for a configuration file is usually best
placed at the end of the list:
```perl
my $app = {
configuration => {
lib/App/Easer/V1.pm view on Meta::CPAN
next
unless grep { $_ eq $name }
($command->{supports} //= [$child])->@*;
return $child;
} ## end for my $child (get_children...)
return;
} ## end sub get_child
sub stock_ChildrenByPrefix ($self, $spec, @prefixes) {
require File::Spec;
my @expanded_inc = map {
my ($v, $dirs) = File::Spec->splitpath($_, 'no-file');
[$v, File::Spec->splitdir($dirs)];
} @INC;
my %seen;
return map {
my @parts = split m{::}mxs, $_ . 'x';
substr(my $bprefix = pop @parts, -1, 1, '');
map {
my ($v, @dirs) = $_->@*;
my $dirs = File::Spec->catdir(@dirs, @parts);
lib/App/Easer/V1.pm view on Meta::CPAN
substr(my $lastpart = $_, -3, 3, '');
join '::', @parts, $lastpart;
} grep {
my $path = File::Spec->catpath($v, $dirs, $_);
(-e $path && ! -d $path)
&& substr($_, 0, length($bprefix)) eq $bprefix
&& substr($_, -3, 3) eq '.pm'
} readdir $dh;
}
else { () }
} @expanded_inc;
} @prefixes;
}
sub expand_children ($self, $spec, $child_spec) {
return $child_spec unless ref($child_spec) eq 'ARRAY';
my ($exe, @args) = $child_spec->@*;
return $self->{factory}->($exe, 'children')->($self, $spec, @args);
}
sub get_children ($self, $spec, $expand = 1) {
return if $spec->{leaf};
return if exists($spec->{children}) && !$spec->{children};
my @children = ($spec->{children} // [])->@*;
# set auto-leaves as 1 by default, new in 0.007002
$self->{application}{configuration}{'auto-leaves'} = 1
unless exists $self->{application}{configuration}{'auto-leaves'};
return
if $self->{application}{configuration}{'auto-leaves'}
&& @children == 0; # no auto-children for leaves under auto-leaves
# skip expansion if $expand is false (default is expand)
@children = map { expand_children($self, $spec, $_) } @children
if $expand;
my @auto =
exists $self->{application}{configuration}{'auto-children'}
? (($self->{application}{configuration}{'auto-children'} // [])->@*)
: (qw< help commands >);
if (exists $spec->{'no-auto'}) {
if (ref $spec->{'no-auto'}) {
my %no = map { $_ => 1 } $spec->{'no-auto'}->@*;
@auto = grep { !$no{$_} } @auto;
}
lib/App/Easer/V1.pod view on Meta::CPAN
prefixes:
- '::' : 'What::Ever::'
- ':' : 'My::App::'
Otherwise, the I<unordered> nature of Perl hashes would risk that the
expansion associated to C<:> is tried first, spoiling the result and
making it unpredictable.
By default, the C<+> character prefix is associated to a mapping into
functions in C<App::Easer::V1> starting with C<stock_>. As an example, the
string C<+CmdLine> is expanded into C<App::Easer::V1::stock_CmdLine>, which
happens to be an existing function (used in parsing command-line
options). It is possible to suppress this expansion by setting a mapping
from C<+> to C<+> in the C<prefixes>, although this will deviate from
the normal working of C<App::Easer::V1>.
=head2 Configuration Parsing Customization
configuration:
name: «string»
collect: «executable»
lib/App/Easer/V1.pod view on Meta::CPAN
=head2 commandline_help
=head2 commit_configuration
=head2 default_getopt_config
=head2 env_namer
=head2 execute
=head2 expand_children
=head2 stock_factory
# just the sub
$subref = factory(sub {});
# eval it - note the initial space in the string
$subref = factory(' sub {}');
# Expand the '+', by default with 'App::Easer::V1#stock_'
lib/App/Easer/V1.pod view on Meta::CPAN
is the default name of a sub to look for in the selected package;
=item * C<$opts>
is a hash reference with additional options, e.g. a C<prefixes> sub-hash or
sub-array mapping string prefixes to expaned ones (see below).
=back
When C<$executable> is a string, it is first I<expanded> according to the
available prefixes in C<< $opts->{prefixes} >>. This contains mapping from
prefixes to expanded versions of those prefixes; it can be either a hash
reference with the mappings, or an array of those hash references (this
allows setting the order to use for doing the expanion, e.g. making sure
that C<:::> is attempted before C<:>, should both be possible).
By default, prefix C<+> is expanded with C<App::Easer::V1::>; e.g. the
input executor C<+CmdLine> becomes C<App::Easer::V1::CmdLine>. It is
possible to change this or disable it (disabling can be achieved by
providing a mapping from C<+> to C<+>, although this will probably make
loading of the package fail in a later stage).
After this expansion of the prefix, if any, the string in C<$executor> is
split into two parts, based on the character C<#>. What comes before is a
I<package> name, what comes after is a I<subroutine> name (defaulting to
C<$default_subname>).
lib/App/Easer/V2.pm view on Meta::CPAN
while (defined(my $parent = $retval->child)) {
$retval = $parent;
}
$slot->{leaf} = $retval;
weaken($slot->{leaf});
}
return $slot->{leaf};
}
# 2024-08-27 expand to allow hashref in addition to arrayref
# backwards-compatibility contract is that overriding this function allows
# returning the list of sources to use, which might be composed of a single
# hashref...
sub sources ($self, @new) {
my $r;
my $slot = $self->slot;
if (@new) { # setter + getter
$r = $slot->{sources} = $new[0];
}
else { # getter only, set default if *nothing* has been set yet
lib/App/Easer/V2.pm view on Meta::CPAN
} ## end sub find_child ($self)
# get the list of children. This only gives back a list of "hints" that
# can be turned into instances via inflate_children. In this case, it's
# module names
sub list_children ($self) {
my @children = $self->children;
# handle auto-loading of children from modules in @INC via prefixes
require File::Spec;
my @expanded_inc = map {
my ($v, $dirs) = File::Spec->splitpath($_, 'no-file');
[$v, File::Spec->splitdir($dirs)];
} @INC;
my %seen;
my @autoloaded_children = map {
my @parts = split m{::}mxs, $_ . 'x';
substr(my $bprefix = pop @parts, -1, 1, '');
map {
my ($v, @dirs) = $_->@*;
my $dirs = File::Spec->catdir(@dirs, @parts);
lib/App/Easer/V2.pm view on Meta::CPAN
substr(my $lastpart = $_, -3, 3, '');
join '::', @parts, $lastpart;
} grep {
my $path = File::Spec->catpath($v, $dirs, $_);
(-e $path && !-d $path)
&& substr($_, 0, length($bprefix)) eq $bprefix
&& substr($_, -3, 3) eq '.pm'
} sort { $a cmp $b } readdir $dh;
} ## end if (opendir my $dh, File::Spec...)
else { () }
} @expanded_inc;
} $self->children_prefixes;
push @autoloaded_children, map {
my $prefix = $_;
my $prefix_length = length($prefix);
grep { !$seen{$_}++ }
grep {
(substr($_, 0, length $prefix) eq $prefix)
&& (index($_, ':', $prefix_length) < 0);
} keys %App::Easer::V2::registered;
} $self->children_prefixes;
lib/App/Easer/V2.pod view on Meta::CPAN
=head2 Option Values Collection (C<sources>)
The collection is performed thanks to I<sources>, which can be set with
the corresponding C<sources> key or method (depending on the style).
C<App::Easer> comes with several sources for getting configurations from
a variety of places, but still leaves the door open to add more
customized ones.
Up to version C<2.007001> included, the C<sources> key could only point
to an I<array reference> holding a list of sources. After that release,
it has been expanded to I<also> accept a I<hash reference>, allowing for
a new and more sophisticated handling of option values collection. In
either case, anyway, processing arrives at a point where there is a
I<list of sources> to be processed as described below; using the I<array
ref> keeps backwards compatibility with the old behaviour. See
L</Sources as hash reference> for additional details on the new way.
Sources are I<considered> with respect to two different ordering
methods: their place in the array pointed by key C<sources> and their
I<priority>. The former sets the order in which data from each source is
collected, the latter sets the precedence while assembling conflicting
( run in 2.050 seconds using v1.01-cache-2.11-cpan-97f6503c9c8 )