view release on metacpan or search on metacpan
docs/docs/10-tutorial-base.md view on Meta::CPAN
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
use App::Easer 'run';
my $application = {
factory => {prefixes => {'#' => 'TuDu#'}},
configuration => {
'auto-leaves' => 1,
'help-on-stderr' => 1,
},
commands => {
MAIN => { ... },
dump => { ... },
list => { ... },
show => { ... },
cat => { ... },
add => { ... },
edit => { ... },
done => { ... },
docs/docs/10-tutorial-base.md view on Meta::CPAN
`foobar` inside package `TuDu`.
As a result, we have a shortcut to point towards functions inside the
`TuDu` package for our implementations.
We are setting a couple of high-level configurations:
- `auto-leaves`: every command without explicit children will be treated
as a leaf command, so it will not get a `help` and a `commands`
sub-commands;
- `help-on-stderr`: help messages (from `help` and `commands`) will be
printed on standard error instead of standard output. This makes it
more difficult to pipe them through a pager (like `more` or `less`),
but avoids that the help messages might be accidentally considered
part of the "real" output of the command.
The rest of the `$application` hash reference is initialized with a
skeleton of all the sub-commands that we aim to support. The structure
is pretty flat - all "real" sub-commands are in fact children to the
`MAIN` entry point.
docs/docs/10-tutorial-base.md view on Meta::CPAN
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
use App::Easer 'run';
my $application = {
factory => {prefixes => {'#' => 'TuDu#'}},
configuration => {
'auto-leaves' => 1,
'help-on-stderr' => 1,
},
commands => {
MAIN => {
help => 'to-do application',
description => 'A simple to-do application',
options => [
{
help => 'path to the configuration file',
getopt => 'config|c=s',
environment => 'TUDU_CONFIG',
docs/templates/getting-started.pl view on Meta::CPAN
# the name of the application, set it above in $APPNAME
name => $APPNAME,
# figure out names of environment variables automatically
'auto-environment' => 1,
# sub-commands without children are leaves (no sub help/commands)
# 'auto-leaves' => 1,
# help goes to standard error by default, override to stdout
# 'help-on-stderr' => 0,
# Where to get the specifications for commands
# specfetch => '+SpecFromHash', # default
# specfetch => '+SpecFromHashOrModule', # possible alternative
},
commands => {
MAIN => {
help => 'An application to do X',
description => 'An application to do X, easily',
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
use App::Easer 'run';
my $application = {
factory => {prefixes => {'#' => 'TuDu#'}},
configuration => {
'auto-leaves' => 1,
'help-on-stderr' => 1,
},
commands => {
MAIN => {
help => 'to-do application',
description => 'A simple to-do application',
options => [
{
help => 'path to the configuration file',
getopt => 'config|c=s',
environment => 'TUDU_CONFIG',
lib/App/Easer/V1.pm view on Meta::CPAN
sub params_validate ($self, $spec, $args) {
my $validator = $spec->{validate}
// $self->{application}{configuration}{validate} // return;
require Params::Validate;
Params::Validate::validate($self->{configs}[-1]->%*, $validator);
} ## end sub params_validate
sub print_commands ($self, $target) {
my $command = fetch_spec_for($self, $target);
my $fh =
$self->{application}{configuration}{'help-on-stderr'}
? \*STDERR
: \*STDOUT;
if (my @children = get_children($self, $command)) {
print {$fh} list_commands($self, \@children);
}
else {
print {$fh} "no sub-commands\n";
}
}
sub print_help ($self, $target) {
my $command = fetch_spec_for($self, $target);
my $enamr = env_namer($self, $command);
my $fh =
$self->{application}{configuration}{'help-on-stderr'}
? \*STDERR
: \*STDOUT;
print {$fh} $command->{help}, "\n\n";
if (defined(my $description = $command->{description})) {
$description =~ s{\A\s+|\s+\z}{}gmxs; # trim
$description =~ s{^}{ }gmxs; # add some indentation
print {$fh} "Description:\n$description\n\n";
}
lib/App/Easer/V1.pod view on Meta::CPAN
factory:
create: «executable»
prefixes: «hash or array of hashes»
configuration:
collect: «executable»
merge: «executable»
specfetch: «executable»
validate: «executable»
sources: «array»
'auto-children': «false or array»
'help-on-stderr': «boolean»
'auto-leaves': «boolean»
'auto-environment': «boolean»
commands:
cmd-1:
«command definition»
cmd-2:
«command definition»
MAIN:
«command definition»
lib/App/Easer/V1.pod view on Meta::CPAN
configuration:
name: «string»
collect: «executable»
merge: «executable»
namenv: «executable»
specfetch: «executable»
validate: «executable»
sources: «array»
'auto-children': «false or array»
'help-on-stderr': «boolean»
'auto-leaves': «boolean»
'auto-environment': «boolean»
The C<name> configuration allows setting a name for the application,
which can e.g. be used to generate automatic names for environment
variables to be associated to command options.
One of the central services provided by C<App::Easer::V1> is the automatic
gathering of options values from several sources (command line,
environment, commands upper in the hierarchy, defaults). Another service
lib/App/Easer/V1.pod view on Meta::CPAN
or less). If this is not the desired behaviour, it is possible to either
disable the addition of the C<auto-children> completely (by setting a
false value), or provide an array of children names that will be added
automatically to each command (again, more or less).
It should be noted that both C<validate> and C<sources> are also part of
the specific setup for each command. As such, they will be rarely set at
the higher C<configuration> level and the whole C<configuration> section
can normally be left out of an application's definition.
Option C<help-on-stderr> allows printing the two stock helper
commands C<help> and C<commands> on standard error instead of standard
output (which is the default).
Option C<auto-leaves> allows setting any command that has no I<explicit>
sub-command as a leaf, which prevents it from getting a C<help> and a
C<commands> sub-command (or whatever has been put to override them). As
of version 0.007002 this is set to a I<true> value by default, but can
still be set to a I<false> value if the automatic sub-commands above are
deemed necessary for commands that have no explicit children in the
hierarchy.
lib/App/Easer/V2.pm view on Meta::CPAN
open $fh, '>', $channel or die "open(): $!\n";
}
elsif ($refch) {
die 'invalid channel';
}
else {
($channel, my $binmode) = split m{:}mxs, $channel, 2;
if ($channel eq '-' || lc($channel) eq '-stdout') {
$fh = \*STDOUT;
}
elsif (lc($channel) eq '-stderr') {
$fh = \*STDERR;
}
else {
open $fh, '>', $channel or die "open('$channel'): $!\n";
}
binmode $fh, $binmode if length($binmode // '');
}
return sub ($cmd, @stuff) {
print {$fh} @stuff;
lib/App/Easer/V2.pod view on Meta::CPAN
a reference to a scalar, where the output will be placed
=item *
a string of the form C<filename[:binmode]>, where C<filename> can B<NOT>
contain the character C<:>. The file will be opened and if the
C<binmode> part is provided, C<binmode()> will be called on the
resulting filehandle with the provided value.
If the C<filename> part is C<-> or C<-stdout> (case insensitive), then
C<STDOUT> will be used. If C<filename> is C<-stderr> (case insensitive),
then C<STDERR> will be used.
=back
=item C<name>
String.
Name of the command. If absent, the first item in the C<alias> array is
used.
t/V1/02-single-with-lib.t view on Meta::CPAN
return 42;
},
'default-child' => '',
}
},
};
subtest 'no input, just defaults' => sub {
test_run($app, [], {}, 'MAIN')->no_exceptions->conf_is({bar => 'buzz'})
->args_are([])->result_is(42)->stdout_like(qr{(?mxs:\A galook! \z)})
->stderr_like(qr{(?mxs:\A gaaaah! \z)});
};
subtest 'env + cmdline, both parameters' => sub {
test_run($app, [qw< --bar BAAZ >], {GALOOK_FOO => 1}, 'MAIN')
->no_exceptions->conf_is({foo => 1, bar => 'BAAZ'})->args_are([])
->result_is(42)->stdout_like(qr{(?mxs:\A galook! \z)})
->stderr_like(qr{(?mxs:\A gaaaah! \z)});
};
test_run($app, ['help'], {}, 'MAIN')->no_exceptions->stdout_like(
qr{(?mxs:
example \s+ command .*?
An \s+ example \s+ command .*?
--foo .*
GALOOK_BAR .*?
help .*?
commands .*?
t/V1/03-multilevel.t view on Meta::CPAN
sub-sub-command \s+ baz .*?
second-level \s+ sub-command \s+ baz .*?
--last .*
)}, 'output of help command'
)->stdout_like(qr{sub-sub-command baz});
};
subtest 'foo baz' => sub {
test_run($app, ['foo', 'baz'], {}, 'baz')
->no_exceptions->result_is('BAZ')->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
subtest 'foo baz' => sub {
test_run($app, [qw< --foo foo --hey you baz --last 12 FP >], {}, 'baz')
->no_exceptions->result_is('BAZ')
->conf_is({foo => 1, bar => 'buzz', hey => 'you', last => 12})
->args_are(['FP'])->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
subtest 'foo (leveraging default sub-child)' => sub {
test_run($app, ['foo'], {}, 'baz')->no_exceptions->result_is('BAZ')
->stdout_like(qr{baz on out})->stderr_like(qr{baz on err});
};
subtest 'Foo (uppercase, leveraging default sub-child)' => sub {
test_run($app, ['Foo'], {}, 'baz')->no_exceptions->result_is('BAZ')
->stdout_like(qr{baz on out})->stderr_like(qr{baz on err});
};
subtest 'Foo commands (note uppercase)' => sub {
test_run($app, [qw< Foo commands >], {}, undef)
->no_exceptions->stdout_like(qr{(?mxs:help: .*? commands:)});
};
subtest 'bar' => sub {
test_run($app, ['bar'], {}, 'bar')->no_exceptions->result_is('Bar')
->stdout_like(qr{bar on out})->stderr_like(qr{bar on err});
};
subtest 'help bar' => sub {
test_run($app, ['help', 'bar'], {}, undef)->no_exceptions->stdout_like(
qr{(?mxs:
sub-command \s+ bar .*?
first-level \s+ sub-command \s+ bar .*?
has \s+ no \s+ options
)}, 'output of help command'
);
};
subtest 'bar help (help is ignored)' => sub {
test_run($app, [qw< bar help >], {}, undef)
->no_exceptions->stdout_like(qr{bar on out})
->stderr_like(qr{bar on err});
};
done_testing();
t/V1/04-multilevel-jsons.t view on Meta::CPAN
},
},
},
};
subtest 'foo baz' => sub {
test_run($app, [qw< --foo foo --hey you baz --last 12 FP >], {}, 'baz')
->no_exceptions->result_is('BAZ')
->conf_is({foo => 1, bar => 'buzz', hey => 'you', last => 12})
->args_are(['FP'])->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
subtest 'foo baz, with config file' => sub {
test_run(
$app,
[
qw< --foo --config >,
tpath('example.json'),
qw< foo --hey you baz --last 12 FP >
],
t/V1/04-multilevel-jsons.t view on Meta::CPAN
)->no_exceptions->result_is('BAZ')->conf_is(
{
foo => 1,
bar => 'buzz',
hey => 'you',
last => 12,
what => 'ever',
config => tpath('example.json')
}
)->args_are(['FP'])->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
$app->{commands}{MAIN}{'config-files'} =
[qw< /etcxn/not/existent >, tpath('example.json')];
subtest 'foo baz, with config file' => sub {
test_run($app, [qw< --foo foo --hey you baz --last 12 FP >], {}, 'baz')
->no_exceptions->result_is('BAZ')->conf_is(
{
foo => 1,
bar => 'buzz',
hey => 'you',
last => 12,
what => 'ever',
}
)->args_are(['FP'])->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
done_testing();
t/V1/05-single-leaf.t view on Meta::CPAN
return 42;
},
leaf => 1,
}
},
};
subtest 'no options, just defaults' => sub {
test_run($app, [], {}, 'MAIN')->no_exceptions->conf_is({bar => 'buzz'})
->args_are([])->result_is(42)->stdout_like(qr{(?mxs:\A galook! \z)})
->stderr_like(qr{(?mxs:\A gaaaah! \z)});
};
subtest 'option --help' => sub {
test_run($app, ['--help'], {}, 'MAIN')
->no_exceptions->conf_is({bar => 'buzz', help => 1})
->args_are([])
->result_is(0)
->stdout_like(qr{(?mxs: Description: .*? Options:)});
};
t/V1/15-spec-from-hash-or-mod.t view on Meta::CPAN
sub-sub-command \s+ baz .*?
second-level \s+ sub-command \s+ baz .*?
--last .*
)}, 'output of help command'
)->stdout_like(qr{sub-sub-command baz});
};
subtest 'foo baz 1' => sub {
test_run($app, ['foo', 'baz'], {}, 'baz')
->no_exceptions->result_is('BAZ')->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
subtest 'foo baz 2' => sub {
test_run($app, [qw< --foo foo --hey you baz --last 12 FP >], {}, 'baz')
->no_exceptions->result_is('BAZ')
->conf_is({foo => 1, bar => 'buzz', hey => 'you', last => 12})
->args_are(['FP'])->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
subtest 'foo (leveraging default sub-child)' => sub {
test_run($app, ['foo'], {}, 'baz')->no_exceptions->result_is('BAZ')
->stdout_like(qr{baz on out})->stderr_like(qr{baz on err});
};
subtest 'Foo (uppercase, leveraging default sub-child)' => sub {
test_run($app, ['Foo'], {}, 'baz')->no_exceptions->result_is('BAZ')
->stdout_like(qr{baz on out})->stderr_like(qr{baz on err});
};
subtest 'Foo commands (note uppercase)' => sub {
test_run($app, [qw< Foo commands >], {}, undef)
->no_exceptions->stdout_like(qr{(?mxs:help: .*? commands:)});
};
subtest 'bar' => sub {
test_run($app, ['bar'], {}, 'bar')->no_exceptions->result_is('Bar')
->stdout_like(qr{bar on out})->stderr_like(qr{bar on err});
};
subtest 'help bar' => sub {
test_run($app, ['help', 'bar'], {}, undef)->no_exceptions->stdout_like(
qr{(?mxs:
sub-command \s+ bar .*?
first-level \s+ sub-command \s+ bar .*?
has \s+ no \s+ options
)}, 'output of help command'
);
};
subtest 'bar help (help is ignored)' => sub {
test_run($app, [qw< bar help >], {}, undef)
->no_exceptions->stdout_like(qr{bar on out})
->stderr_like(qr{bar on err});
};
done_testing();
package Foo;
sub spec {
return {
help => 'sub-command foo',
description => 'first-level sub-command foo',
t/V1/30-custom-collect.t view on Meta::CPAN
},
};
subtest 'no input, just defaults, custom collect' => sub {
test_run($app, [], {}, 'MAIN')
->no_exceptions
->conf_is({this => 'that'})
->args_are(['hey'])
->result_is(42)
->stdout_like(qr{(?mxs:\A galook! \z)})
->stderr_like(qr{(?mxs:\A gaaaah! \z)});
};
delete $app->{configuration}{collect};
subtest 'no input, just defaults, standard collect' => sub {
test_run($app, [], {}, 'MAIN')->no_exceptions->conf_is({bar => 'buzz'})
->args_are([])->result_is(42)->stdout_like(qr{(?mxs:\A galook! \z)})
->stderr_like(qr{(?mxs:\A gaaaah! \z)});
};
done_testing();
t/V1/31-custom-sources.t view on Meta::CPAN
return 42;
},
leaf => 1,
}
},
};
subtest 'no input, just defaults' => sub {
test_run($app, [], {}, 'MAIN')->no_exceptions->conf_is({bar => 'buzz'})
->args_are([])->result_is(42)->stdout_like(qr{(?mxs:\A galook! \z)})
->stderr_like(qr{(?mxs:\A gaaaah! \z)});
};
# Avoid defaults
$app->{configuration}{sources} = [qw< +CmdLine +Environment >];
subtest 'defaults no more in effect, env set' => sub {
test_run($app, [], {GALOOK_FOO => 'BU!'}, 'MAIN')
->no_exceptions
->conf_is({foo => 'BU!'})
->args_are([])->result_is(42)->stdout_like(qr{(?mxs:\A galook! \z)})
->stderr_like(qr{(?mxs:\A gaaaah! \z)});
};
subtest 'defaults no more in effect, cmdline set' => sub {
test_run($app, ['--foo'], {}, 'MAIN')
->no_exceptions
->conf_is({foo => 1})
->args_are([])->result_is(42)->stdout_like(qr{(?mxs:\A galook! \z)})
->stderr_like(qr{(?mxs:\A gaaaah! \z)});
};
done_testing();
t/V1/60-children-naming.t view on Meta::CPAN
print {*STDOUT} 'bar on out';
print {*STDERR} 'bar on err';
return 'Bar';
},
},
},
};
subtest 'bar' => sub {
test_run($app, ['bar'], {}, 'bar')->no_exceptions->result_is('Bar')
->stdout_like(qr{bar on out})->stderr_like(qr{bar on err});
};
subtest 'help bar' => sub {
test_run($app, ['help', 'bar'], {}, undef)->no_exceptions->stdout_like(
qr{(?mxs:
sub-command \s+ bar .*?
first-level \s+ sub-command \s+ bar .*?
has \s+ no \s+ options
)}, 'output of help command'
);
};
subtest 'bar help (help is ignored)' => sub {
test_run($app, [qw< bar help >], {}, undef)
->no_exceptions->stdout_like(qr{bar on out})
->stderr_like(qr{bar on err});
};
subtest 'bar as default' => sub {
test_run($app, [], {}, 'bar')->no_exceptions->result_is('Bar')
->stdout_like(qr{bar on out})->stderr_like(qr{bar on err});
};
done_testing();
t/V1/70-nested-children.t view on Meta::CPAN
print {*STDOUT} 'bar on out';
print {*STDERR} 'bar on err';
return 'Bar';
},
},
},
};
subtest 'foo' => sub {
test_run($app, ['foo'], {}, 'foo')->no_exceptions->result_is('Foo')
->stdout_like(qr{foo on out})->stderr_like(qr{foo on err});
};
subtest 'help foo' => sub {
test_run($app, ['help', 'foo'], {}, undef)->no_exceptions->stdout_like(
qr{(?mxs:
sub-command \s+ foo .*?
first-level \s+ sub-command \s+ foo .*?
hey
)}, 'output of help command'
);
};
subtest 'bar' => sub {
test_run($app, ['bar'], {}, 'bar')->no_exceptions->result_is('Bar')
->stdout_like(qr{bar on out})->stderr_like(qr{bar on err});
};
subtest 'help bar' => sub {
test_run($app, ['help', 'bar'], {}, undef)->no_exceptions->stdout_like(
qr{(?mxs:
sub-command \s+ bar .*?
first-level \s+ sub-command \s+ bar .*?
has \s+ no \s+ options
)}, 'output of help command'
);
};
subtest 'bar help (help is ignored)' => sub {
test_run($app, [qw< bar help >], {}, undef)
->no_exceptions->stdout_like(qr{bar on out})
->stderr_like(qr{bar on err});
};
subtest 'bar as default' => sub {
test_run($app, [], {}, 'bar')->no_exceptions->result_is('Bar')
->stdout_like(qr{bar on out})->stderr_like(qr{bar on err});
};
$app->{commands}{MAIN}{'default-child'}{index} = 0;
subtest 'foo as default' => sub {
test_run($app, [], {}, 'foo')->no_exceptions->result_is('Foo')
->stdout_like(qr{foo on out})->stderr_like(qr{foo on err});
};
done_testing();
t/V1/75-childrenbyprefix.t view on Meta::CPAN
},
],
'default-child' => 'Sampler::CmdBar',
children => [ [ '+ChildrenByPrefix', 'Sampler::Cmd' ] ],
},
},
};
subtest 'foo' => sub {
test_run($app, ['foo'], {}, 'foo')->no_exceptions->result_is('Foo')
->stdout_like(qr{foo on out})->stderr_like(qr{foo on err});
};
subtest 'help foo' => sub {
test_run($app, ['help', 'foo'], {}, undef)->no_exceptions->stdout_like(
qr{(?mxs:
sampler \s+ foo .*?
called \s+ as: \s+ foo .*?
)}, 'output of help command'
);
};
subtest 'bar' => sub {
test_run($app, ['bar'], {}, 'bar')->no_exceptions->result_is('Bar')
->stdout_like(qr{bar on out})->stderr_like(qr{bar on err});
};
subtest 'help bar' => sub {
test_run($app, ['help', 'bar'], {}, undef)->no_exceptions->stdout_like(
qr{(?mxs:
sub-command \s+ bar .*?
first-level \s+ sub-command \s+ bar .*?
has \s+ no \s+ options
)}, 'output of help command'
);
};
subtest 'bar help (help is ignored)' => sub {
test_run($app, [qw< bar help >], {}, undef)
->no_exceptions->stdout_like(qr{bar on out})
->stderr_like(qr{bar on err});
};
subtest 'bar as default' => sub {
test_run($app, [], {}, 'bar')->no_exceptions->result_is('Bar')
->stdout_like(qr{bar on out})->stderr_like(qr{bar on err});
};
subtest 'foo baz' => sub {
test_run($app, ['foo', 'baz'], {}, 'baz')->no_exceptions->result_is('Baz')
->stdout_like(qr{foo baz on out})->stderr_like(qr{foo baz on err});
};
done_testing();
t/V1/LocalTester.pm view on Meta::CPAN
use v5.24;
use experimental 'signatures';
use Capture::Tiny 'capture';
use App::Easer V1 => 'run';
use Test::More;
use Exporter 'import';
our @EXPORT = ('test_run');
sub test_run ($app, $args, $env, $command = 'MAIN') {
my ($stdout, $stderr, @result, $clean_run, $exception);
my $self = bless {}, __PACKAGE__;
local *LocalTester::command_execute = sub ($cmd, $main, $conf, $args) {
return unless $cmd eq ($command // '');
$self->{conf} = $conf;
$self->{args} = $args;
};
eval {
local @ENV{keys $env->%*};
while (my ($k, $v) = each $env->%*) {
if (defined $v) { $ENV{$k} = $v }
else { delete $ENV{$k} }
}
$self->@{qw< stdout stderr result >} = capture {
scalar run($app, $args)
};
1;
} or do { $self->{exception} = $@ };
return $self;
} ## end sub test_run
sub stdout_like ($self, $regex, $name = 'stdout') {
like $self->{stdout} // '', $regex, $name;
return $self;
}
sub diag_stdout ($self) {
diag $self->{stdout};
return $self;
}
sub diag_stderr ($self) {
diag $self->{stderr};
return $self;
}
sub stderr_like ($self, $regex, $name = 'stderr') {
like $self->{stderr} // '', $regex, $name;
return $self;
}
sub conf_is ($self, $expected, $name = 'configuration') {
is_deeply $self->{conf}, $expected, $name;
return $self;
}
sub args_are ($self, $expected, $name = 'residual arguments') {
is_deeply $self->{args}, $expected, $name;
t/V2/02-single-with-lib.t view on Meta::CPAN
LocalTester::command_execute(@_);
print {*STDOUT} 'galook!';
print {*STDERR} 'gaaaah!';
return 42;
},
};
subtest 'no input, just defaults' => sub {
test_run($app, [], {}, 'MAIN')->no_exceptions->conf_is({bar => 'buzz'})
->args_are([])->result_is(42)->stdout_like(qr{(?mxs:\A galook! \z)})
->stderr_like(qr{(?mxs:\A gaaaah! \z)});
};
subtest 'env + cmdline, both parameters' => sub {
test_run($app, [qw< --bar BAAZ >], {GALOOK_FOO => 1}, 'MAIN')
->no_exceptions->conf_is({foo => 1, bar => 'BAAZ'})->args_are([])
->result_is(42)->stdout_like(qr{(?mxs:\A galook! \z)})
->stderr_like(qr{(?mxs:\A gaaaah! \z)});
};
test_run($app, ['help'], {}, 'MAIN')->no_exceptions->stdout_like(
qr{(?mxs:
example \s+ command .*?
An \s+ example \s+ command .*?
--foo .*
GALOOK_BAR .*?
help .*?
commands .*?
t/V2/03-multilevel.t view on Meta::CPAN
sub-sub-command \s+ baz .*?
second-level \s+ sub-command \s+ baz .*?
--last .*
)}, 'output of help command'
)->stdout_like(qr{sub-sub-command baz});
};
subtest 'foo baz' => sub {
test_run($app, ['foo', 'baz'], {}, 'baz')
->no_exceptions->result_is('BAZ')->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
subtest 'foo baz' => sub {
test_run($app, [qw< --foo foo --hey you baz --last 12 FP >], {}, 'baz')
->no_exceptions->result_is('BAZ')
->conf_is({foo => 1, bar => 'buzz', hey => 'you', last => 12})
->args_are(['FP'])->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
subtest 'foo (leveraging default sub-child)' => sub {
test_run($app, ['foo'], {}, 'baz')->no_exceptions->result_is('BAZ')
->stdout_like(qr{baz on out})->stderr_like(qr{baz on err});
};
subtest 'Foo (uppercase, leveraging default sub-child)' => sub {
test_run($app, ['Foo'], {}, 'baz')->no_exceptions->result_is('BAZ')
->stdout_like(qr{baz on out})->stderr_like(qr{baz on err});
};
subtest 'Foo commands (note uppercase)' => sub {
test_run($app, [qw< Foo commands >], {}, undef)
->no_exceptions->stdout_like(qr{(?mxs:help: .*? commands:)});
};
subtest 'bar' => sub {
test_run($app, ['bar'], {}, 'bar')->no_exceptions->result_is('Bar')
->stdout_like(qr{bar on out})->stderr_like(qr{bar on err});
};
subtest 'help bar' => sub {
test_run($app, ['help', 'bar'], {}, undef)->no_exceptions->stdout_like(
qr{(?mxs:
sub-command \s+ bar .*?
first-level \s+ sub-command \s+ bar .*?
has \s+ no \s+ option
)}, 'output of help command'
);
};
subtest 'bar help (help is ignored)' => sub {
test_run($app, [qw< bar help >], {}, undef)
->no_exceptions->stdout_like(qr{bar on out})
->stderr_like(qr{bar on err});
};
done_testing();
t/V2/05.single-leaf.t view on Meta::CPAN
}
print {*STDOUT} 'galook!';
print {*STDERR} 'gaaaah!';
return 42;
},
};
subtest 'no options, just defaults' => sub {
test_run($app, [], {}, 'MAIN')->no_exceptions->conf_is({bar => 'buzz'})
->args_are([])->result_is(42)->stdout_like(qr{(?mxs:\A galook! \z)})
->stderr_like(qr{(?mxs:\A gaaaah! \z)});
};
subtest 'option --help' => sub {
test_run($app, ['--help'], {}, 'MAIN')
->no_exceptions->conf_is({bar => 'buzz', help => 1})
->args_are([])
->result_is(0)
->stdout_like(qr{(?mxs: Description: .*? Options:)});
};
t/V2/15.spec-from-hash-or-mod.t view on Meta::CPAN
sub-sub-command \s+ baz .*?
second-level \s+ sub-command \s+ baz .*?
--last .*
)}, 'output of help command'
)->stdout_like(qr{sub-sub-command baz});
};
subtest 'foo baz 1' => sub {
test_run($app, ['foo', 'baz'], {}, 'baz')
->no_exceptions->result_is('BAZ')->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
subtest 'foo baz 2' => sub {
test_run($app, [qw< --foo foo --hey you baz --last 12 FP >], {}, 'baz')
->no_exceptions->result_is('BAZ')
->conf_is({foo => 1, bar => 'buzz', hey => 'you', last => 12})
->args_are(['FP'])->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
subtest 'foo (leveraging default sub-child)' => sub {
test_run($app, ['foo'], {}, 'baz')->no_exceptions->result_is('BAZ')
->stdout_like(qr{baz on out})->stderr_like(qr{baz on err});
};
subtest 'Foo (uppercase, leveraging default sub-child)' => sub {
test_run($app, ['Foo'], {}, 'baz')->no_exceptions->result_is('BAZ')
->stdout_like(qr{baz on out})->stderr_like(qr{baz on err});
};
subtest 'Foo commands (note uppercase)' => sub {
test_run($app, [qw< Foo commands >], {}, undef)
->no_exceptions->stdout_like(qr{(?mxs:help: .*? commands:)});
};
subtest 'bar' => sub {
test_run($app, ['bar'], {}, 'bar')->no_exceptions->result_is('Bar')
->stdout_like(qr{bar on out})->stderr_like(qr{bar on err});
};
subtest 'help bar' => sub {
test_run($app, ['help', 'bar'], {}, undef)->no_exceptions->stdout_like(
qr{(?mxs:
sub-command \s+ bar .*?
first-level \s+ sub-command \s+ bar .*?
has \s+ no \s+ option
)}, 'output of help command'
);
};
subtest 'bar help (help is ignored)' => sub {
test_run($app, [qw< bar help >], {}, undef)
->no_exceptions->stdout_like(qr{bar on out})
->stderr_like(qr{bar on err});
};
done_testing();
package Foo;
use App::Easer::V2 -command => -spec => {
help => 'sub-command foo',
description => 'first-level sub-command foo',
aliases => ['foo', 'Foo'],
t/V2/20.collect-options.t view on Meta::CPAN
sources =>
[
qw< +CmdLine +Environment +Parent=70 +Default=100 >, # defaults
'+JsonFileFromConfig=40',
],
};
subtest 'foo baz 1' => sub {
test_run($app, ['foo', 'baz'], {}, 'baz')
->no_exceptions->result_is('BAZ')->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
subtest 'foo baz 2' => sub {
test_run(
$app,
[
'--config' => "$file_prefix.1.json",
qw<
--foo foo
--hey you
t/V2/20.collect-options.t view on Meta::CPAN
'baz'
)->no_exceptions->result_is('BAZ')->conf_is(
{
config => "$file_prefix.1.json",
foo => 1,
bar => 'from_general_configuration_file',
hey => 'you',
last => 12
}
)->args_are(['FP'])->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
subtest 'foo baz (tests conf_contains works)' => sub {
test_run(
$app,
[
'--config' => "$file_prefix.2.json",
qw<
--foo foo
--hey you
t/V2/20.collect-options.t view on Meta::CPAN
{},
'baz'
)->no_exceptions->result_is('BAZ')->conf_contains(
{
foo => 1,
bar => 'from_general_configuration_file',
hey => 'you',
last => 12
}
)->args_are(['FP'])->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
$app->{sources} = [
qw< +CmdLine +Environment +Parent=70 +Default=100 >, # defaults
'+JsonFileFromConfig=30', # better than +Parent
[ '+FromTrail=90', qw< defaults foo baz > ],
];
subtest 'foo baz (source from sub-hash)' => sub {
t/V2/20.collect-options.t view on Meta::CPAN
{},
'baz'
)->no_exceptions->result_is('BAZ')->conf_contains(
{
foo => 0,
bar => 'from_general_configuration_file',
hey => 'you',
last => 42
}
)->args_are(['FP'])->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
$app->{sources} = [
qw< +CmdLine +Environment +Parent=70 +Default=100 >, # defaults
'+JsonFileFromConfig=30', # better than +Parent
[ '+FromTrail', {priority => 90}, qw< defaults foo baz > ],
];
subtest 'foo baz (source from sub-hash, .3.json)' => sub {
t/V2/20.collect-options.t view on Meta::CPAN
{},
'baz'
)->no_exceptions->result_is('BAZ')->conf_contains(
{
foo => 0,
bar => 'from_substuff',
hey => 'you',
last => 42
}
)->args_are(['FP'])->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
$app->{sources} = [
qw< +CmdLine +Environment +Default=100 +JsonFileFromConfig=30 >,
'+Parent',
[ '+FromTrail', qw< defaults foo baz > ],
];
subtest 'foo baz (source from sub-hash, not default)' => sub {
test_run(
t/V2/20.collect-options.t view on Meta::CPAN
{},
'baz'
)->no_exceptions->result_is('BAZ')->conf_contains(
{
foo => 0,
bar => 'from_general_configuration_file',
hey => 'you',
last => 42
}
)->args_are(['FP'])->stdout_like(qr{baz on out})
->stderr_like(qr{baz on err});
};
done_testing();
package Foo;
use App::Easer::V2 -command => -spec => {
help => 'sub-command foo',
description => 'first-level sub-command foo',
aliases => ['foo', 'Foo'],
options => [
t/V2/21.options-ordering.t view on Meta::CPAN
$conf{six} = 'six';
}
subtest "$who, all defaults, baseline" => sub {
test_run($app, \@args, \%env, $who)
->no_exceptions
->result_is(ucfirst $who)
->conf_is(\%conf)
->args_are([])
->stdout_like(qr{$who on out})
->stderr_like(qr{$who on err});
};
push @args, '--one', $conf{one} = 'cmdline-one';
subtest "$who, cmdline on ONE", => sub {
test_run($app, \@args, \%env, $who)->no_exceptions->conf_is(\%conf);
};
$env{ONE} = 'whatever, this does not go'; # cmdline overrides this
$env{TWO} = $conf{two} = 'environment-two'; # this goes over default
subtest "$who, cmdline on ONE, environment on ONE and TWO" => sub {
t/V2/22.custom-sources.t view on Meta::CPAN
$conf{six} = 'six';
}
subtest "$who, all defaults, baseline" => sub {
test_run($app, \@args, \%env, $who)
->no_exceptions
->result_is(ucfirst $who)
->conf_is(\%conf)
->args_are([])
->stdout_like(qr{$who on out})
->stderr_like(qr{$who on err});
};
push @args, '--one', $conf{one} = 'cmdline-one';
subtest "$who, cmdline on ONE", => sub {
test_run($app, \@args, \%env, $who)->no_exceptions->conf_is(\%conf);
};
$env{ONE} = 'whatever, this does not go'; # cmdline overrides this
$env{TWO} = $conf{two} = 'environment-two'; # this goes over default
subtest "$who, cmdline on ONE, environment on ONE and TWO" => sub {
t/V2/41.missing-help-no-warning.t view on Meta::CPAN
use experimental 'signatures';
use Test::More;
use File::Basename 'dirname';
use lib dirname(__FILE__);
use LocalTester;
subtest 'run_help' => sub {
my $t = test_run({execute => sub { shift->run_help }}, [], {}, undef)
->no_exceptions
->stdout_like(qr{no concise help yet}, 'default help text')
->stderr_like(qr{\A\s*\z}, 'no complaints on standard error');
};
subtest 'help' => sub {
my $t = test_run({force_auto_children => 1}, ['help'], {}, undef)
->no_exceptions
->stdout_like(qr{no concise help yet}, 'default help text')
->stderr_like(qr{\A\s*\z}, 'no complaints on standard error');
};
done_testing();
t/V2/42.help-help.t view on Meta::CPAN
use experimental 'signatures';
use Test::More;
use File::Basename 'dirname';
use lib dirname(__FILE__);
use LocalTester;
subtest 'help me to help' => sub {
test_run({force_auto_children => 1}, [qw< help help >], {}, undef)
->no_exceptions->stdout_like(qr{Print help for \(sub\)command},
q{help's help text})
->stderr_like(qr{\A\s*\z}, 'no complaints on standard error');
test_run({force_auto_children => 1}, [qw< help commands >], {}, undef)
->no_exceptions->stdout_like(qr{Print list of supported sub-command},
q{command's help text})
->stderr_like(qr{\A\s*\z}, 'no complaints on standard error');
test_run({force_auto_children => 1}, [qw< help tree >], {}, undef)
->no_exceptions->stdout_like(qr{Print tree of supported sub-command},
q{tree's help text})
->stderr_like(qr{\A\s*\z}, 'no complaints on standard error');
};
done_testing();
t/V2/50.lone-dash.t view on Meta::CPAN
#!/usr/bin/env perl
use v5.24;
use experimental 'signatures';
use Test::More;
use File::Basename 'dirname';
use lib dirname(__FILE__);
use LocalTester;
subtest 'lone dash on leaf node' => sub {
test_run({ execute => sub { } }, [qw< foo >], {}, undef)
->no_exceptions->stderr_like(qr{\A\s*\z},
'no complaints on standard error');
};
subtest 'lone dash on an intermediate node' => sub {
# this is how it's done properly when there are children... have to
# set a fallback_to -self to avoid die-ing because the child is not
# found.
test_run(
{
execute => sub { },
fallback_to => '-self',
force_auto_children => 1,
}, [qw< foo >], {}, undef)
->no_exceptions->stderr_like(qr{\A\s*\z},
'no complaints on standard error');
};
done_testing();
t/V2/LocalTester.pm view on Meta::CPAN
sub executor ($cb = undef) {
return sub ($self) {
LocalTester::command_execute($self);
$cb->($self) if $cb;
return $self->name;
};
}
sub test_run ($app, $args, $env, $expected_command = 'MAIN') {
my ($stdout, $stderr, @result, $clean_run, $exception);
my $self = bless {}, __PACKAGE__;
local *LocalTester::command_execute = sub ($cmd) {
my $name = $self->{name} = $cmd->name;
return unless $name eq ($expected_command // '');
$self->{conf} = $cmd->config_hash;
$self->{args} = [$cmd->residual_args];
};
eval {
local @ENV{keys $env->%*};
while (my ($k, $v) = each $env->%*) {
if (defined $v) { $ENV{$k} = $v }
else { delete $ENV{$k} }
}
$self->@{qw< stdout stderr result >} = capture {
scalar run($app, $0, $args->@*)
};
1;
} or do { $self->{exception} = $@ };
return $self;
} ## end sub test_run
sub stdout_like ($self, $regex, $name = 'stdout') {
like $self->{stdout} // '', $regex, $name;
return $self;
t/V2/LocalTester.pm view on Meta::CPAN
sub stdout_unlike ($self, $regex, $name = 'stdout') {
unlike $self->{stdout} // '', $regex, $name;
return $self;
}
sub diag_stdout ($self) {
diag $self->{stdout};
return $self;
}
sub diag_stderr ($self) {
diag $self->{stderr};
return $self;
}
sub stderr_like ($self, $regex, $name = 'stderr') {
like $self->{stderr} // '', $regex, $name;
return $self;
}
sub name_is ($self, $expected, $test_name = undef) {
$test_name //= "command name is '$expected'";
is $self->{name}, $expected, $test_name;
return $self;
}
sub conf_is ($self, $expected, $name = 'configuration') {