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::Server - Create Perinci::CmdLine::Classic object and some functions to access it in a Perl package

VERSION

This document describes version 0.06 of Perinci::CmdLine::Server (from Perl distribution Perinci-CmdLine-Server), released on 2015-09-03.

SYNOPSIS

Running the server

From your Perinci::Access::HTTP::Server-based PSGI application:

 use Perinci::CmdLine::Server qw(create_cmdline_server);
 create_cmdline_server(
     name         => 'app1',
     cmdline_args => {
         url         => '/Some/Module/some_func',
         log_any_app => 0,
     },
 );

Or, shortcut for simple cases:

 use Perinci::CmdLine::Server -app1 => '/Some/Module/some_func';

Or, for testing using peri-htserve:

 % peri-htserve --gepok-unix-sockets /tmp/app1.sock \
     -MPerinci::CmdLine::Server=-app1,/Some/Module/some_func \
     Perinci::CmdLine::Server::app::app1,noload

Using the server for completion

 # foo-complete
 #!perl
 use HTTP::Tiny::UNIX;
 use JSON;

 my $hres = HTTP::Tiny::UNIX->new->post_form(
    'http:/tmp/app1.sock//api/Perinci/CmdLine/Server/app/app1/complete_cmdline',
    {
        cmdline     => $ENV{COMP_LINE},
        point       => $ENV{COMP_POINT},
        '-riap-fmt' => 'json',
    },
 );
 my $rres = decode_json($hres->{content});
 print $rres->[2];

Activate bash tab completion:

 % chmod +x foo-complete
 % complete -C foo-complete foo
 % foo <Tab>

Now foo will be tab-completed using Rinci specification from Some::Module's some_func.

DESCRIPTION

Currently, Perinci::CmdLine::Classic-based CLI applications have a perceptible startup overhead (between 0.15-0.35s or even more, depending on your hardware, those numbers are for 2011-2013 PC/laptop hardware). Some of the cause of the overhead is subroutine wrapping (see Perinci::Sub::Wrapper) which also involves compilation of Sah schemas (see Data::Sah), all of which are necessary for the convenience of using Rinci metadata to specify aspects of your functions.

This level of overhead is a bit annoying when we are doing shell tab completion (Perinci::CmdLine::Classic-based applications call themselves for doing tab completion, e.g. through bash's complete -C progname progname mechanism). Ideally, tab completion should take no longer than 0.05-0.1s to feel instantaneous.

One (temporary?) solution to this annoyance is to start a daemon that listens to Riap requests (either through Unix domain sockets or TCP/IP). This way, the completion external command can just be a lightweight HTTP client which asks the server for the completion and displays the result to STDOUT for bash (this only requires, e.g. HTTP::Tiny::Unix + Complete::Bash).

In the future, other functionalities aside from completion can also be "off-loaded" to the server side to make the CLI program lighter and quicker to start. This might require a refactoring of Perinci::CmdLine::Classic codebase so it's more "stateless" and reusable/safer for multiple requests (perhaps will be made non-OO in the core so it's clear what states are being passed?)

In the future, Perinci::CmdLine::Classic might also be configured to automatically start a daemon after the first run (and retire/kill the daemon after being idle for, say, 30 minute or an hour).

How does it work?

In your Perinci::Access::HTTP::Server-based PSGI application:

 use Perinci::CmdLine::Server qw(create_cmdline_server);
 create_cmdline_server(
     name         => 'app1',
     cmdline_args => {
         url         => '/Some/Module/some_func',
         log_any_app => 0,
     },
 );

This will create an instance of Perinci::CmdLine::Classic object (the cmdline_args argument will be fed to the constructor). It will also create a Perl package dynamically (the default is Perinci::CmdLine::Server::app:: + application name specified in name argument). The package will contain several functions along with their Rinci metadata. The functions can then be accessed over Riap protocol. So far, the only function available is: complete_cmdline. You can use it to request command-line completion. The Perinci::CmdLine::Classic object will persist as long as the process lives. You can of course start several applications.

Caveats

Leaving daemons around could give rise to some security and resource-usage issues. It is ideal in situations where you already have a daemon for other purposes (for example, in Spanel there is already an API daemon service running; the command-line client uses this daemon to request for tab completion).

Some code which normally runs on the client-side will now run on the server-side. For example, the custom_completer and custom_arg_completer code. You have to make sure that authentication and authorization issues are handled.

FUNCTIONS

create_cmdline_server(%args) -> any

Create Perinci::CmdLine::Classic object and some functions to access it in a Perl package.

Currently the functions created are:

 complete_cmdline

Arguments ('*' denotes required arguments):

  • cmdline => obj

    A Perinci::CmdLine::Classic object.

    If you set this argument, you're passing an existing object. Otherwise, a new Perinci::CmdLine::Classic object will be created (with cmdline_args passed to its constructor).

    Note: it is desirable that your existing Perinci::CmdLine::Classic object has its attribute exit => 0 :-) Otherwise, it will exit after the first run().

  • cmdline_args => hash (default: {})

    Arguments to be fed to Perinci::CmdLine::Classic constructor.

    If you specify this argument and not cmdline, a new Perinci::CmdLine::Classic object will be created.

  • name* => str

    Application name.

    This function stores the created functions in a hash, keyed by name. If you create an application with the same name as previously created, the previous instance will be replaced.

  • package => str

    Where to put the functions to access the object.

    The default is Perinci::CmdLine::Server::app:: + <name>. But you can put it somewhere else. The functions will be installed here.

Return value: (any)

SEE ALSO

Perinci::CmdLine::Classic

Perinci::Access::HTTP::Server

HOMEPAGE

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

SOURCE

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

BUGS

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

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) 2015 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.