The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Perinci::CmdLine::Manual::FAQ - FAQs

VERSION

This document describes version 0.52 of Perinci::CmdLine::Manual::FAQ (from Perl distribution Perinci-CmdLine-Lite), released on 2014-11-21.

NOMENCLATURE

[Full] denotes that the answer applies to Perinci::CmdLine only and not Perinci::CmdLine::Lite. [Lite] denotes that the answer is specific to Perinci::CmdLine::Lite. [Any] denotes that the answer is specific to Perinci::CmdLine::Any.

GENERAL

How does P::C compare with other CLI-app frameworks?

The main difference is that Perinci::CmdLine accesses your code through Riap protocol, not directly. This means that aside from local Perl code, P::C can also provide CLI for code in remote hosts/languages. For a very rough demo, download and run this PHP Riap::TCP server https://github.com/sharyanto/php-Phinci/blob/master/demo/phi-tcpserve-terbilang.php on your system. After that, try running:

 % peri-run riap+tcp://localhost:9090/terbilang --help
 % peri-run riap+tcp://localhost:9090/terbilang 1234

Everything from help message, calling, argument checking, tab completion works for remote code as well as local Perl code.

My application is OO?

This framework is currently non-OO and function-centric. There are already several OO-based command-line frameworks on CPAN.

DEBUGGING

How do I debug my program?

[Full] You can set environment DEBUG=1 or TRACE=1. See Log::Any::App for more details.

INPUT

How to accept input from STDIN (or files)?

If you specify 'cmdline_src' to 'stdin' to a 'str' argument, the argument's value will be retrieved from standard input if not specified. Example:

 use Perinci::CmdLine;
 $SPEC{cmd} = {
     v => 1.1,
     args => {
         arg => {
             schema => 'str*',
             cmdline_src => 'stdin',
         },
     },
 };
 sub cmd {
     my %args = @_;
     [200, "OK", "arg is '$args{arg}'"];
 }
 Perinci::CmdLine->new(url=>'/main/cmd')->run;

When run from command line:

 % cat file.txt
 This is content of file.txt
 % cat file.txt | cmd
 arg is 'This is content of file.txt'

If your function argument is an array, array of lines will be provided to your function. A mechanism to be will be provided in the future (currently not yet specified in Rinci::function specification).

But I don't want the whole file content slurped into string/array, I want streaming!

Riap 1.2 defines streaming input (and output), which P::C supports to some degree. If you set your argument property with stream then local input streaming will work. Argument retrieved from file/stdin will be presented to function as filehandle, which the function can then read data from.

Currently, see App::dux for an example on how to accomplish this on function argument of type array. Basically in App::dux, you feed an array tied with Tie::Diamond as a function argument. Thus you can get lines from file/STDIN iteratively with each().

My function has some cmdline_aliases or cmdline_src defined but I want to change it!

For example, your f1 function metadata might look like this:

 package Package::F1;
 our %SPEC;
 $SPEC{f1} = {
     v => 1.1,
     args => {
         foo => {
             cmdline_aliases => { f=> {} },
         },
         bar => { ... },
         fee => { ... },
     },
 };
 sub f1 { ... }
 1;

And your command-line script f1:

 #!perl
 use Perinci::CmdLine;
 Perinci::CmdLine->new(url => '/Package/F1/f1')->run;

Now you want to create a command-line script interface for this function, but with -f as an alias for --fee instead of --foo. This is best done by modifying the metadata and creating a wrapper function to do this, e.g. your command-line script f1 becomes:

 package main;
 use Perinci::CmdLine;
 use Package::F1;
 use Data::Clone;
 our %SPEC;
 $SPEC{f1} = clone $Package::F1::SPEC{f1};
 delete $SPEC{f1}{args}{foo}{cmdline_aliases};
 $SPEC{f1}{args}{fee}{cmdline_aliases} = {f=>{}};
 *f1 = \&Package::F1::f1;
 Perinci::CmdLine->new(url => '/main/f1')->run;

This also demonstrates the convenience of having the metadata as a data structure: you can manipulate it however you want. There is also a convenient function available in Perinci::Sub::Util when you want to create a modified subroutine based on another:

 package main;
 use Perinci::CmdLine;
 use Perinci::Sub::Util qw(gen_modified_sub);

 gen_modified_sub(
     output_name => 'f1',
     base_name   => 'Package::F1::f1',
     modify_args => {
         foo => sub { my $as = shift; delete $as->{cmdline_aliases}   },
         fee => sub { my $as = shift; $as->{cmdline_aliases} = {f=>{} },
     },
 );
 Perinci::CmdLine->new(url => '/main/f1')->run;

OUTPUT AND FORMATTING

How to add support for new output format (e.g. XML, HTML)?

See Perinci::Result::Format.

My function has argument named 'format', but it is blocked by common option '--format'!

To add/remove/rename common options, see the documentation on common_opts attribute. In this case, you want:

 delete $cmd->common_opts->{format};
 #delete $cmd->common_opts->{format_options}; # you might also want this

or perhaps rename it:

 $cmd->common_opts->{output_format} = $cmd->common_opts->{format};
 delete $cmd->common_opts->{format};

How to stream output?

XXX

SHELL COMPLETION

How does completion work with P::C?

Currently only bash completion is supported. Basically, you activate completion by invoking complete -C ProgramName ProgramName in bash. This means, to get completion bash will execute the program with COMP_LINE and COMP_POINT environment set to the command-line and cursor position, respectively. P::C will detect these environment variables and do the completion for you (either providing list of subcommands, list of available options, or argument values). The process is quite transparent to you and is convenient.

Another alternative used by many is to generate complete bash commands statically.

The benefit of using the program to complete itself is convenience/flexibility: when arguments and subcommands change, completion need not be updated separately. If you generate completion commands, it will need to be regenerated and repasted to shell startup file.

The downside of using the program to complete itself is startup overhead. That's why it's important for P::C to keep having a small startup overhead. An alternative, P::C::Lite, is written specifically with this issue in mind. The goal of P::C::Lite is to have startup overhead of under 0.05s, so it can still feel instantaneous. Of course, you also need to do your part by not loading too many modules or by delaying loading modules to avoid having a large startup overhead.

How to do custom completion for my argument?

By default, Perinci::Sub::Complete's complete_arg_val() can employ some heuristics to complete argument values, e.g. from the in clause or max and min:

 $SPEC{set_ticket_status} = {
     v => 1.1,
     args => {
         ticket_id => { ... },
         status => {
             schema => ['str*', in => [qw/new open stalled resolved rejected/],
         },
     },
 }

But if you want to supply custom completion, the Rinci::function specification allows specifying a completion property for your argument, for example:

 use Complete::Util qw(complete_array_elem);
 $SPEC{del_user} = {
     v => 1.1,
     args => {
         username => {
             schema => 'str*',
             req => 1,
             pos => 0,
             completion => sub {
                 my %args = @_;

                 # get list of users from database or whatever
                 my @users = ...;
                 complete_array_elem(array=>\@users, word=>$args{word});
             },
         },
         ...
     },
 };

You can use completion your command-line program:

 % del-user --username <tab>
 % del-user <tab> ; # since the 'username' argument has pos=0

My custom completion does not work, how do I debug it?

Completion works by the shell invoking our (the same) program with COMP_LINE and COMP_POINT environment variables. You can do something like this to see debugging information:

 % COMP_LINE='myprog --arg x' COMP_POINT=13 PERL5OPT=-MLog::Any::App TRACE=1 myprog --arg x

SEE ALSO

Perinci::CmdLine::Manual

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.