The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

App::Easer::V2 - Simplify writing (hierarchical) CLI applications

VERSION

This document describes App::Easer::V2 version {{[ version ]}}.

SYNOPSIS

   #!/usr/bin/env perl
   use v5.24;
   use experimental 'signatures';
   use App::Easer V2 => 'run';
   my $app = {
      aliases     => ['foo'],
      help        => 'this is the main app',
      description => 'Yes, this really is the main app',
      options     => [
         {
            name        => 'foo',
            help        => 'option foo!',
            getopt      => 'foo|f=s',
            environment => 'FOO',
            default     => 'bar',
         },
      ],
      execute => sub ($instance) {
         my $foo = $instance->config('foo');
         say "Hello, $foo!";
         return 0;
      },
      default_child => '-self',    # run execute by default
      children => [
         {
            aliases => ['bar'],
            help => 'this is a sub-command',
            description => 'Yes, this is a sub-command',
            execute => sub { 'Peace!' },
         },
      ],
   };
   exit run($app, $0, @ARGV);

Call examples:

   $ ./example.pl 
   Hello, bar!

   $ ./example.pl --foo World
   Hello, World!

   $ ./example.pl commands
   sub-commands for ./example.pl
               bar: this is a sub-command
            help: print a help command
         commands: list sub-commands

   $ ./example.pl help
   this is the main app

   Description:
      Yes, this really is the main app

   Can be called as: foo

   Options:
               foo: option foo!
                  command-line: mandatory string option
                                 --foo <value>
                                 -f <value>
                     environment: FOO
                        default: bar

   Sub-commands:
               bar: this is a sub-command
            help: print a help command
         commands: list sub-commands

   $ ./example.pl help help
   print a help command

   Description:
      Print help for (sub)command

   Can be called as: help

   This command has no option
   No sub-commands

   $ ./example.pl help commands
   list sub-commands

   Description:
      Print list of supported sub-commands

   Can be called as: commands

   This command has no option
   No sub-commands

   $ ./example.pl inexistent
   cannot find sub-command 'inexistent'

   $ ./example.pl help inexistent
   cannot find sub-command 'inexistent'

DESCRIPTION

NOTE: THIS DOCUMENT HAS TO BE REVIEWED TO MAKE IT EXPLICIT THAT IT REFERS TO VERSION 2 OF THE API.

App::Easer::V2 provides the scaffolding for implementing hierarchical command-line applications in a very fast way. This is Version 2 of the provided API, which does everything described below.

It makes it extremely simple to generate an application based on specific interfacing options, while still leaving ample capabilities for customising the shape of the application. As such, it aims at making simple things easy and complex things possible, in pure Perl spirit.

There are multiple ways to define an application. The two extremes are:

  • a hash reference with all the needed elements inside (see "SYNOPSIS" for an example)

  • a sub-class of App::Easer::V2::Command with methods overriding the needed elements where the programmer sees fit.

App::Easer::V2 allows also to leverage both aspects at the same time for the same command, e.g. setting some parts through a hash reference and other parts with overriding methods.

Top-Level Functions

The following functions are exported by App::Easer::V2:

  • appeaser_api

       my $api_version = appeaser_api();

    Return string V2.

  • d

       d($whatever);

    Sends $whatever through dd and then to warn.

  • dd

       my $dumped = dd($whatever);

    Sends $whatever through Data::Dumper and returns it.

  • run

       my $exit_code = run($cmd_hashref, $0, @ARGV);

    Run a command with the provided parameters (first the name of the command, using $0, then the command-line arguments).

In addition to importing the functions above, the following keys are also supported when useing the module:

  • -command

    The package from where App::Easer::V2 is used is a command and inherits from App::Easer::V2::Command, as well as registering as a command. This is the combined effect of -inherit and -register below.

  • -inherit

    The package from where App::Easer::V2 is used inherits from App::Easer::V2::Command. Usually -command is the right one though.

  • -register

    Register this package as a valid (sub)command.

  • -spec

    Provide a specification hash reference to set many attributes of the specific command. This hash reference is saved into the package's variable app_easer_spec.

The options above allow easily define a new class for a command like this:

   use App::Easer::V2 -command => -spec => { ... };

Anatomy of a run

Running an application can be done in several ways, depending on how the application is represented:

   # application defined as a hashref or arrayref
   use App::Easer V2 => 'run';
   exit run($app, $0, @ARGS);

   # application defined in a class
   use MyApp;
   exit MyApp->new->run($0, @ARGS);

When an application is run, the following high level algorithm is applied:

  • Options are gathered from different sources, like e.g. the command-line, environment variables, parent commands (in lower levels of the command hierarchy), files, or defaults.

  • A commit hook is called, if present, allowing an intermediate command to perform some actions before a sub-command is run. This hook has been anticipated before validation in V2.

  • A validation hook is called, if present, allowing check of the provided configuration, e.g. to spot incompatible options.

  • A sub-command is searched if possible and, if present, the process restarts from the first bullet above with this sub-command.

  • When the final sub-command is reached, the execute hook is run.

Application High Level View

The following list provides all keys/method names that are recognized in the definition of an application with App::Easer::V2. Items markes as Executable can be provided as sub references or with overriding the corresponding method in the derived class.

aliases

Array of strings.

The array contains alias names for the command. The first alias is also considered the name in case a name is missing.

allow_residual_options

Boolean, defaults to false.

Signal that the parsing of the command line must not fail if residual, unrecognized options are found. This allows e.g. building wrappers around external commands without the need to re-implement all of their options.

auto_environment

Boolean, defaults to false.

Signal that all options without an environment key should get one set to value 1, triggering auto-generation of the environment variable name.

children

Array of items.

Provides a list of children for the command. Children can be provided as hash references (whose structure is the same as anytyhing supported by hashy_class), array references (whose first item is the name of a class and the rest is provided to its new method), a string (which leads to a class name that can be instantiated without parameters), or a blessed object.

Anything that does not eventually produce a App::Easer::V2::Command should be considered a liability.

children_prefixes

Array of strings. Defaults to [$pkg . '::Cmd'].

Each string in the array is used as a prefix to look for sub-commands in @INC.

Assuming the main command is implemented in class MyApp, the default value looks for classes named MyApp::Cmd*, e.g. MyApp::CmdFoo and MyApp::CmdBar and ignores classed named MyApp::Common or MyApp::Utils.

commit

Executable with signature:

   sub commit_function ($app) { ... }

where $app is the blessed object for the application.

Perform the commit hook for the application, which is called immediately after the collection of options for the command and before looking further down for sub-commands or calling the execute method. Every weird action like fiddling with the command-line arguments etc. should be done here.

default_child

String. Defaults to help.

The name of a sub-command to invoke if no non-option values are set on the command line for a non-leaf command.

If set to -self, lack of an explicit child name on the command line means that the execute of the command itself should be run.

See also fallback_to.

description

String.

A verbose form of help, usually providing details about how the command works. This is used when generating the whole help for the command.

environment_prefix

String.

A prefix that is applied to an options's name to derive an environment variable name. Used when auto_environment is true and no environment key is present in the option's specification, or when the environment key points to value 1.

execute

Executable.

fallback_to

String.

The name of a sub-command to invoke if the first non-option command-line argument is not recognized as a children command for non-leaf commands.

If set to -self, lack of a valid child name on the command line means that the execute of the command itself should be run.

If set to -default, the default_child is used.

If set to a non-negative integer n, the n-th child is used from the list of children.

If set to a string, the corresponding child is used.

Otherwise, an exception is raised.

See also default_child.

force_auto_children

Boolean. Defaults to false.

Force the generation of the so-called auto-children even for leaf commands (i.e. commands without sub-commands either in children or from the class hierarchy).

Auto-children are the help, commands, and tree sub-commands.

This can come handy when default_child/fallback_to are set to -self and the three words help, commands, and tree are not possible valid values as arguments for the leaf command itself (e.g. when it does not use non-option command line arguments).

getopt_config

Array of strings. See below for defaults.

A list of strings to use to configure Getopt::Long for the specific command.

By default it is set to gnu_getopt. If a command is not a leaf, require_order and pass_through are added too, although the latter only temporarily to allow for chaining (unrecognized options might still trigger an exception). If allow_residual_options is true, pass_through is added.

hashy_class

String. Defaults to App::Easer::V2::Command.

The class name to use for inflating applications defined as hash references into objects.

Anything different from the default is a liability.

help

A concise form of help, ideally a single line to explain what the (sub-)command does. It is used when generating the whole help for the command, as well as the list of sub-commands of the help for the parent command.

help_channel

String. Defaults to -STDOUT:encoding(UTF-8).

Set the output channel for automatic commands help, commands, and tree.

It can be set to:

  • a sub reference, which will be called with the following signature:

       sub channel ($cmd_object, @stuff_to_print);
  • a filehandle, used to print out stuff

  • a reference to a scalar, where the output will be placed

  • a string of the form filename[:binmode], where filename can NOT contain the character :. The file will be opened and if the binmode part is provided, binmode() will be called on the resulting filehandle with the provided value.

    If the filename part is - or -stdout (case insensitive), then STDOUT will be used. If filename is -stderr (case insensitive), then STDERR will be used.

name

String.

Name of the command. If absent, the first item in the alias array is used.

options

Array of items.

See "OPTIONS".

params_validate

Hash reference or undef. Ignored if "validate" is set.

If passed as a hash reference, two keys are supported:

args

call Params::Validate::validate_pos on the residual_args (see "OPTIONS").

config

call Params::Validate::validate on the collected merged configuration (see "OPTIONS").

sources

Array of items.

See "OPTIONS".

validate

Sub reference for performing validation. Will be called during the validation phase and passed the command object instance:

   $validation_sub->($self);

If set, "params_validate" is ignored.

The following YAML representation gives an overview of the elements that define an application managed by App::Easer::V2, highlighting the necessary or strongly suggested ones at the beginning:

  aliases: «array of strings»
  execute: «executable»
  help: «string»
  options: «array of hashes»

  allow_residual_options: «boolean»
  auto_environment: «boolean»
  children: «array of hashes»
  children_prefixes: «array of strings»
  commit: «executable»
  default_child: «string»
  description: «string»
  environment_prefix: «string»
  fallback_to: «string»
  force_auto_children: «boolean»
  hashy_class: «string»
  help_channel: «string»
  name: «string»
  params_validate: «hash»
  sources: «array of items»
  validate: «executable»

As anticipated, it's entirely up to the user to decide what style is best, i.e. define applications through metadata only, through object-oriented derivation, or through a mix of the two. The following examples are aimed at producing the same application:

   # metadata (mostly)
   my $app_as_metadata = {
      aliases => [qw< this that >],
      help => 'this is the application, but also that',
      options => [ { getopt => 'foo|f=s', default => 'bar' } ],
      execute => sub ($app) {
         say 'foo is ', $app->config('foo');
         return 0;
      },
   };

   # class only
   package ThisThatApp;
   use App::Easer::V2 '-command';
   sub aliases ($self) { return [qw< this that >] }
   sub help ($self) { return 'this is the application, but also that' }
   sub options ($self) { [ { getopt => 'foo|f=s', default => 'bar' } ] }
   sub execute ($self) {
      say 'foo is ', $self->config('foo');
      return 0;
   }

   # mixed style
   package ThisThatMixedApp;
   use App::Easer::V2 -command => -spec => {
      aliases => [qw< this that >],
      help => 'this is the application, but also that',
      options => [ { getopt => 'foo|f=s', default => 'bar' } ],
   };
   sub execute ($self) {
      say 'foo is ', $self->config('foo');
      return 0;
   }

The last style allows keeping data mostly as data, while leaving the freedom to implement the logic as proper methods, which can be beneficial for e.g. sharing common logic among several commands.

App::Easer::V2::Command METHODS

When a command is created, it is (usually) an instance of class App::Easer::V2::Command or a descendant. As such, it has the methods explained in the following list, which at the moment appear as public ones although some might be hidden in the future (stable ones are expressely marked so).

auto_children
   my @classes = $self->auto_children;
   my @objects = $self->auto_children(1);

Return a list of the automatic children (help, commands, and tree) as inflatable children, i.e. as fully qualified class names. If an optional true value is passed, children are inflated into objects.

auto_commands
   my $instance = $self->auto_commands;

Returns an instance representing the commands sub-command.

auto_help
   my $instance = $self->auto_help;

Returns an instance representing the help sub-command.

auto_tree
   my $instance = $self->auto_tree;

Returns an instance representing the tree sub-command.

aliases
   my @aliases = $self->aliases;
   my @modified = $self->aliases(\@new_aliases);

Gets/sets the aliases. If not set, name is used.

See "Application High Level View".

allow_residual_options
   my $boolean = $self->allow_residual_options;
   $self->allow_residual_options(0); # disable
   $self->allow_residual_options(1); # enable

See "Application High Level View".

auto_environment
   my $boolean = $self->auto_environment;
   $self->auto_environment(0); # disable
   $self->auto_environment(1); # enable

See "Application High Level View".

call_name
   my $string = $self->call_name;
   $self->call_name('override, for no reason apparently...');

Returns a string with the name used to call the sub command (useful to figure out which of the aliases was used).

The method can also be used to set this name, if needed. This is not guaranteed to be future-proof.

children
   my @direct_children = $self->children;
   $self->children(\@new_direct_children);

Get/set the list of direct children. Other children might be collected automatically from the class hierarchy.

Use of this method is discouraged and not future-proof.

See "Application High Level View".

children_prefixes
   my $aref = $self->children_prefixes;
   $self->children_prefixes(\@new_prefixes);

See "Application High Level View".

collect
   $self->collect;

Collect options values from the configured sources, see "OPTIONS".

Not assumed to be used directly. Overloading is a liability.

commit
   $self->commit;

Commit the collected options.

Not assumed to be used directly.

It can be overloaded to provide a custom behaviour, which can e.g. modify residual_args or config_hash to drive following steps of looking for a sub-command.

config
   my $value = $self->config($key);   # returns a scalar anyway
   my @values = $self->config(@keys);

Retrieve collected option values.

config_hash
   my $merged_hash = $self->config_hash;
   my $detailed    = $self->config_hash(1);

Get the collected option values as a hash.

In the basic case, a merged hash is returned, with values taken from sources according to their priorities.

In the advanced case where a true value is passed as optional argument, a hash with two keys merged and sequence is returned. The explanation of the data format is beyond the scope of this manual page.

default_child
   my $dc = $self->default_child;
   $self->default_child($new_name);

See "Application High Level View".

description
   my $text = $self->description;
   $self->description($updated_description);

See "Application High Level View".

environment_prefix
   my $prefix = $self->environment_prefix;
   $self->environment_prefix($new_prefix);

See "Application High Level View".

environment_variable_name
   my $name = $self->environment_variable_name($opt_hash);

Get the environment variable name set for the specific option described by $opt_hash. If it contains an environment key and it has a name, that is returned, otherwise if it is 1 then an environment variable is generated from envoronment_prefix and name.

execute
   $self->execute;

Code that executes the main logic of the command.

If a sub reference was provided with execute in the application definition, that is called receiving the object as its only parameter. Otherwise, the intended use is to override this method in a derived class for a command.

It is not meant to be called directly.

execution_reason
   my $reason = $self->execution_reason;

A string representing the reason why the specific execute method/callback was selected, can be one of:

  • -default

    The command was selected as the default child set for a command.

  • -fallback

    The command was selected as a result of the fallback method.

  • -leaf

    The command is a leaf and has no sub-commands.

fallback
   my $name = $self->fallback;

Select a fallback child, or the invoking command itself (returning -self) as the command that should be investigated next or executed.

This method is called when there are unrecognized non-option command-line arguments that lead to no child of a non-leaf command. The selection is performed using fallback_to.

fallback_to
   my $name = $self->fallback_to;
   $self->fallback_to($new_fallback);

Get/set the fallback string for figuring out the fallback in case of need. Used by fallback.

See "Application High Level View".

find_child
   my ($child_instance, @child_args) = $self->find_child;

Look for a child to hand execution over. Returns an child instance or undef (which means that the $self is in charge of executing something). This implements the most sensible default, deviations will have to be coded explicitly.

This method is called by run and is not expected to be called elsewhere. It use is discouraged and not future-proof.

Returns a list with the selected children and the arguments to feed to its run method:

  •    (undef, '-leaf')

    if no child exists. In this case, the caller command's execute method should be used.

  •    ($instance, @args)

    if a child is found in $args[0].

  •    ($instance, '-default')

    if the default child is returned.

  •    (undef, '-fallback')

    in case $self is the fallback and should be used for calling the execute method.

  •    ($instance, '-fallback', @args)

    in case the fallback is returned, with the residual unparsed arguments.

find_matching_child
   my $instance = $self->find_matching_child($name);

Find and instantiate a child matching $name, either directly or as an alias.

force_auto_children
   my $boolean = $self->force_auto_children;
   $self->force_auto_children(0); # disable
   $self->force_auto_children(1); # enable

See "Application High Level View".

full_help_text
   my $text = $self->full_help_text;

Get the full auto-generated help text for the command, e.g. to print it.

getopt_config
   my $aref = $self->getopt_config;
   $self->getopt_config(\@custom_getopt_long_configuration);

See "Application High Level View".

hashy_class
   my $class = $self->hashy_class;
   $self->hashy_class($new_class);

Setting this to anything different from App::Easer::V2::Command is a liability, although it might make sense to set to a derived class to provide a common set of methods useful for the specific application.

See "Application High Level View".

help
   my $text = $self->help;
   $self->help($new_help_line);

See "Application High Level View".

help_channel
   my $ch = $self->help_channel;
   $self->help_channel($new_channel);

See "Application High Level View".

inflate_children
   my @instances = $self->inflate_children(@hints);

Turn a list of @hints into corresponding instances.

A blessed hint is always returned unmodified, assuming it's already a valid instance. No check on the class is performed.

A hash reference is inflated using hashy_class.

An array reference assumes that the first element in the array is a valid hashy_class and the rest of the items are passed to its new method.

Anything else is considered a string containing a class to instantiate.

Actual instantiation is done using method instantiate.

Direct use of this method is a liability.

inherit_options
   my @options = $self->inherit_options(@options_names);

Look for matching names in a parent's options. This does technically not put these options inside the command, but it is used internally to achieve this goal.

This method is not meant to be called directly and is subject to become a private method, so its usage is discouraged and not future-proof.

instantiate
   my $instance = $self_or_package->instantiate($class, @args);

This method loads class $class via load_module and returns:

   $class->new(@args);

This is a class method.

list_children
   my @children = $self->list_children;

Return the full list of children for a command, including ones gathered in the class tree and auto-generated ones (these are added only if there are other children or if force_auto_children is set to a true value).

load_module
   my $module_name_copy = $self->load_module($module_name);

Load $module_name with require and return the name itself. The module name can only be provided using :: as separators. No specific bug from the past of Perl is addressed.

merge_hashes
   my $merged = $self->merge_hashes(@inputs);

Takes a list of input hash references and generates a merged version of all of them. Hashes in @inputs are supposed to be provided in priority order, where those coming first take precedence over those coming next.

name
   my $name = $self->name;
   $self->name($new_name);

Get/set name of command. If not present, the first item of aliases is used. If this is not set too, or empty, string ** no name ** is used.

See "Application High Level View".

name_for_option
   my $opt_name = $self->name_for_option($opt_spec_hashref);

Get the option's name, either from its name key, or deriving it from getopt, or from environment. Returns ~~~ if none of them works.

new
   my $instance = $class->new(@spec);
   my $other    = $class->new(\%spec);

Instantiate a new object, using the provided values.

In derived classes, additional specifications are also taken from the package variable $class . '::app_easer_spec', if set.

options
   my @options_aoh = $self->options;
   $self->options(\@new_options);

See "Application High Level View" and "OPTIONS". The returned list if passed through resolve_options anyway, e.g. to inherit options.

params_validate
   my $href_or_undef = $self->params_validate;
   $self->params_validate($new_pv_conf);

See "Application High Level View".

parent
   my $parent_command = $self->parent;

Get the parent command.

ref_to_sub
   my $sub = $self->ref_to_sub($locator);

Turn a locator for a sub into a proper reference to a sub. The $locator can be:

  • a sub reference, that is returned unmodified;

  • an array reference with two items inside, i.e. a class/module name followed by a method/function name

  • a string pointing to the function, either fully qualified as in My::Class::function or as a plain name that will be looked for in the class of $self.

residual_args
   my @residual_args = $self->residual_args;
   $self->residual_args(@new_args);

Access the list of residual (i.e. unparsed) arguments from the command line.

resolve_options
   my @options = $self->resolve_options($single_specification);

Expand a $single_specification into one or more options. Basic optiosn specifications are hash references, but they might be strings like +parent (for inheriting all that is transmitted from a parent command) or regular expressions to bulk import options based on their names.

run
   $self->run($command_name, @arguments);

Run a command. The first parameter is the command name, it should probably be set to $0 when calling the topmost command.

Running means parsing all options and potentially looking for sub-commands, until one is found.

Returns whatever the execute method of the selected sub-command returns.

run_help
   $self->run_help;

Run the auto_help command, which should print out the help for the command itself. This is not usually needed, but comes handy to implement printing the help not via a sub-command but honoring a command-line option, like this:

   sub execute ($self) {
      return $self->run_help if $self->config('help');
      ...
   }
set_config
   $self->set_config(foo => 'bar');

Set a new value for a configuration, overriding what has been found out from the several input sources.

slot
   my $hashref = $self->slot;
   $self->slot($new_data);

App::Easer::V2 uses a blessed hash reference to manage all data related to an object representing a command. To minimize overlapping with a user's object data, all data for App::Easer::V2 is kept in a sub-hash pointed by the slot key, like this:

   bless {
      $class_name => {
         ... all actual App::Easer::V2 stuff here ...
      }
   }, $hashy_class;

This method gives access to this slot and can be overridden to keep this data elsewhere, e.g. in an array element or in an inside-out object, without the need to re-implement all accessors.

slurp
   my $contents   = $self->slurp($filename);
   my $contents_2 = $self->slurp($filename, '<:raw');

Get the whole contents from a file, optionally specifying the mode for openining the file. By default, UTF-8 encoding is assumed and enforced.

Use of this method is discouraged and not future-proof.

sources
   my @sources = $self->sources;
   $self->sources(\@new_sources_list);

See "Application High Level View" and "OPTIONS".

validate
   $self->validate(\&validation_sub);
   $self->validate;

Performs validation.

When a validation sub is set (either calling with a parameter, or setting it via validate in "new") it will be called, receiving $self as the only parameter:

   $sub->($self);

The validator is supposed to throw an exception upon validation failure.

If no validation sub is set, "params_validate" is looked for. If present, validation is applied according to it, using Params::Validate.

OPTIONS

The main capability provided by App::Easer is the flexibility to handle options, collecting them from several sources and merging them together according to priorities.

Supported options are set in an array of hashes (or strings) pointed by the options key. Each hash in the array sets the details regarding a single option. When provided as string, the option is inherited from a parent, allowing the sub-command to expose the same option as the parent. This gives freedom to put options and values either in the parent or in the descendant command, providing flexibility.

This is a YAML overview of how to set one option as a hash:

   name: «string»
   help: «string»
   transmit: «boolean»
   transmit_exact: «boolean»
   getopt: «string»
   environment: «string» or 1
   default: any value

A few keys inside the hash regard the option itself, like:

  • name

    name of the option. This is not mandatory if getopt is present, which gets the name automatically from the first alias of the option itself;

  • help

    some help about the option.

  • transmit

    boolean to set whether an option can be easily "inherited" by a sub-command (without the need to put all attributes of the option once again).

  • transmit_exact

    boolean to set whether the option must be spelled exactly to be inherited (no inheritance via a regular expression).

Option Values Collection

The collection is performed thanks to sources, which can be set with the corresponding sources key or method (depending on the style). 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.

Sources are considered with respect to two different ordering methods: their place in the array pointed by key sources and their priority. The former sets the order in which data from each source is collected, the latter sets the precedence while assembling conflicting data from several different sources (lower priorities means higher precedence).

Each source can be set either as a $locator (see ahead) or as an array reference with the following structure:

   [ $locator, @args ]

The $locator can be a reference to a sub, which is used as the source itself, or a string that allows getting the source sub to handle the specific source. Strings starting with the + character are reserved for sources provided by App::Easer::V2 natively.

If $locator is a plain string, it is possible to set the priority directly inside it with =NN (e.g. +Default=100). It's not necessary to set the priority; if missing, it will be assumed to be 10 more than the previous one in the array (with the first item starting at 10). This also means that, by default, the ordering of sources also doubles down as the ordering of precedence.

The @args part can provide additional arguments to the specific source; its applicability is dependent on the source itself. As the only exception, if the first item of @args is a hash reference, it will be removed from the array and used to gather additional meta-options used directly by App::Easer. At the moment, this is an alternative way to set the priority of the specific source using the key priority. This means that the following examples are equivalent:

   # priority in source name, like anywhere else
   [ '+FromTrail=90', qw< defaults foo baz > ],

   # priority in meta-options first-arguments hash reference
   [ '+FromTrail', {priority => 90}, qw< defaults foo baz > ],

It's not necessary to set the sources explicitly, as by default the following configuration is assumed:

   +CmdLine +Environment +Parent=70 +Default=100

where the respective priorities are, in order, 10, 20, 70, and 100.

Sources provided out of the box by App::Easer::V2 are:

  • +CmdLine

    This source gathers options from the command line arguments. It is set using the getopt key, according to the rules explained in GetOpt::Long. The first alias of an option is also set as the option's name in case key name is missing.

  • +Environment

    This source gets values from environment variables. It is set using the environment key, which can be set to either the name of the environment variable (this is case sensitive) or automatically from higher-level configuration environment_prefix and the option's name in case it is set to value 1 exactly.

  • +Parent

    This source gets values from a parent command (it only applies to sub-commands).

  • +Default

    Get a default value from the default sub-key of the option.

  • +JsonFileFromConfig

    Get keys/values from a JSON file, whose path is pointed by a key in the collected options. By default this key is config. To set a different key, pass this source as an array reference with the source name and the key, like this:

       [ JsonFileFromConfig => 'jcnf' ]
  • +JsonFiles

    Get keys/values from a few JSON files (it's OK if they do not exist). To set the files to try, pass this source as an array reference:

       [ JsonFiles => @paths ]
  • +FromTrail

    Get keys/values from a sub-hash of the already collected options (e.g. after loading them from a configuration file). The trail to the position of the sub-hash is provided like this:

       [ FromTrail => qw< topcmd subcmd additional_values > ]

It is possible to set a default value for the option used by JsonFileFromConfig, as long as the +Default source is placed before JsonFileFromConfig. In this case, it's usually necessary to assign a lower priority value to JsonFileFromConfig to make sure values read from there take precedence over the defaults:

   sources => [qw<
      +CmdLine=10 +Environment=20 +Parent=30 +Default=100
      +JsonFileFromConfig=40
   >];

This is the main reason why the priority has been detached from order of appearance.

Accessing Collected Option Values

Assuming the application object is $self, there are a few methods to get the configurations:

  • config

       my $value = $self->config('foo');
       my @values = $self->config(@multiple_keys);

    get whatever value was computed after collecting values from all sources and taking the one with the best (i.e. lowest) priority.

  • config_hash

       my $merged_hash   = $self->config_hash;
       my $complete_hash = $self->config_hash(1);

    The first form (or any where the only parameter is considered false by Perl) gets the merged form, i.e. where each key has one value associated, based on the priorities set for the sources.

    The second form returns a more complicated data structure where the sequence key points to an array reference containing all details about data gathering. Explanation of this data structure is beyond the scope of this manual page.

    The second form cam be used in case the provided mechanism of prioritization is not sufficient.

  • residual_args

       my @args = $self->residual_args;

    Get all arguments from the command line that were not parsed (and hence left for a sub-command or just unparsed).

MIXED STUFF

Managing help as a command-line option

To get help about a command, App::Easer::V2 provides a help sub-command out of the box most of the times. The exception is for leaf commands, which do not have sub-commands.

This is not a big deal with hierarchical applications, because it's possible to invoke the help sub-command of the parent command and pass the name of the sub-command we need help with:

   # print help about `topcmd`
   $ topcmd help

   # print help about `subcmd` under `topcmd`
   $ topcmd help subcmd

On the other hand, for non-hierarchical applications, the only available command is also a leaf and this hinders getting a meaningful, auto-generated help text off the shelf.

In this case, it's possible to include an option for getting help, like this:

   my $app = {
      options => [
         {
            getopt => 'help|h!',
            help   => 'print help on the command',
         },
         ...

Then, inside the execute sub, it's possible to run the help sub-command explicitly:

   sub execute ($self) {
      return $self->run_help if $self->config('help');

      # ... normal code for "execute"
      # ...
   }

BUGS AND LIMITATIONS

Minimum perl version 5.24.

Report bugs through GitHub (patches welcome) at https://github.com/polettix/App-Easer.

AUTHOR

Flavio Poletti <flavio@polettix.it>

COPYRIGHT AND LICENSE

Copyright 2021 by Flavio Poletti <flavio@polettix.it>

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.