line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Perinci::Sub::Wrapper; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $DATE = '2019-04-15'; # DATE |
4
|
|
|
|
|
|
|
our $VERSION = '0.850'; # VERSION |
5
|
|
|
|
|
|
|
|
6
|
17
|
|
|
17
|
|
227
|
use 5.010001; |
|
17
|
|
|
|
|
45
|
|
7
|
17
|
|
|
17
|
|
89
|
use strict; |
|
17
|
|
|
|
|
256
|
|
|
17
|
|
|
|
|
233
|
|
8
|
17
|
|
|
15
|
|
1527
|
use warnings; |
|
15
|
|
|
|
|
394
|
|
|
15
|
|
|
|
|
416
|
|
9
|
15
|
|
|
15
|
|
5352
|
use experimental 'smartmatch'; |
|
15
|
|
|
|
|
37918
|
|
|
15
|
|
|
|
|
79
|
|
10
|
15
|
|
|
15
|
|
19784
|
use Log::ger; |
|
15
|
|
|
|
|
728
|
|
|
15
|
|
|
|
|
67
|
|
11
|
|
|
|
|
|
|
|
12
|
15
|
|
|
15
|
|
8155
|
use Data::Dmp qw(dmp); |
|
15
|
|
|
|
|
19469
|
|
|
15
|
|
|
|
|
736
|
|
13
|
15
|
|
|
15
|
|
518
|
use Function::Fallback::CoreOrPP qw(clone); |
|
15
|
|
|
|
|
119
|
|
|
15
|
|
|
|
|
481
|
|
14
|
15
|
|
|
15
|
|
5994
|
use Perinci::Sub::Normalize qw(normalize_function_metadata); |
|
15
|
|
|
|
|
12902
|
|
|
15
|
|
|
|
|
741
|
|
15
|
15
|
|
|
15
|
|
6048
|
use Perinci::Sub::Util qw(err); |
|
15
|
|
|
|
|
25630
|
|
|
15
|
|
|
|
|
738
|
|
16
|
|
|
|
|
|
|
|
17
|
15
|
|
|
15
|
|
112
|
use Exporter qw(import); |
|
15
|
|
|
|
|
144
|
|
|
15
|
|
|
|
|
80204
|
|
18
|
|
|
|
|
|
|
our @EXPORT_OK = qw(wrap_sub); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our $Log_Wrapper_Code = $ENV{LOG_PERINCI_WRAPPER_CODE} // 0; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our %SPEC; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
$SPEC{':package'} = { |
25
|
|
|
|
|
|
|
v => 1.1, |
26
|
|
|
|
|
|
|
summary => 'A multi-purpose subroutine wrapping framework', |
27
|
|
|
|
|
|
|
}; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# "protocol version" (v). whenever there's a significant change in the basic |
30
|
|
|
|
|
|
|
# structure of the wrapper, which potentially cause some/a lot of property |
31
|
|
|
|
|
|
|
# handlers to stop working, we increase this. property handler must always state |
32
|
|
|
|
|
|
|
# which version it follows in its meta. if unspecified, it's assumed to be 1. |
33
|
|
|
|
|
|
|
our $protocol_version = 2; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub new { |
36
|
113
|
|
|
113
|
0
|
311
|
my ($class) = @_; |
37
|
113
|
|
|
|
|
612
|
bless {}, $class; |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
sub _check_module { |
41
|
183
|
|
|
183
|
|
554
|
my ($self, $mod) = @_; |
42
|
|
|
|
|
|
|
|
43
|
183
|
50
|
|
|
|
830
|
if ($self->{_args}{core}) { |
44
|
1
|
0
|
|
|
|
7
|
if ($mod =~ /\A(experimental|Scalar::Numeric::Util|Scalar::Util::Numeric::PP)\z/) { |
|
|
0
|
|
|
|
|
|
45
|
1
|
|
|
|
|
3
|
die "BUG: Requested non-core module '$mod' while wrap arg core=1"; |
46
|
|
|
|
|
|
|
} elsif ($mod =~ /\A(warnings|List::Util)\z/) { |
47
|
|
|
|
|
|
|
# core modules |
48
|
|
|
|
|
|
|
} else { |
49
|
1
|
|
|
|
|
6
|
die "BUG: Haven't noted whether module '$mod' is core/non-core"; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
183
|
50
|
|
|
|
537
|
if ($self->{_args}{pp}) { |
54
|
1
|
0
|
|
|
|
1
|
if ($mod =~ /\A(List::Util|Scalar::Numeric::Util)\z/) { |
|
|
0
|
|
|
|
|
|
55
|
1
|
|
|
|
|
337
|
die "BUG: Requested XS module '$mod' while wrap arg pp=1"; |
56
|
|
|
|
|
|
|
} elsif ($mod =~ /\A(experimental|warnings|Scalar::Util::Numeric::PP)\z/) { |
57
|
|
|
|
|
|
|
# pp modules |
58
|
|
|
|
|
|
|
} else { |
59
|
1
|
|
|
|
|
8
|
die "BUG: Haven't noted whether module '$mod' is pure-perl/XS"; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
183
|
50
|
|
|
|
439
|
if ($self->{_args}{core_or_pp}) { |
64
|
1
|
0
|
|
|
|
7
|
if ($mod =~ /\A(Scalar::Numeric::Util)\z/) { |
|
|
0
|
|
|
|
|
|
65
|
1
|
|
|
|
|
91
|
die "BUG: Requested non-core XS module '$mod' while wrap arg core_or_pp=1"; |
66
|
|
|
|
|
|
|
} elsif ($mod =~ /\A(experimental|warnings|List::Util|Scalar::Util::Numeric::PP)\z/) { |
67
|
|
|
|
|
|
|
# core or pp modules |
68
|
|
|
|
|
|
|
} else { |
69
|
1
|
|
|
|
|
2
|
die "BUG: Haven't noted whether module '$mod' is non-core xs or not"; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub _add_module { |
75
|
231
|
|
|
231
|
|
819
|
my ($self, $mod) = @_; |
76
|
231
|
100
|
|
|
|
774
|
unless ($mod ~~ $self->{_modules}) { |
77
|
183
|
|
|
|
|
414
|
local $self->{_cur_section}; |
78
|
183
|
|
|
|
|
449
|
$self->select_section('before_sub_require_modules'); |
79
|
183
|
100
|
|
|
|
1222
|
if ($mod =~ /\A(use|no) (\S+)/) { |
|
|
50
|
|
|
|
|
|
80
|
125
|
|
|
|
|
438
|
$self->_check_module($2); |
81
|
125
|
|
|
|
|
796
|
$self->push_lines("$mod;"); |
82
|
|
|
|
|
|
|
} elsif ($mod =~ /\A\w+(::\w+)*\z/) { |
83
|
59
|
|
|
|
|
202
|
$self->_check_module($mod); |
84
|
59
|
|
|
|
|
213
|
$self->push_lines("require $mod;"); |
85
|
|
|
|
|
|
|
} else { |
86
|
1
|
|
|
|
|
9
|
die "BUG: Invalid module name/statement: $mod"; |
87
|
|
|
|
|
|
|
} |
88
|
183
|
|
|
|
|
514
|
push @{ $self->{_modules} }, $mod; |
|
183
|
|
|
|
|
511
|
|
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub _add_var { |
93
|
101
|
|
|
101
|
|
888
|
my ($self, $var, $value) = @_; |
94
|
101
|
50
|
|
|
|
386
|
unless (exists $self->{_vars}{$var}) { |
95
|
101
|
|
|
|
|
240
|
local $self->{_cur_section}; |
96
|
101
|
|
|
|
|
314
|
$self->select_section('declare_vars'); |
97
|
101
|
|
|
|
|
550
|
$self->push_lines("my \$$var = ".dmp($value).";"); |
98
|
101
|
|
|
|
|
338
|
$self->{_vars}{$var} = $value; |
99
|
|
|
|
|
|
|
} |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub _known_sections { |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# order=>N regulates the order of code. embed=>1 means the code is for embed |
105
|
|
|
|
|
|
|
# mode only and should not be included in dynamic wrapper code. |
106
|
|
|
|
|
|
|
|
107
|
2455
|
|
|
2455
|
|
4530
|
state $val = { |
108
|
|
|
|
|
|
|
before_sub_require_modules => {order=>1}, |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# reserved by wrapper for setting Perl package and declaring 'sub {' |
111
|
|
|
|
|
|
|
OPEN_SUB => {order=>4}, |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# reserved to say 'my %args = @_;' or 'my @args = @_;' etc |
114
|
|
|
|
|
|
|
ACCEPT_ARGS => {order=>5}, |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# reserved to get args values if converted from array/arrayref |
117
|
|
|
|
|
|
|
ACCEPT_ARGS2 => {order=>6}, |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
declare_vars => {order=>7}, |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# for handlers to put stuffs right before eval. for example, 'timeout' |
122
|
|
|
|
|
|
|
# uses this to set ALRM signal handler. |
123
|
|
|
|
|
|
|
before_eval => {order=>10}, |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# reserved by wrapper for generating 'eval {' |
126
|
|
|
|
|
|
|
OPEN_EVAL => {order=>20}, |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# used e.g. to load modules used by validation |
129
|
|
|
|
|
|
|
before_call_before_arg_validation => {order=>31}, |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
before_call_arg_validation => {order=>32}, |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# used e.g. by dependency checking |
134
|
|
|
|
|
|
|
before_call_after_arg_validation => {order=>33}, |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
# feed arguments to sub |
137
|
|
|
|
|
|
|
before_call_feed_args => {order=>48}, |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# for handlers that *must* do stuffs right before call |
140
|
|
|
|
|
|
|
before_call_right_before_call => {order=>49}, |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
# reserved by the wrapper for calling the sub |
143
|
|
|
|
|
|
|
CALL => {order=>50}, |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# for handlers that *must* do stuffs right after call |
146
|
|
|
|
|
|
|
after_call_right_after_call => {order=>51}, |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# reserved by the wrapper for adding/stripping result envelope, this |
149
|
|
|
|
|
|
|
# happens before result validation |
150
|
|
|
|
|
|
|
AFTER_CALL_ADD_OR_STRIP_RESULT_ENVELOPE => {order=>52}, |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# used e.g. to load modules used by validation |
153
|
|
|
|
|
|
|
after_call_before_res_validation => {order=>61}, |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
after_call_res_validation => {order=>62}, |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
after_call_after_res_validation => {order=>63}, |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# reserved by wrapper to put eval end '}' and capturing result in |
160
|
|
|
|
|
|
|
# $_w_res and $@ in $eval_err |
161
|
|
|
|
|
|
|
CLOSE_EVAL => {order=>70}, |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# for handlers to put checks against $eval_err |
164
|
|
|
|
|
|
|
after_eval => {order=>80}, |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# reserved for returning final result '$_w_res' |
167
|
|
|
|
|
|
|
BEFORE_CLOSE_SUB => {order=>99}, |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# reserved for sub closing '}' line |
170
|
|
|
|
|
|
|
CLOSE_SUB => {order=>100}, |
171
|
|
|
|
|
|
|
}; |
172
|
2455
|
|
|
|
|
4232
|
$val; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub section_empty { |
176
|
4821
|
|
|
4821
|
0
|
6214
|
my ($self, $section) = @_; |
177
|
4821
|
|
|
|
|
10330
|
!$self->{_codes}{$section}; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub _needs_eval { |
181
|
111
|
|
|
111
|
|
237
|
my ($self) = @_; |
182
|
111
|
|
33
|
|
|
317
|
!($self->section_empty('before_eval') && |
183
|
|
|
|
|
|
|
$self->section_empty('after_eval')); |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# whether we need to store call result to a variable ($_w_res) |
187
|
|
|
|
|
|
|
sub _needs_store_res { |
188
|
105
|
|
|
105
|
|
247
|
my ($self) = @_; |
189
|
105
|
100
|
|
|
|
486
|
return 1 if $self->{_args}{validate_result}; |
190
|
7
|
50
|
|
|
|
19
|
return 1 if $self->_needs_eval; |
191
|
7
|
|
|
|
|
516
|
my $ks = $self->_known_sections; |
192
|
6
|
|
|
|
|
29
|
for (grep {/^after_call/} keys %$ks) { |
|
132
|
|
|
|
|
194
|
|
193
|
24
|
50
|
|
|
|
38
|
return 1 unless $self->section_empty($_); |
194
|
|
|
|
|
|
|
} |
195
|
6
|
|
|
|
|
16
|
0; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub _check_known_section { |
199
|
1338
|
|
|
1339
|
|
1892
|
my ($self, $section) = @_; |
200
|
1338
|
|
|
|
|
2516
|
my $ks = $self->_known_sections; |
201
|
1338
|
50
|
|
|
|
3226
|
$ks->{$section} or die "BUG: Unknown code section '$section'"; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub _err { |
205
|
366
|
|
|
367
|
|
636
|
my ($self, $c_status, $c_msg) = @_; |
206
|
366
|
100
|
|
|
|
763
|
if ($self->{_meta}{result_naked}) { |
207
|
8
|
|
|
|
|
37
|
$self->push_lines( |
208
|
|
|
|
|
|
|
"warn 'ERROR ' . ($c_status) . ': '. ($c_msg);", |
209
|
|
|
|
|
|
|
'return undef;', |
210
|
|
|
|
|
|
|
); |
211
|
|
|
|
|
|
|
} else { |
212
|
358
|
|
|
|
|
1172
|
$self->push_lines("return [$c_status, $c_msg];"); |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
sub _errif { |
217
|
270
|
|
|
271
|
|
7025
|
my ($self, $c_status, $c_msg, $c_cond) = @_; |
218
|
270
|
|
|
|
|
888
|
$self->push_lines("if ($c_cond) {"); |
219
|
270
|
|
|
|
|
662
|
$self->indent; |
220
|
270
|
|
|
|
|
713
|
$self->_err($c_status, $c_msg); |
221
|
270
|
|
|
|
|
684
|
$self->unindent; |
222
|
270
|
|
|
|
|
477
|
$self->push_lines('}'); |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub select_section { |
226
|
1338
|
|
|
1339
|
0
|
2422
|
my ($self, $section) = @_; |
227
|
1338
|
|
|
|
|
2611
|
$self->_check_known_section($section); |
228
|
1338
|
|
|
|
|
1871
|
$self->{_cur_section} = $section; |
229
|
1338
|
|
|
|
|
1628
|
$self; |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
sub indent { |
233
|
646
|
|
|
647
|
0
|
1079
|
my ($self) = @_; |
234
|
646
|
|
|
|
|
1016
|
my $section = $self->{_cur_section}; |
235
|
646
|
|
50
|
|
|
1256
|
$self->{_codes}{$section} //= undef; |
236
|
646
|
|
|
|
|
855
|
$self->{_levels}{$section}++; |
237
|
646
|
|
|
|
|
818
|
$self; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub unindent { |
241
|
640
|
|
|
641
|
0
|
1044
|
my ($self) = @_; |
242
|
640
|
|
|
|
|
901
|
my $section = $self->{_cur_section}; |
243
|
640
|
|
100
|
|
|
1578
|
$self->{_codes}{$section} //= undef; |
244
|
640
|
|
|
|
|
912
|
$self->{_levels}{$section}--; |
245
|
640
|
|
|
|
|
834
|
$self; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub get_indent_level { |
249
|
86
|
|
|
86
|
0
|
181
|
my ($self) = @_; |
250
|
86
|
|
|
|
|
162
|
my $section = $self->{_cur_section}; |
251
|
86
|
|
50
|
|
|
412
|
$self->{_levels}{$section} // 0; |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# line can be code or comment. code should not contain string literals that |
255
|
|
|
|
|
|
|
# cross lines (i.e. contain literal newlines) because push_lines() might add |
256
|
|
|
|
|
|
|
# comment at the end of each line. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub push_lines { |
259
|
2606
|
|
|
2606
|
0
|
7121
|
my ($self, @lines) = @_; |
260
|
2606
|
|
|
|
|
3690
|
my $section = $self->{_cur_section}; |
261
|
|
|
|
|
|
|
|
262
|
2606
|
100
|
|
|
|
4587
|
unless (exists $self->{_codes}{$section}) { |
263
|
798
|
|
|
|
|
1811
|
unshift @lines, "# * section: $section"; |
264
|
|
|
|
|
|
|
# don't give blank line for the top-most section (order=>0) |
265
|
798
|
50
|
|
|
|
1295
|
unshift @lines, "" if $self->_known_sections->{$section}{order}; |
266
|
798
|
|
|
|
|
1695
|
$self->{_codes}{$section} = []; |
267
|
798
|
|
|
|
|
1732
|
$self->{_levels}{$section} = 0; |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
2606
|
|
|
|
|
3495
|
@lines = map {[$self->{_levels}{$section}, $_]} @lines; |
|
4736
|
|
|
|
|
9899
|
|
271
|
2606
|
50
|
|
|
|
4784
|
if ($self->{_args}{debug}) { |
272
|
0
|
|
|
|
|
0
|
for my $l (@lines) { |
273
|
|
|
|
|
|
|
$l->[2] = |
274
|
|
|
|
|
|
|
$self->{_cur_handler} ? |
275
|
|
|
|
|
|
|
"$self->{_cur_handler} prio=".$self->{_cur_handler_meta}{prio} |
276
|
0
|
0
|
|
|
|
0
|
: ""; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
} |
279
|
2606
|
|
|
|
|
2858
|
push @{$self->{_codes}{$section}}, @lines; |
|
2606
|
|
|
|
|
4954
|
|
280
|
2606
|
|
|
|
|
5420
|
$self; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
sub _join_codes { |
284
|
208
|
|
|
208
|
|
591
|
my ($self, $crit, $prev_section_level) = @_; |
285
|
208
|
|
|
|
|
270
|
my @lines; |
286
|
208
|
|
|
|
|
364
|
my $ks = $self->_known_sections; |
287
|
208
|
|
100
|
|
|
719
|
$prev_section_level //= 0; |
288
|
208
|
|
|
|
|
274
|
my $i = 0; |
289
|
208
|
|
|
|
|
1108
|
for my $s (sort {$ks->{$a}{order} <=> $ks->{$b}{order}} |
|
14900
|
|
|
|
|
18122
|
|
290
|
|
|
|
|
|
|
keys %$ks) { |
291
|
4576
|
100
|
|
|
|
6359
|
next if $self->section_empty($s); |
292
|
1772
|
100
|
|
|
|
2780
|
next unless $crit->(section => $s); |
293
|
678
|
|
|
|
|
981
|
$i++; |
294
|
678
|
|
|
|
|
788
|
for my $l (@{ $self->{_codes}{$s} }) { |
|
678
|
|
|
|
|
1174
|
|
295
|
4015
|
|
|
|
|
4693
|
$l->[0] += $prev_section_level; |
296
|
4015
|
50
|
|
|
|
5646
|
die "BUG: Negative indent level in line $i (section $s): '$l->[1]'" |
297
|
|
|
|
|
|
|
if $l->[0] < 0; |
298
|
4015
|
|
|
|
|
7210
|
my $s = ($self->{_args}{indent} x $l->[0]) . $l->[1]; |
299
|
4015
|
50
|
|
|
|
5763
|
if (defined $l->[2]) { |
300
|
0
|
|
|
|
|
0
|
my $num_ws = 80 - length($s); |
301
|
0
|
0
|
|
|
|
0
|
$num_ws = 1 if $num_ws < 1; |
302
|
0
|
|
|
|
|
0
|
$s .= (" " x $num_ws) . "## $l->[2]"; |
303
|
|
|
|
|
|
|
} |
304
|
4015
|
|
|
|
|
6049
|
push @lines, $s; |
305
|
|
|
|
|
|
|
} |
306
|
678
|
|
|
|
|
1055
|
$prev_section_level += $self->{_levels}{$s}; |
307
|
|
|
|
|
|
|
} |
308
|
208
|
|
|
|
|
1834
|
[join("\n", @lines), $prev_section_level]; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
sub _format_dyn_wrapper_code { |
312
|
52
|
|
|
52
|
|
114
|
my ($self) = @_; |
313
|
52
|
|
|
|
|
129
|
my $ks = $self->_known_sections; |
314
|
|
|
|
|
|
|
$self->_join_codes( |
315
|
|
|
|
|
|
|
sub { |
316
|
443
|
|
|
443
|
|
770
|
my %args = @_; |
317
|
443
|
|
|
|
|
572
|
my $section = $args{section}; |
318
|
443
|
|
|
|
|
1391
|
!$ks->{$section}{embed}; |
319
|
52
|
|
|
|
|
369
|
})->[0]; |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
# for embedded, we need to produce three sections which will be inserted in |
323
|
|
|
|
|
|
|
# different places, demonstrated below: |
324
|
|
|
|
|
|
|
# |
325
|
|
|
|
|
|
|
# $SPEC{foo} = { |
326
|
|
|
|
|
|
|
# ... |
327
|
|
|
|
|
|
|
# }; |
328
|
|
|
|
|
|
|
# sub foo { |
329
|
|
|
|
|
|
|
# my %args = @_; |
330
|
|
|
|
|
|
|
# # do stuffs |
331
|
|
|
|
|
|
|
# } |
332
|
|
|
|
|
|
|
# |
333
|
|
|
|
|
|
|
# becomes: |
334
|
|
|
|
|
|
|
# |
335
|
|
|
|
|
|
|
# #PRESUB1: require modules (inserted before sub declaration) |
336
|
|
|
|
|
|
|
# require Data::Dumper; |
337
|
|
|
|
|
|
|
# require Scalar::Util; |
338
|
|
|
|
|
|
|
# |
339
|
|
|
|
|
|
|
# $SPEC{foo} = { |
340
|
|
|
|
|
|
|
# ... |
341
|
|
|
|
|
|
|
# }; |
342
|
|
|
|
|
|
|
# #PRESUB2: modify metadata piece-by-piece (inserted before sub declaration & |
343
|
|
|
|
|
|
|
# #after $SPEC{foo}). we're avoiding dumping the new modified metadata because |
344
|
|
|
|
|
|
|
# #metadata might contain coderefs which is sometimes problematic when dumping |
345
|
|
|
|
|
|
|
# { |
346
|
|
|
|
|
|
|
# my $meta = $SPEC{foo}; |
347
|
|
|
|
|
|
|
# $meta->{v} = 1.1; |
348
|
|
|
|
|
|
|
# $meta->{result_naked} = 0; |
349
|
|
|
|
|
|
|
# } |
350
|
|
|
|
|
|
|
# |
351
|
|
|
|
|
|
|
# sub foo { |
352
|
|
|
|
|
|
|
# my %args = @_; |
353
|
|
|
|
|
|
|
# #PREAMBLE: before call sections (inserted after accept args), e.g. |
354
|
|
|
|
|
|
|
# #validate arguments, convert argument type, setup eval block |
355
|
|
|
|
|
|
|
# #... |
356
|
|
|
|
|
|
|
# |
357
|
|
|
|
|
|
|
# # do stuffs |
358
|
|
|
|
|
|
|
# |
359
|
|
|
|
|
|
|
# #POSTAMBLE: after call sections (inserted before sub end), e.g. |
360
|
|
|
|
|
|
|
# #validate result, close eval block and do retry/etc. |
361
|
|
|
|
|
|
|
# #... |
362
|
|
|
|
|
|
|
# } |
363
|
|
|
|
|
|
|
sub _format_embed_wrapper_code { |
364
|
52
|
|
|
52
|
|
125
|
my ($self) = @_; |
365
|
|
|
|
|
|
|
|
366
|
52
|
|
|
|
|
115
|
my $res = {}; |
367
|
52
|
|
|
|
|
123
|
my $ks = $self->_known_sections; |
368
|
52
|
|
|
|
|
85
|
my $j; |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
$j = $self->_join_codes( |
371
|
|
|
|
|
|
|
sub { |
372
|
443
|
|
|
443
|
|
756
|
my %args = @_; |
373
|
443
|
|
|
|
|
548
|
my $section = $args{section}; |
374
|
443
|
|
|
|
|
1126
|
$section =~ /\A(before_sub_require_modules)\z/; |
375
|
52
|
|
|
|
|
357
|
}); |
376
|
52
|
|
|
|
|
273
|
$res->{presub1} = $j->[0]; |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# no longer needed/produce, code to modify metadata |
379
|
52
|
|
|
|
|
144
|
$res->{presub2} = ''; |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
$j = $self->_join_codes( |
382
|
|
|
|
|
|
|
sub { |
383
|
443
|
|
|
443
|
|
786
|
my %args = @_; |
384
|
443
|
|
|
|
|
562
|
my $section = $args{section}; |
385
|
443
|
|
|
|
|
561
|
my $order = $ks->{$section}{order}; |
386
|
|
|
|
|
|
|
return 1 if $order > $ks->{ACCEPT_ARGS}{order} && |
387
|
443
|
100
|
100
|
|
|
1292
|
$order < $ks->{CALL}{order}; |
388
|
349
|
|
|
|
|
689
|
0; |
389
|
52
|
|
|
|
|
283
|
}, 1); |
390
|
52
|
|
|
|
|
239
|
$res->{preamble} = $j->[0]; |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
$j = $self->_join_codes( |
393
|
|
|
|
|
|
|
sub { |
394
|
443
|
|
|
443
|
|
700
|
my %args = @_; |
395
|
443
|
|
|
|
|
548
|
my $section = $args{section}; |
396
|
443
|
|
|
|
|
559
|
my $order = $ks->{$section}{order}; |
397
|
|
|
|
|
|
|
return 1 if $order > $ks->{CALL}{order} && |
398
|
443
|
100
|
100
|
|
|
1128
|
$order < $ks->{CLOSE_SUB}{order}; |
399
|
337
|
|
|
|
|
665
|
0; |
400
|
52
|
|
|
|
|
285
|
}, $j->[1]); |
401
|
52
|
|
|
|
|
215
|
$res->{postamble} = $j->[0]; |
402
|
|
|
|
|
|
|
|
403
|
52
|
|
|
|
|
167
|
$res; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
108
|
|
|
108
|
0
|
240
|
sub handlemeta_v { {} } |
407
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_name { {} } |
408
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_summary { {} } |
409
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_description { {} } |
410
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_tags { {} } |
411
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_default_lang { {} } |
412
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_links { {} } |
413
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_text_markup { {} } |
414
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_is_func { {} } |
415
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_is_meth { {} } |
416
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_is_class_meth { {} } |
417
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_examples { {} } |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# after args |
420
|
4
|
|
|
4
|
0
|
15
|
sub handlemeta_features { {v=>2, prio=>15} } |
421
|
|
|
|
|
|
|
sub handle_features { |
422
|
4
|
|
|
4
|
0
|
13
|
my ($self, %args) = @_; |
423
|
|
|
|
|
|
|
|
424
|
4
|
|
|
|
|
7
|
my $meta = $self->{_meta}; |
425
|
4
|
|
50
|
|
|
10
|
my $v = $meta->{features} // {}; |
426
|
|
|
|
|
|
|
|
427
|
4
|
|
|
|
|
11
|
$self->select_section('before_call_before_arg_validation'); |
428
|
|
|
|
|
|
|
|
429
|
4
|
50
|
33
|
|
|
17
|
if ($v->{tx} && $v->{tx}{req}) { |
430
|
4
|
|
|
|
|
13
|
$self->push_lines('', '# check required transaction'); |
431
|
4
|
|
|
|
|
14
|
$self->_errif(412, '"Must run with transaction (pass -tx_manager)"', |
432
|
|
|
|
|
|
|
'!$args{-tx_manager}'); |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
} |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
# run before args |
437
|
108
|
|
|
108
|
0
|
430
|
sub handlemeta_args_as { {v=>2, prio=>1, convert=>1} } |
438
|
|
|
|
|
|
|
sub handle_args_as { |
439
|
108
|
|
|
108
|
0
|
479
|
my ($self, %args) = @_; |
440
|
|
|
|
|
|
|
|
441
|
108
|
|
|
|
|
259
|
my $value = $args{value}; |
442
|
108
|
|
|
|
|
222
|
my $new = $args{new}; |
443
|
108
|
|
|
|
|
223
|
my $meta = $self->{_meta}; |
444
|
108
|
|
100
|
|
|
364
|
my $args_p = $meta->{args} // {}; |
445
|
108
|
|
|
|
|
259
|
my $opt_va = $self->{_args}{validate_args}; |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
# We support conversion of arguments between hash/hashref/array/arrayref. To |
448
|
|
|
|
|
|
|
# make it simple, currently the algorithm is as follow: we first form the |
449
|
|
|
|
|
|
|
# %args hash. If args_as is already 'hash', we just do 'my %args = @_'. |
450
|
|
|
|
|
|
|
# Otherwise, we convert from the other forms. |
451
|
|
|
|
|
|
|
# |
452
|
|
|
|
|
|
|
# We then validate each argument in %args (code generated in 'args' |
453
|
|
|
|
|
|
|
# handler). |
454
|
|
|
|
|
|
|
# |
455
|
|
|
|
|
|
|
# Finally, unless original args_as is 'hash' we convert to the final form |
456
|
|
|
|
|
|
|
# that the wrapped sub expects. |
457
|
|
|
|
|
|
|
# |
458
|
|
|
|
|
|
|
# This setup is optimal when both the sub and generated wrapper accept |
459
|
|
|
|
|
|
|
# 'hash', but suboptimal for other cases (especially positional ones, as |
460
|
|
|
|
|
|
|
# they have to undergo a round-trip to hash even when both accept 'array'). |
461
|
|
|
|
|
|
|
# This will be rectified in the future. |
462
|
|
|
|
|
|
|
|
463
|
108
|
|
66
|
|
|
409
|
my $v = $new // $value; |
464
|
|
|
|
|
|
|
|
465
|
108
|
|
|
|
|
310
|
$self->select_section('ACCEPT_ARGS'); |
466
|
108
|
100
|
|
|
|
338
|
if ($v eq 'hash') { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
467
|
100
|
100
|
|
|
|
380
|
$self->push_lines(q{die 'BUG: Odd number of hash elements supplied' if @_ % 2;}) |
468
|
|
|
|
|
|
|
if $opt_va; |
469
|
100
|
|
|
|
|
214
|
$self->push_lines('my %args = @_;'); |
470
|
|
|
|
|
|
|
} elsif ($v eq 'hashref') { |
471
|
2
|
50
|
|
|
|
9
|
$self->push_lines(q{die 'BUG: $_[0] needs to be hashref' if @_ && ref($_[0]) ne "HASH";}) |
472
|
|
|
|
|
|
|
if $opt_va; |
473
|
2
|
|
|
|
|
6
|
$self->push_lines('my %args = %{$_[0] // {}};'); |
474
|
|
|
|
|
|
|
} elsif ($v =~ /\Aarray(ref)?\z/) { |
475
|
6
|
100
|
|
|
|
34
|
my $ref = $1 ? 1:0; |
476
|
6
|
100
|
|
|
|
19
|
if ($ref) { |
477
|
2
|
50
|
|
|
|
8
|
$self->push_lines(q{die 'BUG: $_[0] needs to be arrayref' if @_ && ref($_[0]) ne "ARRAY";}) |
478
|
|
|
|
|
|
|
if $opt_va; |
479
|
|
|
|
|
|
|
} |
480
|
6
|
|
|
|
|
18
|
$self->push_lines('my %args;'); |
481
|
6
|
|
|
|
|
19
|
$self->select_section('ACCEPT_ARGS2'); |
482
|
6
|
|
|
|
|
29
|
for my $a (sort keys %$args_p) { |
483
|
12
|
|
|
|
|
19
|
my $as = $args_p->{$a}; |
484
|
12
|
|
|
|
|
43
|
my $line = '$args{'.dmp($a).'} = '; |
485
|
12
|
50
|
|
|
|
458
|
defined($as->{pos}) or die "Error in args property for arg '$a': ". |
486
|
|
|
|
|
|
|
"no pos defined"; |
487
|
12
|
|
|
|
|
31
|
my $pos = int($as->{pos} + 0); |
488
|
12
|
50
|
|
|
|
30
|
$pos >= 0 or die "Error in args property for arg '$a': ". |
489
|
|
|
|
|
|
|
"negative value in pos"; |
490
|
12
|
50
|
33
|
|
|
53
|
if ($as->{slurpy} // $as->{greedy}) { |
491
|
0
|
0
|
|
|
|
0
|
if ($ref) { |
492
|
0
|
|
|
|
|
0
|
$line .= '[splice @{$_[0]}, '.$pos.'] if @{$_[0]} > '.$pos; |
493
|
|
|
|
|
|
|
} else { |
494
|
0
|
|
|
|
|
0
|
$line .= '[splice @_, '.$pos.'] if @_ > '.$pos; |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
} else { |
497
|
12
|
100
|
|
|
|
21
|
if ($ref) { |
498
|
4
|
|
|
|
|
12
|
$line .= '$_[0]['.$pos.'] if @{$_[0]} > '.$pos; |
499
|
|
|
|
|
|
|
} else { |
500
|
8
|
|
|
|
|
20
|
$line .= '$_['.$pos.'] if @_ > '.$pos; |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
} |
503
|
12
|
|
|
|
|
36
|
$self->push_lines("$line;"); |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
} else { |
506
|
0
|
|
|
|
|
0
|
die "Unknown args_as value '$v'"; |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
|
509
|
108
|
|
|
|
|
316
|
$self->select_section('ACCEPT_ARGS'); |
510
|
108
|
100
|
|
|
|
531
|
if ($value eq 'hashref') { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
511
|
2
|
|
|
|
|
4
|
$self->push_lines('my $args;'); |
512
|
|
|
|
|
|
|
} elsif ($value eq 'array') { |
513
|
4
|
|
|
|
|
12
|
$self->push_lines('my @args;'); |
514
|
|
|
|
|
|
|
} elsif ($value eq 'arrayref') { |
515
|
2
|
|
|
|
|
5
|
$self->push_lines('my $args;'); |
516
|
|
|
|
|
|
|
} |
517
|
|
|
|
|
|
|
|
518
|
108
|
|
|
|
|
152
|
my $tok; |
519
|
108
|
|
|
|
|
277
|
$self->select_section('before_call_feed_args'); |
520
|
108
|
|
|
|
|
170
|
$v = $value; |
521
|
108
|
100
|
|
|
|
296
|
if ($v eq 'hash') { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
522
|
100
|
|
|
|
|
182
|
$tok = '%args'; |
523
|
|
|
|
|
|
|
} elsif ($v eq 'hashref') { |
524
|
2
|
|
|
|
|
5
|
$tok = '$args'; |
525
|
2
|
|
|
|
|
6
|
$self->push_lines($tok.' = \%args;'); # XXX should we set each arg instead? |
526
|
|
|
|
|
|
|
} elsif ($v =~ /\Aarray(ref)?\z/) { |
527
|
6
|
100
|
|
|
|
19
|
my $ref = $1 ? 1:0; |
528
|
6
|
100
|
|
|
|
18
|
$tok = ($ref ? '$':'@') . 'args'; |
529
|
6
|
|
|
|
|
43
|
for my $a (sort {$args_p->{$a}{pos} <=> $args_p->{$b}{pos}} |
|
6
|
|
|
|
|
25
|
|
530
|
|
|
|
|
|
|
keys %$args_p) { |
531
|
12
|
|
|
|
|
22
|
my $as = $args_p->{$a}; |
532
|
12
|
|
|
|
|
35
|
my $t = '$args{'.dmp($a).'}'; |
533
|
12
|
|
|
|
|
335
|
my $line; |
534
|
12
|
50
|
|
|
|
29
|
defined($as->{pos}) or die "Error in args property for arg '$a': ". |
535
|
|
|
|
|
|
|
"no pos defined"; |
536
|
12
|
|
|
|
|
23
|
my $pos = int($as->{pos} + 0); |
537
|
12
|
50
|
|
|
|
30
|
$pos >= 0 or die "Error in args property for arg '$a': ". |
538
|
|
|
|
|
|
|
"negative value in pos"; |
539
|
12
|
50
|
33
|
|
|
56
|
if ($as->{slurpy} // $as->{greedy}) { |
540
|
0
|
|
|
|
|
0
|
$line = 'splice @args, '.$pos.', scalar(@args)-1, @{'.$t.'}'; |
541
|
|
|
|
|
|
|
} else { |
542
|
12
|
100
|
|
|
|
42
|
$line = '$args'.($ref ? '->':'').'['.$pos."] = $t if exists $t"; |
543
|
|
|
|
|
|
|
} |
544
|
12
|
|
|
|
|
33
|
$self->push_lines("$line;"); |
545
|
|
|
|
|
|
|
} |
546
|
|
|
|
|
|
|
} else { |
547
|
0
|
|
|
|
|
0
|
die "Unknown args_as value '$v'"; |
548
|
|
|
|
|
|
|
} |
549
|
108
|
|
|
|
|
550
|
$self->{_args_token} = $tok; |
550
|
|
|
|
|
|
|
} |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
sub _sah { |
553
|
11
|
|
|
11
|
|
4218
|
require Data::Sah; |
554
|
|
|
|
|
|
|
|
555
|
11
|
|
|
|
|
22775
|
my $self = shift; |
556
|
11
|
|
|
|
|
47
|
state $sah = Data::Sah->new; |
557
|
11
|
|
|
|
|
289
|
$sah; |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
sub _plc { |
561
|
86
|
|
|
86
|
|
147
|
my $self = shift; |
562
|
86
|
|
|
|
|
563
|
state $plc = do { |
563
|
7
|
|
|
|
|
25
|
my $plc = $self->_sah->get_compiler("perl"); |
564
|
7
|
|
|
|
|
336100
|
$plc->comment_style('shell2'); # to make all comment uses ## instead of # |
565
|
7
|
|
|
|
|
107
|
$plc; |
566
|
|
|
|
|
|
|
}; |
567
|
|
|
|
|
|
|
} |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
sub _handle_args { |
570
|
78
|
|
|
78
|
|
338
|
my ($self, %args) = @_; |
571
|
|
|
|
|
|
|
|
572
|
78
|
|
66
|
|
|
430
|
my $v = $args{v} // $self->{_meta}{args}; |
573
|
78
|
50
|
|
|
|
201
|
return unless $v; |
574
|
|
|
|
|
|
|
|
575
|
78
|
|
|
|
|
193
|
my $opt_sin = $self->{_args}{_schema_is_normalized}; |
576
|
78
|
|
|
|
|
157
|
my $opt_va = $self->{_args}{validate_args}; |
577
|
|
|
|
|
|
|
|
578
|
78
|
|
100
|
|
|
302
|
my $prefix = $args{prefix} // ''; |
579
|
78
|
|
100
|
|
|
304
|
my $argsterm = $args{argsterm} // '%args'; |
580
|
|
|
|
|
|
|
|
581
|
78
|
100
|
|
|
|
193
|
if ($opt_va) { |
582
|
76
|
|
|
|
|
288
|
$self->_add_module("use experimental 'smartmatch'"); |
583
|
76
|
|
|
|
|
233
|
$self->select_section('before_call_arg_validation'); |
584
|
76
|
100
|
|
|
|
331
|
$self->push_lines('', '# check args') if $prefix eq ''; |
585
|
76
|
|
|
|
|
351
|
$self->push_lines("for (sort keys $argsterm) {"); |
586
|
76
|
|
|
|
|
196
|
$self->indent; |
587
|
76
|
|
|
|
|
361
|
$self->_errif(400, q["Invalid argument name (please use letters/numbers/underscores only)'].$prefix.q[$_'"], |
588
|
|
|
|
|
|
|
'!/\A(-?)\w+(\.\w+)*\z/o'); |
589
|
76
|
|
|
|
|
613
|
$self->_errif(400, q["Unknown argument '].$prefix.q[$_'"], |
590
|
|
|
|
|
|
|
'!($1 || $_ ~~ '.dmp([sort keys %$v]).')'); |
591
|
76
|
|
|
|
|
236
|
$self->unindent; |
592
|
76
|
|
|
|
|
164
|
$self->push_lines('}'); |
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
|
595
|
78
|
|
|
|
|
323
|
for my $argname (sort keys %$v) { |
596
|
108
|
|
|
|
|
228
|
my $argspec = $v->{$argname}; |
597
|
|
|
|
|
|
|
|
598
|
108
|
|
|
|
|
186
|
my $argterm = $argsterm; |
599
|
108
|
100
|
|
|
|
739
|
if ($argterm =~ /^%\{\s*(.+)\s*\}/) { |
|
|
50
|
|
|
|
|
|
600
|
8
|
|
|
|
|
35
|
$argterm = $1 . "->{'$argname'}"; |
601
|
|
|
|
|
|
|
} elsif ($argterm =~ s/^%/\$/) { |
602
|
100
|
|
|
|
|
267
|
$argterm .= "{'$argname'}"; |
603
|
|
|
|
|
|
|
} else { |
604
|
0
|
|
|
|
|
0
|
$argterm .= "->{'$argname'}"; |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
|
607
|
108
|
|
|
|
|
233
|
my $has_default_prop = exists($argspec->{default}); |
608
|
108
|
|
|
|
|
269
|
my $sch = $argspec->{schema}; |
609
|
|
|
|
|
|
|
|
610
|
108
|
100
|
|
|
|
287
|
if ($sch) { |
|
|
100
|
|
|
|
|
|
611
|
|
|
|
|
|
|
my $has_sch_default = ref($sch) eq 'ARRAY' && |
612
|
78
|
100
|
100
|
|
|
470
|
exists($sch->[1]{default}) ? 1:0; |
613
|
78
|
100
|
|
|
|
182
|
if ($opt_va) { |
614
|
|
|
|
|
|
|
|
615
|
74
|
|
|
|
|
306
|
$self->push_lines("if (exists($argterm)) {"); |
616
|
74
|
|
|
|
|
181
|
$self->indent; |
617
|
|
|
|
|
|
|
|
618
|
74
|
100
|
|
|
|
185
|
if ($argspec->{stream}) { |
619
|
2
|
50
|
|
|
|
15
|
die "Error in schema for argument '$argname': must be str/buf/array if stream=1" |
620
|
|
|
|
|
|
|
unless $sch->[0] =~ /\A(str|buf|array)\z/; # XXX allow 'any' if all of its 'of' values are str/buf/array |
621
|
|
|
|
|
|
|
die "Error in schema for argument '$argname': must specify 'of' array clause if stream=1" |
622
|
2
|
50
|
33
|
|
|
13
|
if $sch->[0] eq 'array' && !$sch->[1]{of}; |
623
|
|
|
|
|
|
|
|
624
|
2
|
|
|
|
|
15
|
$self->_errif( |
625
|
|
|
|
|
|
|
400, |
626
|
|
|
|
|
|
|
qq["Argument '$prefix$argname' (stream) fails validation: must be coderef"], |
627
|
|
|
|
|
|
|
"!(ref($argterm) eq 'CODE')", |
628
|
|
|
|
|
|
|
); |
629
|
2
|
|
|
|
|
6
|
$self->push_lines('{ ## introduce scope because we want to declare a generic variable $i'); |
630
|
2
|
|
|
|
|
5
|
$self->indent; |
631
|
2
|
|
|
|
|
8
|
$self->push_lines( |
632
|
|
|
|
|
|
|
'my $i = -1;', |
633
|
|
|
|
|
|
|
"my \$origsub = $argterm;", |
634
|
|
|
|
|
|
|
'# arg coderef wrapper for validation', |
635
|
|
|
|
|
|
|
"$argterm = sub {", |
636
|
|
|
|
|
|
|
); |
637
|
2
|
|
|
|
|
6
|
$self->indent; |
638
|
2
|
|
|
|
|
6
|
$self->push_lines( |
639
|
|
|
|
|
|
|
'$i++;', |
640
|
|
|
|
|
|
|
"my \$rec = \$origsub->();", |
641
|
|
|
|
|
|
|
'return undef unless defined $rec;', |
642
|
|
|
|
|
|
|
); |
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
|
645
|
74
|
|
|
|
|
128
|
my $dn = $argname; $dn =~ s/\W+/_/g; |
|
74
|
|
|
|
|
177
|
|
646
|
|
|
|
|
|
|
my $cd = $self->_plc->compile( |
647
|
|
|
|
|
|
|
data_name => $dn, |
648
|
|
|
|
|
|
|
data_term => $argspec->{stream} ? '$rec' : $argterm, |
649
|
|
|
|
|
|
|
schema => $argspec->{stream} && $sch->[0] eq 'array' ? $sch->[1]{of} : $sch, |
650
|
|
|
|
|
|
|
schema_is_normalized => $opt_sin, |
651
|
|
|
|
|
|
|
return_type => 'str', |
652
|
|
|
|
|
|
|
indent_level => $self->get_indent_level + 1, |
653
|
|
|
|
|
|
|
core => $self->{_args}{core}, |
654
|
|
|
|
|
|
|
core_or_pp => $self->{_args}{core_or_pp}, |
655
|
|
|
|
|
|
|
pp => $self->{_args}{pp}, |
656
|
74
|
100
|
66
|
|
|
209
|
%{ $self->{_args}{_extra_sah_compiler_args} // {}}, |
|
74
|
100
|
50
|
|
|
616
|
|
657
|
|
|
|
|
|
|
); |
658
|
72
|
50
|
|
|
|
512738
|
die "Incompatible Data::Sah version (cd v=$cd->{v}, expected 2)" unless $cd->{v} == 2; |
659
|
72
|
|
|
|
|
142
|
for my $mod_rec (@{ $cd->{modules} }) { |
|
72
|
|
|
|
|
204
|
|
660
|
208
|
100
|
|
|
|
471
|
next unless $mod_rec->{phase} eq 'runtime'; |
661
|
136
|
|
66
|
|
|
603
|
$self->_add_module($mod_rec->{use_statement} // $mod_rec->{name}); |
662
|
|
|
|
|
|
|
} |
663
|
|
|
|
|
|
|
$self->_add_var($_, $cd->{vars}{$_}) |
664
|
72
|
|
|
|
|
134
|
for sort keys %{ $cd->{vars} }; |
|
72
|
|
|
|
|
257
|
|
665
|
72
|
|
|
|
|
353
|
$cd->{result} =~ s/\A\s+//; |
666
|
72
|
|
|
|
|
379
|
$self->push_lines( |
667
|
|
|
|
|
|
|
"my \$err_$dn;", |
668
|
|
|
|
|
|
|
"$cd->{result};", |
669
|
|
|
|
|
|
|
); |
670
|
72
|
100
|
|
|
|
214
|
if ($argspec->{stream}) { |
671
|
2
|
|
|
|
|
15
|
$self->push_lines( |
672
|
|
|
|
|
|
|
'if ('."\$err_$dn".') { die "Record #$i of streaming argument '."'$prefix$argname'".' ($rec) fails validation: '."\$err_$dn".'" }', |
673
|
|
|
|
|
|
|
'$rec;', |
674
|
|
|
|
|
|
|
); |
675
|
|
|
|
|
|
|
} else { |
676
|
70
|
|
|
|
|
364
|
$self->_errif( |
677
|
|
|
|
|
|
|
400, qq["Argument '$prefix$argname' fails validation: \$err_$dn"], |
678
|
|
|
|
|
|
|
"\$err_$dn"); |
679
|
|
|
|
|
|
|
} |
680
|
72
|
100
|
|
|
|
199
|
if ($argspec->{meta}) { |
681
|
2
|
|
|
|
|
10
|
$self->push_lines("# check subargs of $prefix$argname"); |
682
|
|
|
|
|
|
|
$self->_handle_args( |
683
|
|
|
|
|
|
|
%args, |
684
|
|
|
|
|
|
|
v => $argspec->{meta}{args}, |
685
|
2
|
50
|
|
|
|
31
|
prefix => ($prefix ? "$prefix/" : "") . "$argname/", |
686
|
|
|
|
|
|
|
argsterm => '%{'.$argterm.'}', |
687
|
|
|
|
|
|
|
); |
688
|
|
|
|
|
|
|
} |
689
|
72
|
100
|
|
|
|
192
|
if ($argspec->{element_meta}) { |
690
|
2
|
|
|
|
|
10
|
$self->push_lines("# check element subargs of $prefix$argname"); |
691
|
2
|
|
|
|
|
5
|
my $indexterm = "$prefix$argname"; |
692
|
2
|
|
|
|
|
6
|
$indexterm =~ s/\W+/_/g; |
693
|
2
|
|
|
|
|
5
|
$indexterm = '$i_' . $indexterm; |
694
|
2
|
|
|
|
|
10
|
$self->push_lines('for my '.$indexterm.' (0..$#{ '.$argterm.' }) {'); |
695
|
2
|
|
|
|
|
6
|
$self->indent; |
696
|
2
|
|
|
|
|
13
|
$self->_errif( |
697
|
|
|
|
|
|
|
400, qq("Argument '$prefix$argname\[).qq($indexterm]' fails validation: must be hash"), |
698
|
|
|
|
|
|
|
"ref($argterm\->[$indexterm]) ne 'HASH'"); |
699
|
|
|
|
|
|
|
$self->_handle_args( |
700
|
|
|
|
|
|
|
%args, |
701
|
|
|
|
|
|
|
v => $argspec->{element_meta}{args}, |
702
|
2
|
50
|
|
|
|
24
|
prefix => ($prefix ? "$prefix/" : "") . "$argname\[$indexterm]/", |
703
|
|
|
|
|
|
|
argsterm => '%{'.$argterm.'->['.$indexterm.']}', |
704
|
|
|
|
|
|
|
); |
705
|
2
|
|
|
|
|
7
|
$self->unindent; |
706
|
2
|
|
|
|
|
5
|
$self->push_lines('}'); |
707
|
|
|
|
|
|
|
} |
708
|
72
|
|
|
|
|
191
|
$self->unindent; |
709
|
72
|
100
|
|
|
|
193
|
if ($argspec->{stream}) { |
710
|
2
|
|
|
|
|
7
|
$self->push_lines('}; ## arg coderef wrapper'); |
711
|
2
|
|
|
|
|
4
|
$self->unindent; |
712
|
2
|
|
|
|
|
5
|
$self->push_lines('} ## close scope'); |
713
|
2
|
|
|
|
|
5
|
$self->unindent; |
714
|
|
|
|
|
|
|
} |
715
|
72
|
100
|
|
|
|
238
|
if ($has_default_prop) { |
|
|
100
|
|
|
|
|
|
716
|
|
|
|
|
|
|
$self->push_lines( |
717
|
|
|
|
|
|
|
'} else {', |
718
|
8
|
|
|
|
|
58
|
" $argterm //= ".dmp($argspec->{default}).";"); |
719
|
|
|
|
|
|
|
} elsif ($has_sch_default) { |
720
|
|
|
|
|
|
|
$self->push_lines( |
721
|
|
|
|
|
|
|
'} else {', |
722
|
8
|
|
|
|
|
42
|
" $argterm //= ".dmp($sch->[1]{default}).";"); |
723
|
|
|
|
|
|
|
} |
724
|
72
|
|
|
|
|
234
|
$self->push_lines("} ## if exists arg $prefix$argname"); |
725
|
|
|
|
|
|
|
} # if opt_va |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
} elsif ($has_default_prop) { |
728
|
|
|
|
|
|
|
# doesn't have schema but have 'default' property, we still need to |
729
|
|
|
|
|
|
|
# set default here |
730
|
2
|
|
|
|
|
9
|
$self->push_lines("$argterm = ".dmp($argspec->{default}). |
731
|
|
|
|
|
|
|
" if !exists($argterm);"); |
732
|
|
|
|
|
|
|
} |
733
|
106
|
100
|
100
|
|
|
1114
|
if ($argspec->{req} && $opt_va) { |
734
|
18
|
|
|
|
|
94
|
$self->_errif( |
735
|
|
|
|
|
|
|
400, qq["Missing required argument: $argname"], |
736
|
|
|
|
|
|
|
"!exists($argterm)"); |
737
|
|
|
|
|
|
|
} |
738
|
|
|
|
|
|
|
} # for arg |
739
|
|
|
|
|
|
|
} |
740
|
|
|
|
|
|
|
|
741
|
74
|
|
|
74
|
0
|
329
|
sub handlemeta_args { {v=>2, prio=>10} } |
742
|
|
|
|
|
|
|
sub handle_args { |
743
|
74
|
|
|
74
|
0
|
320
|
my ($self, %args) = @_; |
744
|
74
|
|
|
|
|
350
|
$self->_handle_args(%args); |
745
|
|
|
|
|
|
|
} |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
# after args |
748
|
4
|
|
|
4
|
0
|
16
|
sub handlemeta_args_rels { {v=>2, prio=>11} } |
749
|
|
|
|
|
|
|
sub handle_args_rels { |
750
|
4
|
|
|
4
|
0
|
15
|
my ($self, %args) = @_; |
751
|
|
|
|
|
|
|
|
752
|
4
|
|
33
|
|
|
20
|
my $v = $args{v} // $self->{_meta}{args_rels}; |
753
|
4
|
50
|
|
|
|
10
|
return unless $v; |
754
|
|
|
|
|
|
|
|
755
|
4
|
|
50
|
|
|
15
|
my $argsterm = $args{argsterm} // '%args'; |
756
|
|
|
|
|
|
|
|
757
|
4
|
|
|
|
|
10
|
$self->select_section('before_call_arg_validation'); |
758
|
4
|
|
|
|
|
9
|
$self->push_lines('', '# check args_rels'); |
759
|
|
|
|
|
|
|
|
760
|
4
|
|
|
|
|
7
|
my $dn = "args_rels"; |
761
|
4
|
|
|
|
|
11
|
my $hc = $self->_sah->get_compiler("human"); |
762
|
4
|
|
|
|
|
17501
|
my $cd_h = $hc->init_cd; |
763
|
4
|
|
33
|
|
|
1738
|
$cd_h->{args}{lang} //= $cd_h->{default_lang}; |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
my $cd = $self->_plc->compile( |
766
|
|
|
|
|
|
|
data_name => $dn, |
767
|
|
|
|
|
|
|
data_term => "\\$argsterm", |
768
|
|
|
|
|
|
|
schema => ['hash', $v], |
769
|
|
|
|
|
|
|
return_type => 'str', |
770
|
|
|
|
|
|
|
indent_level => $self->get_indent_level + 1, |
771
|
|
|
|
|
|
|
human_hash_values => { |
772
|
|
|
|
|
|
|
field => $hc->_xlt($cd_h, "argument"), |
773
|
|
|
|
|
|
|
fields => $hc->_xlt($cd_h, "arguments"), |
774
|
|
|
|
|
|
|
}, |
775
|
|
|
|
|
|
|
core => $self->{_args}{core}, |
776
|
|
|
|
|
|
|
core_or_pp => $self->{_args}{core_or_pp}, |
777
|
|
|
|
|
|
|
pp => $self->{_args}{pp}, |
778
|
4
|
|
|
|
|
15
|
); |
779
|
2
|
50
|
|
|
|
18022
|
die "Incompatible Data::Sah version (cd v=$cd->{v}, expected 2)" unless $cd->{v} == 2; |
780
|
2
|
|
|
|
|
5
|
for my $mod_rec (@{ $cd->{modules} }) { |
|
2
|
|
|
|
|
8
|
|
781
|
6
|
100
|
|
|
|
16
|
next unless $mod_rec->{phase} eq 'runtime'; |
782
|
4
|
|
66
|
|
|
19
|
$self->_add_module($mod_rec->{use_statement} // $mod_rec->{name}); |
783
|
|
|
|
|
|
|
} |
784
|
2
|
|
|
|
|
5
|
$self->_add_var($_, $cd->{vars}{$_}) for sort keys %{ $cd->{vars} }; |
|
2
|
|
|
|
|
8
|
|
785
|
2
|
|
|
|
|
11
|
$cd->{result} =~ s/\A\s+//; |
786
|
2
|
|
|
|
|
13
|
$self->push_lines( |
787
|
|
|
|
|
|
|
"my \$err_$dn;", |
788
|
|
|
|
|
|
|
"$cd->{result};", |
789
|
|
|
|
|
|
|
); |
790
|
2
|
|
|
|
|
10
|
$self->_errif( |
791
|
|
|
|
|
|
|
400, qq["\$err_$dn"], |
792
|
|
|
|
|
|
|
"\$err_$dn"); |
793
|
|
|
|
|
|
|
} |
794
|
|
|
|
|
|
|
|
795
|
14
|
|
|
14
|
0
|
41
|
sub handlemeta_result { {v=>2, prio=>50} } |
796
|
|
|
|
|
|
|
sub handle_result { |
797
|
14
|
|
|
14
|
0
|
608
|
require Data::Sah; |
798
|
|
|
|
|
|
|
|
799
|
14
|
|
|
|
|
3050
|
my ($self, %args) = @_; |
800
|
|
|
|
|
|
|
|
801
|
14
|
|
|
|
|
29
|
my $meta = $self->{_meta}; |
802
|
14
|
|
|
|
|
30
|
my $v = $meta->{result}; |
803
|
14
|
50
|
|
|
|
34
|
return unless $v; |
804
|
|
|
|
|
|
|
|
805
|
14
|
|
|
|
|
29
|
my $opt_sin = $self->{_args}{_schema_is_normalized}; |
806
|
14
|
|
|
|
|
24
|
my $opt_vr = $self->{_args}{validate_result}; |
807
|
|
|
|
|
|
|
|
808
|
14
|
|
|
|
|
30
|
my %schemas_by_status; # key = status, value = schema |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
# collect and check handlers |
811
|
|
|
|
|
|
|
my %handler_args; |
812
|
14
|
|
|
|
|
0
|
my %handler_metas; |
813
|
14
|
|
|
|
|
46
|
for my $k0 (keys %$v) { |
814
|
18
|
|
|
|
|
28
|
my $k = $k0; |
815
|
18
|
|
|
|
|
36
|
$k =~ s/\..+//; |
816
|
18
|
100
|
|
|
|
44
|
next if $k =~ /\A_/; |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
# check builtin result spec key |
819
|
16
|
50
|
|
|
|
95
|
next if $k =~ /\A( |
820
|
|
|
|
|
|
|
summary|description|tags|default_lang| |
821
|
|
|
|
|
|
|
schema|statuses|stream| |
822
|
|
|
|
|
|
|
x |
823
|
|
|
|
|
|
|
)\z/x; |
824
|
|
|
|
|
|
|
# try a property module first |
825
|
0
|
|
|
|
|
0
|
require "Perinci/Sub/Property/result/$k.pm"; |
826
|
0
|
|
|
|
|
0
|
my $meth = "handlemeta_result__$k"; |
827
|
0
|
0
|
|
|
|
0
|
unless ($self->can($meth)) { |
828
|
0
|
|
|
|
|
0
|
die "No handler for property result/$k0 ($meth)"; |
829
|
|
|
|
|
|
|
} |
830
|
0
|
|
|
|
|
0
|
my $hm = $self->$meth; |
831
|
0
|
|
0
|
|
|
0
|
$hm->{v} //= 1; |
832
|
0
|
0
|
|
|
|
0
|
next unless defined $hm->{prio}; |
833
|
|
|
|
|
|
|
die "Please update property handler result/$k which is still at v=$hm->{v} ". |
834
|
|
|
|
|
|
|
"(needs v=$protocol_version)" |
835
|
0
|
0
|
|
|
|
0
|
unless $hm->{v} == $protocol_version; |
836
|
|
|
|
|
|
|
my $ha = { |
837
|
0
|
|
|
|
|
0
|
prio=>$hm->{prio}, value=>$v->{$k0}, property=>$k0, |
838
|
|
|
|
|
|
|
meth=>"handle_result__$k", |
839
|
|
|
|
|
|
|
}; |
840
|
0
|
|
|
|
|
0
|
$handler_args{$k} = $ha; |
841
|
0
|
|
|
|
|
0
|
$handler_metas{$k} = $hm; |
842
|
|
|
|
|
|
|
} |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
# call all the handlers in order |
845
|
14
|
|
|
|
|
40
|
for my $k (sort {$handler_args{$a}{prio} <=> $handler_args{$b}{prio}} |
|
0
|
|
|
|
|
0
|
|
846
|
|
|
|
|
|
|
keys %handler_args) { |
847
|
0
|
|
|
|
|
0
|
my $ha = $handler_args{$k}; |
848
|
0
|
|
|
|
|
0
|
my $meth = $ha->{meth}; |
849
|
0
|
|
|
|
|
0
|
local $self->{_cur_handler} = $meth; |
850
|
0
|
|
|
|
|
0
|
local $self->{_cur_handler_meta} = $handler_metas{$k}; |
851
|
0
|
|
|
|
|
0
|
local $self->{_cur_handler_args} = $ha; |
852
|
0
|
|
|
|
|
0
|
$self->$meth(args=>\%args, meta=>$meta, %$ha); |
853
|
|
|
|
|
|
|
} |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
# validate result |
856
|
14
|
|
|
|
|
19
|
my @modules; |
857
|
14
|
100
|
100
|
|
|
60
|
if ($v->{schema} && $opt_vr) { |
858
|
4
|
|
|
|
|
9
|
$schemas_by_status{200} = $v->{schema}; |
859
|
|
|
|
|
|
|
} |
860
|
14
|
50
|
33
|
|
|
37
|
if ($v->{statuses} && $opt_vr) { |
861
|
0
|
|
|
|
|
0
|
for my $s (keys %{$v->{statuses}}) { |
|
0
|
|
|
|
|
0
|
|
862
|
0
|
|
|
|
|
0
|
my $sv = $v->{statuses}{$s}; |
863
|
0
|
0
|
|
|
|
0
|
if ($sv->{schema}) { |
864
|
0
|
|
|
|
|
0
|
$schemas_by_status{$s} = $sv->{schema}; |
865
|
|
|
|
|
|
|
} |
866
|
|
|
|
|
|
|
} |
867
|
|
|
|
|
|
|
} |
868
|
|
|
|
|
|
|
|
869
|
14
|
|
|
|
|
29
|
my $sub_name = $self->{_args}{sub_name}; |
870
|
|
|
|
|
|
|
|
871
|
14
|
100
|
|
|
|
40
|
if ($opt_vr) { |
872
|
12
|
|
|
|
|
33
|
$self->select_section('after_call_res_validation'); |
873
|
|
|
|
|
|
|
$self->push_lines( |
874
|
|
|
|
|
|
|
'my $_w_res2 = $_w_res->[2];', |
875
|
12
|
100
|
|
|
|
53
|
'my $_w_res_is_stream = $_w_res->[3]{stream} // ' . ($v->{stream} ? 1:0) . ';', |
876
|
|
|
|
|
|
|
); |
877
|
12
|
|
|
|
|
32
|
$self->_errif( |
878
|
|
|
|
|
|
|
500, |
879
|
|
|
|
|
|
|
q["Stream result must be coderef"], |
880
|
|
|
|
|
|
|
'$_w_res_is_stream && ref($_w_res2) ne "CODE"', |
881
|
|
|
|
|
|
|
); |
882
|
12
|
|
|
|
|
51
|
for my $s (sort keys %schemas_by_status) { |
883
|
4
|
|
|
|
|
7
|
my $sch = $schemas_by_status{$s}; |
884
|
4
|
100
|
|
|
|
25
|
if ($v->{stream}) { |
885
|
2
|
50
|
|
|
|
11
|
die "Error in result schema: must be str/buf/array if stream=1" |
886
|
|
|
|
|
|
|
unless $sch->[0] =~ /\A(str|buf|array)\z/; # XXX allow 'any' if all of its 'of' values are str/buf/array |
887
|
|
|
|
|
|
|
die "Error in result schema: must specify 'of' array clause if stream=1" |
888
|
2
|
50
|
33
|
|
|
13
|
if $sch->[0] eq 'array' && !$sch->[1]{of}; |
889
|
|
|
|
|
|
|
} |
890
|
4
|
|
|
|
|
16
|
$self->push_lines("if (\$_w_res->[0] == $s) {"); |
891
|
4
|
|
|
|
|
8
|
$self->indent; |
892
|
4
|
|
|
|
|
19
|
$self->push_lines('if (!$_w_res_is_stream) {'); |
893
|
4
|
|
|
|
|
10
|
$self->indent; |
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
# validation for when not a stream |
896
|
|
|
|
|
|
|
my $cd = $self->_plc->compile( |
897
|
|
|
|
|
|
|
data_name => '_w_res2', |
898
|
|
|
|
|
|
|
# err_res can clash on arg named 'res' |
899
|
|
|
|
|
|
|
err_term => '$_w_err2_res', |
900
|
|
|
|
|
|
|
schema => $sch, |
901
|
|
|
|
|
|
|
schema_is_normalized => $opt_sin, |
902
|
|
|
|
|
|
|
return_type => 'str', |
903
|
|
|
|
|
|
|
indent_level => $self->get_indent_level + 1, |
904
|
|
|
|
|
|
|
core => $self->{_args}{core}, |
905
|
|
|
|
|
|
|
core_or_pp => $self->{_args}{core_or_pp}, |
906
|
|
|
|
|
|
|
pp => $self->{_args}{pp}, |
907
|
4
|
|
50
|
|
|
15
|
%{ $self->{_args}{_extra_sah_compiler_args} // {}}, |
|
4
|
|
|
|
|
32
|
|
908
|
|
|
|
|
|
|
); |
909
|
4
|
50
|
|
|
|
66905
|
die "Incompatible Data::Sah version (cd v=$cd->{v}, expected 2)" unless $cd->{v} == 2; |
910
|
4
|
|
|
|
|
10
|
for my $mod_rec (@{ $cd->{modules} }) { |
|
4
|
|
|
|
|
12
|
|
911
|
16
|
100
|
|
|
|
36
|
next unless $mod_rec->{phase} eq 'runtime'; |
912
|
10
|
|
66
|
|
|
42
|
$self->_add_module($mod_rec->{use_statement} // $mod_rec->{name}); |
913
|
|
|
|
|
|
|
} |
914
|
|
|
|
|
|
|
$self->_add_var($_, $cd->{vars}{$_}) |
915
|
4
|
|
|
|
|
7
|
for sort keys %{ $cd->{vars} }; |
|
4
|
|
|
|
|
17
|
|
916
|
4
|
|
|
|
|
10
|
$self->push_lines("my \$_w_err2_res;"); |
917
|
4
|
|
|
|
|
21
|
$cd->{result} =~ s/\A\s+//; |
918
|
4
|
|
|
|
|
15
|
$self->push_lines("$cd->{result};"); |
919
|
4
|
|
|
|
|
20
|
$self->_errif( |
920
|
|
|
|
|
|
|
500, |
921
|
|
|
|
|
|
|
qq["BUG: Result from sub $sub_name (\$_w_res2) fails validation: ]. |
922
|
|
|
|
|
|
|
qq[\$_w_err2_res"], |
923
|
|
|
|
|
|
|
"\$_w_err2_res"); |
924
|
4
|
|
|
|
|
9
|
$self->unindent; |
925
|
4
|
|
|
|
|
8
|
$self->push_lines("} else {"); # stream |
926
|
4
|
|
|
|
|
10
|
$self->indent; |
927
|
4
|
|
|
|
|
12
|
$self->push_lines( |
928
|
|
|
|
|
|
|
'my $i = -1;', |
929
|
|
|
|
|
|
|
'# wrap result coderef for validation', |
930
|
|
|
|
|
|
|
'$_w_res->[2] = sub {', |
931
|
|
|
|
|
|
|
); |
932
|
4
|
|
|
|
|
9
|
$self->indent; |
933
|
4
|
|
|
|
|
10
|
$self->push_lines( |
934
|
|
|
|
|
|
|
'$i++;', |
935
|
|
|
|
|
|
|
'my $rec = $_w_res2->();', |
936
|
|
|
|
|
|
|
'return undef unless defined $rec;', |
937
|
|
|
|
|
|
|
); |
938
|
|
|
|
|
|
|
# generate schema code once again, this time for when stream |
939
|
|
|
|
|
|
|
$cd = $self->_plc->compile( |
940
|
|
|
|
|
|
|
data_name => 'rec', |
941
|
|
|
|
|
|
|
# err_res can clash on arg named 'res' |
942
|
|
|
|
|
|
|
err_term => '$rec_err', |
943
|
|
|
|
|
|
|
schema => $sch->[0] eq 'array' ? $sch->[1]{of} : $sch, |
944
|
|
|
|
|
|
|
schema_is_normalized => $opt_sin, |
945
|
|
|
|
|
|
|
return_type => 'str', |
946
|
|
|
|
|
|
|
indent_level => $self->get_indent_level + 1, |
947
|
|
|
|
|
|
|
core => $self->{_args}{core}, |
948
|
|
|
|
|
|
|
core_or_pp => $self->{_args}{core_or_pp}, |
949
|
|
|
|
|
|
|
pp => $self->{_args}{pp}, |
950
|
4
|
100
|
50
|
|
|
10
|
%{ $self->{_args}{_extra_sah_compiler_args} // {}}, |
|
4
|
|
|
|
|
27
|
|
951
|
|
|
|
|
|
|
); |
952
|
4
|
50
|
|
|
|
8296
|
die "Incompatible Data::Sah version (cd v=$cd->{v}, expected 2)" unless $cd->{v} == 2; |
953
|
|
|
|
|
|
|
# XXX no need to require modules required by validator? |
954
|
4
|
|
|
|
|
12
|
$self->push_lines('my $rec_err;'); |
955
|
4
|
|
|
|
|
18
|
$cd->{result} =~ s/\A\s+//; |
956
|
4
|
|
|
|
|
17
|
$self->push_lines("$cd->{result};"); |
957
|
4
|
|
|
|
|
9
|
$self->push_lines('if ($rec_err) { die "BUG: Result stream record #$i ($rec) fails validation: $rec_err" }'); |
958
|
4
|
|
|
|
|
9
|
$self->push_lines('$rec;'); |
959
|
4
|
|
|
|
|
9
|
$self->unindent; |
960
|
4
|
|
|
|
|
10
|
$self->push_lines('}; ## result coderef wrapper'); |
961
|
4
|
|
|
|
|
10
|
$self->unindent; |
962
|
4
|
|
|
|
|
7
|
$self->push_lines("} ## if stream"); |
963
|
4
|
|
|
|
|
10
|
$self->unindent; |
964
|
4
|
|
|
|
|
10
|
$self->push_lines("} ## if status=$s"); |
965
|
|
|
|
|
|
|
} # for schemas_by_status |
966
|
|
|
|
|
|
|
} |
967
|
|
|
|
|
|
|
} |
968
|
|
|
|
|
|
|
|
969
|
6
|
|
|
6
|
0
|
28
|
sub handlemeta_result_naked { {v=>2, prio=>99, convert=>1} } |
970
|
|
|
|
|
|
|
sub handle_result_naked { |
971
|
6
|
|
|
6
|
0
|
34
|
my ($self, %args) = @_; |
972
|
|
|
|
|
|
|
|
973
|
6
|
|
|
|
|
17
|
my $old = $args{value}; |
974
|
6
|
|
33
|
|
|
20
|
my $v = $args{new} // $old; |
975
|
|
|
|
|
|
|
|
976
|
6
|
50
|
|
|
|
19
|
return if !!$v == !!$old; |
977
|
|
|
|
|
|
|
|
978
|
6
|
|
|
|
|
19
|
$self->select_section('AFTER_CALL_ADD_OR_STRIP_RESULT_ENVELOPE'); |
979
|
6
|
100
|
|
|
|
19
|
if ($v) { |
980
|
2
|
|
|
|
|
7
|
$self->push_lines( |
981
|
|
|
|
|
|
|
'', '# strip result envelope', |
982
|
|
|
|
|
|
|
'$_w_res = $_w_res->[2];', |
983
|
|
|
|
|
|
|
); |
984
|
|
|
|
|
|
|
} else { |
985
|
4
|
|
|
|
|
15
|
$self->push_lines( |
986
|
|
|
|
|
|
|
'', '# add result envelope', |
987
|
|
|
|
|
|
|
'$_w_res = [200, "OK", $_w_res];', |
988
|
|
|
|
|
|
|
); |
989
|
|
|
|
|
|
|
} |
990
|
|
|
|
|
|
|
} |
991
|
|
|
|
|
|
|
|
992
|
4
|
|
|
4
|
0
|
13
|
sub handlemeta_deps { {v=>2, prio=>0.5} } |
993
|
|
|
|
|
|
|
sub handle_deps { |
994
|
4
|
|
|
4
|
0
|
17
|
my ($self, %args) = @_; |
995
|
4
|
|
|
|
|
10
|
my $value = $args{value}; |
996
|
4
|
|
|
|
|
7
|
my $meta = $self->{_meta}; |
997
|
4
|
|
|
|
|
8
|
my $v = $self->{_args}{meta_name}; |
998
|
4
|
|
|
|
|
10
|
$self->select_section('before_call_after_arg_validation'); |
999
|
4
|
|
|
|
|
11
|
$self->push_lines('', '# check dependencies'); |
1000
|
4
|
|
|
|
|
15
|
$self->_add_module("Perinci::Sub::DepChecker"); |
1001
|
|
|
|
|
|
|
#$self->push_lines('use Data::Dump; dd '.$v.';'); |
1002
|
4
|
|
|
|
|
16
|
$self->push_lines('my $_w_deps_res = Perinci::Sub::DepChecker::check_deps('. |
1003
|
|
|
|
|
|
|
$v.'->{deps});'); |
1004
|
4
|
|
|
|
|
17
|
$self->_errif(412, '"Deps failed: $_w_deps_res"', '$_w_deps_res'); |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
# we handle some deps our own |
1007
|
4
|
50
|
|
|
|
16
|
if ($value->{tmp_dir}) { |
1008
|
0
|
|
|
|
|
0
|
$self->_errif(412, '"Dep failed: please specify -tmp_dir"', |
1009
|
|
|
|
|
|
|
'!$args{-tmp_dir}'); |
1010
|
|
|
|
|
|
|
} |
1011
|
4
|
50
|
|
|
|
12
|
if ($value->{trash_dir}) { |
1012
|
0
|
|
|
|
|
0
|
$self->_errif(412, '"Dep failed: please specify -trash_dir"', |
1013
|
|
|
|
|
|
|
'!$args{-trash_dir}'); |
1014
|
|
|
|
|
|
|
} |
1015
|
4
|
50
|
|
|
|
22
|
if ($value->{undo_trash_dir}) { |
1016
|
0
|
|
|
|
|
0
|
$self->_errif(412, '"Dep failed: please specify -undo_trash_dir"', |
1017
|
|
|
|
|
|
|
'!($args{-undo_trash_dir} || $args{-tx_manager} || '. |
1018
|
|
|
|
|
|
|
'$args{-undo_action} && $args{-undo_action}=~/\A(?:undo|redo)\z/)'); |
1019
|
|
|
|
|
|
|
} |
1020
|
|
|
|
|
|
|
} |
1021
|
|
|
|
|
|
|
|
1022
|
110
|
|
|
110
|
0
|
223
|
sub handlemeta_x { {} } |
1023
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_entity_v { {} } |
1024
|
0
|
|
|
0
|
0
|
0
|
sub handlemeta_entity_date { {} } |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
sub _reset_work_data { |
1027
|
108
|
|
|
108
|
|
437
|
my ($self, %args) = @_; |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
# to make it stand out more, all work/state data is prefixed with |
1030
|
|
|
|
|
|
|
# underscore. |
1031
|
|
|
|
|
|
|
|
1032
|
108
|
|
|
|
|
522
|
$self->{_cur_section} = undef; |
1033
|
108
|
|
|
|
|
271
|
$self->{_cur_handler} = undef; |
1034
|
108
|
|
|
|
|
246
|
$self->{_cur_handler_args} = undef; |
1035
|
108
|
|
|
|
|
201
|
$self->{_cur_handler_meta} = undef; |
1036
|
108
|
|
|
|
|
254
|
$self->{_levels} = {}; |
1037
|
108
|
|
|
|
|
250
|
$self->{_codes} = {}; |
1038
|
108
|
|
|
|
|
259
|
$self->{_modules} = []; # modules loaded by wrapper sub |
1039
|
108
|
|
|
|
|
578
|
$self->{$_} = $args{$_} for keys %args; |
1040
|
|
|
|
|
|
|
} |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
sub wrap { |
1043
|
112
|
|
|
112
|
0
|
834
|
require Scalar::Util; |
1044
|
|
|
|
|
|
|
|
1045
|
112
|
|
|
|
|
384
|
my ($self, %args) = @_; |
1046
|
|
|
|
|
|
|
|
1047
|
112
|
|
|
|
|
259
|
my $wrap_log_prop = "x.perinci.sub.wrapper.logs"; |
1048
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
# required arguments |
1050
|
112
|
|
|
|
|
217
|
my $sub = $args{sub}; |
1051
|
112
|
|
|
|
|
273
|
my $sub_name = $args{sub_name}; |
1052
|
112
|
50
|
66
|
|
|
405
|
$sub || $sub_name or return [400, "Please specify sub or sub_name"]; |
1053
|
112
|
50
|
|
|
|
314
|
$args{meta} or return [400, "Please specify meta"]; |
1054
|
112
|
|
|
|
|
219
|
my $meta_name = $args{meta_name}; |
1055
|
|
|
|
|
|
|
# we clone the meta because we'll replace stuffs |
1056
|
112
|
|
|
|
|
336
|
my $meta = clone($args{meta}); |
1057
|
112
|
|
100
|
|
|
1900
|
my $wrap_logs = $meta->{$wrap_log_prop} // []; |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
# currently internal args, not exposed/documented |
1060
|
112
|
|
50
|
|
|
603
|
$args{_compiled_package} //= 'Perinci::Sub::Wrapped'; |
1061
|
112
|
|
|
|
|
228
|
my $comppkg = $args{_compiled_package}; |
1062
|
|
|
|
|
|
|
$args{_schema_is_normalized} //= |
1063
|
112
|
100
|
66
|
|
|
808
|
$wrap_logs->[-1] && $wrap_logs->[-1]{normalize_schema} ? 1 : 0; |
|
|
|
100
|
|
|
|
|
1064
|
112
|
|
50
|
|
|
485
|
$args{_embed} //= 0; |
1065
|
112
|
|
50
|
|
|
525
|
$args{_extra_sah_compiler_args} //= undef; |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
# defaults for arguments |
1068
|
112
|
|
50
|
|
|
703
|
$args{indent} //= " " x 4; |
1069
|
112
|
|
100
|
|
|
478
|
$args{convert} //= {}; |
1070
|
112
|
|
50
|
|
|
544
|
$args{compile} //= 1; |
1071
|
112
|
|
100
|
|
|
527
|
$args{log} //= 1; |
1072
|
|
|
|
|
|
|
$args{validate_args} //= 0 |
1073
|
|
|
|
|
|
|
# function might want to disable validate_args by default, e.g. if |
1074
|
|
|
|
|
|
|
# source code has been processed with |
1075
|
|
|
|
|
|
|
# Dist::Zilla::Plugin::Rinci::Validate |
1076
|
112
|
100
|
50
|
|
|
322
|
if $meta->{'x.perinci.sub.wrapper.disable_validate_args'}; |
1077
|
|
|
|
|
|
|
$args{validate_args} //= 0 |
1078
|
|
|
|
|
|
|
# by default do not validate args again if previous wrapper(s) have |
1079
|
|
|
|
|
|
|
# already done it |
1080
|
112
|
100
|
50
|
|
|
414
|
if (grep {$_->{validate_args}} @$wrap_logs); |
|
2
|
|
|
|
|
14
|
|
1081
|
112
|
|
100
|
|
|
548
|
$args{validate_args} //= 1; |
1082
|
|
|
|
|
|
|
$args{validate_result} //= 0 |
1083
|
|
|
|
|
|
|
# function might want to disable validate_result by default, e.g. if |
1084
|
|
|
|
|
|
|
# source code has been processed with |
1085
|
|
|
|
|
|
|
# Dist::Zilla::Plugin::Rinci::Validate |
1086
|
112
|
100
|
50
|
|
|
299
|
if $meta->{'x.perinci.sub.wrapper.disable_validate_result'}; |
1087
|
|
|
|
|
|
|
$args{validate_result} //= 0 |
1088
|
|
|
|
|
|
|
# by default do not validate result again if previous wrapper(s) have |
1089
|
|
|
|
|
|
|
# already done it |
1090
|
112
|
100
|
50
|
|
|
302
|
if (grep {$_->{validate_result}} @$wrap_logs); |
|
2
|
|
|
|
|
11
|
|
1091
|
112
|
|
100
|
|
|
580
|
$args{validate_result} //= 1; |
1092
|
112
|
|
33
|
|
|
526
|
$args{core} //= $ENV{PERINCI_WRAPPER_CORE}; |
1093
|
112
|
|
33
|
|
|
556
|
$args{core_or_pp} //= $ENV{PERINCI_WRAPPER_CORE_OR_PP}; |
1094
|
112
|
|
33
|
|
|
624
|
$args{pp} //= $ENV{PERINCI_WRAPPER_PP}; |
1095
|
|
|
|
|
|
|
|
1096
|
112
|
|
|
|
|
177
|
my $sub_ref_name; |
1097
|
|
|
|
|
|
|
# if sub_name is not provided, create a unique name for it. it is needed by |
1098
|
|
|
|
|
|
|
# the wrapper-generated code (e.g. printing error messages) |
1099
|
112
|
100
|
66
|
|
|
392
|
if (!$sub_name || $sub) { |
1100
|
110
|
|
|
|
|
570
|
my $n = $comppkg . "::sub".Scalar::Util::refaddr($sub); |
1101
|
15
|
|
|
14
|
|
501
|
no strict 'refs'; no warnings; ${$n} = $sub; |
|
14
|
|
|
14
|
|
122
|
|
|
14
|
|
|
|
|
504
|
|
|
14
|
|
|
|
|
431
|
|
|
14
|
|
|
|
|
49
|
|
|
14
|
|
|
|
|
630
|
|
|
110
|
|
|
|
|
177
|
|
|
110
|
|
|
|
|
511
|
|
1102
|
14
|
|
|
14
|
|
88
|
use experimental 'smartmatch'; |
|
14
|
|
|
|
|
134
|
|
|
14
|
|
|
|
|
158
|
|
1103
|
110
|
50
|
|
|
|
316
|
if (!$sub_name) { |
1104
|
110
|
|
|
|
|
346
|
$args{sub_name} = $sub_name = '$' . $n; |
1105
|
|
|
|
|
|
|
} |
1106
|
110
|
|
|
|
|
279
|
$sub_ref_name = '$' . $n; |
1107
|
|
|
|
|
|
|
} |
1108
|
|
|
|
|
|
|
# if meta name is not provided, we store the meta somewhere, it is needed by |
1109
|
|
|
|
|
|
|
# the wrapper-generated code (e.g. deps clause). |
1110
|
112
|
50
|
|
|
|
480
|
if (!$meta_name) { |
1111
|
112
|
|
|
|
|
406
|
my $n = $comppkg . "::meta".Scalar::Util::refaddr($meta); |
1112
|
14
|
|
|
14
|
|
1521
|
no strict 'refs'; no warnings; ${$n} = $meta; |
|
14
|
|
|
14
|
|
50
|
|
|
14
|
|
|
|
|
365
|
|
|
14
|
|
|
|
|
88
|
|
|
14
|
|
|
|
|
95
|
|
|
14
|
|
|
|
|
577
|
|
|
112
|
|
|
|
|
191
|
|
|
112
|
|
|
|
|
565
|
|
1113
|
14
|
|
|
14
|
|
362
|
use experimental 'smartmatch'; |
|
14
|
|
|
|
|
32
|
|
|
14
|
|
|
|
|
60
|
|
1114
|
112
|
|
|
|
|
390
|
$args{meta_name} = $meta_name = '$' . $n; |
1115
|
|
|
|
|
|
|
} |
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
# shallow copy |
1118
|
112
|
|
|
|
|
200
|
my $opt_cvt = { %{ $args{convert} } }; |
|
112
|
|
|
|
|
345
|
|
1119
|
112
|
|
|
|
|
229
|
my $opt_sin = $args{_schema_is_normalized}; |
1120
|
|
|
|
|
|
|
|
1121
|
112
|
100
|
|
|
|
687
|
$meta = normalize_function_metadata($meta) |
1122
|
|
|
|
|
|
|
unless $opt_sin; |
1123
|
|
|
|
|
|
|
|
1124
|
108
|
|
|
|
|
55907
|
$self->_reset_work_data(_args=>\%args, _meta=>$meta); |
1125
|
|
|
|
|
|
|
|
1126
|
|
|
|
|
|
|
# add properties from convert, if not yet mentioned in meta |
1127
|
108
|
|
|
|
|
375
|
for (keys %$opt_cvt) { |
1128
|
10
|
100
|
|
|
|
41
|
$meta->{$_} = undef unless exists $meta->{$_}; |
1129
|
|
|
|
|
|
|
} |
1130
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
# mark in the metadata that we have done the wrapping, so future wrapping |
1132
|
|
|
|
|
|
|
# can avoid needless duplicated functionality (like validating args twice). |
1133
|
|
|
|
|
|
|
# note that handler can log their mark too. |
1134
|
|
|
|
|
|
|
{ |
1135
|
108
|
|
100
|
|
|
189
|
my @wrap_log = @{ $meta->{$wrap_log_prop} // [] }; |
|
108
|
|
|
|
|
195
|
|
|
108
|
|
|
|
|
581
|
|
1136
|
|
|
|
|
|
|
push @wrap_log, { |
1137
|
|
|
|
|
|
|
validate_args => $args{validate_args}, |
1138
|
|
|
|
|
|
|
validate_result => $args{validate_result}, |
1139
|
108
|
|
|
|
|
576
|
normalize_schema => !$opt_sin, |
1140
|
|
|
|
|
|
|
}; |
1141
|
108
|
100
|
|
|
|
345
|
if ($args{log}) { |
1142
|
106
|
|
|
|
|
338
|
$meta->{$wrap_log_prop} = \@wrap_log; |
1143
|
|
|
|
|
|
|
} |
1144
|
|
|
|
|
|
|
} |
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
# start iterating over properties |
1147
|
|
|
|
|
|
|
|
1148
|
108
|
|
|
|
|
484
|
$self->select_section('OPEN_SUB'); |
1149
|
108
|
|
|
|
|
576
|
$self->push_lines( |
1150
|
|
|
|
|
|
|
"package $comppkg;", 'sub {'); |
1151
|
108
|
|
|
|
|
359
|
$self->indent; |
1152
|
|
|
|
|
|
|
|
1153
|
108
|
|
100
|
|
|
511
|
$meta->{args_as} //= "hash"; |
1154
|
|
|
|
|
|
|
|
1155
|
108
|
100
|
|
|
|
476
|
if ($meta->{args_as} =~ /hash/) { |
1156
|
102
|
|
|
|
|
236
|
$self->select_section('before_call_after_arg_validation'); |
1157
|
|
|
|
|
|
|
# tell function it's being wrapped, currently disabled |
1158
|
|
|
|
|
|
|
#$self->push_lines('$args{-wrapped} = 1;'); |
1159
|
|
|
|
|
|
|
} |
1160
|
|
|
|
|
|
|
|
1161
|
108
|
|
|
|
|
376
|
my %props = map {$_=>1} keys %$meta; |
|
434
|
|
|
|
|
845
|
|
1162
|
108
|
|
|
|
|
360
|
$props{$_} = 1 for keys %$opt_cvt; |
1163
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
# collect and check handlers |
1165
|
108
|
|
|
|
|
220
|
my %handler_args; |
1166
|
|
|
|
|
|
|
my %handler_metas; |
1167
|
108
|
|
|
|
|
278
|
for my $k0 (keys %props) { |
1168
|
434
|
|
|
|
|
537
|
my $k = $k0; |
1169
|
434
|
|
|
|
|
893
|
$k =~ s/\..+//; |
1170
|
434
|
100
|
|
|
|
888
|
next if $k =~ /\A_/; |
1171
|
432
|
50
|
|
|
|
786
|
next if $handler_args{$k}; |
1172
|
|
|
|
|
|
|
#if ($k ~~ $self->{_args}{skip}) { |
1173
|
|
|
|
|
|
|
# $log->tracef("Skipped property %s (mentioned in skip)", $k); |
1174
|
|
|
|
|
|
|
# next; |
1175
|
|
|
|
|
|
|
#} |
1176
|
432
|
50
|
|
|
|
1244
|
return [500, "Invalid property name $k"] unless $k =~ /\A\w+\z/; |
1177
|
432
|
|
|
|
|
838
|
my $meth = "handlemeta_$k"; |
1178
|
432
|
50
|
|
|
|
1531
|
unless ($self->can($meth)) { |
1179
|
|
|
|
|
|
|
# try a property module first |
1180
|
0
|
|
|
|
|
0
|
require "Perinci/Sub/Property/$k.pm"; |
1181
|
0
|
0
|
|
|
|
0
|
unless ($self->can($meth)) { |
1182
|
0
|
|
|
|
|
0
|
return [500, "No handler for property $k0 ($meth)"]; |
1183
|
|
|
|
|
|
|
} |
1184
|
|
|
|
|
|
|
} |
1185
|
432
|
|
|
|
|
1301
|
my $hm = $self->$meth; |
1186
|
432
|
|
100
|
|
|
1248
|
$hm->{v} //= 1; |
1187
|
432
|
100
|
|
|
|
1014
|
next unless defined $hm->{prio}; |
1188
|
|
|
|
|
|
|
die "Please update property handler $k which is still at v=$hm->{v} ". |
1189
|
|
|
|
|
|
|
"(needs v=$protocol_version)" |
1190
|
214
|
50
|
|
|
|
565
|
unless $hm->{v} == $protocol_version; |
1191
|
|
|
|
|
|
|
my $ha = { |
1192
|
214
|
|
|
|
|
965
|
prio=>$hm->{prio}, value=>$meta->{$k0}, property=>$k0, |
1193
|
|
|
|
|
|
|
meth=>"handle_$k", |
1194
|
|
|
|
|
|
|
}; |
1195
|
214
|
100
|
|
|
|
475
|
if (exists $opt_cvt->{$k0}) { |
1196
|
|
|
|
|
|
|
return [501, "Property '$k0' does not support conversion"] |
1197
|
10
|
50
|
|
|
|
35
|
unless $hm->{convert}; |
1198
|
10
|
|
|
|
|
27
|
$ha->{new} = $opt_cvt->{$k0}; |
1199
|
10
|
|
|
|
|
29
|
$meta->{$k0} = $opt_cvt->{$k0}; |
1200
|
|
|
|
|
|
|
} |
1201
|
214
|
|
|
|
|
398
|
$handler_args{$k} = $ha; |
1202
|
214
|
|
|
|
|
448
|
$handler_metas{$k} = $hm; |
1203
|
|
|
|
|
|
|
} |
1204
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
# call all the handlers in order |
1206
|
108
|
|
|
|
|
583
|
for my $k (sort {$handler_args{$a}{prio} <=> $handler_args{$b}{prio}} |
|
117
|
|
|
|
|
500
|
|
1207
|
|
|
|
|
|
|
keys %handler_args) { |
1208
|
214
|
|
|
|
|
363
|
my $ha = $handler_args{$k}; |
1209
|
214
|
|
|
|
|
352
|
my $meth = $ha->{meth}; |
1210
|
214
|
|
|
|
|
454
|
local $self->{_cur_handler} = $meth; |
1211
|
214
|
|
|
|
|
414
|
local $self->{_cur_handler_meta} = $handler_metas{$k}; |
1212
|
214
|
|
|
|
|
362
|
local $self->{_cur_handler_args} = $ha; |
1213
|
214
|
|
|
|
|
1130
|
$self->$meth(args=>\%args, meta=>$meta, %$ha); |
1214
|
|
|
|
|
|
|
} |
1215
|
|
|
|
|
|
|
|
1216
|
104
|
|
|
|
|
403
|
my $needs_store_res = $self->_needs_store_res; |
1217
|
104
|
100
|
|
|
|
277
|
if ($needs_store_res) { |
1218
|
98
|
|
|
|
|
328
|
$self->_add_var('_w_res'); |
1219
|
|
|
|
|
|
|
} |
1220
|
|
|
|
|
|
|
|
1221
|
104
|
|
|
|
|
293
|
$self->select_section('CALL'); |
1222
|
104
|
|
66
|
|
|
299
|
my $sn = $sub_ref_name // $sub_name; |
1223
|
|
|
|
|
|
|
$self->push_lines( |
1224
|
|
|
|
|
|
|
($needs_store_res ? '$_w_res = ' : "") . |
1225
|
|
|
|
|
|
|
$sn. ($sn =~ /^\$/ ? "->" : ""). |
1226
|
104
|
100
|
|
|
|
919
|
"(".$self->{_args_token}.");"); |
|
|
100
|
|
|
|
|
|
1227
|
104
|
100
|
|
|
|
321
|
if ($args{validate_result}) { |
1228
|
98
|
|
|
|
|
288
|
$self->select_section('after_call_before_res_validation'); |
1229
|
98
|
100
|
|
|
|
347
|
unless ($meta->{result_naked}) { |
1230
|
96
|
|
|
|
|
441
|
$self->push_lines( |
1231
|
|
|
|
|
|
|
'', |
1232
|
|
|
|
|
|
|
'# check that sub produces enveloped result', |
1233
|
|
|
|
|
|
|
'unless (ref($_w_res) eq "ARRAY" && $_w_res->[0]) {', |
1234
|
|
|
|
|
|
|
); |
1235
|
96
|
|
|
|
|
236
|
$self->indent; |
1236
|
96
|
50
|
|
|
|
393
|
if (log_is_trace) { |
1237
|
0
|
|
|
|
|
0
|
$self->_add_module('Data::Dumper'); |
1238
|
0
|
|
|
|
|
0
|
$self->push_lines( |
1239
|
|
|
|
|
|
|
'local $Data::Dumper::Purity = 1;', |
1240
|
|
|
|
|
|
|
'local $Data::Dumper::Terse = 1;', |
1241
|
|
|
|
|
|
|
'local $Data::Dumper::Indent = 0;', |
1242
|
|
|
|
|
|
|
); |
1243
|
0
|
|
|
|
|
0
|
$self->_err(500, |
1244
|
|
|
|
|
|
|
qq['BUG: Sub $sub_name does not produce envelope: '.]. |
1245
|
|
|
|
|
|
|
qq[Data::Dumper::Dumper(\$_w_res)]); |
1246
|
|
|
|
|
|
|
} else { |
1247
|
96
|
|
|
|
|
600
|
$self->_err(500, |
1248
|
|
|
|
|
|
|
qq['BUG: Sub $sub_name does not produce envelope']); |
1249
|
|
|
|
|
|
|
} |
1250
|
96
|
|
|
|
|
366
|
$self->unindent; |
1251
|
96
|
|
|
|
|
235
|
$self->push_lines('}'); |
1252
|
|
|
|
|
|
|
} |
1253
|
|
|
|
|
|
|
} |
1254
|
|
|
|
|
|
|
|
1255
|
104
|
|
|
|
|
313
|
my $use_eval = $self->_needs_eval; |
1256
|
104
|
50
|
|
|
|
297
|
if ($use_eval) { |
1257
|
0
|
|
|
|
|
0
|
$self->select_section('CLOSE_EVAL'); |
1258
|
0
|
|
|
|
|
0
|
$self->push_lines('return $_w_res;'); |
1259
|
0
|
|
|
|
|
0
|
$self->unindent; |
1260
|
0
|
|
|
|
|
0
|
$self->_add_var('_w_eval_err'); |
1261
|
0
|
|
|
|
|
0
|
$self->push_lines( |
1262
|
|
|
|
|
|
|
'};', |
1263
|
|
|
|
|
|
|
'$_w_eval_err = $@;'); |
1264
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
# _needs_eval will automatically be enabled here, due after_eval being |
1266
|
|
|
|
|
|
|
# filled |
1267
|
0
|
|
|
|
|
0
|
$self->select_section('after_eval'); |
1268
|
0
|
|
|
|
|
0
|
$self->push_lines('warn $_w_eval_err if $_w_eval_err;'); |
1269
|
0
|
|
|
|
|
0
|
$self->_errif(500, '"Function died: $_w_eval_err"', '$_w_eval_err'); |
1270
|
|
|
|
|
|
|
|
1271
|
0
|
|
|
|
|
0
|
$self->select_section('OPEN_EVAL'); |
1272
|
0
|
|
|
|
|
0
|
$self->push_lines('eval {'); |
1273
|
0
|
|
|
|
|
0
|
$self->indent; |
1274
|
|
|
|
|
|
|
} |
1275
|
|
|
|
|
|
|
|
1276
|
|
|
|
|
|
|
# return sub result |
1277
|
104
|
|
|
|
|
293
|
$self->select_section('BEFORE_CLOSE_SUB'); |
1278
|
104
|
100
|
|
|
|
368
|
$self->push_lines('return $_w_res;') if $needs_store_res; |
1279
|
104
|
|
|
|
|
277
|
$self->select_section('CLOSE_SUB'); |
1280
|
104
|
|
|
|
|
240
|
$self->unindent; |
1281
|
104
|
|
|
|
|
291
|
$self->push_lines('}'); # wrapper sub |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
# return wrap result |
1284
|
104
|
|
|
|
|
674
|
my $result = { |
1285
|
|
|
|
|
|
|
sub_name => $sub_name, |
1286
|
|
|
|
|
|
|
sub_ref_name => $sub_ref_name, |
1287
|
|
|
|
|
|
|
meta => $meta, |
1288
|
|
|
|
|
|
|
meta_name => $meta_name, |
1289
|
|
|
|
|
|
|
use_eval => $use_eval, |
1290
|
|
|
|
|
|
|
}; |
1291
|
104
|
100
|
|
|
|
305
|
if ($args{embed}) { |
1292
|
52
|
|
|
|
|
238
|
$result->{source} = $self->_format_embed_wrapper_code; |
1293
|
|
|
|
|
|
|
} else { |
1294
|
52
|
|
|
|
|
222
|
my $source = $self->_format_dyn_wrapper_code; |
1295
|
52
|
50
|
33
|
|
|
368
|
if ($Log_Wrapper_Code && log_is_trace()) { |
1296
|
0
|
|
|
|
|
0
|
require String::LineNumber; |
1297
|
|
|
|
|
|
|
log_trace("wrapper code:\n%s", |
1298
|
0
|
0
|
0
|
|
|
0
|
$ENV{LINENUM} // 1 ? |
1299
|
|
|
|
|
|
|
String::LineNumber::linenum($source) : |
1300
|
|
|
|
|
|
|
$source); |
1301
|
|
|
|
|
|
|
} |
1302
|
52
|
|
|
|
|
126
|
$result->{source} = $source; |
1303
|
52
|
50
|
|
|
|
170
|
if ($args{compile}) { |
1304
|
52
|
|
|
8
|
|
6240
|
my $wrapped = eval $source; |
|
8
|
|
|
8
|
|
58
|
|
|
8
|
|
|
|
|
16
|
|
|
8
|
|
|
|
|
76
|
|
|
8
|
|
|
|
|
587
|
|
|
8
|
|
|
|
|
17
|
|
|
8
|
|
|
|
|
2246
|
|
1305
|
52
|
50
|
33
|
|
|
367
|
die "BUG: Wrapper code can't be compiled: $@" if $@ || !$wrapped; |
1306
|
52
|
|
|
|
|
166
|
$result->{sub} = $wrapped; |
1307
|
|
|
|
|
|
|
} |
1308
|
|
|
|
|
|
|
} |
1309
|
|
|
|
|
|
|
|
1310
|
104
|
|
|
|
|
1081
|
[200, "OK", $result]; |
1311
|
|
|
|
|
|
|
} |
1312
|
|
|
|
|
|
|
|
1313
|
|
|
|
|
|
|
$SPEC{wrap_sub} = { |
1314
|
|
|
|
|
|
|
v => 1.1, |
1315
|
|
|
|
|
|
|
summary => 'Wrap subroutine to do various things, '. |
1316
|
|
|
|
|
|
|
'like enforcing Rinci properties', |
1317
|
|
|
|
|
|
|
result => { |
1318
|
|
|
|
|
|
|
summary => 'The wrapped subroutine along with its new metadata', |
1319
|
|
|
|
|
|
|
description => <<'_', |
1320
|
|
|
|
|
|
|
|
1321
|
|
|
|
|
|
|
Aside from wrapping the subroutine, the wrapper will also create a new metadata |
1322
|
|
|
|
|
|
|
for the subroutine. The new metadata is a clone of the original, with some |
1323
|
|
|
|
|
|
|
properties changed, e.g. schema in `args` and `result` normalized, some values |
1324
|
|
|
|
|
|
|
changed according to the `convert` argument, some defaults set, etc. |
1325
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
The new metadata will also contain (or append) the wrapping log located in the |
1327
|
|
|
|
|
|
|
`x.perinci.sub.wrapper.logs` attribute. The wrapping log marks that the wrapper |
1328
|
|
|
|
|
|
|
has added some functionality (like validating arguments or result) so that |
1329
|
|
|
|
|
|
|
future nested wrapper can choose to avoid duplicating the same functionality. |
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
_ |
1332
|
|
|
|
|
|
|
schema=>['hash*'=>{keys=>{ |
1333
|
|
|
|
|
|
|
sub=>'code*', |
1334
|
|
|
|
|
|
|
source=>['any*' => of => ['str*', ['hash*' => each_value=>'str*']]], |
1335
|
|
|
|
|
|
|
meta=>'hash*', |
1336
|
|
|
|
|
|
|
}}], |
1337
|
|
|
|
|
|
|
}, |
1338
|
|
|
|
|
|
|
args => { |
1339
|
|
|
|
|
|
|
sub => { |
1340
|
|
|
|
|
|
|
schema => 'str*', |
1341
|
|
|
|
|
|
|
summary => 'The code to be wrapped', |
1342
|
|
|
|
|
|
|
description => <<'_', |
1343
|
|
|
|
|
|
|
|
1344
|
|
|
|
|
|
|
At least one of `sub` or `sub_name` must be specified. |
1345
|
|
|
|
|
|
|
|
1346
|
|
|
|
|
|
|
_ |
1347
|
|
|
|
|
|
|
}, |
1348
|
|
|
|
|
|
|
sub_name => { |
1349
|
|
|
|
|
|
|
schema => 'str*', |
1350
|
|
|
|
|
|
|
summary => 'The name of the subroutine, '. |
1351
|
|
|
|
|
|
|
'e.g. func or Foo::func (qualified)', |
1352
|
|
|
|
|
|
|
description => <<'_', |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
At least one of `sub` or `sub_name` must be specified. |
1355
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
_ |
1357
|
|
|
|
|
|
|
}, |
1358
|
|
|
|
|
|
|
meta => { |
1359
|
|
|
|
|
|
|
schema => 'hash*', |
1360
|
|
|
|
|
|
|
summary => 'The function metadata', |
1361
|
|
|
|
|
|
|
req => 1, |
1362
|
|
|
|
|
|
|
}, |
1363
|
|
|
|
|
|
|
meta_name => { |
1364
|
|
|
|
|
|
|
schema => 'str*', |
1365
|
|
|
|
|
|
|
summary => 'Where to find the metadata, e.g. "$SPEC{foo}"', |
1366
|
|
|
|
|
|
|
description => <<'_', |
1367
|
|
|
|
|
|
|
|
1368
|
|
|
|
|
|
|
Some wrapper code (e.g. handler for `dep` property) needs to refer to the |
1369
|
|
|
|
|
|
|
function metadata. If not provided, the wrapper will store the function metadata |
1370
|
|
|
|
|
|
|
in a unique variable (e.g. `$Perinci::Sub::Wrapped::meta34127816`). |
1371
|
|
|
|
|
|
|
|
1372
|
|
|
|
|
|
|
_ |
1373
|
|
|
|
|
|
|
}, |
1374
|
|
|
|
|
|
|
convert => { |
1375
|
|
|
|
|
|
|
schema => 'hash*', |
1376
|
|
|
|
|
|
|
summary => 'Properties to convert to new value', |
1377
|
|
|
|
|
|
|
description => <<'_', |
1378
|
|
|
|
|
|
|
|
1379
|
|
|
|
|
|
|
Not all properties can be converted, but these are a partial list of those that |
1380
|
|
|
|
|
|
|
can: v (usually do not need to be specified when converting from 1.0 to 1.1, |
1381
|
|
|
|
|
|
|
will be done automatically), args_as, result_naked, default_lang. |
1382
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
_ |
1384
|
|
|
|
|
|
|
}, |
1385
|
|
|
|
|
|
|
compile => { |
1386
|
|
|
|
|
|
|
schema => ['bool' => {default=>1}], |
1387
|
|
|
|
|
|
|
summary => 'Whether to compile the generated wrapper', |
1388
|
|
|
|
|
|
|
description => <<'_', |
1389
|
|
|
|
|
|
|
|
1390
|
|
|
|
|
|
|
Can be set to 0 to not actually wrap but just return the generated wrapper |
1391
|
|
|
|
|
|
|
source code. |
1392
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
_ |
1394
|
|
|
|
|
|
|
}, |
1395
|
|
|
|
|
|
|
compile => { |
1396
|
|
|
|
|
|
|
schema => ['bool' => {default=>1}], |
1397
|
|
|
|
|
|
|
summary => 'Whether to compile the generated wrapper', |
1398
|
|
|
|
|
|
|
description => <<'_', |
1399
|
|
|
|
|
|
|
|
1400
|
|
|
|
|
|
|
Can be set to 0 to not actually wrap but just return the generated wrapper |
1401
|
|
|
|
|
|
|
source code. |
1402
|
|
|
|
|
|
|
|
1403
|
|
|
|
|
|
|
_ |
1404
|
|
|
|
|
|
|
}, |
1405
|
|
|
|
|
|
|
debug => { |
1406
|
|
|
|
|
|
|
schema => [bool => {default=>0}], |
1407
|
|
|
|
|
|
|
summary => 'Generate code with debugging', |
1408
|
|
|
|
|
|
|
description => <<'_', |
1409
|
|
|
|
|
|
|
|
1410
|
|
|
|
|
|
|
If turned on, will produce various debugging in the generated code. Currently |
1411
|
|
|
|
|
|
|
what this does: |
1412
|
|
|
|
|
|
|
|
1413
|
|
|
|
|
|
|
* add more comments (e.g. for each property handler) |
1414
|
|
|
|
|
|
|
|
1415
|
|
|
|
|
|
|
_ |
1416
|
|
|
|
|
|
|
}, |
1417
|
|
|
|
|
|
|
validate_args => { |
1418
|
|
|
|
|
|
|
schema => ['bool'], |
1419
|
|
|
|
|
|
|
summary => 'Whether wrapper should validate arguments', |
1420
|
|
|
|
|
|
|
description => <<'_', |
1421
|
|
|
|
|
|
|
|
1422
|
|
|
|
|
|
|
If set to true, will validate arguments. Validation error will cause status 400 |
1423
|
|
|
|
|
|
|
to be returned. The default is to enable this unless previous wrapper(s) have |
1424
|
|
|
|
|
|
|
already done this. |
1425
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
_ |
1427
|
|
|
|
|
|
|
}, |
1428
|
|
|
|
|
|
|
validate_result => { |
1429
|
|
|
|
|
|
|
schema => ['bool'], |
1430
|
|
|
|
|
|
|
summary => 'Whether wrapper should validate arguments', |
1431
|
|
|
|
|
|
|
description => <<'_', |
1432
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
If set to true, will validate sub's result. Validation error will cause wrapper |
1434
|
|
|
|
|
|
|
to return status 500 instead of sub's result. The default is to enable this |
1435
|
|
|
|
|
|
|
unless previous wrapper(s) have already done this. |
1436
|
|
|
|
|
|
|
|
1437
|
|
|
|
|
|
|
_ |
1438
|
|
|
|
|
|
|
}, |
1439
|
|
|
|
|
|
|
core => { |
1440
|
|
|
|
|
|
|
summary => 'If set to true, will avoid the use of non-core modules', |
1441
|
|
|
|
|
|
|
schema => 'bool', |
1442
|
|
|
|
|
|
|
}, |
1443
|
|
|
|
|
|
|
core_or_pp => { |
1444
|
|
|
|
|
|
|
summary => 'If set to true, will avoid the use of non-core XS modules', |
1445
|
|
|
|
|
|
|
schema => 'bool', |
1446
|
|
|
|
|
|
|
description => <<'_', |
1447
|
|
|
|
|
|
|
|
1448
|
|
|
|
|
|
|
In other words, will stick to core or pure-perl modules only. |
1449
|
|
|
|
|
|
|
|
1450
|
|
|
|
|
|
|
_ |
1451
|
|
|
|
|
|
|
}, |
1452
|
|
|
|
|
|
|
pp => { |
1453
|
|
|
|
|
|
|
summary => 'If set to true, will avoid the use of XS modules', |
1454
|
|
|
|
|
|
|
schema => 'bool', |
1455
|
|
|
|
|
|
|
}, |
1456
|
|
|
|
|
|
|
}, |
1457
|
|
|
|
|
|
|
}; |
1458
|
|
|
|
|
|
|
sub wrap_sub { |
1459
|
112
|
|
|
112
|
1
|
663
|
__PACKAGE__->new->wrap(@_); |
1460
|
|
|
|
|
|
|
} |
1461
|
|
|
|
|
|
|
|
1462
|
|
|
|
|
|
|
1; |
1463
|
|
|
|
|
|
|
# ABSTRACT: A multi-purpose subroutine wrapping framework |
1464
|
|
|
|
|
|
|
|
1465
|
|
|
|
|
|
|
__END__ |
1466
|
|
|
|
|
|
|
|
1467
|
|
|
|
|
|
|
=pod |
1468
|
|
|
|
|
|
|
|
1469
|
|
|
|
|
|
|
=encoding UTF-8 |
1470
|
|
|
|
|
|
|
|
1471
|
|
|
|
|
|
|
=head1 NAME |
1472
|
|
|
|
|
|
|
|
1473
|
|
|
|
|
|
|
Perinci::Sub::Wrapper - A multi-purpose subroutine wrapping framework |
1474
|
|
|
|
|
|
|
|
1475
|
|
|
|
|
|
|
=head1 VERSION |
1476
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
This document describes version 0.850 of Perinci::Sub::Wrapper (from Perl distribution Perinci-Sub-Wrapper), released on 2019-04-15. |
1478
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
=head1 SYNOPSIS |
1480
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
For dynamic usage: |
1482
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
use Perinci::Sub::Wrapper qw(wrap_sub); |
1484
|
|
|
|
|
|
|
my $res = wrap_sub(sub_name => "mysub", meta=>{...}); |
1485
|
|
|
|
|
|
|
my ($wrapped_sub, $meta) = ($res->[2]{sub}, $res->[2]{meta}); |
1486
|
|
|
|
|
|
|
$wrapped_sub->(); # call the wrapped function |
1487
|
|
|
|
|
|
|
|
1488
|
|
|
|
|
|
|
=head1 DESCRIPTION |
1489
|
|
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
Perinci::Sub::Wrapper (PSW for short) is an extensible subroutine wrapping |
1491
|
|
|
|
|
|
|
framework. It generates code to do stuffs before calling your subroutine, like |
1492
|
|
|
|
|
|
|
validate arguments, convert arguments from positional/array to named/hash or |
1493
|
|
|
|
|
|
|
vice versa, etc; as well as generate code to do stuffs after calling your |
1494
|
|
|
|
|
|
|
subroutine, like retry calling for a number of times if subroutine returns a |
1495
|
|
|
|
|
|
|
non-success status, check subroutine result against a schema, etc). Some other |
1496
|
|
|
|
|
|
|
things it can do: apply a timeout, currying, and so on. |
1497
|
|
|
|
|
|
|
|
1498
|
|
|
|
|
|
|
PSW differs from other function composition or decoration system like Python |
1499
|
|
|
|
|
|
|
decorators (or its Perl equivalent L<Python::Decorator>) in a couple of ways: |
1500
|
|
|
|
|
|
|
|
1501
|
|
|
|
|
|
|
=over |
1502
|
|
|
|
|
|
|
|
1503
|
|
|
|
|
|
|
=item * Single wrapper |
1504
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
Instead of multiple/nested wrapping for implementing different features, PSW |
1506
|
|
|
|
|
|
|
is designed to generate a single large wrapper around your code, i.e.: |
1507
|
|
|
|
|
|
|
|
1508
|
|
|
|
|
|
|
sub _wrapper_for_your_sub { |
1509
|
|
|
|
|
|
|
... |
1510
|
|
|
|
|
|
|
# do various stuffs before calling: |
1511
|
|
|
|
|
|
|
|
1512
|
|
|
|
|
|
|
# e.g. start timer |
1513
|
|
|
|
|
|
|
# e.g. convert, prefill, validate arguments |
1514
|
|
|
|
|
|
|
my @args = ...; |
1515
|
|
|
|
|
|
|
... |
1516
|
|
|
|
|
|
|
your_sub(@args); |
1517
|
|
|
|
|
|
|
... |
1518
|
|
|
|
|
|
|
# do various stuffs after calling |
1519
|
|
|
|
|
|
|
... |
1520
|
|
|
|
|
|
|
# e.g. report times |
1521
|
|
|
|
|
|
|
# e.g. perform retry |
1522
|
|
|
|
|
|
|
# e.g. convert or envelope results |
1523
|
|
|
|
|
|
|
|
1524
|
|
|
|
|
|
|
# return result |
1525
|
|
|
|
|
|
|
} |
1526
|
|
|
|
|
|
|
|
1527
|
|
|
|
|
|
|
Multiple functionalities will be added and combined in this single wrapper |
1528
|
|
|
|
|
|
|
subroutine in the appropriate location. This is done to reduce function call |
1529
|
|
|
|
|
|
|
overhead or depth of nested call levels. And also to make it easier to embed the |
1530
|
|
|
|
|
|
|
wrapping code to your source code (see L<Dist::Zilla::Plugin::Rinci::Wrap>). |
1531
|
|
|
|
|
|
|
|
1532
|
|
|
|
|
|
|
Of course, you can still wrap multiple times if wanted. |
1533
|
|
|
|
|
|
|
|
1534
|
|
|
|
|
|
|
=item * Rinci |
1535
|
|
|
|
|
|
|
|
1536
|
|
|
|
|
|
|
The wrapper code is built according to the L<Rinci> metadata you provide. Rinci |
1537
|
|
|
|
|
|
|
allows you to specify various things for your function, e.g. list of arguments |
1538
|
|
|
|
|
|
|
including the expected data type of each argument and whether an argument is |
1539
|
|
|
|
|
|
|
required or optional. PSW can then be used to generate the necessary code to |
1540
|
|
|
|
|
|
|
enforce this specification, e.g. generate validator for the function arguments. |
1541
|
|
|
|
|
|
|
|
1542
|
|
|
|
|
|
|
Since Rinci specification is extensible, you can describe additional stuffs for |
1543
|
|
|
|
|
|
|
your function and write a PSW plugin to generate the necessary code to implement |
1544
|
|
|
|
|
|
|
your specification. An example is C<timeout> to specify execution time limit, |
1545
|
|
|
|
|
|
|
implemented by L<Perinci::Sub::Property::timeout> which generates code to call |
1546
|
|
|
|
|
|
|
function inside an C<eval()> block and use C<alarm()> to limit the execution. |
1547
|
|
|
|
|
|
|
Another example is C<retry> property, implemented by |
1548
|
|
|
|
|
|
|
L<Perinci::Sub::Property::retry> which generates code to call function inside a |
1549
|
|
|
|
|
|
|
simple retry loop. |
1550
|
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
=back |
1552
|
|
|
|
|
|
|
|
1553
|
|
|
|
|
|
|
Normally you do not use PSW directly in your applications. You might want to |
1554
|
|
|
|
|
|
|
check out L<Perinci::Access::Perl> and L<Perinci::Exporter> on examples of |
1555
|
|
|
|
|
|
|
wrapping function dynamically (during runtime), or |
1556
|
|
|
|
|
|
|
L<Dist::Zilla::Plugin::Rinci::Wrap> on an example of embedding the generated |
1557
|
|
|
|
|
|
|
wrapping code to source code during build. |
1558
|
|
|
|
|
|
|
|
1559
|
|
|
|
|
|
|
=head1 EXTENDING |
1560
|
|
|
|
|
|
|
|
1561
|
|
|
|
|
|
|
The framework is simple and extensible. Please delve directly into the source |
1562
|
|
|
|
|
|
|
code for now. Some notes: |
1563
|
|
|
|
|
|
|
|
1564
|
|
|
|
|
|
|
The internal uses OO. |
1565
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
The main wrapper building mechanism is in the C<wrap()> method. |
1567
|
|
|
|
|
|
|
|
1568
|
|
|
|
|
|
|
For each Rinci property, it will call C<handle_NAME()> wrapper handler method. |
1569
|
|
|
|
|
|
|
The C<handlemeta_NAME()> methods are called first, to determine order of |
1570
|
|
|
|
|
|
|
processing. You can supply these methods either by subclassing the class or, |
1571
|
|
|
|
|
|
|
more simply, monkeypatching the method in the C<Perinci::Sub::Wrapper> package. |
1572
|
|
|
|
|
|
|
|
1573
|
|
|
|
|
|
|
The wrapper handler method will be called with a hash argument, containing these |
1574
|
|
|
|
|
|
|
keys: B<value> (property value), B<new> (this key will exist if C<convert> |
1575
|
|
|
|
|
|
|
argument of C<wrap()> exists, to convert a property to a new value). |
1576
|
|
|
|
|
|
|
|
1577
|
|
|
|
|
|
|
For properties that have name in the form of C<NAME1.NAME2.NAME3> (i.e., dotted) |
1578
|
|
|
|
|
|
|
only the first part of the name will be used (i.e., C<handle_NAME1()>). |
1579
|
|
|
|
|
|
|
|
1580
|
|
|
|
|
|
|
=head1 VARIABLES |
1581
|
|
|
|
|
|
|
|
1582
|
|
|
|
|
|
|
=head2 $Log_Wrapper_Code (BOOL) |
1583
|
|
|
|
|
|
|
|
1584
|
|
|
|
|
|
|
Whether to log wrapper result. Default is from environment variable |
1585
|
|
|
|
|
|
|
LOG_PERINCI_WRAPPER_CODE, or false. Logging is done with L<Log::ger> at trace |
1586
|
|
|
|
|
|
|
level. |
1587
|
|
|
|
|
|
|
|
1588
|
|
|
|
|
|
|
=head1 RINCI FUNCTION METADATA |
1589
|
|
|
|
|
|
|
|
1590
|
|
|
|
|
|
|
=head2 x.perinci.sub.wrapper.disable_validate_args => bool |
1591
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
Can be set to 1 to set C<validate_args> to 0 by default. This is used e.g. if |
1593
|
|
|
|
|
|
|
you already embed/insert code to validate arguments by other means and do not |
1594
|
|
|
|
|
|
|
want to repeat validating arguments. E.g. used if you use |
1595
|
|
|
|
|
|
|
L<Dist::Zilla::Plugin::Rinci::Validate>. |
1596
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
=head2 x.perinci.sub.wrapper.disable_validate_result => bool |
1598
|
|
|
|
|
|
|
|
1599
|
|
|
|
|
|
|
Can be set to 1 to set C<validate_result> to 0 by default. This is used e.g. if |
1600
|
|
|
|
|
|
|
you already embed/insert code to validate result by other means and do not want |
1601
|
|
|
|
|
|
|
to repeat validating result. E.g. used if you use |
1602
|
|
|
|
|
|
|
L<Dist::Zilla::Plugin::Rinci::Validate>. |
1603
|
|
|
|
|
|
|
|
1604
|
|
|
|
|
|
|
=head2 x.perinci.sub.wrapper.logs => array |
1605
|
|
|
|
|
|
|
|
1606
|
|
|
|
|
|
|
Generated/added by this module to the function metadata for every wrapping done. |
1607
|
|
|
|
|
|
|
Used to avoid adding repeated code, e.g. to validate result or arguments. |
1608
|
|
|
|
|
|
|
|
1609
|
|
|
|
|
|
|
=head1 PERFORMANCE NOTES |
1610
|
|
|
|
|
|
|
|
1611
|
|
|
|
|
|
|
The following numbers are produced on an Intel Core i5-2400 3.1GHz desktop using |
1612
|
|
|
|
|
|
|
PSW v0.51 and Perl v5.18.2. Operating system is Debian sid (64bit). |
1613
|
|
|
|
|
|
|
|
1614
|
|
|
|
|
|
|
For perspective, empty subroutine (C<< sub {} >>) as well as C<< sub { [200, |
1615
|
|
|
|
|
|
|
"OK"] } >> can be called around 5.3 mil/sec. |
1616
|
|
|
|
|
|
|
|
1617
|
|
|
|
|
|
|
Wrapping this subroutine C<< sub { [200, "OK"] } >> and this simple metadata C<< |
1618
|
|
|
|
|
|
|
{v=>1.1} >> using default options yields call performance for C<< $sub->() >> of |
1619
|
|
|
|
|
|
|
about 0.9 mil/sec. With C<< validate_args=>0 >> and C<< validate_result=>0 >>, |
1620
|
|
|
|
|
|
|
it's 1.5 mil/sec. |
1621
|
|
|
|
|
|
|
|
1622
|
|
|
|
|
|
|
As more (and more complex) arguments are introduced and validated, overhead will |
1623
|
|
|
|
|
|
|
increase. The significant portion of the overhead is in argument validation. For |
1624
|
|
|
|
|
|
|
example, this metadata C<< {v=>1.1, args=>{a=>{schema=>"int"}}} >> yields 0.5 |
1625
|
|
|
|
|
|
|
mil/sec. |
1626
|
|
|
|
|
|
|
|
1627
|
|
|
|
|
|
|
=head1 FUNCTIONS |
1628
|
|
|
|
|
|
|
|
1629
|
|
|
|
|
|
|
|
1630
|
|
|
|
|
|
|
=head2 wrap_sub |
1631
|
|
|
|
|
|
|
|
1632
|
|
|
|
|
|
|
Usage: |
1633
|
|
|
|
|
|
|
|
1634
|
|
|
|
|
|
|
wrap_sub(%args) -> [status, msg, payload, meta] |
1635
|
|
|
|
|
|
|
|
1636
|
|
|
|
|
|
|
Wrap subroutine to do various things, like enforcing Rinci properties. |
1637
|
|
|
|
|
|
|
|
1638
|
|
|
|
|
|
|
This function is not exported by default, but exportable. |
1639
|
|
|
|
|
|
|
|
1640
|
|
|
|
|
|
|
Arguments ('*' denotes required arguments): |
1641
|
|
|
|
|
|
|
|
1642
|
|
|
|
|
|
|
=over 4 |
1643
|
|
|
|
|
|
|
|
1644
|
|
|
|
|
|
|
=item * B<compile> => I<bool> (default: 1) |
1645
|
|
|
|
|
|
|
|
1646
|
|
|
|
|
|
|
Whether to compile the generated wrapper. |
1647
|
|
|
|
|
|
|
|
1648
|
|
|
|
|
|
|
Can be set to 0 to not actually wrap but just return the generated wrapper |
1649
|
|
|
|
|
|
|
source code. |
1650
|
|
|
|
|
|
|
|
1651
|
|
|
|
|
|
|
=item * B<convert> => I<hash> |
1652
|
|
|
|
|
|
|
|
1653
|
|
|
|
|
|
|
Properties to convert to new value. |
1654
|
|
|
|
|
|
|
|
1655
|
|
|
|
|
|
|
Not all properties can be converted, but these are a partial list of those that |
1656
|
|
|
|
|
|
|
can: v (usually do not need to be specified when converting from 1.0 to 1.1, |
1657
|
|
|
|
|
|
|
will be done automatically), args_as, result_naked, default_lang. |
1658
|
|
|
|
|
|
|
|
1659
|
|
|
|
|
|
|
=item * B<core> => I<bool> |
1660
|
|
|
|
|
|
|
|
1661
|
|
|
|
|
|
|
If set to true, will avoid the use of non-core modules. |
1662
|
|
|
|
|
|
|
|
1663
|
|
|
|
|
|
|
=item * B<core_or_pp> => I<bool> |
1664
|
|
|
|
|
|
|
|
1665
|
|
|
|
|
|
|
If set to true, will avoid the use of non-core XS modules. |
1666
|
|
|
|
|
|
|
|
1667
|
|
|
|
|
|
|
In other words, will stick to core or pure-perl modules only. |
1668
|
|
|
|
|
|
|
|
1669
|
|
|
|
|
|
|
=item * B<debug> => I<bool> (default: 0) |
1670
|
|
|
|
|
|
|
|
1671
|
|
|
|
|
|
|
Generate code with debugging. |
1672
|
|
|
|
|
|
|
|
1673
|
|
|
|
|
|
|
If turned on, will produce various debugging in the generated code. Currently |
1674
|
|
|
|
|
|
|
what this does: |
1675
|
|
|
|
|
|
|
|
1676
|
|
|
|
|
|
|
=over |
1677
|
|
|
|
|
|
|
|
1678
|
|
|
|
|
|
|
=item * add more comments (e.g. for each property handler) |
1679
|
|
|
|
|
|
|
|
1680
|
|
|
|
|
|
|
=back |
1681
|
|
|
|
|
|
|
|
1682
|
|
|
|
|
|
|
=item * B<meta>* => I<hash> |
1683
|
|
|
|
|
|
|
|
1684
|
|
|
|
|
|
|
The function metadata. |
1685
|
|
|
|
|
|
|
|
1686
|
|
|
|
|
|
|
=item * B<meta_name> => I<str> |
1687
|
|
|
|
|
|
|
|
1688
|
|
|
|
|
|
|
Where to find the metadata, e.g. "$SPEC{foo}". |
1689
|
|
|
|
|
|
|
|
1690
|
|
|
|
|
|
|
Some wrapper code (e.g. handler for C<dep> property) needs to refer to the |
1691
|
|
|
|
|
|
|
function metadata. If not provided, the wrapper will store the function metadata |
1692
|
|
|
|
|
|
|
in a unique variable (e.g. C<$Perinci::Sub::Wrapped::meta34127816>). |
1693
|
|
|
|
|
|
|
|
1694
|
|
|
|
|
|
|
=item * B<pp> => I<bool> |
1695
|
|
|
|
|
|
|
|
1696
|
|
|
|
|
|
|
If set to true, will avoid the use of XS modules. |
1697
|
|
|
|
|
|
|
|
1698
|
|
|
|
|
|
|
=item * B<sub> => I<str> |
1699
|
|
|
|
|
|
|
|
1700
|
|
|
|
|
|
|
The code to be wrapped. |
1701
|
|
|
|
|
|
|
|
1702
|
|
|
|
|
|
|
At least one of C<sub> or C<sub_name> must be specified. |
1703
|
|
|
|
|
|
|
|
1704
|
|
|
|
|
|
|
=item * B<sub_name> => I<str> |
1705
|
|
|
|
|
|
|
|
1706
|
|
|
|
|
|
|
The name of the subroutine, e.g. func or Foo::func (qualified). |
1707
|
|
|
|
|
|
|
|
1708
|
|
|
|
|
|
|
At least one of C<sub> or C<sub_name> must be specified. |
1709
|
|
|
|
|
|
|
|
1710
|
|
|
|
|
|
|
=item * B<validate_args> => I<bool> |
1711
|
|
|
|
|
|
|
|
1712
|
|
|
|
|
|
|
Whether wrapper should validate arguments. |
1713
|
|
|
|
|
|
|
|
1714
|
|
|
|
|
|
|
If set to true, will validate arguments. Validation error will cause status 400 |
1715
|
|
|
|
|
|
|
to be returned. The default is to enable this unless previous wrapper(s) have |
1716
|
|
|
|
|
|
|
already done this. |
1717
|
|
|
|
|
|
|
|
1718
|
|
|
|
|
|
|
=item * B<validate_result> => I<bool> |
1719
|
|
|
|
|
|
|
|
1720
|
|
|
|
|
|
|
Whether wrapper should validate arguments. |
1721
|
|
|
|
|
|
|
|
1722
|
|
|
|
|
|
|
If set to true, will validate sub's result. Validation error will cause wrapper |
1723
|
|
|
|
|
|
|
to return status 500 instead of sub's result. The default is to enable this |
1724
|
|
|
|
|
|
|
unless previous wrapper(s) have already done this. |
1725
|
|
|
|
|
|
|
|
1726
|
|
|
|
|
|
|
=back |
1727
|
|
|
|
|
|
|
|
1728
|
|
|
|
|
|
|
Returns an enveloped result (an array). |
1729
|
|
|
|
|
|
|
|
1730
|
|
|
|
|
|
|
First element (status) is an integer containing HTTP status code |
1731
|
|
|
|
|
|
|
(200 means OK, 4xx caller error, 5xx function error). Second element |
1732
|
|
|
|
|
|
|
(msg) is a string containing error message, or 'OK' if status is |
1733
|
|
|
|
|
|
|
200. Third element (payload) is optional, the actual result. Fourth |
1734
|
|
|
|
|
|
|
element (meta) is called result metadata and is optional, a hash |
1735
|
|
|
|
|
|
|
that contains extra information. |
1736
|
|
|
|
|
|
|
|
1737
|
|
|
|
|
|
|
Return value: The wrapped subroutine along with its new metadata (hash) |
1738
|
|
|
|
|
|
|
|
1739
|
|
|
|
|
|
|
|
1740
|
|
|
|
|
|
|
Aside from wrapping the subroutine, the wrapper will also create a new metadata |
1741
|
|
|
|
|
|
|
for the subroutine. The new metadata is a clone of the original, with some |
1742
|
|
|
|
|
|
|
properties changed, e.g. schema in C<args> and C<result> normalized, some values |
1743
|
|
|
|
|
|
|
changed according to the C<convert> argument, some defaults set, etc. |
1744
|
|
|
|
|
|
|
|
1745
|
|
|
|
|
|
|
The new metadata will also contain (or append) the wrapping log located in the |
1746
|
|
|
|
|
|
|
C<x.perinci.sub.wrapper.logs> attribute. The wrapping log marks that the wrapper |
1747
|
|
|
|
|
|
|
has added some functionality (like validating arguments or result) so that |
1748
|
|
|
|
|
|
|
future nested wrapper can choose to avoid duplicating the same functionality. |
1749
|
|
|
|
|
|
|
|
1750
|
|
|
|
|
|
|
=for Pod::Coverage ^(new|handle(meta)?_.+|wrap|add_.+|section_empty|indent|unindent|get_indent_level|select_section|push_lines)$ |
1751
|
|
|
|
|
|
|
|
1752
|
|
|
|
|
|
|
=head1 METHODS |
1753
|
|
|
|
|
|
|
|
1754
|
|
|
|
|
|
|
The OO interface is only used internally or when you want to extend the wrapper. |
1755
|
|
|
|
|
|
|
|
1756
|
|
|
|
|
|
|
=head1 FAQ |
1757
|
|
|
|
|
|
|
|
1758
|
|
|
|
|
|
|
=head2 General |
1759
|
|
|
|
|
|
|
|
1760
|
|
|
|
|
|
|
=over |
1761
|
|
|
|
|
|
|
|
1762
|
|
|
|
|
|
|
=item * What is a function wrapper? |
1763
|
|
|
|
|
|
|
|
1764
|
|
|
|
|
|
|
A wrapper function calls the target function but with additional behaviors. The |
1765
|
|
|
|
|
|
|
goal is similar to function composition or decorator system like in Python (or |
1766
|
|
|
|
|
|
|
its Perl equivalent L<Python::Decorator>) where you use a higher-order function |
1767
|
|
|
|
|
|
|
which accepts another function and modifies it. |
1768
|
|
|
|
|
|
|
|
1769
|
|
|
|
|
|
|
It is used to add various functionalities, e.g.: cache/memoization, singleton, |
1770
|
|
|
|
|
|
|
adding benchmarking/timing around function call, logging, argument validation |
1771
|
|
|
|
|
|
|
(parameter checking), checking pre/post-condition, authentication/authorization |
1772
|
|
|
|
|
|
|
checking, etc. The Python folks use decorators quite a bit; see discussions on |
1773
|
|
|
|
|
|
|
the Internet on those. |
1774
|
|
|
|
|
|
|
|
1775
|
|
|
|
|
|
|
=item * How is PSW different from Python::Decorator? |
1776
|
|
|
|
|
|
|
|
1777
|
|
|
|
|
|
|
PSW uses dynamic code generation (it generates Perl code on the fly). It also |
1778
|
|
|
|
|
|
|
creates a single large wrapper instead of nested wrappers. It builds wrapper |
1779
|
|
|
|
|
|
|
code according to L<Rinci> specification. |
1780
|
|
|
|
|
|
|
|
1781
|
|
|
|
|
|
|
=item * Why use code generation? |
1782
|
|
|
|
|
|
|
|
1783
|
|
|
|
|
|
|
Mainly because L<Data::Sah>, which is the module used to do argument validation, |
1784
|
|
|
|
|
|
|
also uses code generation. Data::Sah allows us to do data validation at full |
1785
|
|
|
|
|
|
|
Perl speed, which can be one or two orders of magnitude faster than |
1786
|
|
|
|
|
|
|
"interpreter" modules like L<Data::FormValidator>. |
1787
|
|
|
|
|
|
|
|
1788
|
|
|
|
|
|
|
=item * Why use a single large wrapper? |
1789
|
|
|
|
|
|
|
|
1790
|
|
|
|
|
|
|
This is just a design approach. It can impose some restriction for wrapper code |
1791
|
|
|
|
|
|
|
authors, since everything needs to be put in a single subroutine, but has nice |
1792
|
|
|
|
|
|
|
properties like less stack trace depth and less function call overhead. |
1793
|
|
|
|
|
|
|
|
1794
|
|
|
|
|
|
|
=back |
1795
|
|
|
|
|
|
|
|
1796
|
|
|
|
|
|
|
=head2 Debugging |
1797
|
|
|
|
|
|
|
|
1798
|
|
|
|
|
|
|
=over |
1799
|
|
|
|
|
|
|
|
1800
|
|
|
|
|
|
|
=item * How to display the wrapper code being generated? |
1801
|
|
|
|
|
|
|
|
1802
|
|
|
|
|
|
|
If environment variable L<LOG_PERINCI_WRAPPER_CODE> or package variable |
1803
|
|
|
|
|
|
|
$Log_Perinci_Wrapper_Code is set to true, generated wrapper source code is |
1804
|
|
|
|
|
|
|
logged at trace level using L<Log::ger>. It can be displayed, for example: |
1805
|
|
|
|
|
|
|
|
1806
|
|
|
|
|
|
|
% LOG_PERINCI_WRAPPER_CODE=1 TRACE=1 \ |
1807
|
|
|
|
|
|
|
perl -MLog::ger::LevelFromEnv -MLog::ger::Output=Screen \ |
1808
|
|
|
|
|
|
|
-MPerinci::Sub::Wrapper=wrap_sub \ |
1809
|
|
|
|
|
|
|
-e 'wrap_sub(sub=>sub{}, meta=>{v=>1.1, args=>{a=>{schema=>"int"}}});' |
1810
|
|
|
|
|
|
|
|
1811
|
|
|
|
|
|
|
Note that L<Data::Sah> (the module used to generate validator code) observes |
1812
|
|
|
|
|
|
|
C<LOG_SAH_VALIDATOR_CODE>, but during wrapping this environment flag is |
1813
|
|
|
|
|
|
|
currently disabled by this module, so you need to set |
1814
|
|
|
|
|
|
|
L<LOG_PERINCI_WRAPPER_CODE> instead. |
1815
|
|
|
|
|
|
|
|
1816
|
|
|
|
|
|
|
=back |
1817
|
|
|
|
|
|
|
|
1818
|
|
|
|
|
|
|
=head2 caller() doesn't work from inside my wrapped code! |
1819
|
|
|
|
|
|
|
|
1820
|
|
|
|
|
|
|
Wrapping adds at least one or two levels of calls: one for the wrapper |
1821
|
|
|
|
|
|
|
subroutine itself, the other is for the eval trap when necessary. |
1822
|
|
|
|
|
|
|
|
1823
|
|
|
|
|
|
|
This poses a problem if you need to call caller() from within your wrapped code; |
1824
|
|
|
|
|
|
|
it will also be off by at least one or two. |
1825
|
|
|
|
|
|
|
|
1826
|
|
|
|
|
|
|
The solution is for your function to use the caller() replacement, provided by |
1827
|
|
|
|
|
|
|
L<Perinci::Sub::Util>. Or use embedded mode, where the wrapper code won't add |
1828
|
|
|
|
|
|
|
extra subroutine calls. |
1829
|
|
|
|
|
|
|
|
1830
|
|
|
|
|
|
|
=head1 ENVIRONMENT |
1831
|
|
|
|
|
|
|
|
1832
|
|
|
|
|
|
|
=head2 LOG_PERINCI_WRAPPER_CODE (bool) |
1833
|
|
|
|
|
|
|
|
1834
|
|
|
|
|
|
|
If set to 1, will log the generated wrapper code. This value is used to set |
1835
|
|
|
|
|
|
|
C<$Log_Wrapper_Code> if it is not already set. |
1836
|
|
|
|
|
|
|
|
1837
|
|
|
|
|
|
|
=head2 PERINCI_WRAPPER_CORE => bool |
1838
|
|
|
|
|
|
|
|
1839
|
|
|
|
|
|
|
Set default for wrap argument C<core>. |
1840
|
|
|
|
|
|
|
|
1841
|
|
|
|
|
|
|
=head2 PERINCI_WRAPPER_CORE_OR_PP => bool |
1842
|
|
|
|
|
|
|
|
1843
|
|
|
|
|
|
|
Set default for wrap argument C<core_or_pp>. |
1844
|
|
|
|
|
|
|
|
1845
|
|
|
|
|
|
|
=head2 PERINCI_WRAPPER_PP => bool |
1846
|
|
|
|
|
|
|
|
1847
|
|
|
|
|
|
|
Set default for wrap argument C<pp>. |
1848
|
|
|
|
|
|
|
|
1849
|
|
|
|
|
|
|
=head1 HOMEPAGE |
1850
|
|
|
|
|
|
|
|
1851
|
|
|
|
|
|
|
Please visit the project's homepage at L<https://metacpan.org/release/Perinci-Sub-Wrapper>. |
1852
|
|
|
|
|
|
|
|
1853
|
|
|
|
|
|
|
=head1 SOURCE |
1854
|
|
|
|
|
|
|
|
1855
|
|
|
|
|
|
|
Source repository is at L<https://github.com/perlancar/perl-Perinci-Sub-Wrapper>. |
1856
|
|
|
|
|
|
|
|
1857
|
|
|
|
|
|
|
=head1 BUGS |
1858
|
|
|
|
|
|
|
|
1859
|
|
|
|
|
|
|
Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-Sub-Wrapper> |
1860
|
|
|
|
|
|
|
|
1861
|
|
|
|
|
|
|
When submitting a bug or request, please include a test-file or a |
1862
|
|
|
|
|
|
|
patch to an existing test-file that illustrates the bug or desired |
1863
|
|
|
|
|
|
|
feature. |
1864
|
|
|
|
|
|
|
|
1865
|
|
|
|
|
|
|
=head1 SEE ALSO |
1866
|
|
|
|
|
|
|
|
1867
|
|
|
|
|
|
|
L<Perinci>, L<Rinci> |
1868
|
|
|
|
|
|
|
|
1869
|
|
|
|
|
|
|
L<Python::Decorator> |
1870
|
|
|
|
|
|
|
|
1871
|
|
|
|
|
|
|
L<Dist::Zilla::Plugin::Rinci::Wrap> |
1872
|
|
|
|
|
|
|
|
1873
|
|
|
|
|
|
|
L<Dist::Zilla::Plugin::Rinci::Validate> |
1874
|
|
|
|
|
|
|
|
1875
|
|
|
|
|
|
|
=head1 AUTHOR |
1876
|
|
|
|
|
|
|
|
1877
|
|
|
|
|
|
|
perlancar <perlancar@cpan.org> |
1878
|
|
|
|
|
|
|
|
1879
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
1880
|
|
|
|
|
|
|
|
1881
|
|
|
|
|
|
|
This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2012, 2011 by perlancar@cpan.org. |
1882
|
|
|
|
|
|
|
|
1883
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
1884
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
1885
|
|
|
|
|
|
|
|
1886
|
|
|
|
|
|
|
=cut |