NAME
Perinci::Access::InProcess - Use Rinci access protocol (Riap) to access Perl code
VERSION
version 0.43
SYNOPSIS
# in Your/Module.pm
package My::Module;
our %SPEC;
$SPEC{mult2} = {
v => 1.1,
summary => 'Multiple two numbers',
args => {
a => { schema=>'float*', req=>1, pos=>0 },
b => { schema=>'float*', req=>1, pos=>1 },
},
examples => [
{args=>{a=>2, b=>3}, result=>6},
],
};
sub mult2 {
my %args = @_;
[200, "OK", $args{a} * $args{b}];
}
$SPEC{multn} = {
v => 1.1,
summary => 'Multiple many numbers',
args => {
n => { schema=>[array=>{of=>'float*'}], req=>1, pos=>0, greedy=>1 },
},
};
sub multn {
my %args = @_;
my @n = @{$args{n}};
my $res = 0;
if (@n) {
$res = shift(@n);
$res *= $_ while $_ = shift(@n);
}
return [200, "OK", $res];
}
1;
# in another file
use Perinci::Access::InProcess;
my $pa = Perinci::Access::Process->new();
# list all functions in package
my $res = $pa->request(list => '/My/Module/', {type=>'function'});
# -> [200, "OK", ['pl:/My/Module/mult2', 'pl:/My/Module/multn']]
# call function
my $res = $pa->request(call => 'pl:/My/Module/mult2', {args=>{a=>2, b=>3}});
# -> [200, "OK", 6]
# get function metadata
$res = $pa->request(meta => '/Foo/Bar/multn');
# -> [200, "OK", {v=>1.1, summary=>'Multiple many numbers', ...}]
DESCRIPTION
This class implements Rinci access protocol (Riap) to access local Perl code. This might seem like a long-winded and slow way to access things that are already accessible from Perl like functions and metadata (in %SPEC
). Indeed, if you do not need Riap, you can access your module just like any normal Perl module.
Supported features:
Basic Riap actions
These include
info
,actions
,meta
,list
, andcall
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 packageFoo::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, periai will then 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
(converted to URI object) 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.
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 does not die and already validates its arguments, you do not want Sah schemas in the metadata to be normalized, etc).
Wrapping is implemented inside
get_meta()
andget_code()
.extra_wrapper_args => HASH
If set, will be passed to Perinci::Sub::Wrapper's wrap_sub() when wrapping subroutines. Some applications of this include: adding
timeout
orresult_postfilter
properties to functions.This is only relevant if you enable
wrap
.extra_wrapper_convert => HASH
If set, will be passed to Perinci::Sub::Wrapper wrap_sub()'s
convert
argument when wrapping subroutines. Some applications of this include: changingdefault_lang
of metadata.This is only relevant if you enable
wrap
.cache_size => INT (default: 100)
Specify cache size (in number of items). Cache saves the result of function wrapping so future requests to the same function need not involve wrapping again. Setting this to 0 disables caching.
Caching is implemented inside
get_meta()
andget_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. Likeallow_paths
, value can be a string (e.g./spanel/api/
) or regex (e.g.qr{^/[^/]+/api/}
) or an array of those.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.
$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
FAQ
Why wrap?
The wrapping process accomplishes several things, among others: checking of metadata, normalization of schemas in metadata, also argument validation and exception trapping in function.
The function wrapping introduces a small overhead when performing a sub call (typically around several to tens of microseconds on an Intel Core i5 1.7GHz notebook). This is usually smaller than the overhead of Perinci::Access::InProcess itself (typically in the range of 100 microseconds). But if you are concerned about the wrapping overhead, see the use_wrapped_sub
option.
Why %SPEC?
The name was first chosen when during Sub::Spec era, so it stuck.
SEE ALSO
AUTHOR
Steven Haryanto <stevenharyanto@gmail.com>
COPYRIGHT AND LICENSE
This software is copyright (c) 2013 by Steven Haryanto.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.