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

NAME

Perinci::CmdLine::Base - Base class for Perinci::CmdLine{,::Lite}

VERSION

This document describes version 0.36 of Perinci::CmdLine::Base (from Perl distribution Perinci-CmdLine-Lite), released on 2014-10-24.

REQUEST KEYS

The various values in the $r hash/stash.

  • action => str

    Selected action to use. Usually set from the common options.

  • format => str

    Selected format to use. Usually set from the common option --format.

  • read_config => bool

  • config_paths => array of str

  • config_profile => str

  • parse_argv_res => array

    Enveloped result of parse_argv().

  • subcommand_name => str

    Also set by parse_argv(). The subcommand name in effect, either set explicitly by user using --cmd or the first command-line argument, or set implicitly with the default_subcommand attribute. Undef if there is no subcommand name in effect.

  • subcommand_name_from => str

    Also set by parse_argv(). Tells how the subcommand_name request key is set. Value is either --cmd (if set through --cmd common option), arg (if set through first command-line argument), default_subcommand (if set to default_subcommand attribute), or undef if there is no subcommand_name set.

  • subcommand_data => hash

    Also set by parse_argv(). Subcommand data, including its URL, summary (if exists), and so on. Note that if there is no subcommand, this will contain data for the main command, i.e. URL will be set from url attribute, summary from summary attribute, and so on. This is a convenient way to get what URL and summary to use, and so on.

  • skip_parse_subcommand_argv => bool

    Checked by parse_argv(). Can be set to 1, e.g. in common option handler for --help or --version to skip parsing @ARGV for per-subcommand options.

  • args => hash

    Also taken from parse_argv() result.

  • meta => hash

    Result of get_meta().

  • dry_run => bool

    Whether to pass -dry_run special argument to function.

  • res => array

    Enveloped result of action_ACTION().

  • fres => str

    Result from hook_format_result().

  • output_handle => handle

    Set by select_output_handle() to choose output handle. Normally it's STDOUT but can also be pipe to pager (if paging is turned on).

ATTRIBUTES

actions => array

Contains a list of known actions and their metadata. Keys should be action names, values should be metadata. Metadata is a hash containing these keys:

common_opts => hash

A hash of common options, which are command-line options that are not associated with any subcommand. Each option is itself a specification hash containing these keys:

  • category (str)

    Optional, for grouping options in help/usage message, defaults to Common options.

  • getopt (str)

    Required, for Getopt::Long specification.

  • handler (code)

    Required, for Getopt::Long specification. Note that the handler will receive <($geopt, $val, $r)> (an extra $r).

  • usage (str)

    Optional, displayed in usage line in help/usage text.

  • summary (str)

    Optional, displayed in description of the option in help/usage text.

  • show_in_usage (bool or code, default: 1)

    A flag, can be set to 0 if we want to skip showing this option in usage in --help, to save some space. The default is to show all, except --subcommand when we are executing a subcommand (obviously).

  • show_in_options (bool or code, default: 1)

    A flag, can be set to 0 if we want to skip showing this option in options in --help. The default is to 0 for --help and --version in compact help. Or --subcommands, if we are executing a subcommand (obviously).

  • order (int)

    Optional, for ordering. Lower number means higher precedence, defaults to 1.

A partial example from the default set by the framework:

 {
     help => {
         category        => 'Common options',
         getopt          => 'help|h|?',
         usage           => '--help (or -h, -?)',
         handler         => sub { ... },
         order           => 0,
         show_in_options => sub { $ENV{VERBOSE} },
     },
     format => {
         category    => 'Common options',
         getopt      => 'format=s',
         summary     => 'Choose output format, e.g. json, text',
         handler     => sub { ... },
     },
     undo => {
         category => 'Undo options',
         getopt   => 'undo',
         ...
     },
     ...
 }

The default contains: help (getopt help|h|?), version (getopt version|v), action (getopt action), format (getopt format=s), format_options (getopt format-options=s), json*, yaml*, perl*. If there are more than one subcommands, this will also be added: list (getopt list|l). If dry-run is supported by function, there will also be: dry_run (getopt dry-run). If undo is turned on, there will also be: undo (getopt undo), redo (getopt redo), history (getopt history), clear_history (getopt clear-history).

*) Currently only added if you say use Perinci::CmdLine 1.04.

Sometimes you do not want some options, e.g. to remove format and format_options:

 delete $cmd->common_opts->{format};
 delete $cmd->common_opts->{format_options};
 $cmd->run;

Sometimes you want to rename some command-line options, e.g. to change version to use capital -V instead of -v:

 $cmd->common_opts->{version}{getopt} = 'version|V';

Sometimes you want to add subcommands as common options instead. For example:

 $cmd->common_opts->{halt} = {
     category    => 'Server options',
     getopt      => 'halt',
     summary     => 'Halt the server',
     handler     => sub {
         my ($go, $val, $r) = @_;
         $r->{subcommand_name} = 'shutdown';
     },
 };

This will make:

 % cmd --halt

equivalent to executing the 'shutdown' subcommand:

 % cmd shutdown

completion => code

Will be passed to Perinci::Sub::Complete's complete_cli_arg(). See its documentation for more details.

default_subcommand => str

Set subcommand to this if user does not specify which to use (either via first command-line argument or --cmd option). See also: get_subcommand_from_arg.

get_subcommand_from_arg => int (default: 1)

The default is 1, which is to get subcommand from the first command-line argument except when there is default_subcommand defined. Other valid values are: 0 (not getting from first command-line argument), 2 (get from first command-line argument even though there is default_subcommand defined).

description => str

exit => bool (default: 1)

formats => array

Available output formats.

pass_cmdline_object => bool (default: 0)

Whether to pass special argument -cmdline containing the cmdline object to function. This can be overriden using the pass_cmdline_object on a per-subcommand basis.

Passing the cmdline object can be useful, e.g. to call run_help(), etc.

program_name => str

Default is from PERINCI_CMDLINE_PROGRAM_NAME environment or from $0.

riap_client => float (default: 1.1)

Specify Riap protocol version to use. Will be passed to riap_client_args.

riap_client => obj

Optional. Can be set to Perinci::Access (or compatible) instance. Sometimes a Riap request needs to be performed, e.g. when requesting completion to the server. If this is empty, the request won't be done.

See Perinci::CmdLine where it is set by default. In Perinci::CmdLine::Lite, this is left undefined by default.

riap_version => float (default: 1.1)

Will be passed to Riap client constructor as well.

riap_client_args => hash

Arguments to pass to Perinci::Access constructor. This is useful for passing e.g. HTTP basic authentication to Riap client (Perinci::Access::HTTP::Client):

 riap_client_args => {handler_args => {user=>$USER, password=>$PASS}}

subcommands => hash | code

Should be a hash of subcommand specifications or a coderef.

Each subcommand specification is also a hash(ref) and should contain these keys:

  • url (str, required)

    Location of function (accessed via Riap).

  • summary (str, optional)

    Will be retrieved from function metadata at url if unset

  • description (str, optional)

    Shown in verbose help message, if description from function metadata is unset.

  • tags (array of str, optional)

    For grouping or categorizing subcommands, e.g. when displaying list of subcommands.

  • log_any_app (bool, optional)

    Whether to load Log::Any::App, default is true. For subcommands that need fast startup you can try turning this off for said subcommands. See "LOGGING" for more details.

  • use_utf8 (bool, optional)

    Whether to issue "binmode(STDOUT, ":utf8")". See "LOGGING" for more details.

  • undo (bool, optional)

    Can be set to 0 to disable transaction for this subcommand; this is only relevant when undo attribute is set to true.

  • show_in_help (bool, optional, default 1)

    If you have lots of subcommands, and want to show only some of them in --help message, set this to 0 for subcommands that you do not want to show.

  • pass_cmdline_object (bool, optional, default 0)

    To override pass_cmdline_object attribute on a per-subcommand basis.

  • args (hash, optional)

    If specified, will send the arguments (as well as arguments specified via the command-line). This can be useful for a function that serves more than one subcommand, e.g.:

     subcommands => {
         sub1 => {
             summary => 'Subcommand one',
             url     => '/some/func',
             args    => {flag=>'one'},
         },
         sub2 => {
             summary => 'Subcommand two',
             url     => '/some/func',
             args    => {flag=>'two'},
         },
     }

    In the example above, both subcommand sub1 and sub2 point to function at /some/func. But the function can differentiate between the two via the flag argument being sent.

     % cmdprog sub1 --foo 1 --bar 2
     % cmdprog sub2 --foo 2

    In the first invocation, function will receive arguments {foo=>1, bar=>2, flag=>'one'} and for the second: {foo=>2, flag=>'two'}.

Subcommands can also be a coderef, for dynamic list of subcommands. The coderef will be called as a method with hash arguments. It can be called in two cases. First, if called without argument name (usually when doing --subcommands) it must return a hashref of subcommand specifications. If called with argument name it must return subcommand specification for subcommand with the requested name only.

summary => str

tags => array of str

url => str

Required if you only want to run one function. URL should point to a function entity.

Alternatively you can provide multiple functions from which the user can select using the first argument (see subcommands).

read_config => bool (default: 1)

Whether to read configuration files.

config_dirs => array of str

Which directories to look for configuration file. The default is to look at the system location and then per-user home directory. On Unix, it's ["/etc", $ENV{HOME}].

config_filename => str

Configuration filename. The default is program_name . ".conf". For example, if your program is named foo-bar, config_filename will be foo-bar.conf.

METHODS

$cmd->run() => ENVRES

Will parse command-line arguments with parse_argv(), select/set subcommand, call hooks, run the appropriate run_ACTION() method, and finally format and display the result.

The run_ACTION() methods will be passed $r and is supposed to return an enveloped result. The result will then be put in $r->{res}.

If exit attribute is true, will exit with the action's envelope result status. If status is 200, exit code is 0. Otherwise exit code is status minus 300. So, a response [501, "Not implemented"] will result in exit code of 201.

If exit attribute is false, will simply return the action result ($r->{res}). And will also return the exit code in $r->{res}[3]{'x.perinci.cmdline.base.exit_code'}.

$cmd->do_completion() => ENVRES

Called by run().

$cmd->parse_argv() => ENVRES

Called by run().

$cmd->get_meta($r, $url) => ENVRES

Called by parse_argv() or do_completion(). Subclass has to implement this.

HOOKS

All hooks will receive the argument $r, a per-request hash/stash. The list below is by order of calling.

$cmd->hook_before_run($r)

Called at the start of run(). Can be used to set some initial values of other $r keys. Or setup the logger.

$cmd->hook_after_parse_argv($r)

Called after run() calls parse_argv() and before it checks the result. $r-{parse_argv_res}> will contain the result of parse_argv(). The hook gets a chance to, e.g. fill missing arguments from other source.

$cmd->hook_format_result($r)

The hook is supposed to format result in $res-{res}> (an array).

$cmd->hook_display_result($r)

The hook is supposed to display the formatted result (stored in $r-{fres}>) to STDOUT. But in the case of streaming output, this hook can also set it up.

$cmd->hook_after_run($r)

Called at the end of run(), right before it exits (if exit attribute is true) or returns $r-{res}>. The hook has a chance to modify exit code or result.

METADATA PROPERTY ATTRIBUTE

This module observes the following Rinci metadata property attributes:

cmdline.default_format => STR

Set default output format (if user does not specify via --format command-line option).

RESULT_METADATA

This module interprets the following result metadata property/attribute:

property: is_stream => BOOL

XXX should perhaps be defined as standard in Rinci::function.

If set to 1, signify that result is a stream. Result must be a glob, or an object that responds to getline() and eof() (like a Perl IO::Handle object), or an array/tied array. Format must currently be text (streaming YAML might be supported in the future). Items of result will be displayed to output as soon as it is retrieved, and unlike non-streams, it can be infinite.

An example function:

 $SPEC{cat_file} = { ... };
 sub cat_file {
     my %args = @_;
     open my($fh), "<", $args{path} or return [500, "Can't open file: $!"];
     [200, "OK", $fh, {is_stream=>1}];
 }

another example:

 use Tie::Simple;
 $SPEC{uc_file} = { ... };
 sub uc_file {
     my %args = @_;
     open my($fh), "<", $args{path} or return [500, "Can't open file: $!"];
     my @ary;
     tie @ary, "Tie::Simple", undef,
         SHIFT     => sub { eof($fh) ? undef : uc(~~<$fh> // "") },
         FETCHSIZE => sub { eof($fh) ? 0 : 1 };
     [200, "OK", \@ary, {is_stream=>1}];
 }

See also Data::Unixish and App::dux which deals with streams.

attribute: cmdline.exit_code => int

Instruct Perinci::CmdLine to use this exit code, instead of using (function status - 300).

attribute: cmdline.result => any

Replace result. Can be useful for example in this case:

 sub is_palindrome {
     my %args = @_;
     my $str = $args{str};
     my $is_palindrome = $str eq reverse($str);
     [200, "OK", $is_palindrome,
      {"cmdline.result" => ($is_palindrome ? "Palindrome" : "Not palindrome")}];
 }

When called as a normal function we return boolean value. But as a CLI, we display a more user-friendly message.

attribute: cmdline.default_format => str

Default format to use. Can be useful when you want to display the result using a certain format by default, but still allows user to override the default.

attribute: cmdline.page_result => bool

If you want to filter the result through pager (currently defaults to $ENV{PAGER} or less -FRSX), you can set cmdline.page_result in result metadata to true.

For example:

 $SPEC{doc} = { ... };
 sub doc {
     ...
     [200, "OK", $doc, {"cmdline.page_result"=>1}];
 }

attribute: cmdline.pager => STR

Instruct Perinci::CmdLine to use specified pager instead of $ENV{PAGER} or the default less or more.

attribute: cmdline.skip_format => bool (default: 0)

When we want the command-line framework to just print the result without any formatting.

attribute: x.perinci.cmdline.base.exit_code => int

This is added by this module, so exit code can be tested.

ENVIRONMENT

  • PAGER => str

    Like in other programs, can be set to select the pager program (when cmdline.page_result result metadata is active). Can also be set to '' or 0 to explicitly disable paging even though cmd.page_result result metadata is active.

  • PERINCI_CMDLINE_PROGRAM_NAME => STR

    Can be used to set CLI program name.

HOMEPAGE

Please visit the project's homepage at https://metacpan.org/release/Perinci-CmdLine-Lite.

SOURCE

Source repository is at https://github.com/perlancar/perl-Perinci-CmdLine-Lite.

BUGS

Please report any bugs or feature requests on the bugtracker website https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-CmdLine-Lite

When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.

AUTHOR

perlancar <perlancar@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2014 by perlancar@cpan.org.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.