File Coverage

blib/lib/Perinci/Access/Schemeless.pm
Criterion Covered Total %
statement 435 546 79.6
branch 182 296 61.4
condition 85 116 73.2
subroutine 59 73 80.8
pod 3 43 6.9
total 764 1074 71.1


line stmt bran cond sub pod time code
1             package Perinci::Access::Schemeless;
2              
3             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
4             our $DATE = '2020-05-18'; # DATE
5             our $DIST = 'Perinci-Access-Perl'; # DIST
6             our $VERSION = '0.893'; # VERSION
7              
8 3     3   158977 use 5.010001;
  3         26  
9 3     3   18 use strict;
  3         6  
  3         62  
10 3     3   15 use warnings;
  3         5  
  3         106  
11 3     3   907 use experimental 'smartmatch';
  3         11163  
  3         19  
12 3     3   7069 use Log::ger;
  3         174  
  3         14  
13              
14 3     3   1163 use parent qw(Perinci::Access::Base);
  3         281  
  3         19  
15              
16 3     3   10875 use List::Util qw(first);
  3         6  
  3         261  
17 3     3   1351 use Perinci::Object;
  3         1350  
  3         234  
18 3     3   1475 use Perinci::Sub::Normalize qw(normalize_function_metadata);
  3         3836  
  3         204  
19 3     3   1818 use Perinci::Sub::Util qw(err);
  3         7156  
  3         215  
20 3     3   22 use Scalar::Util qw(blessed);
  3         6  
  3         262  
21 3     3   2230 use Module::Path::More qw(module_path);
  3         3645  
  3         194  
22 3     3   1506 use Package::Util::Lite qw(package_exists);
  3         1354  
  3         176  
23 3     3   1689 use Tie::Cache;
  3         9136  
  3         108  
24 3     3   22 use URI::Split qw(uri_split uri_join);
  3         6  
  3         5621  
25              
26             our $re_perl_package =
27             qr/\A[A-Za-z_][A-Za-z_0-9]*(::[A-Za-z_0-9][A-Za-z_0-9]*)*\z/;
28              
29             sub new {
30 36     36 1 159770 require Class::Inspector;
31              
32 36         10805 my $class = shift;
33 36         172 my $self = $class->SUPER::new(@_);
34              
35             # build a list of supported actions for each type of entity
36 36         395 my %typeacts = (
37             package => [],
38             function => [],
39             variable => [],
40             ); # key = type, val = [[ACTION, META], ...]
41              
42 36         68 my @comacts;
43 36         64 for my $meth (@{Class::Inspector->methods(ref $self)}) {
  36         166  
44 2846 100       35859 next unless $meth =~ /^actionmeta_(.+)/;
45 685         1371 my $act = $1;
46 685         1579 my $meta = $self->$meth();
47 685         1378 $self->{_actionmetas}{$act} = $meta;
48 685         894 for my $type (@{$meta->{applies_to}}) {
  685         1232  
49 685 100       1187 if ($type eq '*') {
50 469         1324 push @comacts, [$act, $meta];
51             } else {
52 216         323 push @{$typeacts{$type}}, [$act, $meta];
  216         672  
53             }
54             }
55             }
56 36         234 for my $type (keys %typeacts) {
57 1623         3341 $typeacts{$type} = { map {$_->[0] => $_->[1]}
58 108         195 @{$typeacts{$type}}, @comacts };
  108         238  
59             }
60 36         155 $self->{_typeacts} = \%typeacts;
61              
62 36   50     203 $self->{cache_size} //= 100; # for caching metadata & code
63             #$self->{use_tx} //= 0;
64 36   100     141 $self->{wrap} //= 1;
65             #$self->{custom_tx_manager} //= undef;
66 36   50     146 $self->{load} //= 1;
67 36   100     151 $self->{normalize_metadata} //= 1;
68             #$self->{after_load}
69             #$self->{allow_paths}
70             #$self->{deny_paths}
71             #$self->{allow_schemes}
72             #$self->{deny_schemes}
73             #$self->{package_prefix}
74 36   50     264 $self->{debug} //= $ENV{PERINCI_ACCESS_SCHEMELESS_DEBUG} // 0;
      33        
75 36   50     165 $self->{accept_argv} //= 1;
76              
77 36 50       102 if ($self->{cache_size} > 0) {
78 36         57 my %metacache;
79 36         229 tie %metacache, 'Tie::Cache', $self->{cache_size};
80 36         1050 $self->{_meta_cache} = \%metacache;
81 36         63 my %codecache;
82 36         108 tie %codecache, 'Tie::Cache', $self->{cache_size};
83 36         721 $self->{_code_cache} = \%codecache;
84             }
85              
86 36         190 $self;
87             }
88              
89             # for older Perinci::Access::Base 0.28-, to remove later
90       0     sub _init {}
91              
92             # if paths=/a/b, will match /a/b as well as /a/b/c
93             sub __match_paths {
94 30     30   1617 my ($path, $paths) = @_;
95              
96 30 100       136 my $pathslash = $path =~ m!/\z! ? $path : "$path/";
97              
98 30 50       89 for (ref($paths) eq 'ARRAY' ? @$paths : $paths) {
99 30 100       56 if (ref($_) eq 'Regexp') {
100 8 100       63 return 1 if $path =~ $_;
101             } else {
102 22 100       58 if (m!/\z!) {
103 10 100 100     64 return 1 if $_ eq $pathslash || index($pathslash, $_) == 0;
104             } else {
105 12         26 my $p = "$_/";
106 12 100 100     79 return 1 if $p eq $path || index($pathslash, $p) == 0;
107             }
108             }
109             }
110 15         62 0;
111             }
112              
113             # if paths=/a/b, will match /a/b as well as /a/b/c AS WELL AS /a and /. only
114             # suitable for 'list' action, e.g. allow_path is '/a/b' but we can do 'list /'
115             # and 'list /a' too (but not 'list /c').
116             sub __match_paths2 {
117 437     437   5271 my ($path, $paths) = @_;
118              
119 437 100       1221 my $pathslash = $path =~ m!/\z! ? $path : "$path/";
120              
121 437 50       886 for (ref($paths) eq 'ARRAY' ? @$paths : $paths) {
122 437 100       713 if (ref($_) eq 'Regexp') {
123             # we can't match a regex against a string, so we just pass here
124 52         157 return 1;
125             } else {
126 385 100       674 if (m!/\z!) {
127 10 100 100     77 return 1 if $_ eq $pathslash || index($_, $pathslash) == 0 ||
      100        
128             index($pathslash, $_) == 0;
129             } else {
130 375         580 my $p = "$_/";
131 375 100 100     1906 return 1 if $p eq $path || index($p, $pathslash) == 0 ||
      100        
132             index($pathslash, $p) == 0 ;
133             }
134             }
135             }
136 309         881 0;
137             }
138              
139             sub _parse_uri {
140 84     84   184 my ($self, $req) = @_;
141              
142 84         168 my $path = $req->{-uri_path};
143 84 100       206 if (defined $self->{allow_paths}) {
144 5         60 my $allow;
145 5 100       19 if ($self->{_actionmetas}{$req->{action}}{allow_request_parent_path}) {
146 4         10 $allow = __match_paths2($path, $self->{allow_paths});
147             } else {
148 1         5 $allow = __match_paths($path, $self->{allow_paths});
149             }
150 5 100       19 return err(403, "Forbidden uri path (does not match allow_paths)")
151             unless $allow;
152             }
153 83 100 100     256 if (defined($self->{deny_paths}) &&
154             __match_paths($path, $self->{deny_paths})) {
155 1         5 return err(403, "Forbidden uri path (matches deny_paths)");
156             }
157              
158 82   100     342 my $sch = $req->{-uri_scheme} // "";
159 82 100 100     278 if (defined($self->{allow_schemes}) && !($sch ~~ $self->{allow_schemes})) {
160 3         15 return err(501,
161             "Unsupported uri scheme (does not match allow_schemes)");
162             }
163 79 100 100     215 if (defined($self->{deny_schemes}) && ($sch ~~ $self->{deny_schemes})) {
164 1         5 return err(501, "Unsupported uri scheme (matches deny_schemes)");
165             }
166              
167 78         134 my ($dir, $leaf, $perl_package);
168 78 50       385 if ($path =~ m!(.*)/(.*)!) {
169 78         225 $dir = $1;
170 78         160 $leaf = $2;
171             } else {
172 0         0 $dir = $path;
173 0         0 $leaf = '';
174             }
175 78         170 for ($perl_package) {
176 78         121 $_ = $dir;
177 78         276 s!^/+!!;
178 78         293 s!/+!::!g;
179 78 50       290 if (defined $self->{package_prefix}) {
180 0 0       0 $_ = $self->{package_prefix} . (length($_) ? "::":"") . $_;
181             }
182             }
183 78 50 66     630 return err(400, "Invalid perl package name: $perl_package")
184             if $perl_package && $perl_package !~ $re_perl_package;
185              
186 78         146 my $type;
187 78 100       192 if (length $leaf) {
188 43 100       131 if ($leaf =~ /^[%\@\$]/) {
189 2         5 $type = 'variable';
190             } else {
191 41         75 $type = 'function';
192             }
193             } else {
194 35         64 $type = 'package';
195             # make sure path ends in /, to ease processing
196 35 50       135 $req->{-uri_path} .= "/" unless $path =~ m!/\z!;
197             }
198              
199 78         168 $req->{-uri_dir} = $dir;
200 78         144 $req->{-uri_leaf} = $leaf;
201 78         140 $req->{-perl_package} = $perl_package;
202 78         136 $req->{-type} = $type;
203              
204             #$log->tracef("TMP: req=%s", $req);
205 78         181 return;
206             }
207              
208             # key = module_p, val = error resp or undef if successful
209             my %loadcache;
210             tie %loadcache, 'Tie::Cache', 200;
211              
212             sub _load_module {
213 65     65   121 my ($self, $req) = @_;
214              
215 65         141 my $pkg = $req->{-perl_package};
216              
217             # skip there is no module to load
218 65 100       147 return if !$pkg;
219              
220             # if we are instructed not to load any module, we just check via existence
221             # of packages
222 64 50       162 unless ($self->{load}) {
223 0 0       0 return if package_exists($pkg);
224 0         0 return err(500, "Package $pkg does not exist");
225             }
226              
227 64         110 my $module_p = $pkg;
228 64         243 $module_p =~ s!::!/!g;
229 64         132 $module_p .= ".pm";
230              
231             # module has been required before and successfully loaded
232 64 100       207 return if $INC{$module_p};
233              
234             # module has been required before and failed
235             return err(500, "Module $pkg has failed to load previously" .
236             $loadcache{$module_p} ?
237             ": $loadcache{$module_p}[0] - $loadcache{$module_p}[1]" :
238             "")
239 46 50       105 if exists($INC{$module_p});
    100          
240              
241             # use cache result (for caching errors, or packages like 'main' and 'CORE'
242             # where no modules for such packages exist)
243 45 100       187 return $loadcache{$module_p} if exists $loadcache{$module_p};
244              
245             # load and cache negative result
246 41         310 my $res;
247             {
248 41         70 my $fullpath = module_path(module=>$pkg, find_pmc=>0, find_prefix=>1);
  41         146  
249              
250             # when the module path does not exist, but the package does, we can
251             # ignore this error. for example: main, CORE, etc.
252 41         14955 my $pkg_exists = package_exists($pkg);
253              
254 41 100       1156 if (!$fullpath) {
    100          
255 30 100       99 last if $pkg_exists;
256 1         5 $res = [404, "Can't find module or prefix path for package $pkg"];
257 1         2 last;
258             } elsif ($fullpath !~ /\.pm$/) {
259 7 50       27 last if $pkg_exists;
260 0         0 $res = [405, "Can only find a prefix path for package $pkg"];
261 0         0 last;
262             }
263 4         10 eval { require $module_p };
  4         3424  
264 4 100       9647 if ($@) {
265 1 50       45 die if $ENV{PERINCI_ACCESS_SCHEMELESS_DEBUG};
266 1         6 $res = [500, "Can't load module $pkg (probably compile error): $@"];
267 1         3 last;
268             }
269             # load is successful
270 3 100       14 if ($self->{after_load}) {
271 1         2 eval { $self->{after_load}($self, module=>$pkg) };
  1         5  
272 1 50       7 if ($@) {
273 0 0       0 die if $ENV{PERINCI_ACCESS_SCHEMELESS_DEBUG};
274 0         0 log_error("after_load for package $pkg dies: $@");
275             }
276             }
277             }
278 41         240 $loadcache{$module_p} = $res;
279 41         512 return $res;
280             }
281              
282             sub __inject_entity_v_date {
283 3     3   27 no strict 'refs';
  3         7  
  3         514  
284              
285 44     44   93 my ($req, $meta) = @_;
286              
287 44         92 my $pkg = $req->{-perl_package};
288 44 100       121 unless (defined $meta->{entity_v}) {
289 38         60 my $ver = ${"$pkg\::VERSION"};
  38         152  
290 38 100       105 if (defined $ver) {
291 25         60 $meta->{entity_v} = $ver;
292             }
293             }
294 44 100       109 unless (defined $meta->{entity_date}) {
295 41         66 my $date = ${"$pkg\::DATE"};
  41         120  
296 41 100       106 if (defined $date) {
297 13         27 $meta->{entity_date} = $date;
298             }
299             }
300             }
301              
302             sub get_meta {
303 3     3   21 no strict 'refs';
  3         7  
  3         2170  
304              
305 64     64 0 130 my ($self, $req) = @_;
306              
307 64         163 my $pkg = $req->{-perl_package};
308 64         128 my $leaf = $req->{-uri_leaf};
309 64         105 my $type = $req->{-type};
310 64 100       165 if (!length($pkg)) {
311 5 100       16 if (length $leaf) {
312             # 404 for all non-subpackage entity directly under /
313 1 50       8 return [404, "No metadata for ::$leaf (".
314             (package_exists($pkg) ? "package '$pkg' exists, perhaps you mentioned '$pkg' somewhere without actually loading the module, or perhaps '$leaf' is a typo?" :
315             "package '$pkg' doesn't exist, perhaps '$pkg' or '$leaf' is a typo?").
316             ")"
317             ];
318             } else {
319             # empty metadata for root (/)
320 4         15 $req->{-meta} = {v=>1.1};
321 4         12 return;
322             }
323             }
324              
325 59         136 my $name = "$pkg\::$leaf";
326 59 100       359 if ($self->{_meta_cache}{$name}) {
327 17         419 $req->{-meta} = $self->{_meta_cache}{$name};
328             $req->{-orig_meta} = $self->{_orig_meta_cache}{$name}
329 17 50       288 if $self->{_orig_meta_cache}{$name};
330 17         45 return;
331             }
332              
333 42         731 my $res = $self->_load_module($req);
334             # missing module (but existing prefix) is okay for package, we construct an
335             # empty package metadata for it
336 42 100 33     314 return $res if $res && !($type eq 'package' && $res->[0] == 405);
      66        
337              
338 36         64 my $meta;
339 36         58 my $metas = \%{"$pkg\::SPEC"};
  36         153  
340 36   100     151 $meta = $metas->{ $leaf || ":package" };
341              
342 36 100 100     148 if (!$meta && $type eq 'package') {
343 6         19 $meta = {v=>1.1};
344             }
345              
346 36 100       104 return err(404,
347             join("",
348             "No metadata for $name (package '$pkg' exists, ",
349             "perhaps you mentioned '$pkg' ",
350             "somewhere without actually loading the module, or ",
351             "perhaps '$leaf' is a typo?)",
352             )) unless $meta;
353              
354 33 50       74 if ($res) {
355 0 0       0 if ($res->[0] == 405) {
    0          
356 0         0 $meta = {v=>1.1}; # empty package metadata for dir
357             } elsif ($res->[0] != 200) {
358 0         0 return $res;
359             }
360             }
361              
362             # normalize has only been implemented for function
363 33 100 100     153 if ($type eq 'function' && $self->{normalize_metadata}) {
364 18         36 eval { $meta = normalize_function_metadata($meta) };
  18         69  
365 18 50       10142 if ($@) {
366 0 0       0 die if $ENV{PERINCI_ACCESS_SCHEMELESS_DEBUG};
367 0         0 return [500, "Can't normalize function metadata: $@"];
368             }
369              
370 18   100     79 $meta->{args} //= {};
371 18         49 $meta->{_orig_args_as} = $meta->{args_as};
372 18         43 $meta->{args_as} = 'hash';
373 18         35 $meta->{_orig_result_naked} = $meta->{result_naked};
374 18         41 $meta->{result_naked} = 0;
375 18         36 my $sfp = $self->{set_function_properties};
376 18 50       51 if ($sfp) {
377 0         0 for (keys %$sfp) {
378 0         0 $meta->{"_orig_$_"} = $meta->{$_};
379 0         0 $meta->{$_} = $sfp->{$_};
380             }
381             }
382             }
383              
384 33         102 __inject_entity_v_date($req, $meta);
385              
386 33 50       89 if ($self->{cache_size} > 0) {
387 33         158 $self->{_meta_cache}{$name} = $meta;
388             }
389              
390 33         1624 $req->{-meta} = $meta;
391 33         80 return;
392             }
393              
394             sub get_code {
395 19     19 0 44 my ($self, $req) = @_;
396              
397             # because we're lazy, we assume here that type is already function. it
398             # should be okay since get_code() is only called by action_call().
399              
400 19         54 my $name = $req->{-perl_package} . "::" . $req->{-uri_leaf};
401 19 100       113 if ($self->{_code_cache}{$name}) {
402 6         109 $req->{-code} = $self->{_code_cache}{$name};
403 6         83 return;
404             }
405              
406 13         206 my $res = $self->_load_module($req);
407 13 50       34 return $res if $res;
408              
409             return err(404, "Can't find function $req->{-uri_leaf} in ".
410             "module $req->{-perl_package}")
411 13 50       22 unless defined &{$name};
  13         55  
412              
413             # we get our own meta and not use get_meta() because we want to get the
414             # original metadata
415 13         26 my $meta;
416             {
417 3     3   24 no strict 'refs';
  3         9  
  3         1206  
  13         19  
418 13         23 my $metas = \%{"$req->{-perl_package}::SPEC"};
  13         91  
419 13   50     56 $meta = $metas->{ $req->{-uri_leaf} || ":package" };
420             }
421              
422 13 50       30 return err(404, "Can't find function metadata $req->{-uri_leaf} in ".
423             "module $req->{-perl_package}")
424             unless $meta;
425              
426 13         24 my $code;
427             GET_CODE:
428             {
429             # we don't need to wrap
430 13 100 66     19 if (!$self->{wrap} ||
  13   100     81  
431             $meta->{"x.perinci.sub.wrapper.logs"} &&
432 1     1   8 (first {$_->{validate_args}}
433 1         7 @{ $meta->{"x.perinci.sub.wrapper.logs"} })
434             ) {
435 2         6 $code = \&{$name};
  2         8  
436 2         4 last GET_CODE;
437             }
438              
439 11         1939 require Perinci::Sub::Wrapper;
440 11         17368 my $sfp = $self->{set_function_properties};
441              
442 11 50       81 my $wrapres = Perinci::Sub::Wrapper::wrap_sub(
443             sub_name=>$name, meta=>$meta,
444             convert=>{args_as=>'hash', result_naked=>0,
445             ($sfp ? %$sfp : ())
446             });
447 11 50       183221 return err(500, "Can't wrap function", $wrapres)
448             unless $wrapres->[0] == 200;
449 11         32 $code = $wrapres->[2]{sub};
450              
451 11 50       50 if ($self->{cache_size} > 0) {
452 11         83 $self->{_code_cache}{$name} = $code;
453             # also put wrapper-generated meta in the cache, so further meta
454             # request can use this. the metadata from wrapper contains wrapper
455             # logs (x.perinci.sub.wrapper.logs) which can be helpful hint for
456             # some uses.
457 11         555 my $newmeta = $wrapres->[2]{meta};
458 11         31 $newmeta->{_orig_result_naked} = $meta->{result_naked};
459 11         23 $newmeta->{_orig_args_as} = $meta->{args_as};
460 11 50       31 if ($sfp) {
461 0         0 for (keys %$sfp) {
462 0         0 $newmeta->{"_orig_$_"} = $meta->{$_};
463             }
464             }
465 11         35 __inject_entity_v_date($req, $newmeta);
466 11         47 $self->{_meta_cache}{$name} = $newmeta;
467             }
468             }
469              
470 13         718 $req->{-code} = $code;
471 13         35 return;
472             }
473              
474             sub request {
475 3     3   24 no strict 'refs';
  3         6  
  3         2938  
476              
477 85     85 1 251097 my ($self, $action, $uri, $extra) = @_;
478              
479 85 50       241 return err(400, "Please specify URI") unless $uri;
480              
481 85   100     146 my $req = { action=>$action, uri=>$uri, %{$extra // {}} };
  85         491  
482 85         366 my $res = $self->check_request($req);
483 85 50       3264 return $res if $res;
484              
485 85 100       472 return err(501, "Action '$action' not implemented")
486             unless $self->can("actionmeta_$action");
487              
488 84         177 my $am = $self->${\("actionmeta_$action")};
  84         298  
489              
490 84         239 $res = $self->_parse_uri($req);
491 84 100       511 return $res if $res;
492              
493             return err(501, "Action '$action' not implemented for ".
494             "'$req->{-type}' entity")
495             unless $am->{applies_to}[0] eq '*' ||
496 78 100 100     279 $req->{-type} ~~ @{ $am->{applies_to} };
  36         191  
497              
498 76         197 my $meth = "action_$action";
499             # check transaction
500              
501 76         264 $res = $self->$meth($req);
502 76 50       2759 if ($self->{debug}) {
503 0   0     0 $res->[3] //= {};
504             $res->[3]{debug} = {
505 0         0 req => $req,
506             };
507             }
508 76         486 $res;
509             }
510              
511             sub parse_url {
512 2     2 1 10 my ($self, $uri) = @_;
513 2 50       6 die "Please specify url" unless $uri;
514 2         7 my ($sch, $auth, $path) = uri_split($uri);
515             return {
516             # to mark that we are schemeless
517 2         34 proto=>'',
518             path=>$path,
519             };
520             }
521              
522             sub actionmeta_info { +{
523 45     45 0 166 applies_to => ['*'],
524             summary => "Get general information on code entity",
525             needs_meta => 0,
526             needs_code => 0,
527             } }
528              
529             sub action_info {
530 7     7 0 18 my ($self, $req) = @_;
531              
532 7         22 my $mres = $self->get_meta($req);
533 7 100       24 return $mres if $mres;
534              
535             my $res = {
536             uri => $req->{uri},
537             type => $req->{-type},
538 6         22 };
539              
540 6         17 [200, "OK (info action)", $res];
541             }
542              
543             sub actionmeta_actions { +{
544 44     44 0 264 applies_to => ['*'],
545             summary => "List available actions for code entity",
546             needs_meta => 0,
547             needs_code => 0,
548             } }
549              
550             sub action_actions {
551 8     8 0 21 my ($self, $req) = @_;
552              
553 8         20 my $mres = $self->get_meta($req);
554 8 100       21 return $mres if $mres;
555              
556 4         9 my @res;
557 4         8 for my $k (sort keys %{ $self->{_typeacts}{$req->{-type}} }) {
  4         43  
558 60         106 my $v = $self->{_typeacts}{$req->{-type}}{$k};
559 60 50       89 if ($req->{detail}) {
560 0         0 push @res, {name=>$k, summary=>$v->{summary}};
561             } else {
562 60         115 push @res, $k;
563             }
564             }
565 4         17 [200, "OK (actions action)", \@res];
566             }
567              
568             sub actionmeta_list { +{
569 46     46 0 160 applies_to => ['package'],
570             summary => "List code entities inside this package code entity",
571             # this means, even if allow_path is '/a/b', we allow request on '/a' or '/'.
572             allow_request_parent_path => 1,
573             } }
574              
575             sub action_list {
576 10     10 0 1262 require Module::List;
577              
578 10         12342 my ($self, $req) = @_;
579 10         25 my $detail = $req->{detail};
580 10   50     48 my $f_type = $req->{type} || "";
581              
582 10         18 my @res;
583              
584             my $filter_path = sub {
585 511     511   733 my $path = shift;
586 511 100 100     1152 if (defined($self->{allow_paths}) &&
587             !__match_paths2($path, $self->{allow_paths})) {
588 306         778 return 0;
589             }
590 205 100 66     454 if (defined($self->{deny_paths}) &&
591             __match_paths2($path, $self->{deny_paths})) {
592 48         114 return 0;
593             }
594 157         303 1;
595 10         50 };
596              
597 10         19 my %mem;
598              
599             # get submodules
600 10 50 33     36 unless ($f_type && $f_type ne 'package') {
601             my $lres = Module::List::list_modules(
602 10 100       78 $req->{-perl_package} ? "$req->{-perl_package}\::" : "",
603             {list_modules=>1, list_prefixes=>1});
604 10         23140 my $dir = $req->{-uri_dir};
605 10         235 for my $m (sort keys %$lres) {
606 387         863 $m =~ s!::$!!;
607 387         749 $m =~ s!.+::!!;
608 387         724 my $path = "$dir/$m/";
609 387 100       620 next unless $filter_path->($path);
610 62 100       164 next if $mem{$path}++;
611 58 100       104 if ($detail) {
612 18         60 push @res, {uri=>"$m/", type=>"package"};
613             } else {
614 40         105 push @res, "$m/";
615             }
616             }
617             }
618              
619 10         62 my $res = $self->_load_module($req);
620 10 100 66     73 return $res if $res && $res->[0] != 405;
621              
622             # get all entities from this module
623 3     3   26 no strict 'refs';
  3         6  
  3         4385  
624 9         15 my $spec = \%{"$req->{-perl_package}\::SPEC"};
  9         49  
625 9         20 my $dir = $req->{-uri_dir};
626 9         99 for my $e (sort keys %$spec) {
627 130 100       264 next if $e =~ /^:/;
628 124         228 my $path = "$dir/$e";
629 124 100       186 next unless $filter_path->($path);
630 95 50       254 next if $mem{$path}++;
631 95 100       252 my $t = $e =~ /^[%\@\$]/ ? 'variable' : 'function';
632 95 50 33     181 next if $f_type && $f_type ne $t;
633 95 100       149 if ($detail) {
634 29         74 push @res, {
635             uri=>$e, type=>$t,
636             };
637             } else {
638 66         134 push @res, $e;
639             }
640             }
641              
642 9         92 [200, "OK (list action)", \@res];
643             }
644              
645             sub actionmeta_meta { +{
646 66     66 0 226 applies_to => ['*'],
647             summary => "Get metadata",
648             } }
649              
650             sub action_meta {
651 26     26 0 60 my ($self, $req) = @_;
652              
653 26         74 my $res = $self->get_meta($req);
654 26 100       258 return $res if $res;
655              
656 21         63 [200, "OK (meta action)", $req->{-meta}];
657             }
658              
659             sub actionmeta_call { +{
660 56     56 0 187 applies_to => ['function'],
661             summary => "Call function",
662             } }
663              
664             sub action_call {
665 19     19 0 684 require UUID::Random;
666              
667 19         270 my ($self, $req) = @_;
668              
669 19         45 my $res;
670              
671             my $tm; # = does client mention tx_id?
672 19 50       56 if (defined $req->{tx_id}) {
673 0         0 $res = $self->_pre_tx_action($req);
674 0 0       0 return $res if $res;
675 0         0 $tm = $self->{_tx_manager};
676 0         0 $tm->{_tx_id} = $req->{tx_id};
677             }
678              
679 19         53 $res = $self->get_meta($req);
680 19 50       52 return $res if $res;
681 19         51 $res = $self->get_code($req);
682 19 50       58 return $res if $res;
683              
684 19         31 my %args;
685              
686             # try to convert from argv if given argv
687 19 100 66     76 if (exists($req->{argv}) && $self->{accept_argv}) {
688 2         1766 require Perinci::Sub::GetArgs::Argv;
689             $res = Perinci::Sub::GetArgs::Argv::get_args_from_argv(
690 2         24 argv => [@{ $req->{argv} }],
691             meta => $req->{-meta},
692 2         14497 );
693 2 100       14927 return err(400, "Can't parse argv", $res) unless $res->[0] == 200;
694 1         3 %args = %{ $res->[2] };
  1         5  
695             } else {
696 17   100     28 %args = %{ $req->{args} // {} };
  17         100  
697             }
698              
699 18         87 my $risub = risub($req->{-meta});
700              
701 18 100       3934 if ($req->{dry_run}) {
702 3 100       12 return err(412, "Function does not support dry run")
703             unless $risub->can_dry_run;
704 2 100       23 if ($risub->feature('dry_run')) {
705 1         12 $args{-dry_run} = 1;
706             } else {
707 1         10 $args{-dry_run} = 1;
708 1         3 $args{-tx_action} = 'check_state';
709 1         5 $args{-tx_action_id} = UUID::Random::generate();
710 1         50 undef $tm;
711             }
712             }
713              
714 17 100       61 if ($risub->feature('progress')) {
715 1         1702 require Progress::Any;
716 1         6948 $args{-progress} = Progress::Any->get_indicator();
717             }
718              
719 17 50       323 if ($tm) {
720             $res = $tm->action(
721             f => "$req->{-perl_package}::$req->{-uri_leaf}", args=>\%args,
722             confirm => $req->{confirm},
723 0         0 );
724 0 0       0 $tm->{_tx_id} = undef if $tm;
725             } else {
726 17 100       56 $args{-confirm} = 1 if $req->{confirm};
727 17         28 eval { $res = $req->{-code}->(%args) };
  17         445  
728 17 100       844 if ($@) {
729 1 50       6 die if $ENV{PERINCI_ACCESS_SCHEMELESS_DEBUG};
730 1         7 $res = err(500, "Function died: $@");
731             }
732             }
733              
734             # add hint that result is binary
735 17 100       109 if (defined $res->[2]) {
736 7 50 66     50 if ($req->{-meta}{result} && $req->{-meta}{result}{schema} &&
      33        
737             $req->{-meta}{result}{schema}[0] eq 'buf') {
738 0         0 $res->[3]{'x.hint.result_binary'} = 1;
739             }
740             }
741              
742 17         55 $res;
743             }
744              
745             sub actionmeta_complete_arg_val { +{
746 41     41 0 125 applies_to => ['function'],
747             summary => "Complete function's argument value"
748             } }
749              
750             sub action_complete_arg_val {
751 5     5 0 1798 require Perinci::Sub::Complete;
752              
753 5         16879 my ($self, $req) = @_;
754 5 100       21 my $arg = $req->{arg} or return err(400, "Please specify arg");
755 4   100     14 my $word = $req->{word} // "";
756 4         6 my $ci = $req->{ci};
757              
758 4         13 my $res = $self->get_meta($req);
759 4 50       13 return $res if $res;
760             [200, "OK (complete_arg_val action)",
761 4   100     19 Perinci::Sub::Complete::complete_arg_val(meta=>$req->{-meta}, word=>$word,
762             arg=>$arg, ci=>$ci) // []];
763             }
764              
765             sub actionmeta_complete_arg_elem { +{
766 36     36 0 111 applies_to => ['function'],
767             summary => "Complete function's argument element value"
768             } }
769              
770             sub action_complete_arg_elem {
771 0     0 0 0 require Perinci::Sub::Complete;
772              
773 0         0 my ($self, $req) = @_;
774 0 0       0 my $arg = $req->{arg} or return err(400, "Please specify arg");
775             defined(my $index = $req->{index})
776 0 0       0 or return err(400, "Please specify index");
777 0   0     0 my $word = $req->{word} // "";
778 0         0 my $ci = $req->{ci};
779              
780 0         0 my $res = $self->get_meta($req);
781 0 0       0 return $res if $res;
782             [200, "OK (complete_arg_elem action)",
783             Perinci::Sub::Complete::complete_arg_elem(
784 0   0     0 meta=>$req->{-meta}, word=>$word, arg=>$arg, ci=>$ci, index=>$index,
785             ) // []];
786             }
787              
788             sub actionmeta_child_metas { +{
789 37     37 0 115 applies_to => ['package'],
790             summary => "Get metadata of all child entities",
791             } }
792              
793             sub action_child_metas {
794 1     1 0 4 my ($self, $req) = @_;
795              
796 1         5 my $res = $self->action_list($req);
797 1 50       8 return $res unless $res->[0] == 200;
798 1         2 my $ents = $res->[2];
799              
800 1         2 my %res;
801             my %om;
802             my $base = uri_join(
803 1         6 $req->{-uri_scheme}, $req->{-uri_auth}, $req->{-uri_dir});
804              
805 1         28 for my $ent (@$ents) {
806 6         24 $res = $self->request(meta => "$base/$ent");
807             # ignore failed request
808 6 50       18 next unless $res->[0] == 200;
809 6         15 $res{$ent} = $res->[2];
810             }
811 1         6 [200, "OK (child_metas action)", \%res];
812             }
813              
814             sub actionmeta_get { +{
815 36     36 0 117 applies_to => ['variable'],
816             summary => "Get value of variable",
817             } }
818              
819             sub action_get {
820 3     3   26 no strict 'refs';
  3         6  
  3         4054  
821              
822 0     0 0 0 my ($self, $req) = @_;
823 0         0 local $req->{-uri_leaf} = $req->{-uri_leaf};
824              
825             # extract prefix
826 0 0       0 $req->{-uri_leaf} =~ s/^([%\@\$])//
827             or return err(500, "BUG: Unknown variable prefix");
828 0         0 my $prefix = $1;
829 0         0 my $name = $req->{-perl_package} . "::" . $req->{-uri_leaf};
830             my $res =
831 0         0 $prefix eq '$' ? ${$name} :
832 0         0 $prefix eq '@' ? \@{$name} :
833 0 0       0 $prefix eq '%' ? \%{$name} :
  0 0       0  
    0          
834             undef;
835 0         0 [200, "OK (get action)", $res];
836             }
837              
838             sub _pre_tx_action {
839 0     0   0 my ($self, $req) = @_;
840              
841             return err(501, "Transaction not supported by server")
842 0 0       0 unless $self->{use_tx};
843              
844             # instantiate custom tx manager, per request if necessary
845 0 0       0 if (ref($self->{custom_tx_manager}) eq 'CODE') {
    0          
846 0         0 eval {
847 0         0 $self->{_tx_manager} = $self->{custom_tx_manager}->($self);
848 0 0       0 die $self->{_tx_manager} unless blessed($self->{_tx_manager});
849             };
850 0 0       0 if ($@) {
851 0 0       0 die if $ENV{PERINCI_ACCESS_SCHEMELESS_DEBUG};
852 0         0 return err(500, "Can't initialize custom tx manager: ".
853             "$self->{_tx_manager}: $@");
854             }
855             } elsif (!blessed($self->{_tx_manager})) {
856 0   0     0 my $tm_cl = $self->{custom_tx_manager} // "Perinci::Tx::Manager";
857 0         0 my $tm_cl_p = $tm_cl; $tm_cl_p =~ s!::!/!g; $tm_cl_p .= ".pm";
  0         0  
  0         0  
858 0         0 eval {
859 0         0 require $tm_cl_p;
860 0         0 $self->{_tx_manager} = $tm_cl->new(pa => $self);
861 0 0       0 die $self->{_tx_manager} unless blessed($self->{_tx_manager});
862             };
863 0 0       0 if ($@) {
864 0 0       0 die if $ENV{PERINCI_ACCESS_SCHEMELESS_DEBUG};
865 0         0 return err(500, "Can't initialize tx manager ($tm_cl): $@");
866             }
867             # we just want to force newer version, we currently can't specify this
868             # in Makefile.PL because peritm's tests use us. this might be rectified
869             # in the future.
870 0 0       0 if ($tm_cl eq 'Perinci::Tx::Manager') {
871 0 0       0 $Perinci::Tx::Manager::VERSION >= 0.29
872             or die "Your Perinci::Tx::Manager is too old, ".
873             "please install v0.29 or later";
874             }
875             }
876              
877 0         0 return;
878             }
879              
880             sub actionmeta_begin_tx { +{
881 36     36 0 123 applies_to => ['*'],
882             summary => "Start a new transaction",
883             } }
884              
885             sub action_begin_tx {
886 0     0 0 0 my ($self, $req) = @_;
887 0         0 my $res = $self->_pre_tx_action($req);
888 0 0       0 return $res if $res;
889              
890             $self->{_tx_manager}->begin(
891             tx_id => $req->{tx_id},
892             summary => $req->{summary},
893 0         0 );
894             }
895              
896             sub actionmeta_commit_tx { +{
897 36     36 0 120 applies_to => ['*'],
898             summary => "Commit a transaction",
899             } }
900              
901             sub action_commit_tx {
902 0     0 0 0 my ($self, $req) = @_;
903 0         0 my $res = $self->_pre_tx_action($req);
904 0 0       0 return $res if $res;
905              
906             $self->{_tx_manager}->commit(
907             tx_id => $req->{tx_id},
908 0         0 );
909             }
910              
911             sub actionmeta_savepoint_tx { +{
912 36     36 0 108 applies_to => ['*'],
913             summary => "Create a savepoint in a transaction",
914             } }
915              
916             sub action_savepoint_tx {
917 0     0 0 0 my ($self, $req) = @_;
918 0         0 my $res = $self->_pre_tx_action($req);
919 0 0       0 return $res if $res;
920              
921             $self->{_tx_manager}->savepoint(
922             tx_id => $req->{tx_id},
923             sp => $req->{tx_spid},
924 0         0 );
925             }
926              
927             sub actionmeta_release_tx_savepoint { +{
928 36     36 0 106 applies_to => ['*'],
929             summary => "Release a transaction savepoint",
930             } }
931              
932             sub action_release_tx_savepoint {
933 0     0 0 0 my ($self, $req) =\ @_;
934 0         0 my $res = $self->_pre_tx_action($req);
935 0 0       0 return $res if $res;
936              
937             $self->{_tx_manager}->release_savepoint(
938             tx_id => $req->{tx_id},
939             sp => $req->{tx_spid},
940 0         0 );
941             }
942              
943             sub actionmeta_rollback_tx { +{
944 36     36 0 122 applies_to => ['*'],
945             summary => "Rollback a transaction (optionally to a savepoint)",
946             } }
947              
948             sub action_rollback_tx {
949 0     0 0 0 my ($self, $req) = @_;
950 0         0 my $res = $self->_pre_tx_action($req);
951 0 0       0 return $res if $res;
952              
953             $self->{_tx_manager}->rollback(
954             tx_id => $req->{tx_id},
955             sp => $req->{tx_spid},
956 0         0 );
957             }
958              
959             sub actionmeta_list_txs { +{
960 36     36 0 116 applies_to => ['*'],
961             summary => "List transactions",
962             } }
963              
964             sub action_list_txs {
965 0     0 0 0 my ($self, $req) = @_;
966 0         0 my $res = $self->_pre_tx_action($req);
967 0 0       0 return $res if $res;
968              
969             $self->{_tx_manager}->list(
970             detail => $req->{detail},
971             tx_status => $req->{tx_status},
972             tx_id => $req->{tx_id},
973 0         0 );
974             }
975              
976             sub actionmeta_undo { +{
977 36     36 0 114 applies_to => ['*'],
978             summary => "Undo a committed transaction",
979             } }
980              
981             sub action_undo {
982 0     0 0 0 my ($self, $req) = @_;
983 0         0 my $res = $self->_pre_tx_action($req);
984 0 0       0 return $res if $res;
985              
986             $self->{_tx_manager}->undo(
987             tx_id => $req->{tx_id},
988             confirm => $req->{confirm},
989 0         0 );
990             }
991              
992             sub actionmeta_redo { +{
993 36     36 0 123 applies_to => ['*'],
994             summary => "Redo an undone committed transaction",
995             } }
996              
997             sub action_redo {
998 0     0 0 0 my ($self, $req) = @_;
999 0         0 my $res = $self->_pre_tx_action($req);
1000 0 0       0 return $res if $res;
1001              
1002             $self->{_tx_manager}->redo(
1003             tx_id => $req->{tx_id},
1004             confirm => $req->{confirm},
1005 0         0 );
1006             }
1007              
1008             sub actionmeta_discard_tx { +{
1009 36     36 0 107 applies_to => ['*'],
1010             summary => "Discard (forget) a committed transaction",
1011             } }
1012              
1013             sub action_discard_tx {
1014 0     0 0 0 my ($self, $req) = @_;
1015 0         0 my $res = $self->_pre_tx_action($req);
1016 0 0       0 return $res if $res;
1017              
1018             $self->{_tx_manager}->discard(
1019             tx_id => $req->{tx_id},
1020 0         0 );
1021             }
1022              
1023             sub actionmeta_discard_all_txs { +{
1024 36     36 0 154 applies_to => ['*'],
1025             summary => "Discard (forget) all committed transactions",
1026             } }
1027              
1028             sub action_discard_all_txs {
1029 0     0 0   my ($self, $req) = @_;
1030 0           my $res = $self->_pre_tx_action($req);
1031 0 0         return $res if $res;
1032              
1033             $self->{_tx_manager}->discard_all(
1034             # XXX select client
1035 0           );
1036             }
1037              
1038             1;
1039             # ABSTRACT: Base class for Perinci::Access::Perl
1040              
1041             __END__
1042              
1043             =pod
1044              
1045             =encoding UTF-8
1046              
1047             =head1 NAME
1048              
1049             Perinci::Access::Schemeless - Base class for Perinci::Access::Perl
1050              
1051             =head1 VERSION
1052              
1053             This document describes version 0.893 of Perinci::Access::Schemeless (from Perl distribution Perinci-Access-Perl), released on 2020-05-18.
1054              
1055             =head1 DESCRIPTION
1056              
1057             This class is the base class for L<Perinci::Access::Perl>, and by default acts
1058             like Perinci::Access::Perl (e.g. given uri C</Foo/Bar/baz> it will refer to
1059             function C<baz> in Perl package C<Foo::Bar>; it also looks for Rinci metadata in
1060             C<%SPEC> package variables by default). But this class is designed to be
1061             flexible: you can override aspects of it so it can map uri to different Perl
1062             packages (e.g. using option like C<package_prefix>), you can retrieve Rinci
1063             metadata from a database or whatever, etc.
1064              
1065             Supported features:
1066              
1067             =over
1068              
1069             =item * Basic Riap actions
1070              
1071             These include C<info>, C<actions>, C<meta>, C<list>, and C<call> actions.
1072              
1073             =item * Transaction/undo
1074              
1075             According to L<Rinci::Transaction>.
1076              
1077             =item * Function wrapping
1078              
1079             Wrapping is used to convert argument passing style, produce result envelope, add
1080             argument validation, as well as numerous other functionalities. See
1081             L<Perinci::Sub::Wrapper> for more details on wrapping. The default behavior will
1082             call wrapped functions.
1083              
1084             =item * Custom location of metadata
1085              
1086             By default, metadata are assumed to be stored embedded in Perl source code in
1087             C<%SPEC> package variables (with keys matching function names, C<$variable>
1088             names, or C<:package> for the package metadata itself).
1089              
1090             You can override C<get_meta()> to provide custom behavior. For example, you can
1091             store metadata in separate file or database.
1092              
1093             =item * Custom code entity tree
1094              
1095             By default, tree are formed by traversing Perl packages and their contents, for
1096             example if a C<list> action is requested on uri C</Foo/Bar/> then the contents
1097             of package C<Foo::Bar> and its subpackages will be traversed for the entities.
1098              
1099             You can override C<action_list()> to provide custom behavior. For example, you
1100             can lookup from the database.
1101              
1102             =item * Progress indicator
1103              
1104             Functions can express that they do progress updating through the C<features>
1105             property in its metadata:
1106              
1107             features => {
1108             progress => 1,
1109             ...
1110             }
1111              
1112             For these functions, this class will pass a special argument C<-progress>
1113             containing L<Progress::Any> object. Functions can update progress using this
1114             object.
1115              
1116             =back
1117              
1118             =head2 How request is processed
1119              
1120             User calls C<< $pa->request($action => $uri, \%extras) >>. Internally, the
1121             method creates a hash C<$req> which contains Riap request keys as well as
1122             internal information about the Riap request (the latter will be prefixed with
1123             dash C<->). Initially it will contain C<action> and C<uri> and the C<%extras>
1124             keys from the request() arguments sent by the user.
1125              
1126             Internal C<_parse_uri()> method will be called to parse C<uri> into C<-uri_dir>
1127             (the "dir" part), C<-uri_leaf> (the "basename" part), and C<-perl_package>.
1128             Forbidden or invalid paths will cause this method to return an enveloped error
1129             response and the request to stop. For example, if C<uri> is C</Foo/Bar/> then
1130             C<-uri_dir> is C</Foo/Bar/> and C<-uri_leaf> is an empty string. If C<uri> is
1131             C</Foo/Bar/baz> then C<-uri_dir> is C</Foo/Bar/> while C<-uri_leaf> is C<baz>.
1132             C<-uri_dir> will be used for the C<list> action. In both cases, C<-perl_package>
1133             will be set to C<Foo::Bar>.
1134              
1135             The code entity type is then determined currently using a few simple heuristic
1136             rules: if C<-uri_leaf> is empty string, type is C<package>. If C<-uri_leaf>
1137             begins with C<[$%@]>, type is C<variable>. Otherwise, type is C<function>.
1138             C<-type> will be set.
1139              
1140             After this, the appropriate C<action_ACTION()> method will be called. For
1141             example if action is C<meta> then C<action_meta()> method will be called, with
1142             C<$req> as the argument. This will in turn, depending on the action, either call
1143             C<get_meta()> (for example if action is C<meta>) or C<get_code()> (for example
1144             if action is C<call>), also with C<$req> as the argument. C<get_meta()> and
1145             C<get_code()> should return nothing on success, and set either C<-meta> (a
1146             defhash containing Rinci metadata) or C<-code> (a coderef), respectively. On
1147             error, they must return an enveloped error response.
1148              
1149             C<get_meta()> or C<get_code()> might call C<_load_module()> to load Perl modules
1150             if the C<load> attribute is set to true.
1151              
1152             =for Pod::Coverage ^(actionmeta_.+|action_.+|get_(meta|code))$
1153              
1154             =head1 ADDED RESULT METADATA
1155              
1156             This class might add the following property/attribute in result metadata:
1157              
1158             =head2 x.hint.result_binary => bool
1159              
1160             If result's schema type is C<buf>, then this class will set this attribute to
1161             true, to give hints to result formatters.
1162              
1163             =head1 METHODS
1164              
1165             =head2 PKG->new(%attrs) => OBJ
1166              
1167             Instantiate object. Known attributes:
1168              
1169             =over 4
1170              
1171             =item * load => BOOL (default: 1)
1172              
1173             Whether to load Perl modules that are requested.
1174              
1175             =item * after_load => CODE
1176              
1177             If set, code will be executed the first time Perl module is successfully loaded.
1178              
1179             =item * wrap => BOOL (default: 1)
1180              
1181             If set to false, then wil use original subroutine and metadata instead of
1182             wrapped ones, for example if you are very concerned about performance (do not
1183             want to add another eval {} and subroutine call introduced by wrapping) or do
1184             not need the functionality provided by the wrapper (e.g. your function already
1185             validates its arguments, accepts arguments as hash, and returns enveloped
1186             result).
1187              
1188             Wrapping is implemented inside C<get_code()>.
1189              
1190             =item * set_function_properties => HASH
1191              
1192             If set, will be passed to L<Perinci::Sub::Wrapper> wrap_sub()'s C<convert>
1193             argument when wrapping subroutines. Some applications of this include: changing
1194             C<default_lang> of metadata.
1195              
1196             This is only relevant if you enable C<wrap>.
1197              
1198             =item * cache_size => INT (default: 100)
1199              
1200             Specify cache size (in number of items), for caching metadata and wrapping
1201             result. Setting this to 0 disables caching.
1202              
1203             Caching is implemented inside C<get_meta()> and C<get_code()> so you might want
1204             to implement your own caching if you override those.
1205              
1206             =item * allow_paths => REGEX|STR|ARRAY
1207              
1208             If defined, only requests with C<uri> matching specified path will be allowed.
1209             Can be a string (e.g. C</spanel/api/>) or regex (e.g. C<< qr{^/[^/]+/api/} >>)
1210             or an array of those.
1211              
1212             =item * deny_paths => REGEX|STR|ARRAY
1213              
1214             If defined, requests with C<uri> matching specified path will be denied. Like
1215             C<allow_paths>, value can be a string (e.g. C</spanel/api/>) or regex (e.g. C<<
1216             qr{^/[^/]+/api/} >>) or an array of those.
1217              
1218             =item * allow_schemes => REGEX|STR|ARRAY
1219              
1220             By default this class does not care about schemes, it only looks at the uri
1221             path. You can use this option to limit allowed schemes.
1222              
1223             =item * deny_schemes => REGEX|STR|ARRAY
1224              
1225             By default this class does not care about schemes, it only looks at the uri
1226             path. You can use this option to specify forbidden schemes.
1227              
1228             =item * use_tx => BOOL (default: 0)
1229              
1230             Whether to allow transaction requests from client. Since this can cause the
1231             server to store transaction/undo data, this must be explicitly allowed.
1232              
1233             You need to install L<Perinci::Tx::Manager> for transaction support (unless you
1234             are using another transaction manager).
1235              
1236             =item * custom_tx_manager => STR|CODE
1237              
1238             Can be set to a string (class name) or a code that is expected to return a
1239             transaction manager class.
1240              
1241             By default, L<Perinci::Tx::Manager> is instantiated and maintained (not
1242             reinstantiated on every request), but if C<custom_tx_manager> is a coderef, it
1243             will be called on each request to get transaction manager. This can be used to
1244             instantiate Perinci::Tx::Manager in a custom way, e.g. specifying per-user
1245             transaction data directory and limits, which needs to be done on a per-request
1246             basis.
1247              
1248             =item * accept_argv => BOOL (default: 1)
1249              
1250             From version 0.64, C<argv> key is accepted by the C<call> action and will be
1251             converted to C<args>. This server-side conversion from C<argv> to <args> can
1252             handle coderefs in C<cmdline_aliases> (and probably other things too) compared
1253             when doing conversion at the client-side.
1254              
1255             This option allows disabling this behavior.
1256              
1257             =back
1258              
1259             =head2 $pa->request($action => $server_url, \%extra) => $res
1260              
1261             Process Riap request and return enveloped result. $server_url will be used as
1262             the Riap request key 'uri', as there is no server in this case.
1263              
1264             =head2 $pa->parse_url($server_url) => HASH
1265              
1266             =head1 ENVIRONMENT
1267              
1268             =head2 PERINCI_ACCESS_SCHEMELESS_DEBUG_DIE
1269              
1270             Bool. If set to true, will not return exception (e.g. function dies when
1271             executed) as status in result envelope; but instead will rethrow the error. This
1272             will make checking the error (e.g. with L<Devel::Confess> using C<-d:Confess>
1273             perl flag) slightly easier.
1274              
1275             =head1 HOMEPAGE
1276              
1277             Please visit the project's homepage at L<https://metacpan.org/release/Perinci-Access-Perl>.
1278              
1279             =head1 SOURCE
1280              
1281             Source repository is at L<https://github.com/perlancar/perl-Perinci-Access-Perl>.
1282              
1283             =head1 BUGS
1284              
1285             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-Access-Perl>
1286              
1287             When submitting a bug or request, please include a test-file or a
1288             patch to an existing test-file that illustrates the bug or desired
1289             feature.
1290              
1291             =head1 SEE ALSO
1292              
1293             L<Riap>, L<Rinci>
1294              
1295             =head1 AUTHOR
1296              
1297             perlancar <perlancar@cpan.org>
1298              
1299             =head1 COPYRIGHT AND LICENSE
1300              
1301             This software is copyright (c) 2020, 2019, 2017, 2016, 2015, 2014, 2013, 2012 by perlancar@cpan.org.
1302              
1303             This is free software; you can redistribute it and/or modify it under
1304             the same terms as the Perl 5 programming language system itself.
1305              
1306             =cut