App-Easer

 view release on metacpan or  search on metacpan

lib/App/Easer/Tutorial/V2_008.pod  view on Meta::CPAN

         {
            aliases => [qw< list >],
            help    => 'list names of available name/value pairs',
            description => '',
            options => [ 'db' ],
            execute => sub ($self) {
               my $dbpath = $self->config('db') // die "no db provided\n";
               my $data = load_json($dbpath);
               say {*STDOUT} $_ for sort { $a cmp $b } keys($data->%*);
               exit 0;
            },
         },
      ],
   };

   sub load_json ($path) {
      open my $fh, '<:raw', $path;
      local $/;
      return JSON::PP::decode_json(<$fh>);
   }

   sub save_json ($path, $data) {
      state $encoder = JSON::PP->new->ascii->canonical->pretty;
      open my $fh, '>:raw', $path;
      print {$fh} $encoder->encode($data);
   }

   exit(run($app, $0, @ARGV) // 0);

=head3 Where's the inheritance?

The are very little changes with respect to the previous iteration, so
let's look at them in more detail:

   my $app = {
      ...
      options => [
         {
            ...
            # Set the option as "inheritable" by children
            transmit    => 1,
         }
      ],
      children => [
         {
            ...
            options => [
               'db',  # <-- inherit option definition from parent
               ...


There are two halves to options definition inheritance: the parent marks
an option as available for inheritance setting a true value for key
C<transmit>, and the child gets it by putting its name in the list of
options (as opposed to a full hash-based definition).


=head3 Why C<transmit>? Because C<+parent>.

You might be wondering why setting options explicitly as C<transmit>
instead of providing them all and let the child command decide. This has
to do with dealing with inheritance of I<many> options all at a time.

If a child's C<options> array has this:


         {
            ...
            options => [
               '+parent',
               ...

it will inherit I<all> options that are marked as C<transmit> in the
parent.

Inheritance might also be more fine-tuned by means of regular
expressions in the child. Suppose that your program supports a list of
options for connecting to a HTTP server and another list of options for
connecting to a database:

   # in the root command
   ...
   options => [
      { getopt => 'http_url=s',  transmit => 1 },
      { getopt => 'http_user=s', transmit => 1 },
      { getopt => 'http_pass=s', transmit => 1 },

      { getopt => 'db_url=s',  transmit => 1 },
      { getopt => 'db_user=s', transmit => 1 },
      { getopt => 'db_pass=s', transmit => 1 },
   ]

You might then want to provide sub-commands that focus on the HTTP or
the database portions only, like e.g. a sub-command to check whether
connectivity is available. In this case, it would be great to just
inherit the relevant options from the parent, instead of all of them, in
order to avoid cluttering the C<help>/C<usage> commands with options
that make no sense:

   # children of the root command
   children => [
      {
         aliases => [ qw< check-db-connectivity > ],
         options => [ '(?mxs: \A db_ )' ]
         ...
      },
      {
         aliases => [ qw< check-http-connectivity > ],
         options => [ '(?mxs: \A http_ )' ]
         ...
      },
      ...

=head2 Pass 3: C<commit> options along the way

Sometimes it can be hard to pre-determine a default value for an option
because its value might depend on multiple other values.

In a single command this is rarely a problem, because the specific
computation for the default value might be done at the beginning of the
C<execute> callback.

lib/App/Easer/Tutorial/V2_008.pod  view on Meta::CPAN

               say 'foo here!';
               return 0;
            },
         },
         {
            aliases => [qw< bar >],
            execute => sub ($self) {
               say 'bar here!';
               return 0;
            },
         },
      ],
      execute => sub ($self) {
         my @args = $self->residual_args;
         say "MAIN (root) here! Also got (@args)";
         return 0;
      },
      default_child => '-self',

      #####################################################################
      # this sets the MAIN command as the default command to run if no
      # child is found when additional residual-args are provided on the
      # command line
      fallback_to   => '-self',
   };

   exit(run($app, $0, @ARGV) // 0);

This works now:

   $ root-exec-with-fallback galook burp
   MAIN (root) here! Also got (galook burp)

It's also possible to set C<fallback_to> to string C<-default> to just
replicate whatever is set for C<default_child> (should you ever change
your mind and want the two mirror each other).

If you need more flexibility, take a look at key C<fallback> in the main
documentation.


=head3 Why was this executed?

L<App::Easer> does its best to figure out which command/sub-command
should be executed. As we saw in the previous sub-sections, it might
have different reasons for running a specific command, be it because
it's the default or a fallback. If you need it, you can call
C<execution_reason> to figure out why, receiving back a string among
C<-leaf>, C<-default>, or C<-fallback>.


=head2 Pass 8: C<aliases> and C<call_name>

Each command in L<App::Easer> supports a C<name> to set the command's
name. Many times, though, it's useful to also support I<aliases> for a
command, e.g. if you want your users to call sub-command C<list> with a
shorter version C<ls>.

Key C<aliases> in the command's specification allows setting these
aliases. As a matter of fact, you might just set it and forget about
C<name>, which will be set to the first alias in case. You decide what's
best for you:

   my $app1 = {
      name => 'foo',
      aliases => [qw< bar baz >],
      ...
   };

   my $same_as_app1 = {
      aliases => [qw< foo bar baz >],
      ...
   };

Sometimes you might want to provide different behaviours for different
aliases, but the underlying implementation is basically the same
(including e.g. command-line options) and you don't want to fire up two
different sub-commands. In this case, you can call method C<call_name>
on the command provided in the callback to figure out how the
sub-command was actually invoked.

Example:

   #!/usr/bin/env perl
   use v5.24;
   use warnings;
   use English;
   use experimental qw< signatures >;

   use App::Easer::V2 qw< run >;

   my $app = {
      aliases => [qw< MAIN >],
      sources => 'v2.008',
      config_hash_key => 'v2.008',
      children => [
         {
            aliases => [qw< foo bar >],
            execute => sub ($self) {
               my $name = $self->call_name;
               say "$name here!";
               return 0;
            },
         },
      ],
   };

   exit(run($app, $0, @ARGV) // 0);

Sample calls:

   $ check-name foo
   foo here!

   $ check-name bar
   bar here!

   # let's double check that it does not syphon up everything!
   $ check-name baz
   cannot find sub-command 'baz'



( run in 0.761 second using v1.01-cache-2.11-cpan-39bf76dae61 )