App-Easer
view release on metacpan or search on metacpan
lib/App/Easer/V1.pm view on Meta::CPAN
for my $cfg ($spec, $self->{application}{configuration}) {
next unless exists $cfg->{dispatch};
my $sub = $self->{factory}->($cfg->{dispatch}, 'dispatch');
defined(my $child = $sub->($self, $spec, $args)) or return;
return ($child, $child);
}
# regular course here, no point in going forth without children
return unless has_children($self, $spec);
# use defaults if there's no argument to investigate
return fetch_subcommand_default($self, $spec) unless $args->@*;
# try to get a child from the first argument
if (my $child = get_child($self, $spec, $args->[0])) {
return ($child, shift $args->@*); # consumed arg name
}
# the first argument didn't help, but we might want to fallback
for my $cfg ($spec, $self->{application}{configuration}) {
if (exists $cfg->{fallback}) { # executable
defined(my $fb = $cfg->{fallback}) or return;
my $sub = $self->{factory}->($fb, 'fallback'); # "resolve"
defined(my $child = $sub->($self, $spec, $args)) or return;
return ($child, $child);
}
if (exists $spec->{'fallback-to'}) {
defined(my $fbto = $spec->{'fallback-to'}) or return;
return ($fbto, $fbto);
}
return fetch_subcommand_default($self, $spec)
if $cfg->{'fallback-to-default'};
}
# no fallback at this point... it's an error, build a message and die!
my @names = map { $_->[1] } $self->{trail}->@*;
shift @names; # remove first one
my $path = join '/', @names, $args->[0]; # $args->[0] was the candidate
die "cannot find sub-command '$path'\n";
} ## end sub fetch_subcommand_wh
sub generate_factory ($c) {
my $w = \&stock_factory; # default factory
$w = stock_factory($c->{create}, 'factory', $c) if defined $c->{create};
return sub ($e, $d = '') { $w->($e, $d, $c) };
}
sub get_child ($self, $spec, $name) {
for my $child (get_children($self, $spec)) {
my $command = fetch_spec_for($self, $child);
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);
if (opendir my $dh, File::Spec->catpath($v, $dirs, '')) {
grep { ! $seen{$_}++ }
map {
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;
}
elsif ($spec->{'no-auto'} eq '*') {
@auto = ();
}
else {
die "invalid no-auto, array or '*' are allowed\n";
}
} ## end if (exists $spec->{'no-auto'...})
return (@children, @auto);
} ## end sub get_children
# traverse a whole @$list of sub-commands from $start. This is used to
# list "commands" at a certain sub-level or show help
sub get_descendant ($self, $start, $list) {
my $target = $start;
my $path;
for my $desc ($list->@*) {
$path = defined($path) ? "$path/$desc" : $desc;
my $command = fetch_spec_for($self, $target)
or die "cannot find sub-command '$path'\n";
defined($target = get_child($self, $command, $desc))
or die "cannot find sub-command '$path'\n";
} ## end for my $desc ($list->@*)
# check that this last is associated to a real command
return $target if fetch_spec_for($self, $target);
die "cannot find sub-command '$path'\n";
} ## end sub get_descendant
sub has_children ($self, $spec) { get_children($self, $spec, 0) ? 1 : 0 }
sub hash_merge {
my (%retval, %is_overridable);
for my $href (@_) {
for my $src_key (keys $href->%*) {
my $dst_key = $src_key;
my $this_overridable;
if ($dst_key =~ m{\A //= (.*) \z}mxs) { # overridable
$dst_key = $1;
$is_overridable{$dst_key} = 1 unless exists $retval{$dst_key};
$this_overridable = 1;
}
$retval{$dst_key} = $href->{$src_key}
if $is_overridable{$dst_key} || ! exists($retval{$dst_key});
$is_overridable{$dst_key} = 0 unless $this_overridable;
}
}
return \%retval;
# was a simple: return {map { $_->%* } reverse @_};
}
( run in 0.828 second using v1.01-cache-2.11-cpan-97f6503c9c8 )