NAME

Perinci::Access::Schemeless - Base class for Perinci::Access::Perl

VERSION

This document describes version 0.900 of Perinci::Access::Schemeless (from Perl distribution Perinci-Access-Perl), released on 2023-10-24.

DESCRIPTION

This class is the base class for Perinci::Access::Perl, and by default acts like Perinci::Access::Perl (e.g. given uri /Foo/Bar/baz it will refer to function baz in Perl package Foo::Bar; it also looks for Rinci metadata in %SPEC package variables by default). But this class is designed to be flexible: you can override aspects of it so it can map uri to different Perl packages (e.g. using option like package_prefix), you can retrieve Rinci metadata from a database or whatever, etc.

Supported features:

  • Basic Riap actions

    These include info, actions, meta, list, and call actions.

  • Transaction/undo

    According to Rinci::Transaction.

  • Function wrapping

    Wrapping is used to convert argument passing style, produce result envelope, add argument validation, as well as numerous other functionalities. See Perinci::Sub::Wrapper for more details on wrapping. The default behavior will call wrapped functions.

  • Custom location of metadata

    By default, metadata are assumed to be stored embedded in Perl source code in %SPEC package variables (with keys matching function names, $variable names, or :package for the package metadata itself).

    You can override get_meta() to provide custom behavior. For example, you can store metadata in separate file or database.

  • Custom code entity tree

    By default, tree are formed by traversing Perl packages and their contents, for example if a list action is requested on uri /Foo/Bar/ then the contents of package Foo::Bar and its subpackages will be traversed for the entities.

    You can override action_list() to provide custom behavior. For example, you can lookup from the database.

  • Progress indicator

    Functions can express that they do progress updating through the features property in its metadata:

    features => {
        progress => 1,
        ...
    }

    For these functions, this class will pass a special argument -progress containing Progress::Any object. Functions can update progress using this object.

How request is processed

User calls $pa->request($action => $uri, \%extras). Internally, the method creates a hash $req which contains Riap request keys as well as internal information about the Riap request (the latter will be prefixed with dash -). Initially it will contain action and uri and the %extras keys from the request() arguments sent by the user.

Internal _parse_uri() method will be called to parse uri into -uri_dir (the "dir" part), -uri_leaf (the "basename" part), and -perl_package. Forbidden or invalid paths will cause this method to return an enveloped error response and the request to stop. For example, if uri is /Foo/Bar/ then -uri_dir is /Foo/Bar/ and -uri_leaf is an empty string. If uri is /Foo/Bar/baz then -uri_dir is /Foo/Bar/ while -uri_leaf is baz. -uri_dir will be used for the list action. In both cases, -perl_package will be set to Foo::Bar.

The code entity type is then determined currently using a few simple heuristic rules: if -uri_leaf is empty string, type is package. If -uri_leaf begins with [$%@], type is variable. Otherwise, type is function. -type will be set.

After this, the appropriate action_ACTION() method will be called. For example if action is meta then action_meta() method will be called, with $req as the argument. This will in turn, depending on the action, either call get_meta() (for example if action is meta) or get_code() (for example if action is call), also with $req as the argument. get_meta() and get_code() should return nothing on success, and set either -meta (a defhash containing Rinci metadata) or -code (a coderef), respectively. On error, they must return an enveloped error response.

get_meta() or get_code() might call _load_module() to load Perl modules if the load attribute is set to true.

ADDED RESULT METADATA

This class might add the following property/attribute in result metadata:

x.hint.result_binary => bool

If result's schema type is buf, then this class will set this attribute to true, to give hints to result formatters.

METHODS

PKG->new(%attrs) => OBJ

Instantiate object. Known attributes:

  • load => BOOL (default: 1)

    Whether to load Perl modules that are requested.

  • after_load => CODE

    If set, code will be executed the first time Perl module is successfully loaded.

  • wrap => BOOL (default: 1)

    If set to false, then wil use original subroutine and metadata instead of wrapped ones, for example if you are very concerned about performance (do not want to add another eval {} and subroutine call introduced by wrapping) or do not need the functionality provided by the wrapper (e.g. your function already validates its arguments, accepts arguments as hash, and returns enveloped result).

    Wrapping is implemented inside get_code().

  • set_function_properties => HASH

    If set, will be passed to Perinci::Sub::Wrapper wrap_sub()'s convert argument when wrapping subroutines. Some applications of this include: changing default_lang of metadata.

    This is only relevant if you enable wrap.

  • cache_size => INT (default: 100)

    Specify cache size (in number of items), for caching metadata and wrapping result. Setting this to 0 disables caching.

    Caching is implemented inside get_meta() and get_code() so you might want to implement your own caching if you override those.

  • allow_paths => REGEX|STR|ARRAY

    If defined, only requests with uri matching specified path will be allowed. Can be a string (e.g. /spanel/api/) or regex (e.g. qr{^/[^/]+/api/}) or an array of those.

  • deny_paths => REGEX|STR|ARRAY

    If defined, requests with uri matching specified path will be denied. Like allow_paths, value can be a string (e.g. /spanel/api/) or regex (e.g. qr{^/[^/]+/api/}) or an array of those.

  • allow_schemes => REGEX|STR|ARRAY

    By default this class does not care about schemes, it only looks at the uri path. You can use this option to limit allowed schemes.

  • deny_schemes => REGEX|STR|ARRAY

    By default this class does not care about schemes, it only looks at the uri path. You can use this option to specify forbidden schemes.

  • use_tx => BOOL (default: 0)

    Whether to allow transaction requests from client. Since this can cause the server to store transaction/undo data, this must be explicitly allowed.

    You need to install Perinci::Tx::Manager for transaction support (unless you are using another transaction manager).

  • custom_tx_manager => STR|CODE

    Can be set to a string (class name) or a code that is expected to return a transaction manager class.

    By default, Perinci::Tx::Manager is instantiated and maintained (not reinstantiated on every request), but if custom_tx_manager is a coderef, it will be called on each request to get transaction manager. This can be used to instantiate Perinci::Tx::Manager in a custom way, e.g. specifying per-user transaction data directory and limits, which needs to be done on a per-request basis.

  • accept_argv => BOOL (default: 1)

    From version 0.64, argv key is accepted by the call action and will be converted to args. This server-side conversion from argv to <args> can handle coderefs in cmdline_aliases (and probably other things too) compared when doing conversion at the client-side.

    This option allows disabling this behavior.

$pa->request($action => $server_url, \%extra) => $res

Process Riap request and return enveloped result. $server_url will be used as the Riap request key 'uri', as there is no server in this case.

$pa->parse_url($server_url) => HASH

ENVIRONMENT

PERINCI_ACCESS_SCHEMELESS_DEBUG_DIE

Bool. If set to true, will not return exception (e.g. function dies when executed) as status in result envelope; but instead will rethrow the error. This will make checking the error (e.g. with Devel::Confess using -d:Confess perl flag) slightly easier.

HOMEPAGE

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

SOURCE

Source repository is at https://github.com/perlancar/perl-Perinci-Access-Perl.

SEE ALSO

Riap, Rinci

AUTHOR

perlancar <perlancar@cpan.org>

CONTRIBUTING

To contribute, you can send patches by email/via RT, or send pull requests on GitHub.

Most of the time, you don't need to build the distribution yourself. You can simply modify the code, then test via:

% prove -l

If you want to build the distribution (e.g. to try to install it locally on your system), you can install Dist::Zilla, Dist::Zilla::PluginBundle::Author::PERLANCAR, Pod::Weaver::PluginBundle::Author::PERLANCAR, and sometimes one or two other Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond that are considered a bug and can be reported to me.

COPYRIGHT AND LICENSE

This software is copyright (c) 2023, 2022, 2020, 2019, 2017, 2016, 2015, 2014, 2013, 2012 by perlancar <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.

BUGS

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

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.