line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
1
|
|
|
1
|
|
4933
|
use 5.010; |
|
1
|
|
|
|
|
31
|
|
2
|
1
|
|
|
1
|
|
900
|
use utf8; |
|
1
|
|
|
|
|
18
|
|
|
1
|
|
|
|
|
28
|
|
3
|
1
|
|
|
1
|
|
59
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
50
|
|
4
|
1
|
|
|
1
|
|
13
|
use warnings; |
|
1
|
|
|
|
|
16
|
|
|
1
|
|
|
|
|
144
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Banal::Util::Mini; # git description: d948342 |
7
|
|
|
|
|
|
|
# vim: set ts=2 sts=2 sw=2 tw=115 et : |
8
|
|
|
|
|
|
|
# ABSTRACT: Provide several utility functions with minimal dependencies. |
9
|
|
|
|
|
|
|
# KEYWORDS: Util utility light-weight |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $VERSION = '0.001'; |
12
|
|
|
|
|
|
|
# AUTHORITY |
13
|
|
|
|
|
|
|
|
14
|
1
|
|
|
1
|
|
37
|
use Carp qw(croak); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
250
|
|
15
|
1
|
|
|
1
|
|
8
|
use Scalar::Util qw(blessed refaddr reftype); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
159
|
|
16
|
1
|
|
|
1
|
|
17
|
use List::Util 1.45 qw(any first none pairs uniq); |
|
1
|
|
|
|
|
41
|
|
|
1
|
|
|
|
|
302
|
|
17
|
1
|
|
|
1
|
|
607
|
use List::MoreUtils qw(arrayify firstres listcmp); |
|
1
|
|
|
|
|
14357
|
|
|
1
|
|
|
|
|
9
|
|
18
|
1
|
|
|
1
|
|
1251
|
use overload; # TAU : Required by flatten() and hence arrayify() routines copied from List::MoreUtils; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
30
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
|
22
|
1
|
|
|
1
|
|
762
|
use Data::Printer qw(p np); # During development only. TODO: comment this line out later. |
|
1
|
|
|
|
|
41255
|
|
|
1
|
|
|
|
|
7
|
|
23
|
|
|
|
|
|
|
|
24
|
1
|
|
|
1
|
|
7871
|
use namespace::autoclean; |
|
1
|
|
|
|
|
9362
|
|
|
1
|
|
|
|
|
4
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
27
|
1
|
|
|
1
|
|
497
|
use Exporter::Shiny; |
|
1
|
|
|
|
|
449
|
|
|
1
|
|
|
|
|
8
|
|
28
|
1
|
|
|
1
|
|
79
|
use vars qw(@EXPORT_OK); |
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
97
|
|
29
|
|
|
|
|
|
|
BEGIN { |
30
|
1
|
|
|
1
|
|
6
|
@EXPORT_OK = qw( |
31
|
|
|
|
|
|
|
msg |
32
|
|
|
|
|
|
|
polyvalent |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
hash_access |
35
|
|
|
|
|
|
|
hash_lookup |
36
|
|
|
|
|
|
|
hash_lookup_staged |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
inverse_dict |
39
|
|
|
|
|
|
|
inverse_mapping |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
maybe |
42
|
|
|
|
|
|
|
maybe_kv |
43
|
|
|
|
|
|
|
peek |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
tidy_arrayify |
46
|
|
|
|
|
|
|
first_viable |
47
|
|
|
|
|
|
|
invoke_first_existing_method |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
affixed |
50
|
|
|
|
|
|
|
prefixed |
51
|
|
|
|
|
|
|
suffixed |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sanitize_env_var_name |
54
|
|
|
|
|
|
|
sanitize_subroutine_name |
55
|
|
|
|
|
|
|
sanitize_identifier_name |
56
|
|
|
|
|
|
|
); |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# Add function aliases with underscore prefixes (single & double) |
59
|
1
|
|
|
|
|
4
|
my @ok = @EXPORT_OK; |
60
|
1
|
|
|
|
|
2
|
foreach my $pfx ('_', '__') { |
61
|
1
|
|
|
1
|
|
6
|
{ no strict 'refs'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
105
|
|
|
2
|
|
|
|
|
3
|
|
62
|
2
|
|
|
|
|
4
|
*{ __PACKAGE__ . '::' . $pfx . $_ } = \&{ __PACKAGE__ . '::' . $_ } for @ok ; |
|
38
|
|
|
|
|
154
|
|
|
38
|
|
|
|
|
141
|
|
63
|
|
|
|
|
|
|
} |
64
|
2
|
|
|
|
|
5
|
push @EXPORT_OK, ( map {; $pfx . $_ } (@ok) ); |
|
38
|
|
|
|
|
2006
|
|
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
#say STDERR 'EXPORT_OK : ' . np @EXPORT_OK; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ |
72
|
|
|
|
|
|
|
# UTILITY FUNCTIONS |
73
|
|
|
|
|
|
|
#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
#---------------------------------------------------------- |
76
|
|
|
|
|
|
|
# CLASS / OBJECT related functions |
77
|
|
|
|
|
|
|
#---------------------------------------------------------- |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
####################################### |
80
|
|
|
|
|
|
|
sub polyvalent { # Helps with the parameter processing of polyvalent (object or class) methods |
81
|
|
|
|
|
|
|
####################################### |
82
|
0
|
|
|
0
|
0
|
|
my $proto = shift; |
83
|
0
|
0
|
|
|
|
|
my $self = blessed $proto ? $proto : $proto->new(); |
84
|
0
|
|
|
|
|
|
my $class = blessed $self; |
85
|
0
|
0
|
|
|
|
|
wantarray ? ($self, $class, $proto) : $self; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
####################################### |
90
|
|
|
|
|
|
|
sub msg(@) { # Message text builder to be used in error output (warn, die, ...) |
91
|
|
|
|
|
|
|
####################################### |
92
|
0
|
0
|
|
0
|
0
|
|
my $o = blessed ($_[0]) ? shift : caller(); |
93
|
0
|
|
0
|
|
|
|
state $pfx = eval { $o->_msg_pfx(@_) } // ''; |
|
0
|
|
|
|
|
|
|
94
|
0
|
|
|
|
|
|
join ('', $pfx, @_, "\n") |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
#.......................................................... |
99
|
|
|
|
|
|
|
# STRING/TEXT processing functions |
100
|
|
|
|
|
|
|
#.......................................................... |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub prefixed ($@) { |
103
|
0
|
0
|
|
0
|
0
|
|
my %opts = %{ ref ($_[0]) eq 'HASH' ? shift : +{ prefix => shift} }; |
|
0
|
|
|
|
|
|
|
104
|
0
|
|
|
|
|
|
affixed(\%opts, @_ ) |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub suffixed ($@) { |
108
|
0
|
0
|
|
0
|
0
|
|
my %opts = %{ ref ($_[0]) eq 'HASH' ? shift : +{ suffix => shift} }; |
|
0
|
|
|
|
|
|
|
109
|
0
|
|
|
|
|
|
affixed(\%opts, @_ ) |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
sub affixed ($@) { |
113
|
0
|
0
|
|
0
|
0
|
|
my %opts = %{ ref ($_[0]) eq 'HASH' ? shift : +{} }; |
|
0
|
|
|
|
|
|
|
114
|
0
|
0
|
0
|
|
|
|
my $pfx = exists $opts{prefix} ? ( $opts{prefix} // '') : ''; |
115
|
0
|
0
|
0
|
|
|
|
my $sfx = exists $opts{suffix} ? ( $opts{suffix} // '') : ''; |
116
|
0
|
|
|
|
|
|
map {; $pfx . $_ . $sfx } @_ |
|
0
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
#&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
120
|
0
|
|
|
0
|
0
|
|
sub sanitize_env_var_name (;$) { &sanitize_identifier_name } |
121
|
0
|
|
|
0
|
0
|
|
sub sanitize_subroutine_name (;$) { &sanitize_identifier_name } |
122
|
|
|
|
|
|
|
sub sanitize_identifier_name (;$) { |
123
|
|
|
|
|
|
|
#&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
124
|
|
|
|
|
|
|
# cleanse (sanitize) the name by replacing non-alphanumeric chars with underscores. |
125
|
0
|
0
|
|
0
|
0
|
|
my $name = (@_) ? shift : $_; # If no argument is given, use the default SCALAR variable as our argument. |
126
|
|
|
|
|
|
|
|
127
|
0
|
|
|
|
|
|
$name =~ s/[^_A-Za-z0-9]/_/g; |
128
|
0
|
|
|
|
|
|
return $name; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
133
|
|
|
|
|
|
|
# HASH related functions |
134
|
|
|
|
|
|
|
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
#&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
137
|
|
|
|
|
|
|
sub peek { |
138
|
|
|
|
|
|
|
#&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
139
|
0
|
|
|
0
|
0
|
|
my ($h, $keys) = @_; |
140
|
0
|
|
|
|
|
|
my @keys = tidy_arrayify($keys); |
141
|
0
|
|
|
|
|
|
my $v; |
142
|
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
|
foreach my $key (@keys) { |
144
|
0
|
0
|
|
|
|
|
$v = exists $h->{$key} ? $h->{$key} : undef; |
145
|
0
|
0
|
|
|
|
|
return $v if defined($v); |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# Allow falling back to a set of defaults |
149
|
|
|
|
|
|
|
# In scalar context, the first one defined wins. |
150
|
|
|
|
|
|
|
# In list context, we return a list that contains all of the defined results |
151
|
|
|
|
|
|
|
# wantarray ? ( grep { defined } @_ ) : first { defined } @_; |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# [TAU] @ [ 2019-02-02 ] : Adopting a simpler behaviour, as below. |
154
|
0
|
|
|
0
|
|
|
$v = first { defined } @_; |
|
0
|
|
|
|
|
|
|
155
|
0
|
0
|
0
|
|
|
|
wantarray ? ( $v // () ) : $v; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# accumulate hash entries, given a set of ket value pairs. |
160
|
|
|
|
|
|
|
# The result will only include those pairs where both the key |
161
|
|
|
|
|
|
|
# and the value are 'defined'. |
162
|
0
|
|
|
0
|
0
|
|
sub maybe { &maybe_kv } |
163
|
|
|
|
|
|
|
sub maybe_kv { |
164
|
0
|
|
|
0
|
0
|
|
my @r; # result is accumulated in an array (instead of a hash), so that we can use 'push' |
165
|
0
|
0
|
0
|
|
|
|
push @r, ( shift // () ) if (@_ % 2); # This is how we deal with an odd number of args (including a single arg) |
166
|
|
|
|
|
|
|
|
167
|
0
|
|
|
|
|
|
foreach my $pair ( pairs @_ ) { |
168
|
0
|
|
|
|
|
|
my ( $key, $value ) = @$pair; |
169
|
0
|
0
|
0
|
|
|
|
push @r, ($key => $value) if defined($key) && defined ($value); |
170
|
|
|
|
|
|
|
} |
171
|
0
|
0
|
|
|
|
|
wantarray ? (@r) : +{@r} |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
####################################### |
176
|
|
|
|
|
|
|
sub hash_access { |
177
|
|
|
|
|
|
|
####################################### |
178
|
|
|
|
|
|
|
# FUNCTION: deep hash access via multiple succesive keys that each signify a level deeper than the previous. |
179
|
|
|
|
|
|
|
# hash_access ($h, key1, key2, key3, ...) |
180
|
0
|
|
|
0
|
0
|
|
my $node = shift; |
181
|
0
|
|
|
|
|
|
foreach my $k (@_) { |
182
|
0
|
0
|
0
|
|
|
|
return unless defined $node && defined $k; |
183
|
0
|
0
|
|
|
|
|
return unless eval { exists $node->{$k} }; |
|
0
|
|
|
|
|
|
|
184
|
0
|
|
|
|
|
|
$node = $node->{$k}; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
$node |
187
|
0
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
####################################### |
190
|
|
|
|
|
|
|
sub inverse_mapping { |
191
|
|
|
|
|
|
|
####################################### |
192
|
0
|
|
|
0
|
0
|
|
my @k = tidy_arrayify (shift); |
193
|
0
|
|
|
|
|
|
my @v = tidy_arrayify (@_); |
194
|
0
|
|
|
|
|
|
my @res; |
195
|
|
|
|
|
|
|
|
196
|
0
|
|
|
|
|
|
foreach my $v (@v) { |
197
|
0
|
|
|
|
|
|
do { push @res, ($v => $_) } for @k; |
|
0
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
} |
199
|
0
|
0
|
|
|
|
|
return wantarray ? (@res) : +{ @res }; |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
######################################## |
203
|
|
|
|
|
|
|
sub inverse_dict { |
204
|
|
|
|
|
|
|
######################################## |
205
|
0
|
|
|
0
|
0
|
|
my %h; |
206
|
0
|
|
|
|
|
|
%h = (%h, %{; shift } ) while ( ref($_[0]) eq 'HASH'); |
|
0
|
|
|
|
|
|
|
207
|
0
|
|
|
|
|
|
%h = (%h, @_); |
208
|
0
|
|
|
|
|
|
my %res; |
209
|
|
|
|
|
|
|
|
210
|
0
|
|
|
|
|
|
while (my ($k, $v) = each %h) { |
211
|
0
|
|
|
|
|
|
%res = (%res, inverse_mapping($k, $v)); |
212
|
|
|
|
|
|
|
} |
213
|
0
|
0
|
|
|
|
|
wantarray ? (%res) : \%res |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
######################################## |
218
|
|
|
|
|
|
|
sub hash_lookup { # lookup($key, sources =>[], depots => []) |
219
|
|
|
|
|
|
|
######################################## |
220
|
0
|
0
|
|
0
|
0
|
|
my $key = (@_ % 2) ? shift : undef; |
221
|
0
|
|
|
|
|
|
my %opts = (@_); |
222
|
0
|
|
0
|
|
|
|
$key //= $opts{key}; |
223
|
0
|
|
|
|
|
|
my $debug = $key =~ /dist/; |
224
|
0
|
|
|
|
|
|
local $_; # allows us to be called in the likes of map / grep; as well as our little recursion below. |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
#say STDERR " Looking up '$key' ... OPTIONS are : " . np %opts if $debug; |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# DEPOTS are hash refs that will be used for looking up SOURCES themselves, when those are strings (instead of a hash refs) |
229
|
0
|
|
|
|
|
|
my @depots = ( grep { defined $_ } arrayify( @opts{qw(depot depots)}) ); |
|
0
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# SOURCES are hash refs that will be tried in order for key lookup. |
232
|
|
|
|
|
|
|
# Alternatively, these may be denoted by strings, in which case they will themsleves be looked up in the 'depots' |
233
|
0
|
|
|
|
|
|
my @sources = ( grep { defined $_ } arrayify( @opts{qw(source sources)}) ); |
|
0
|
|
|
|
|
|
|
234
|
0
|
0
|
0
|
|
|
|
@sources = map { ref($_) ? $_ : ( eval { hash_lookup("$_", sources=>[ @depots] ) } // () ) } @sources; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
SOURCE: |
237
|
0
|
|
|
|
|
|
foreach my $h ( @sources ) { |
238
|
0
|
0
|
0
|
|
|
|
next SOURCE unless defined($h) && ref($h); # Don't bother checking reftype. This allows for eventual fancy overloading to work. |
239
|
0
|
0
|
|
|
|
|
next SOURCE unless defined $h; |
240
|
0
|
0
|
|
|
|
|
next SOURCE unless exists $h->{$key}; |
241
|
0
|
|
|
|
|
|
my $v = $h->{$key}; |
242
|
|
|
|
|
|
|
|
243
|
0
|
0
|
|
|
|
|
return wantarray ? ( $v ) : $v; |
244
|
|
|
|
|
|
|
} # sources |
245
|
|
|
|
|
|
|
|
246
|
0
|
|
|
|
|
|
die "Can't find the '$key' in any of the hash sources." |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
####################################### |
252
|
|
|
|
|
|
|
sub hash_lookup_staged { |
253
|
|
|
|
|
|
|
####################################### |
254
|
|
|
|
|
|
|
# Returns the first found item (corresponding to any of the given keys) in any of the hash sources. |
255
|
0
|
|
|
0
|
0
|
|
local $_; |
256
|
0
|
|
|
|
|
|
my %opt = @_; |
257
|
0
|
|
|
|
|
|
my @keys = tidy_arrayify($opt{keys}); |
258
|
|
|
|
|
|
|
# my $sources = $opt{sources} // [ ]; |
259
|
|
|
|
|
|
|
# $sources = [ $sources ] if ref $sources eq 'HASH'; |
260
|
0
|
|
|
|
|
|
my @sources = tidy_arrayify($opt{source}, $opt{sources}); |
261
|
0
|
|
|
|
|
|
my $debug = $opt{debug}; |
262
|
0
|
|
|
|
|
|
my $res; |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
SOURCE : |
265
|
0
|
|
|
|
|
|
foreach my $h (@sources) { |
266
|
0
|
0
|
0
|
|
|
|
next SOURCE unless defined($h) && ( reftype($h) eq 'HASH'); |
267
|
0
|
|
|
|
|
|
my $map_keys = $opt{source_opts}{refaddr $h}{map_keys}; |
268
|
0
|
0
|
|
|
|
|
my @mkeys = defined($map_keys) ? ( $map_keys->(@keys) ) : (@keys); |
269
|
|
|
|
|
|
|
KEY : |
270
|
0
|
|
|
|
|
|
foreach my $key (@mkeys) { |
271
|
0
|
0
|
|
|
|
|
next KEY unless defined $key; |
272
|
|
|
|
|
|
|
# say STDERR " Hash lookup for key '$key' in hash '$h' ..." if $debug; |
273
|
0
|
0
|
|
|
|
|
next KEY unless exists $h->{$key}; |
274
|
0
|
|
|
|
|
|
$res = $h->{$key}; |
275
|
|
|
|
|
|
|
# say STDERR " Value found for key '$key' => : '$res'\n" if $debug; |
276
|
0
|
0
|
|
|
|
|
last SOURCE if defined $res; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
0
|
0
|
|
|
|
|
die "Can't find (in any of the given sources) the given keys [@keys] !" unless defined $res; |
281
|
|
|
|
|
|
|
|
282
|
0
|
|
|
|
|
|
return $res; |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
289
|
|
|
|
|
|
|
# ARRAY & LIST related functions |
290
|
|
|
|
|
|
|
#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
###################################### |
293
|
0
|
|
|
0
|
0
|
|
sub tidy_arrayify(;@) { local $_; my @res = ( grep { defined $_ } ( uniq( arrayify( @_) ))) } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
####################################### |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
#=begin STOLEN_FROM_List_MoreUtils |
297
|
|
|
|
|
|
|
# ------------------------------------------------------ |
298
|
|
|
|
|
|
|
# TAU: The two routines, as well as the comment about 'leaks' were stolen from C |
299
|
|
|
|
|
|
|
# The only thing I did was privatizing names and turning 'flatten' into a proper subroutine (instead of a scalar CODE closure) |
300
|
|
|
|
|
|
|
# That allowed me to get rid of a warning. |
301
|
|
|
|
|
|
|
# ------------------------------------------------------ |
302
|
|
|
|
|
|
|
# "leaks" when lexically hidden in arrayify. |
303
|
|
|
|
|
|
|
# sub flatten { map { (ref $_ and ("ARRAY" eq ref $_ or overload::Method($_, '@{}'))) ? (flatten(@{$_})) : ($_) } @_; } |
304
|
|
|
|
|
|
|
# sub arrayify { map { flatten($_) } @_; } |
305
|
|
|
|
|
|
|
# #=cut |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
#&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
309
|
|
|
|
|
|
|
sub first_viable (&@) { |
310
|
|
|
|
|
|
|
#&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
311
|
0
|
|
|
0
|
0
|
|
my $f = shift; # CODE BLOCK or subroutine ref. A closure is OK, too. |
312
|
0
|
|
|
|
|
|
my @e = (); |
313
|
0
|
|
|
|
|
|
local $_; |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
#local $@; # so that we don't mess up caller's eval/error handling. |
316
|
0
|
|
|
|
|
|
eval { 1 }; # resets $@ to whatever perl considers to be 'success'; |
|
0
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# This part, as well as the general flow, is copied shamelessly from the 'first()' function in C. |
319
|
0
|
0
|
0
|
|
|
|
unless ( length ref $f && eval { $f = \&$f; 1 } ) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
320
|
0
|
|
|
|
|
|
require Carp; |
321
|
0
|
|
|
|
|
|
Carp::croak("Not a subroutine reference"); |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# Return the result of the first viable evaluation (i.e. first one that doesn't die on us, for whatever reason ) |
325
|
0
|
|
|
|
|
|
foreach ( @_) { |
326
|
0
|
|
|
|
|
|
my ($item) = ($_); |
327
|
|
|
|
|
|
|
|
328
|
0
|
0
|
|
|
|
|
if (wantarray) { my @v = ( eval { $f->() } ); return @v unless $@; } |
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
329
|
0
|
0
|
|
|
|
|
else { my $v = eval { $f->() } ; return $v unless $@; } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
# No luck. Save the error, for an eventual error stack output if we die. |
332
|
0
|
|
|
|
|
|
push @e, { |
333
|
|
|
|
|
|
|
item => $item, err => $@, |
334
|
|
|
|
|
|
|
msg=> "Failed to invoke CODE BLOCK on item '$item', with the error : '$@'\n", |
335
|
|
|
|
|
|
|
}; |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
# NO LUCK with any invocation. |
339
|
|
|
|
|
|
|
# At this point, '$@' would normally be set to a true value by the last failed eval. |
340
|
0
|
0
|
|
|
|
|
if (@e) { |
341
|
0
|
|
|
|
|
|
my @emsg = map { $_->{msg} } @e; |
|
0
|
|
|
|
|
|
|
342
|
0
|
|
|
|
|
|
my $name = (caller(0))[3]; # The name of this particular subroutine. |
343
|
0
|
|
|
|
|
|
croak "$name : Failed to sucessfully invoke any of the given code blocks!\n" |
344
|
|
|
|
|
|
|
. "Here's the list of all errors:\n\n @emsg" |
345
|
|
|
|
|
|
|
} |
346
|
0
|
|
|
|
|
|
return; |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
#&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
350
|
|
|
|
|
|
|
sub invoke_first_existing_method { |
351
|
|
|
|
|
|
|
#&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& |
352
|
0
|
|
|
0
|
0
|
|
my $o = shift; |
353
|
0
|
|
|
|
|
|
my @methods = arrayify(@_); |
354
|
0
|
|
|
|
|
|
my @args = (); |
355
|
|
|
|
|
|
|
|
356
|
0
|
|
|
0
|
|
|
first_viable { $o->$_(@args) } @methods; |
|
0
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
1; |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
__END__ |