| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Sub::Middler; |
|
2
|
2
|
|
|
2
|
|
237426
|
use 5.024000; |
|
|
2
|
|
|
|
|
9
|
|
|
3
|
2
|
|
|
2
|
|
17
|
use strict; |
|
|
2
|
|
|
|
|
11
|
|
|
|
2
|
|
|
|
|
59
|
|
|
4
|
2
|
|
|
2
|
|
18
|
use warnings; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
154
|
|
|
5
|
2
|
|
|
2
|
|
13
|
use feature "refaliasing"; |
|
|
2
|
|
|
|
|
6
|
|
|
|
2
|
|
|
|
|
468
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = 'v0.4.0'; |
|
9
|
2
|
|
|
2
|
|
1257
|
use Export::These qw; |
|
|
2
|
|
|
|
|
2032
|
|
|
|
2
|
|
|
|
|
15
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
sub new { |
|
12
|
|
|
|
|
|
|
#simply an array... |
|
13
|
2
|
|
|
2
|
1
|
245007
|
bless [], __PACKAGE__; |
|
14
|
|
|
|
|
|
|
} |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# register sub refs to middleware makers |
|
17
|
|
|
|
|
|
|
sub register { |
|
18
|
2
|
|
|
2
|
|
2413
|
no warnings "experimental"; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
340
|
|
|
19
|
7
|
|
|
7
|
1
|
1278
|
\my @middleware=$_[0]; #self |
|
20
|
7
|
|
|
|
|
10
|
my $sub=$_[1]; |
|
21
|
|
|
|
|
|
|
#die "Middleware must be a CODE reference" unless ref($sub) eq "CODE"; |
|
22
|
7
|
|
|
|
|
12
|
push @middleware, $sub; |
|
23
|
7
|
|
|
|
|
13
|
return $_[0]; #allow chaining |
|
24
|
|
|
|
|
|
|
} |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
*append=\®ister; |
|
27
|
|
|
|
|
|
|
*add=\®ister; |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# Link together sub and give each one an index |
|
31
|
|
|
|
|
|
|
# Required argument is the 'dispatcher' which is the end point to call |
|
32
|
|
|
|
|
|
|
# |
|
33
|
|
|
|
|
|
|
sub _sink_sub; |
|
34
|
|
|
|
|
|
|
sub link { |
|
35
|
2
|
|
|
2
|
|
14
|
no warnings "experimental"; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
3680
|
|
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
#die "A CODE reference is required when linking middleware" unless(@_ >=2 and ref $_[1] eq "CODE"); |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
|
|
40
|
2
|
|
|
2
|
1
|
13
|
\my @self=shift; #self; |
|
41
|
|
|
|
|
|
|
|
|
42
|
2
|
|
|
|
|
18
|
my $dispatcher=_sink_sub shift, 1; |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
|
|
45
|
2
|
|
|
|
|
14
|
my @args=@_; |
|
46
|
|
|
|
|
|
|
|
|
47
|
2
|
|
|
|
|
5
|
my @mw; # The generated subs |
|
48
|
|
|
|
|
|
|
|
|
49
|
2
|
|
|
|
|
6
|
my @middleware=@self; |
|
50
|
2
|
|
|
|
|
8
|
for(@middleware){ |
|
51
|
7
|
|
|
|
|
30
|
$_=_sink_sub $_; |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
|
|
54
|
2
|
|
|
|
|
9
|
for my $i (reverse 0..@middleware-1){ |
|
55
|
7
|
|
|
|
|
36
|
my $maker=$middleware[$i]; |
|
56
|
7
|
100
|
|
|
|
18
|
my $next=($i==@middleware-1)?$dispatcher:$mw[$i+1]; |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
|
|
59
|
7
|
|
|
|
|
18
|
$mw[$i]=$maker->($next, $i, @args); |
|
60
|
|
|
|
|
|
|
} |
|
61
|
|
|
|
|
|
|
|
|
62
|
2
|
50
|
|
|
|
21
|
@middleware?$mw[0]:$dispatcher; |
|
63
|
|
|
|
|
|
|
} |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub linker { |
|
66
|
1
|
|
|
1
|
0
|
220223
|
my $dispatch=pop; |
|
67
|
|
|
|
|
|
|
|
|
68
|
1
|
|
|
|
|
10
|
my $chain=Sub::Middler->new; |
|
69
|
1
|
|
|
|
|
5
|
$chain->register($_) for @_; |
|
70
|
1
|
|
|
|
|
5
|
$chain->link($dispatch); |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
} |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub _sink_sub { |
|
75
|
9
|
|
|
9
|
|
16
|
my $in=$_[0]; |
|
76
|
9
|
|
|
|
|
15
|
my $is_dispatch=$_[1]; |
|
77
|
|
|
|
|
|
|
|
|
78
|
9
|
100
|
|
|
|
30
|
return $in if ref $in eq "CODE"; |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
my $wrap=sub { |
|
81
|
5
|
|
|
5
|
|
5
|
my $next=shift; |
|
82
|
5
|
|
|
|
|
4
|
my $out; |
|
83
|
|
|
|
|
|
|
|
|
84
|
5
|
|
|
|
|
13
|
for (ref $in){ |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
|
|
87
|
5
|
100
|
|
|
|
19
|
if(/SCALAR/){ |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
$out=$is_dispatch |
|
89
|
|
|
|
|
|
|
?sub { |
|
90
|
0
|
|
|
|
|
0
|
$$in.="@{$_[0]}"; |
|
|
0
|
|
|
|
|
0
|
|
|
91
|
0
|
0
|
|
|
|
0
|
$_[1] and $_[1]->(); # Auto call call back |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
:sub { |
|
94
|
|
|
|
|
|
|
#Convert into string |
|
95
|
3
|
|
|
|
|
5
|
$$in.="@{$_[0]}"; |
|
|
3
|
|
|
|
|
11
|
|
|
96
|
3
|
|
|
|
|
6
|
&$next; |
|
97
|
|
|
|
|
|
|
} |
|
98
|
1
|
50
|
|
|
|
4
|
} |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
elsif(/ARRAY/){ |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
$out=$is_dispatch |
|
103
|
|
|
|
|
|
|
?sub { |
|
104
|
|
|
|
|
|
|
# Copy and append into array, |
|
105
|
3
|
|
|
|
|
5
|
push @$in, @{$_[0]}; |
|
|
3
|
|
|
|
|
9
|
|
|
106
|
3
|
50
|
|
|
|
28
|
$_[1] and $_[1]->(); |
|
107
|
|
|
|
|
|
|
} |
|
108
|
|
|
|
|
|
|
:sub { |
|
109
|
|
|
|
|
|
|
# Copy and append into array, |
|
110
|
3
|
|
|
|
|
111
|
push @$in, @{$_[0]}; |
|
|
3
|
|
|
|
|
9
|
|
|
111
|
3
|
|
|
|
|
7
|
&$next; |
|
112
|
|
|
|
|
|
|
} |
|
113
|
2
|
100
|
|
|
|
30
|
} |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
elsif(/HASH/) { |
|
116
|
|
|
|
|
|
|
$out=$is_dispatch |
|
117
|
|
|
|
|
|
|
?sub { |
|
118
|
|
|
|
|
|
|
# copy into hash |
|
119
|
0
|
|
|
|
|
0
|
for (my $i=0; $i<$_[0]->@*; $i+=2){ |
|
120
|
0
|
|
|
|
|
0
|
$in->{$_[0][$i]}=$_[0][$i+1]; |
|
121
|
|
|
|
|
|
|
} |
|
122
|
|
|
|
|
|
|
############################ |
|
123
|
|
|
|
|
|
|
# for my($k,$v)(@{$_[0]}){ # |
|
124
|
|
|
|
|
|
|
# $in->{$k}=$v; # |
|
125
|
|
|
|
|
|
|
# } # |
|
126
|
|
|
|
|
|
|
############################ |
|
127
|
0
|
0
|
|
|
|
0
|
$_[1] and $_[1]->(); |
|
128
|
|
|
|
|
|
|
} |
|
129
|
|
|
|
|
|
|
:sub { |
|
130
|
|
|
|
|
|
|
# copy into hash |
|
131
|
3
|
|
|
|
|
8
|
for (my $i=0; $i<$_[0]->@*; $i+=2){ |
|
132
|
9
|
|
|
|
|
27
|
$in->{$_[0][$i]}=$_[0][$i+1]; |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
############################ |
|
135
|
|
|
|
|
|
|
# for my($k,$v)(@{$_[0]}){ # |
|
136
|
|
|
|
|
|
|
# $in->{$k}=$v; # |
|
137
|
|
|
|
|
|
|
# } # |
|
138
|
|
|
|
|
|
|
############################ |
|
139
|
3
|
|
|
|
|
7
|
&$next; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
1
|
50
|
|
|
|
17
|
} |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
elsif(/REF/){ |
|
145
|
1
|
|
|
|
|
2
|
my $r=$$in; |
|
146
|
1
|
50
|
|
|
|
4
|
if(ref $r eq "CODE"){ |
|
147
|
|
|
|
|
|
|
# treat a ref to a code ref as |
|
148
|
|
|
|
|
|
|
$out=$is_dispatch |
|
149
|
|
|
|
|
|
|
?sub { |
|
150
|
0
|
|
|
|
|
0
|
my @res=&$r; |
|
151
|
0
|
0
|
|
|
|
0
|
$_[1] and $_[1]->(); |
|
152
|
|
|
|
|
|
|
} |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
:sub { |
|
155
|
3
|
|
|
|
|
7
|
my @res=&$r; |
|
156
|
|
|
|
|
|
|
#$next->(@res); |
|
157
|
3
|
|
|
|
|
22
|
&$next; |
|
158
|
|
|
|
|
|
|
} |
|
159
|
1
|
50
|
|
|
|
5
|
} |
|
160
|
|
|
|
|
|
|
else { |
|
161
|
0
|
|
|
|
|
0
|
die "should not get here"; |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
else { |
|
165
|
0
|
|
|
|
|
0
|
die "Could not link unkown reference: ". ref $in; |
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
} |
|
168
|
5
|
|
|
|
|
9
|
$out; |
|
169
|
5
|
|
|
|
|
17
|
}; |
|
170
|
5
|
100
|
|
|
|
12
|
$is_dispatch?$wrap->():$wrap; |
|
171
|
|
|
|
|
|
|
} |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
1; |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=head1 NAME |
|
176
|
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
Sub::Middler - Middleware subroutine chaining |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
use strict; |
|
182
|
|
|
|
|
|
|
use warings; |
|
183
|
|
|
|
|
|
|
use Sub::Middler; |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
my @array; |
|
187
|
|
|
|
|
|
|
my %hash; |
|
188
|
|
|
|
|
|
|
my $scalar; |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
# append results in variables |
|
191
|
|
|
|
|
|
|
my $head=linker |
|
192
|
|
|
|
|
|
|
# Short cut to store (copy/append) in array |
|
193
|
|
|
|
|
|
|
\@array |
|
194
|
|
|
|
|
|
|
# Short cut to modifiy inputs |
|
195
|
|
|
|
|
|
|
=>\sub { $_*=2 for @{$_[0]}}, |
|
196
|
|
|
|
|
|
|
# Short cut to store in hash |
|
197
|
|
|
|
|
|
|
=>\%hash, |
|
198
|
|
|
|
|
|
|
# Short cut to stringyfiy and append to scalar |
|
199
|
|
|
|
|
|
|
=>\$scalar; |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
$head->([1,2,3,4,], sub {...}) |
|
203
|
|
|
|
|
|
|
# inputs ready cb |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
use strict; |
|
207
|
|
|
|
|
|
|
use warnings; |
|
208
|
|
|
|
|
|
|
use Sub::Middler; |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
my $middler=Sub::Middler->new; |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
$middler->register(mw1(x=>1)); |
|
213
|
|
|
|
|
|
|
$middler->register(mw2(y=>10)); |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
my $head=$middler->link( |
|
216
|
|
|
|
|
|
|
sub { |
|
217
|
|
|
|
|
|
|
print "Result: $_[0]\n"; |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
); |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
$head->(0); # Call the Chain |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# Middleware 1 |
|
224
|
|
|
|
|
|
|
sub mw1 { |
|
225
|
|
|
|
|
|
|
my %options=@_; |
|
226
|
|
|
|
|
|
|
sub { |
|
227
|
|
|
|
|
|
|
my ($next, $index, @optional)=@_; |
|
228
|
|
|
|
|
|
|
sub { |
|
229
|
|
|
|
|
|
|
my $work=$_[0]+$options{x}; |
|
230
|
|
|
|
|
|
|
$next->($work); |
|
231
|
|
|
|
|
|
|
} |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# Middleware 2 |
|
236
|
|
|
|
|
|
|
sub mw2 { |
|
237
|
|
|
|
|
|
|
my %options=@_; |
|
238
|
|
|
|
|
|
|
sub { |
|
239
|
|
|
|
|
|
|
my ($next, $index, @optional)=@_; |
|
240
|
|
|
|
|
|
|
sub { |
|
241
|
|
|
|
|
|
|
my $work= $_[0]*$options{y}; |
|
242
|
|
|
|
|
|
|
$next->( $work); |
|
243
|
|
|
|
|
|
|
} |
|
244
|
|
|
|
|
|
|
} |
|
245
|
|
|
|
|
|
|
} |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
A small module, facilitating linking subroutines together, acting as middleware |
|
250
|
|
|
|
|
|
|
,filters or chains with low runtime overhead. |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
To achieve this, the 'complexity' is offloaded to the definition of |
|
253
|
|
|
|
|
|
|
middleware/filters subroutines. They must be wrapped in subroutines |
|
254
|
|
|
|
|
|
|
appropriately to facilitate the lexical binding of linking variables. |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
This differs from other 'sub chaining' modules as it does not use a loop |
|
257
|
|
|
|
|
|
|
internally to iterate over a list of subroutines at runtime. As such there is |
|
258
|
|
|
|
|
|
|
no implicit synchronous call to the 'next' item in the chain. Each stage can run |
|
259
|
|
|
|
|
|
|
the following stage synchronously or asynchronously or not at all. Each element |
|
260
|
|
|
|
|
|
|
in the chain is responsible for how and when it calls the 'next'. |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
Finally the arguments and signatures of each stage of middleware are completely |
|
263
|
|
|
|
|
|
|
user defined and are not interfered with by this module. This allows reuse of |
|
264
|
|
|
|
|
|
|
the C<@_> array in calling subsequent stages for ultimate performance if you |
|
265
|
|
|
|
|
|
|
know what you're doing. |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
As a general guide it's suggested the last argument to a stage be a subroutine |
|
268
|
|
|
|
|
|
|
reference to allow callbacks and asynchronous usage. Instead of a flat list of |
|
269
|
|
|
|
|
|
|
multiple inputs into a stage, it is suggested to also contain these in an array |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
From v0.4.0, shortcuts can be used to to bypass writing the nestled |
|
272
|
|
|
|
|
|
|
subroutines subroutines for some common use cases. A reference to a |
|
273
|
|
|
|
|
|
|
SCALAR/ARRAY/HASH/CODE can be used instead of custom middleware |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=head1 API |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=head2 Inline linking |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
linker mw1, ..., dispatch |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
From v0.3.0, the C subroutine is exported and will do an inline build |
|
282
|
|
|
|
|
|
|
and link for a given middleware and dispatch routine |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
The return value is the head of the linked chain, and is equivalent to created |
|
285
|
|
|
|
|
|
|
a C object, adding middleware, and the calling the link method. |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=head2 Short Cuts |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
Instead of writing custom middleware, references to variables and CODE can be |
|
292
|
|
|
|
|
|
|
used instead. |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
If an array reference is used, all elements from the first argument will be |
|
295
|
|
|
|
|
|
|
appended to the array |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
If an hash reference is used, the elements from the first argument will be |
|
298
|
|
|
|
|
|
|
treated as key value pairs and set the corresponding elements in the target |
|
299
|
|
|
|
|
|
|
hash |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
If a scalar reference is use, the elements from the first argument will be |
|
302
|
|
|
|
|
|
|
converted to strings and appending to the target variable |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
If a reference is a CODE reference is used, the underlying subroutine is |
|
306
|
|
|
|
|
|
|
expected to modify the first argument elements in place. The return value is |
|
307
|
|
|
|
|
|
|
not used. |
|
308
|
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
In all the above cases, the next link in the chain is automatically called with |
|
311
|
|
|
|
|
|
|
the same arguments, making chaining and saving intermediate values easy |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=head2 Managing a chain |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=head3 new |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
my $object=Sub::Middler->new; |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
Creates a empty middler object ready to accept middleware. The object is a |
|
321
|
|
|
|
|
|
|
blessed array reference which stores the middleware directly. |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=head3 register |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
$object->register(my_middlware()); |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
Appends the middleware to the internal list for later linking. |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=head3 append, add |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Alias for register |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=head3 link |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
$object->link($last,[@args]); |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
Links together the registered middleware in the sequence of addition. Each |
|
338
|
|
|
|
|
|
|
middleware is intrinsically linked to the next middleware in the list. The last |
|
339
|
|
|
|
|
|
|
middleware being linked to the C<$last> argument, which must be a code ref. |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
The C<$last> ref MUST be a regular subroutine reference, acting as the |
|
342
|
|
|
|
|
|
|
'kernel' as described in following sections. |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
Calls C if C<$last> is not a code ref. |
|
345
|
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
Any optional additional arguments C<@args> are passed to this function are |
|
347
|
|
|
|
|
|
|
passed on to each 'maker' sub after the C<$next> and C<$index>, parameters. |
|
348
|
|
|
|
|
|
|
This gives an alternative approach to distributing configuration data to each |
|
349
|
|
|
|
|
|
|
item in the chain prior to runtime. It is up to each item's maker sub to store |
|
350
|
|
|
|
|
|
|
relevant passed values as they see fit. |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=head2 Creating Middleware |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
To achieve low over head in linking middleware, functional programming |
|
355
|
|
|
|
|
|
|
techniques (higher order functions) are utilised. This also give the greatest |
|
356
|
|
|
|
|
|
|
flexibility to the middleware, as signatures are completely user defined. |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
The trade off is that the middleware must be defined in a certain code |
|
359
|
|
|
|
|
|
|
structure. While this isn't difficult, it takes a minute to wrap your head |
|
360
|
|
|
|
|
|
|
around. |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
=head3 Middlware Definition |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
Middleware must be a subroutine (top/name) which returns a anonymous subroutine |
|
366
|
|
|
|
|
|
|
(maker), which also returns a anonymous subroutine to perform work (kernel). |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
This sounds complicated by this is what is looks like in code: |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub my_middleware { (1) Top/name subroutine |
|
371
|
|
|
|
|
|
|
my %options=@_; Store any config |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
sub { (2) maker sub is returned |
|
374
|
|
|
|
|
|
|
my ($next, $index, @optional)=@_; (3) Must store at least $next |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
sub { (4) Returns the kernel sub |
|
377
|
|
|
|
|
|
|
# Code here implements your middleware |
|
378
|
|
|
|
|
|
|
# %options are lexically accessable here |
|
379
|
|
|
|
|
|
|
# as are the @optional parameters |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# Execute the next item in the chain |
|
383
|
|
|
|
|
|
|
$next->(...); (5) Does work and calls the next entry |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
(6) Post work if applicable |
|
387
|
|
|
|
|
|
|
} |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
} |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
=over |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
=item Top Subroutine |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
The top sub routine (1) can take any arguments you desire and can be called |
|
396
|
|
|
|
|
|
|
what you like. The idea is it represents your middleware/filter and stores any |
|
397
|
|
|
|
|
|
|
setup lexically for the B sub to close over. It returns the B |
|
398
|
|
|
|
|
|
|
sub. |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=item Maker Subroutine |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
This anonymous sub (2) closes over the variables stored in B and is the |
|
403
|
|
|
|
|
|
|
input to this module (via C). When being linked (called) by this |
|
404
|
|
|
|
|
|
|
module it is provided at least two arguments: the reference to the next item in |
|
405
|
|
|
|
|
|
|
the chain and the current middleware index. These B be stored to be |
|
406
|
|
|
|
|
|
|
useful, but can be called anything you like (3). |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
Any optional/additional arguments supplied during a call to C are also |
|
409
|
|
|
|
|
|
|
used as arguments 'as is' to all maker subroutines in the chain. |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=item Kernel subroutine |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
This anonymous subroutine (4) actually performs the work of the |
|
415
|
|
|
|
|
|
|
middleware/filter. After work is done, the next item in the chain must be |
|
416
|
|
|
|
|
|
|
called explicitly (5). This supports synchronous or asynchronous middleware. |
|
417
|
|
|
|
|
|
|
Any extra work can be performed after the chain is completed after this call |
|
418
|
|
|
|
|
|
|
(6). |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=back |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=head2 LINKING CHAINS |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
Multiple chains of middleware can be linked together. This needs to be done in |
|
426
|
|
|
|
|
|
|
reverse order. The last chain after being linked, becomes the C<$last> item |
|
427
|
|
|
|
|
|
|
when linking the preceding chain and so on. |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=head2 EXAMPLES |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
The synopsis example can be found in the examples directory of this |
|
433
|
|
|
|
|
|
|
distribution. |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
L and L links together subs. They provide other |
|
439
|
|
|
|
|
|
|
features that this module does not. |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
These iterate over a list of subroutines at runtime to achieve named subs etc. |
|
442
|
|
|
|
|
|
|
where as this module pre links subroutines together, reducing overhead. |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
=head1 AUTHOR |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
Ruben Westerberg, Edrclaw@mac.comE |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=head1 REPOSITORTY and BUGS |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
Please report any bugs via git hub: L |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
Copyright (C) 2025 by Ruben Westerberg |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
This library is free software; you can redistribute it |
|
458
|
|
|
|
|
|
|
and/or modify it under the same terms as Perl or the MIT |
|
459
|
|
|
|
|
|
|
license. |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=head1 DISCLAIMER OF WARRANTIES |
|
462
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS |
|
464
|
|
|
|
|
|
|
OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE |
|
465
|
|
|
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
|
466
|
|
|
|
|
|
|
PARTICULAR PURPOSE. |
|
467
|
|
|
|
|
|
|
=cut |
|
468
|
|
|
|
|
|
|
|