line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Hash::Wrap; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# ABSTRACT: create on-the-fly objects from hashes |
4
|
|
|
|
|
|
|
|
5
|
20
|
|
|
20
|
|
2689787
|
use 5.01000; |
|
20
|
|
|
|
|
132
|
|
6
|
|
|
|
|
|
|
|
7
|
20
|
|
|
20
|
|
467
|
use strict; |
|
20
|
|
|
|
|
49
|
|
|
20
|
|
|
|
|
284
|
|
8
|
20
|
|
|
20
|
|
308
|
use warnings; |
|
20
|
|
|
|
|
54
|
|
|
20
|
|
|
|
|
354
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
## no critic(ValuesAndExpressions::ProhibitAccessOfPrivateData) |
11
|
|
|
|
|
|
|
|
12
|
20
|
|
|
20
|
|
481
|
use Scalar::Util; |
|
20
|
|
|
|
|
47
|
|
|
20
|
|
|
|
|
664
|
|
13
|
20
|
|
|
18
|
|
464
|
use Digest::MD5; |
|
18
|
|
|
|
|
35
|
|
|
18
|
|
|
|
|
3582
|
|
14
|
|
|
|
|
|
|
our $VERSION = '0.22'; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our @EXPORT = qw[ wrap_hash ]; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our @CARP_NOT = qw( Hash::Wrap ); |
19
|
|
|
|
|
|
|
our $DEBUG = 0; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# copied from Damian Conway's PPR: PerlIdentifier |
22
|
18
|
|
|
18
|
|
257
|
use constant PerlIdentifier => qr/\A([^\W\d]\w*+)\z/; |
|
18
|
|
|
|
|
36
|
|
|
18
|
|
|
|
|
3948
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
our %REGISTRY; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub _croak { |
27
|
21
|
|
|
21
|
|
116
|
require Carp; |
28
|
21
|
|
|
|
|
3090
|
goto \&Carp::croak; |
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub _croak_class_method { |
32
|
0
|
|
|
0
|
|
0
|
my ( $class, $method ) = @_; |
33
|
0
|
|
0
|
|
|
0
|
$class = ref( $class ) || $class; |
34
|
0
|
|
|
|
|
0
|
_croak ( qq[Can't locate class method "$method" via package "$class"] ); |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub _croak_object_method { |
38
|
10
|
|
|
10
|
|
25
|
my ( $object, $method ) = @_; |
39
|
10
|
|
33
|
|
|
53
|
my $class = Scalar::Util::blessed( $object ) || ref( $object ) || $object; |
40
|
10
|
|
|
|
|
64
|
_croak ( qq[Can't locate object method "$method" via package "$class"] ); |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub _find_symbol { |
45
|
99
|
|
|
99
|
|
202
|
my ( $package, $symbol, $reftype ) = @_; |
46
|
|
|
|
|
|
|
|
47
|
18
|
|
|
18
|
|
315
|
no strict 'refs'; ## no critic (ProhibitNoStrict) |
|
18
|
|
|
|
|
46
|
|
|
18
|
|
|
|
|
8864
|
|
48
|
99
|
|
|
|
|
119
|
my $candidate = *{"$package\::$symbol"}{SCALAR}; |
|
99
|
|
|
|
|
305
|
|
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
return $$candidate |
51
|
|
|
|
|
|
|
if defined $candidate |
52
|
|
|
|
|
|
|
&& 2 == |
53
|
99
|
100
|
66
|
|
|
621
|
grep { defined $_->[0] && defined $_->[1] ? $_->[0] eq $_->[1] : 1 } |
|
198
|
50
|
33
|
|
|
924
|
|
54
|
|
|
|
|
|
|
[ $reftype->[0], Scalar::Util::reftype $candidate ], |
55
|
|
|
|
|
|
|
[ $reftype->[1], Scalar::Util::reftype $$candidate ]; |
56
|
|
|
|
|
|
|
|
57
|
0
|
|
|
|
|
0
|
_croak( "Unable to find scalar \$$symbol in class $package" ); |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# this is called only if the method doesn't exist. |
61
|
|
|
|
|
|
|
sub _generate_accessor { |
62
|
77
|
|
|
77
|
|
159
|
my ( $hash_class, $class, $key ) = @_; |
63
|
|
|
|
|
|
|
|
64
|
77
|
|
|
|
|
239
|
my %dict = ( |
65
|
|
|
|
|
|
|
key => $key, |
66
|
|
|
|
|
|
|
class => $class, |
67
|
|
|
|
|
|
|
); |
68
|
|
|
|
|
|
|
|
69
|
77
|
|
|
|
|
178
|
my $code = $REGISTRY{$hash_class}{accessor_template}; |
70
|
77
|
|
|
|
|
221
|
my $coderef = _compile_from_tpl( \$code, \%dict ); |
71
|
77
|
50
|
|
|
|
244
|
_croak_about_code( \$code, 'accessor' ) |
72
|
|
|
|
|
|
|
if $@; |
73
|
|
|
|
|
|
|
|
74
|
77
|
|
|
|
|
1317
|
return $coderef; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub _generate_predicate { |
78
|
4
|
|
|
4
|
|
11
|
my ( $hash_class, $class, $key ) = @_; |
79
|
|
|
|
|
|
|
|
80
|
4
|
|
|
|
|
33
|
my %dict = ( |
81
|
|
|
|
|
|
|
key => $key, |
82
|
|
|
|
|
|
|
class => $class, |
83
|
|
|
|
|
|
|
); |
84
|
|
|
|
|
|
|
|
85
|
4
|
|
|
|
|
8
|
my $code = $REGISTRY{$hash_class}{predicate_template}; |
86
|
4
|
|
|
|
|
17
|
my $coderef = _compile_from_tpl( \$code, \%dict ); |
87
|
4
|
50
|
|
|
|
12
|
_croak_about_code( \$code, 'predicate' ) |
88
|
|
|
|
|
|
|
if $@; |
89
|
|
|
|
|
|
|
|
90
|
4
|
|
|
|
|
94
|
return $coderef; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub _autoload { |
95
|
61
|
|
|
61
|
|
127
|
my ( $hash_class, $method, $object ) = @_; |
96
|
|
|
|
|
|
|
|
97
|
61
|
|
|
|
|
300
|
my ( $class, $key ) = $method =~ /(.*)::(.*)/; |
98
|
|
|
|
|
|
|
|
99
|
61
|
50
|
|
|
|
233
|
_croak_class_method( $object, $key ) |
100
|
|
|
|
|
|
|
unless Scalar::Util::blessed( $object ); |
101
|
|
|
|
|
|
|
|
102
|
61
|
100
|
66
|
|
|
239
|
if ( exists $REGISTRY{$hash_class}{predicate_template} |
103
|
|
|
|
|
|
|
&& $key =~ /^has_(.*)/ ) |
104
|
|
|
|
|
|
|
{ |
105
|
4
|
|
|
|
|
10
|
return _generate_predicate( $hash_class, $class, $1 ); |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
_croak_object_method( $object, $key ) |
109
|
57
|
100
|
|
|
|
906
|
unless $REGISTRY{$hash_class}{validate}->( $object, $key ); |
110
|
|
|
|
|
|
|
|
111
|
50
|
|
|
|
|
123
|
_generate_accessor( $hash_class, $class, $key ); |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub _can { |
115
|
55
|
|
|
55
|
|
151
|
my ( $self, $key, $CLASS ) = @_; |
116
|
|
|
|
|
|
|
|
117
|
55
|
|
|
|
|
181
|
my $class = Scalar::Util::blessed( $self ); |
118
|
55
|
50
|
|
|
|
156
|
return if !defined $class; |
119
|
|
|
|
|
|
|
|
120
|
55
|
100
|
|
|
|
244
|
if ( !exists $self->{$key} ) { |
121
|
|
|
|
|
|
|
|
122
|
23
|
100
|
|
|
|
80
|
if ( exists $Hash::Wrap::REGISTRY{$class}{methods}{$key} ) { |
123
|
|
|
|
|
|
|
## no critic (ProhibitNoStrict) |
124
|
18
|
|
|
18
|
|
153
|
no strict 'refs'; |
|
18
|
|
|
|
|
34
|
|
|
18
|
|
|
|
|
1274
|
|
125
|
12
|
|
|
|
|
29
|
my $method = "${class}::$key"; |
126
|
12
|
|
|
|
|
17
|
return *{$method}{CODE}; |
|
12
|
|
|
|
|
66
|
|
127
|
|
|
|
|
|
|
} |
128
|
11
|
|
|
|
|
171
|
return; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
32
|
|
|
|
|
101
|
my $method = "${class}::$key"; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
## no critic (ProhibitNoStrict) |
134
|
18
|
|
|
18
|
|
230
|
no strict 'refs'; |
|
18
|
|
|
|
|
33
|
|
|
18
|
|
|
|
|
19480
|
|
135
|
|
|
|
|
|
|
return *{$method}{CODE} |
136
|
32
|
|
66
|
|
|
51
|
|| Hash::Wrap::_generate_accessor( $CLASS, $class, $key ); |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub import { |
140
|
56
|
|
|
56
|
|
49303
|
shift; |
141
|
56
|
|
|
|
|
187
|
my $caller = caller; |
142
|
|
|
|
|
|
|
|
143
|
56
|
|
|
|
|
142
|
my @imports = @_; |
144
|
56
|
100
|
|
|
|
181
|
push @imports, @EXPORT unless @imports; |
145
|
|
|
|
|
|
|
|
146
|
56
|
|
|
|
|
96
|
my @return; |
147
|
|
|
|
|
|
|
|
148
|
56
|
|
|
|
|
124
|
for my $args ( @imports ) { |
149
|
59
|
100
|
|
|
|
246
|
if ( !ref $args ) { |
|
|
50
|
|
|
|
|
|
150
|
|
|
|
|
|
|
_croak( "$args is not exported by ", __PACKAGE__ ) |
151
|
5
|
100
|
|
|
|
12
|
unless grep { /$args/ } @EXPORT; |
|
5
|
|
|
|
|
88
|
|
152
|
|
|
|
|
|
|
|
153
|
4
|
|
|
|
|
12
|
$args = { -as => $args }; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
elsif ( 'HASH' ne ref $args ) { |
157
|
|
|
|
|
|
|
_croak( |
158
|
|
|
|
|
|
|
"argument to ", |
159
|
|
|
|
|
|
|
__PACKAGE__, |
160
|
|
|
|
|
|
|
"::import must be string or hash" |
161
|
0
|
0
|
|
|
|
0
|
) unless grep { /$args/ } @EXPORT; |
|
0
|
|
|
|
|
0
|
|
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
else { |
164
|
|
|
|
|
|
|
# make a copy as it gets modified later on |
165
|
54
|
|
|
|
|
185
|
$args = {%$args}; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
_croak( "cannot mix -base and -class" ) |
169
|
58
|
100
|
100
|
|
|
216
|
if !!$args->{-base} && exists $args->{-class}; |
170
|
|
|
|
|
|
|
|
171
|
57
|
|
33
|
|
|
296
|
$DEBUG = $ENV{HASH_WRAP_DEBUG} // delete $args->{-debug}; |
172
|
|
|
|
|
|
|
|
173
|
57
|
100
|
|
|
|
147
|
$args->{-as} = 'wrap_hash' unless exists $args->{-as}; |
174
|
57
|
|
|
|
|
137
|
my $name = delete $args->{-as}; |
175
|
|
|
|
|
|
|
|
176
|
57
|
100
|
|
|
|
139
|
if ( defined $name ) { |
177
|
|
|
|
|
|
|
|
178
|
56
|
100
|
|
|
|
262
|
if ( defined( my $reftype = Scalar::Util::reftype( $name ) ) ) { |
|
|
100
|
|
|
|
|
|
179
|
|
|
|
|
|
|
|
180
|
9
|
50
|
66
|
|
|
45
|
_croak( |
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
181
|
|
|
|
|
|
|
"-as must be undefined or a string or a reference to a scalar" |
182
|
|
|
|
|
|
|
) |
183
|
|
|
|
|
|
|
if $reftype ne 'SCALAR' |
184
|
|
|
|
|
|
|
&& $reftype ne 'VSTRING' |
185
|
|
|
|
|
|
|
&& $reftype ne 'REF' |
186
|
|
|
|
|
|
|
&& $reftype ne 'GLOB' |
187
|
|
|
|
|
|
|
&& $reftype ne 'LVALUE' |
188
|
|
|
|
|
|
|
&& $reftype ne 'REGEXP'; |
189
|
|
|
|
|
|
|
|
190
|
8
|
|
|
|
|
20
|
$args->{-as_scalar_ref} = $name; |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
elsif ( $name eq '-return' ) { |
195
|
10
|
|
|
|
|
31
|
$args->{-as_return} = 1; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
56
|
100
|
|
|
|
146
|
if ( $args->{-base} ) { |
200
|
|
|
|
|
|
|
_croak( "don't use -as => -return with -base" ) |
201
|
3
|
50
|
|
|
|
8
|
if $args->{-as_return}; |
202
|
3
|
|
|
|
|
5
|
$args->{-class} = $caller; |
203
|
3
|
50
|
|
|
|
8
|
$args->{-new} = 1 unless !!$args->{-new}; |
204
|
3
|
|
|
|
|
6
|
_build_class( $caller, $name, $args ); |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
else { |
208
|
53
|
|
|
|
|
155
|
_build_class( $caller, $name, $args ); |
209
|
48
|
100
|
|
|
|
109
|
if ( defined $name ) { |
210
|
47
|
|
|
|
|
117
|
my $sub = _build_constructor( $caller, $name, $args ); |
211
|
45
|
100
|
|
|
|
136
|
push @return, $sub if $args->{-as_return}; |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# clean out known attributes |
216
|
49
|
|
|
|
|
225
|
delete @{$args}{ |
217
|
49
|
|
|
|
|
94
|
qw[ -as -as_return -as_scalar_ref -base -class -clone |
218
|
|
|
|
|
|
|
-copy -defined -exists -immutable -lockkeys -lvalue |
219
|
|
|
|
|
|
|
-methods -new -predicate -recurse -undef ] |
220
|
|
|
|
|
|
|
}; |
221
|
|
|
|
|
|
|
|
222
|
49
|
100
|
|
|
|
185
|
if ( keys %$args ) { |
223
|
1
|
|
|
|
|
6
|
_croak( "unknown options passed to ", |
224
|
|
|
|
|
|
|
__PACKAGE__, "::import: ", join( ', ', keys %$args ) ); |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
45
|
|
|
|
|
22981
|
return @return; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub _build_class { |
232
|
56
|
|
|
56
|
|
130
|
my ( $caller, $name, $attr ) = @_; |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# in case we're called inside a recursion and the recurse count |
235
|
|
|
|
|
|
|
# has hit zero, default behavior is no recurse, so remove it so |
236
|
|
|
|
|
|
|
# the attr signature computed below isn't contaminated by a |
237
|
|
|
|
|
|
|
# useless -recurse => 0 attribute. |
238
|
56
|
100
|
|
|
|
167
|
if ( exists $attr->{-recurse} ) { |
239
|
|
|
|
|
|
|
_croak( "-recurse must be a number" ) |
240
|
14
|
100
|
|
|
|
94
|
unless Scalar::Util::looks_like_number( $attr->{-recurse} ); |
241
|
13
|
100
|
|
|
|
62
|
delete $attr->{-recurse} if $attr->{-recurse} == 0; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
55
|
100
|
|
|
|
217
|
if ( !defined $attr->{-class} ) { |
|
|
100
|
|
|
|
|
|
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
my @class = map { |
247
|
42
|
|
|
|
|
221
|
( my $key = $_ ) =~ s/-//; |
|
74
|
|
|
|
|
242
|
|
248
|
74
|
50
|
|
|
|
275
|
( $key, defined $attr->{$_} ? $attr->{$_} : "" ) |
249
|
|
|
|
|
|
|
} sort keys %$attr; |
250
|
|
|
|
|
|
|
|
251
|
42
|
|
|
|
|
514
|
$attr->{-class} = join '::', 'Hash::Wrap::Class', |
252
|
|
|
|
|
|
|
Digest::MD5::md5_hex( @class ); |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
elsif ( $attr->{-class} eq '-caller' ) { |
256
|
2
|
100
|
|
|
|
7
|
_croak( "can't set -class => '-caller' if -as is not a plain string" ) |
257
|
|
|
|
|
|
|
if ref $name; |
258
|
1
|
|
|
|
|
3
|
$attr->{-class} = $caller . '::' . $name; |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
54
|
|
|
|
|
114
|
my $class = $attr->{-class}; |
262
|
|
|
|
|
|
|
|
263
|
54
|
100
|
|
|
|
142
|
return $class if defined $REGISTRY{$class}; |
264
|
52
|
|
|
|
|
178
|
my $rentry = $REGISTRY{$class} = { methods => {} }; |
265
|
|
|
|
|
|
|
|
266
|
52
|
|
|
|
|
93
|
my %closures; |
267
|
|
|
|
|
|
|
my %dict = ( |
268
|
|
|
|
|
|
|
class => $class, |
269
|
|
|
|
|
|
|
signature => '', |
270
|
|
|
|
|
|
|
body => [], |
271
|
|
|
|
|
|
|
autoload_attr => '', |
272
|
|
|
|
|
|
|
validate_inline => 'exists $self->{\<>}', |
273
|
|
|
|
|
|
|
validate_method => 'exists $self->{$key}', |
274
|
|
|
|
|
|
|
set => '$self->{q[\<>]} = $_[0] if @_;', |
275
|
|
|
|
|
|
|
return_value => '$self->{q[\<>]}', |
276
|
|
|
|
|
|
|
recursion_constructor => '', |
277
|
52
|
|
|
|
|
252
|
meta => [ map { ( qq[q($_) => q($attr->{$_}),] ) } keys %$attr ], |
|
140
|
|
|
|
|
737
|
|
278
|
|
|
|
|
|
|
predicate_template => '', |
279
|
|
|
|
|
|
|
); |
280
|
|
|
|
|
|
|
|
281
|
52
|
100
|
|
|
|
231
|
if ( $attr->{-lvalue} ) { |
282
|
5
|
50
|
|
|
|
15
|
if ( $] lt '5.016000' ) { |
283
|
|
|
|
|
|
|
_croak( "lvalue accessors require Perl 5.16 or later" ) |
284
|
0
|
0
|
|
|
|
0
|
if $attr->{-lvalue} < 0; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
else { |
287
|
5
|
|
|
|
|
8
|
$dict{autoload_attr} = q[: lvalue]; |
288
|
5
|
|
|
|
|
8
|
$dict{signature} = q[: lvalue]; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
52
|
100
|
|
|
|
141
|
if ( $attr->{-undef} ) { |
293
|
7
|
|
|
|
|
9
|
$dict{validate_method} = q[ 1 ]; |
294
|
7
|
|
|
|
|
8
|
$dict{validate_inline} = q[ 1 ]; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
52
|
100
|
|
|
|
120
|
if ( $attr->{-exists} ) { |
298
|
14
|
100
|
|
|
|
158
|
$dict{exists} = $attr->{-exists} =~ PerlIdentifier ? $1 : 'exists'; |
299
|
14
|
|
|
|
|
22
|
push @{ $dict{body} }, q[ sub <> { exists $_[0]->{$_[1] } } ]; |
|
14
|
|
|
|
|
55
|
|
300
|
14
|
|
|
|
|
52
|
$rentry->{methods}{$dict{exists}} = undef; |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
|
303
|
52
|
100
|
|
|
|
127
|
if ( $attr->{-defined} ) { |
304
|
2
|
100
|
|
|
|
13
|
$dict{defined} = $attr->{-defined} =~ PerlIdentifier ? $1 : 'defined'; |
305
|
2
|
|
|
|
|
4
|
push @{ $dict{body} }, q[ sub <> { defined $_[0]->{$_[1] } } ]; |
|
2
|
|
|
|
|
5
|
|
306
|
2
|
|
|
|
|
4
|
$rentry->{methods}{$dict{defined}} = undef; |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
52
|
100
|
|
|
|
137
|
if ( $attr->{-immutable} ) { |
310
|
1
|
|
|
|
|
2
|
$dict{set} = <<'END'; |
311
|
|
|
|
|
|
|
if ( @_ ) { |
312
|
|
|
|
|
|
|
require Carp; |
313
|
|
|
|
|
|
|
Carp::croak( q[Modification of a read-only value attempted]) |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
END |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
52
|
100
|
|
|
|
125
|
if ( $attr->{-recurse} ) { |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# decrement recursion limit. It's infinite recursion if |
321
|
|
|
|
|
|
|
# -recurse < 0; always set to -1 so we keep using the same |
322
|
|
|
|
|
|
|
# class. Note that -recurse will never be zero upon entrance |
323
|
|
|
|
|
|
|
# of this block, as -recurse => 0 is removed from the |
324
|
|
|
|
|
|
|
# attributes way upstream. |
325
|
|
|
|
|
|
|
|
326
|
8
|
100
|
|
|
|
42
|
$dict{recurse_limit} = --$attr->{-recurse} < 0 ? -1 : $attr->{-recurse}; |
327
|
|
|
|
|
|
|
|
328
|
8
|
|
|
|
|
17
|
$dict{return_value} = <<'END'; |
329
|
|
|
|
|
|
|
return 'HASH' eq (Scalar::Util::reftype( $self->{q[\<>]} ) // '') |
330
|
|
|
|
|
|
|
&& ! Scalar::Util::blessed( $self->{q[\<>]} ) |
331
|
|
|
|
|
|
|
? $<>::recurse_into_hash->( $self->{q[\<>]} ) |
332
|
|
|
|
|
|
|
: $self->{q[\<>]}; |
333
|
|
|
|
|
|
|
END |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# do a two-step initialization of the constructor. If |
336
|
|
|
|
|
|
|
# the initialization sub is stored in $recurse_into_hash, and then |
337
|
|
|
|
|
|
|
# $recurse_into_hash is set to the actual constructor I worry that |
338
|
|
|
|
|
|
|
# Perl may decide to garbage collect the setup subroutine while it's |
339
|
|
|
|
|
|
|
# busy setting $recurse_into_hash. So, store the |
340
|
|
|
|
|
|
|
# initialization sub in something other than $recurse_into_hash. |
341
|
|
|
|
|
|
|
|
342
|
8
|
|
|
|
|
13
|
$dict{recursion_constructor} = <<'END'; |
343
|
|
|
|
|
|
|
our $recurse_into_hash; |
344
|
|
|
|
|
|
|
our $setup_recurse_into_hash = sub { |
345
|
|
|
|
|
|
|
require Hash::Wrap; |
346
|
|
|
|
|
|
|
( $recurse_into_hash ) = Hash::Wrap->import ( { %$attr, -as => '-return', |
347
|
|
|
|
|
|
|
-recurse => <> } ); |
348
|
|
|
|
|
|
|
goto &$recurse_into_hash; |
349
|
|
|
|
|
|
|
}; |
350
|
|
|
|
|
|
|
$recurse_into_hash = $setup_recurse_into_hash; |
351
|
|
|
|
|
|
|
END |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
my %attr = ( %$attr, |
354
|
|
|
|
|
|
|
-recurse => --$attr->{-recurse} < 0 ? -1 : $attr->{-recurse}, |
355
|
8
|
100
|
|
|
|
57
|
); |
356
|
8
|
|
|
|
|
38
|
delete @attr{ qw( -as_scalar_ref -class -base -as ) }; |
357
|
8
|
|
|
|
|
23
|
$closures{'$attr'} = \%attr; |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
|
362
|
52
|
100
|
|
|
|
102
|
if ( $attr->{-predicate} ) { |
363
|
1
|
|
|
|
|
2
|
$dict{predicate_template} = <<'END'; |
364
|
|
|
|
|
|
|
our $predicate_template = q[ |
365
|
|
|
|
|
|
|
package \<>; |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
use Scalar::Util (); |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
sub has_\<> { |
370
|
|
|
|
|
|
|
my $self = shift; |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
Hash::Wrap::_croak_class_method( $self, "has_\<>" ) |
373
|
|
|
|
|
|
|
unless Scalar::Util::blessed( $self ); |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
return exists $self->{\<>}; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
$Hash::Wrap::REGISTRY{methods}{'has_\<>'} = undef; |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
\&has_\<>; |
381
|
|
|
|
|
|
|
]; |
382
|
|
|
|
|
|
|
END |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
|
385
|
52
|
|
|
|
|
108
|
my $class_template = <<'END'; |
386
|
|
|
|
|
|
|
package <>; |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
<> |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
use Scalar::Util (); |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
our $meta = { <> }; |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
our $validate = sub { |
395
|
|
|
|
|
|
|
my ( $self, $key ) = @_; |
396
|
|
|
|
|
|
|
return <>; |
397
|
|
|
|
|
|
|
}; |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
<> |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
our $accessor_template = q[ |
402
|
|
|
|
|
|
|
package \<>; |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
use Scalar::Util (); |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
sub \<> <> { |
407
|
|
|
|
|
|
|
my $self = shift; |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
Hash::Wrap::_croak_class_method( $self, "\<>" ) |
410
|
|
|
|
|
|
|
unless Scalar::Util::blessed( $self ); |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
Hash::Wrap::_croak_object_method( $self, "\<>" ) |
413
|
|
|
|
|
|
|
unless ( <> ); |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
<> |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
return <>; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
\&\<>; |
420
|
|
|
|
|
|
|
]; |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
<> |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
<> |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
our $AUTOLOAD; |
428
|
|
|
|
|
|
|
sub AUTOLOAD <> { |
429
|
|
|
|
|
|
|
goto &{ Hash::Wrap::_autoload( q[<>], $AUTOLOAD, $_[0] ) }; |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
sub DESTROY { } |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
sub can { |
435
|
|
|
|
|
|
|
return Hash::Wrap::_can( @_, q[<>] ); |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
1; |
439
|
|
|
|
|
|
|
END |
440
|
|
|
|
|
|
|
|
441
|
52
|
50
|
|
|
|
159
|
_compile_from_tpl( \$class_template, \%dict, \%closures ) |
442
|
|
|
|
|
|
|
or _croak_about_code( \$class_template, "class $class" ); |
443
|
|
|
|
|
|
|
|
444
|
52
|
100
|
|
|
|
207
|
if ( !!$attr->{-new} ) { |
445
|
5
|
50
|
|
|
|
19
|
my $name = $attr->{-new} =~ PerlIdentifier ? $1 : 'new'; |
446
|
5
|
|
|
|
|
27
|
_build_constructor( $class, $name, { %$attr, -as_method => 1 } ); |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
|
449
|
52
|
100
|
|
|
|
139
|
if ( $attr->{-methods} ) { |
450
|
|
|
|
|
|
|
|
451
|
16
|
|
|
|
|
37
|
my $methods = $attr->{-methods}; |
452
|
16
|
100
|
|
|
|
63
|
_croak( "-methods option value must be a hashref" ) |
453
|
|
|
|
|
|
|
unless 'HASH' eq ref $methods; |
454
|
|
|
|
|
|
|
|
455
|
15
|
|
|
|
|
79
|
for my $mth ( keys %$methods ) { |
456
|
16
|
100
|
|
|
|
123
|
_croak( "method name '$mth' is not a valid Perl identifier" ) |
457
|
|
|
|
|
|
|
if $mth !~ PerlIdentifier; |
458
|
|
|
|
|
|
|
|
459
|
15
|
|
|
|
|
37
|
my $code = $methods->{$mth}; |
460
|
15
|
100
|
|
|
|
54
|
_croak( qq{value for method "$mth" must be a coderef} ) |
461
|
|
|
|
|
|
|
unless 'CODE' eq ref $code; |
462
|
18
|
|
|
18
|
|
235
|
no strict 'refs'; ## no critic (ProhibitNoStrict) |
|
18
|
|
|
|
|
44
|
|
|
18
|
|
|
|
|
18717
|
|
463
|
14
|
|
|
|
|
23
|
*{"${class}::${mth}"} = $code; |
|
14
|
|
|
|
|
94
|
|
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
|
466
|
13
|
|
|
|
|
60
|
$rentry->{methods}{$_} = undef for keys %$methods; |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
|
469
|
49
|
|
|
|
|
122
|
push @CARP_NOT, $class; |
470
|
|
|
|
|
|
|
$rentry->{accessor_template} |
471
|
49
|
|
|
|
|
207
|
= _find_symbol( $class, "accessor_template", [ "SCALAR", undef ] ); |
472
|
|
|
|
|
|
|
|
473
|
49
|
100
|
|
|
|
173
|
if ( $attr->{-predicate} ) { |
474
|
|
|
|
|
|
|
$rentry->{predicate_template} |
475
|
1
|
|
|
|
|
2
|
= _find_symbol( $class, "predicate_template", [ "SCALAR", undef ] ); |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
|
478
|
49
|
|
|
|
|
132
|
$rentry->{validate} = _find_symbol( $class, 'validate', [ 'REF', 'CODE' ] ); |
479
|
|
|
|
|
|
|
|
480
|
49
|
|
|
|
|
262
|
Scalar::Util::weaken( $rentry->{validate} ); |
481
|
|
|
|
|
|
|
|
482
|
49
|
|
|
|
|
205
|
return $class; |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
sub _build_constructor { |
486
|
52
|
|
|
52
|
|
114
|
my ( $package, $name, $args ) = @_; |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# closure for user provided clone sub |
489
|
52
|
|
|
|
|
66
|
my $clone; |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
_croak( "cannot mix -copy and -clone" ) |
492
|
52
|
100
|
100
|
|
|
146
|
if exists $args->{-copy} && exists $args->{-clone}; |
493
|
|
|
|
|
|
|
|
494
|
51
|
|
|
|
|
217
|
my %dict = ( |
495
|
|
|
|
|
|
|
package => $package, |
496
|
|
|
|
|
|
|
constructor_name => $name, |
497
|
|
|
|
|
|
|
use => [], |
498
|
|
|
|
|
|
|
package_return_value => '1;', |
499
|
|
|
|
|
|
|
); |
500
|
|
|
|
|
|
|
|
501
|
51
|
|
|
|
|
71
|
$dict{class} = do { |
502
|
51
|
100
|
|
|
|
106
|
if ( $args->{-as_method} ) { |
503
|
5
|
|
|
|
|
8
|
'shift;'; |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
else { |
506
|
|
|
|
|
|
|
|
507
|
46
|
|
|
|
|
141
|
'q[' . $args->{-class} . '];'; |
508
|
|
|
|
|
|
|
} |
509
|
|
|
|
|
|
|
}; |
510
|
|
|
|
|
|
|
|
511
|
51
|
|
|
|
|
71
|
$dict{copy} = do { |
512
|
51
|
100
|
|
|
|
198
|
if ( $args->{-copy} ) { |
|
|
100
|
|
|
|
|
|
513
|
1
|
|
|
|
|
6
|
'$hash = { %{ $hash } };'; |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
elsif ( exists $args->{-clone} ) { |
517
|
3
|
100
|
|
|
|
8
|
if ( 'CODE' eq ref $args->{-clone} ) { |
518
|
1
|
|
|
|
|
2
|
$clone = $args->{-clone}; |
519
|
1
|
|
|
|
|
3
|
'$hash = $clone->($hash);'; |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
else { |
522
|
2
|
|
|
|
|
6
|
push @{ $dict{use} }, q[use Storable ();]; |
|
2
|
|
|
|
|
4
|
|
523
|
2
|
|
|
|
|
7
|
'$hash = Storable::dclone $hash;'; |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
}; |
527
|
|
|
|
|
|
|
|
528
|
51
|
|
|
|
|
67
|
$dict{lock} = do { |
529
|
51
|
100
|
|
|
|
261
|
if ( $args->{-immutable} ) { |
|
|
100
|
|
|
|
|
|
530
|
1
|
|
|
|
|
2
|
push @{ $dict{use} }, q[use Hash::Util ();]; |
|
1
|
|
|
|
|
2
|
|
531
|
1
|
|
|
|
|
2
|
'Hash::Util::lock_hash(%$hash)'; |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
elsif ( defined $args->{-lockkeys} ) { |
534
|
|
|
|
|
|
|
|
535
|
3
|
100
|
|
|
|
11
|
if ( 'ARRAY' eq ref $args->{-lockkeys} ) { |
|
|
50
|
|
|
|
|
|
536
|
|
|
|
|
|
|
_croak( |
537
|
|
|
|
|
|
|
"-lockkeys: attribute name ($_) is not a valid Perl identifier" |
538
|
2
|
|
|
|
|
3
|
) for grep { $_ !~ PerlIdentifier } @{ $args->{-lockkeys} }; |
|
4
|
|
|
|
|
21
|
|
|
2
|
|
|
|
|
4
|
|
539
|
|
|
|
|
|
|
|
540
|
1
|
|
|
|
|
2
|
push @{ $dict{use} }, q[use Hash::Util ();]; |
|
1
|
|
|
|
|
2
|
|
541
|
|
|
|
|
|
|
'Hash::Util::lock_keys_plus(%$hash, qw{ ' |
542
|
1
|
|
|
|
|
2
|
. join( ' ', @{ $args->{-lockkeys} } ) . ' });'; |
|
1
|
|
|
|
|
4
|
|
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
elsif ( $args->{-lockkeys} ) { |
545
|
1
|
|
|
|
|
1
|
push @{ $dict{use} }, q[use Hash::Util ();]; |
|
1
|
|
|
|
|
3
|
|
546
|
1
|
|
|
|
|
3
|
'Hash::Util::lock_keys(%$hash)'; |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
} |
549
|
|
|
|
|
|
|
}; |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
# return the constructor sub from the factory and don't insert the |
552
|
|
|
|
|
|
|
# name into the package namespace |
553
|
50
|
100
|
100
|
|
|
281
|
if ( $args->{-as_scalar_ref} || $args->{-as_return} ) { |
554
|
17
|
|
|
|
|
35
|
$dict{package_return_value} = ''; |
555
|
17
|
|
|
|
|
35
|
$dict{constructor_name} = ''; |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
#<<< no tidy |
559
|
50
|
|
|
|
|
91
|
my $code = q[ |
560
|
|
|
|
|
|
|
package <>; |
561
|
|
|
|
|
|
|
<> |
562
|
|
|
|
|
|
|
< |
563
|
|
|
|
|
|
|
use Scalar::Util (); |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
no warnings 'redefine'; |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
sub <> (;$) { |
568
|
|
|
|
|
|
|
my $class = <> |
569
|
|
|
|
|
|
|
my $hash = shift // {}; |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
if ( 'HASH' ne Scalar::Util::reftype($hash) ) { |
572
|
|
|
|
|
|
|
require Carp; |
573
|
|
|
|
|
|
|
Carp::croak( "argument to <>::<> must be a hashref" ) |
574
|
|
|
|
|
|
|
} |
575
|
|
|
|
|
|
|
<> |
576
|
|
|
|
|
|
|
bless $hash, $class; |
577
|
|
|
|
|
|
|
<> |
578
|
|
|
|
|
|
|
} |
579
|
|
|
|
|
|
|
<> |
580
|
|
|
|
|
|
|
]; |
581
|
|
|
|
|
|
|
#>>> |
582
|
|
|
|
|
|
|
|
583
|
50
|
|
33
|
|
|
212
|
my $result = _compile_from_tpl( \$code, \%dict, { '$clone' => $clone } ) |
584
|
|
|
|
|
|
|
|| _croak( |
585
|
|
|
|
|
|
|
"error generating constructor (as $name) subroutine: $@\n$code" ); |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
# caller asked for a coderef to be stuffed into a scalar |
588
|
50
|
100
|
|
|
|
168
|
${$name} = $result if $args->{-as_scalar_ref}; |
|
7
|
|
|
|
|
17
|
|
589
|
50
|
|
|
|
|
211
|
return $result; |
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
sub _croak_about_code { |
593
|
0
|
|
|
0
|
|
0
|
my ( $code, $what ) = @_; |
594
|
0
|
|
|
|
|
0
|
my $error = $@; |
595
|
0
|
|
|
|
|
0
|
_line_number_code( $code ); |
596
|
0
|
|
|
|
|
0
|
_croak( qq[error compiling $what: $error\n$$code] ); |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
sub _line_number_code { |
600
|
0
|
|
|
0
|
|
0
|
my ( $code ) = @_; |
601
|
0
|
|
|
|
|
0
|
my $space = length( $$code =~ tr/\n// ); |
602
|
0
|
|
|
|
|
0
|
my $line = 0; |
603
|
0
|
|
|
|
|
0
|
$$code =~ s/^/sprintf "%${space}d: ", ++$line/emg; |
|
0
|
|
|
|
|
0
|
|
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
sub _compile_from_tpl { |
608
|
183
|
|
|
183
|
|
314
|
my ( $code, $dict, $closures ) = @_; |
609
|
|
|
|
|
|
|
|
610
|
183
|
100
|
100
|
|
|
599
|
if ( defined $closures && %$closures) { |
611
|
|
|
|
|
|
|
$dict->{closures} |
612
|
58
|
|
|
|
|
163
|
= join( "\n", map { "my $_ = \$closures->{'$_'};" } keys %$closures ); |
|
58
|
|
|
|
|
266
|
|
613
|
|
|
|
|
|
|
} |
614
|
|
|
|
|
|
|
|
615
|
183
|
|
|
|
|
493
|
_interpolate( $code, $dict ); |
616
|
|
|
|
|
|
|
|
617
|
183
|
50
|
|
|
|
375
|
if ( $DEBUG ) { |
618
|
0
|
|
|
|
|
0
|
my $code = $$code; |
619
|
0
|
|
|
|
|
0
|
_line_number_code( \$code ); |
620
|
0
|
|
|
|
|
0
|
print STDERR $code; |
621
|
|
|
|
|
|
|
} |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
|
624
|
18
|
50
|
66
|
18
|
|
204
|
eval( $$code ); ## no critic (ProhibitStringyEval) |
|
18
|
100
|
33
|
18
|
|
39
|
|
|
18
|
100
|
33
|
18
|
|
3177
|
|
|
18
|
100
|
33
|
17
|
|
1544
|
|
|
18
|
100
|
33
|
15
|
|
5876
|
|
|
18
|
100
|
66
|
14
|
|
590
|
|
|
18
|
50
|
33
|
12
|
|
124
|
|
|
18
|
50
|
33
|
11
|
|
47
|
|
|
18
|
100
|
50
|
10
|
|
1766
|
|
|
17
|
100
|
50
|
9
|
|
237
|
|
|
17
|
50
|
50
|
9
|
|
65
|
|
|
17
|
100
|
|
9
|
|
2186
|
|
|
15
|
100
|
|
5
|
|
284
|
|
|
15
|
50
|
|
5
|
|
58
|
|
|
15
|
50
|
|
4
|
|
930
|
|
|
14
|
50
|
|
4
|
|
195
|
|
|
14
|
50
|
|
4
|
|
33
|
|
|
14
|
50
|
|
4
|
|
1480
|
|
|
12
|
50
|
|
4
|
|
1000
|
|
|
12
|
50
|
|
3
|
|
2830
|
|
|
12
|
50
|
|
3
|
|
1012
|
|
|
11
|
50
|
|
3
|
|
667
|
|
|
11
|
50
|
|
3
|
|
2749
|
|
|
11
|
50
|
|
3
|
|
799
|
|
|
10
|
50
|
|
3
|
|
212
|
|
|
10
|
50
|
|
3
|
|
24
|
|
|
10
|
50
|
|
3
|
|
849
|
|
|
9
|
50
|
|
3
|
|
189
|
|
|
9
|
50
|
|
3
|
|
22
|
|
|
9
|
50
|
|
28
|
|
818
|
|
|
9
|
50
|
|
20
|
|
200
|
|
|
9
|
50
|
|
8
|
|
24
|
|
|
9
|
50
|
|
1
|
|
649
|
|
|
9
|
50
|
|
1
|
|
344
|
|
|
9
|
50
|
|
3
|
|
21
|
|
|
9
|
50
|
|
0
|
|
707
|
|
|
5
|
50
|
|
0
|
|
59
|
|
|
5
|
50
|
|
0
|
|
14
|
|
|
5
|
50
|
|
0
|
|
480
|
|
|
5
|
50
|
|
0
|
|
155
|
|
|
5
|
50
|
|
0
|
|
15
|
|
|
5
|
50
|
|
0
|
|
597
|
|
|
4
|
50
|
|
0
|
|
176
|
|
|
4
|
|
|
20
|
|
13
|
|
|
4
|
|
|
16
|
|
133
|
|
|
4
|
|
|
12
|
|
192
|
|
|
4
|
|
|
13
|
|
12
|
|
|
4
|
|
|
15
|
|
379
|
|
|
4
|
|
|
13
|
|
247
|
|
|
4
|
|
|
19
|
|
13
|
|
|
4
|
|
|
17
|
|
341
|
|
|
4
|
|
|
9
|
|
47
|
|
|
4
|
|
|
6
|
|
12
|
|
|
4
|
|
|
3
|
|
208
|
|
|
4
|
|
|
5
|
|
155
|
|
|
4
|
|
|
11
|
|
12
|
|
|
4
|
|
|
8
|
|
513
|
|
|
4
|
|
|
10
|
|
115
|
|
|
3
|
|
|
5
|
|
6
|
|
|
3
|
|
|
3
|
|
202
|
|
|
3
|
|
|
8
|
|
21
|
|
|
3
|
|
|
1
|
|
6
|
|
|
3
|
|
|
1
|
|
393
|
|
|
3
|
|
|
4
|
|
24
|
|
|
3
|
|
|
14
|
|
12
|
|
|
3
|
|
|
7
|
|
336
|
|
|
3
|
|
|
3
|
|
18
|
|
|
3
|
|
|
3
|
|
7
|
|
|
3
|
|
|
1
|
|
323
|
|
|
3
|
|
|
1
|
|
22
|
|
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
519
|
|
|
3
|
|
|
|
|
20
|
|
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
153
|
|
|
3
|
|
|
|
|
16
|
|
|
3
|
|
|
|
|
14
|
|
|
3
|
|
|
|
|
323
|
|
|
3
|
|
|
|
|
19
|
|
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
365
|
|
|
3
|
|
|
|
|
21
|
|
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
324
|
|
|
3
|
|
|
|
|
24
|
|
|
3
|
|
|
|
|
95
|
|
|
3
|
|
|
|
|
528
|
|
|
183
|
|
|
|
|
14339
|
|
|
28
|
|
|
|
|
18564
|
|
|
28
|
|
|
|
|
91
|
|
|
20
|
|
|
|
|
4064
|
|
|
20
|
|
|
|
|
66
|
|
|
8
|
|
|
|
|
4678
|
|
|
8
|
|
|
|
|
22
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
671
|
|
|
1
|
|
|
|
|
10
|
|
|
3
|
|
|
|
|
370
|
|
|
3
|
|
|
|
|
10
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
30
|
|
|
|
|
902
|
|
|
30
|
|
|
|
|
192
|
|
|
30
|
|
|
|
|
160
|
|
|
30
|
|
|
|
|
135
|
|
|
30
|
|
|
|
|
186
|
|
|
25
|
|
|
|
|
701
|
|
|
25
|
|
|
|
|
155
|
|
|
24
|
|
|
|
|
121
|
|
|
23
|
|
|
|
|
57
|
|
|
24
|
|
|
|
|
161
|
|
|
16
|
|
|
|
|
2391
|
|
|
13
|
|
|
|
|
3218
|
|
|
11
|
|
|
|
|
7214
|
|
|
9
|
|
|
|
|
1342
|
|
|
10
|
|
|
|
|
2421
|
|
|
11
|
|
|
|
|
1290
|
|
|
10
|
|
|
|
|
2492
|
|
|
12
|
|
|
|
|
2104
|
|
|
17
|
|
|
|
|
2381
|
|
|
13
|
|
|
|
|
11585
|
|
|
24
|
|
|
|
|
1869
|
|
|
21
|
|
|
|
|
2160
|
|
|
17
|
|
|
|
|
111
|
|
|
15
|
|
|
|
|
1363
|
|
|
18
|
|
|
|
|
254
|
|
|
21
|
|
|
|
|
4586
|
|
|
20
|
|
|
|
|
91
|
|
|
19
|
|
|
|
|
105
|
|
|
17
|
|
|
|
|
47
|
|
|
18
|
|
|
|
|
102
|
|
|
7
|
|
|
|
|
85
|
|
|
5
|
|
|
|
|
2049
|
|
|
5
|
|
|
|
|
65
|
|
|
5
|
|
|
|
|
19
|
|
|
4
|
|
|
|
|
19
|
|
|
3
|
|
|
|
|
7
|
|
|
5
|
|
|
|
|
1147
|
|
|
5
|
|
|
|
|
2083
|
|
|
5
|
|
|
|
|
20
|
|
|
4
|
|
|
|
|
21
|
|
|
1
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
50
|
|
|
5
|
|
|
|
|
122
|
|
|
5
|
|
|
|
|
106
|
|
|
5
|
|
|
|
|
1750
|
|
|
4
|
|
|
|
|
12
|
|
|
4
|
|
|
|
|
28
|
|
|
2
|
|
|
|
|
135
|
|
|
7
|
|
|
|
|
13141
|
|
|
8
|
|
|
|
|
39
|
|
|
7
|
|
|
|
|
40
|
|
|
1
|
|
|
|
|
11
|
|
|
1
|
|
|
|
|
46
|
|
|
7
|
|
|
|
|
45
|
|
|
2
|
|
|
|
|
15
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
11
|
|
|
1
|
|
|
|
|
45
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
44
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
10
|
|
|
2
|
|
|
|
|
601
|
|
|
2
|
|
|
|
|
12
|
|
|
2
|
|
|
|
|
12
|
|
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
11
|
|
|
2
|
|
|
|
|
55
|
|
|
13
|
|
|
|
|
13230
|
|
|
8
|
|
|
|
|
38
|
|
|
8
|
|
|
|
|
56
|
|
|
4
|
|
|
|
|
12
|
|
|
4
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
72
|
|
|
6
|
|
|
|
|
7514
|
|
|
6
|
|
|
|
|
30
|
|
|
6
|
|
|
|
|
28
|
|
|
3
|
|
|
|
|
8
|
|
|
2
|
|
|
|
|
5045
|
|
|
5
|
|
|
|
|
23
|
|
|
1
|
|
|
|
|
5
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
3367
|
|
|
1
|
|
|
|
|
13
|
|
|
1
|
|
|
|
|
5
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
10
|
|
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
sub _interpolate { |
628
|
2076
|
|
|
2076
|
|
2597
|
my ( $tpl, $dict, $work ) = @_; |
629
|
2076
|
100
|
|
|
|
3088
|
$work = { loop => {} } unless defined $work; |
630
|
|
|
|
|
|
|
|
631
|
2076
|
|
|
|
|
3917
|
$$tpl =~ s{(\\)?\<\<(\w+)\>\> |
632
|
|
|
|
|
|
|
}{ |
633
|
2375
|
100
|
|
|
|
4015
|
if ( defined $1 ) { |
634
|
439
|
|
|
|
|
1195
|
"<<$2>>"; |
635
|
|
|
|
|
|
|
} |
636
|
|
|
|
|
|
|
else { |
637
|
1937
|
|
|
|
|
2848
|
my $key = lc $2; |
638
|
1937
|
|
|
|
|
2482
|
my $v = $dict->{$key}; |
639
|
1937
|
100
|
|
|
|
2341
|
if ( defined $v ) { |
640
|
1893
|
100
|
|
|
|
2995
|
$v = join( "\n", @$v ) |
641
|
|
|
|
|
|
|
if 'ARRAY' eq ref $v; |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
_croak( "circular interpolation loop detected for $key" ) |
644
|
1892
|
100
|
|
|
|
3387
|
if $work->{loop}{$key}++; |
645
|
1892
|
|
|
|
|
3251
|
_interpolate( \$v, $dict, $work ); |
646
|
1892
|
|
|
|
|
2214
|
--$work->{loop}{$key}; |
647
|
1892
|
|
|
|
|
6456
|
$v; |
648
|
|
|
|
|
|
|
} |
649
|
|
|
|
|
|
|
else { |
650
|
44
|
|
|
|
|
163
|
''; |
651
|
|
|
|
|
|
|
} |
652
|
|
|
|
|
|
|
} |
653
|
|
|
|
|
|
|
}gex; |
654
|
2075
|
|
|
|
|
2669
|
return; |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
1; |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
# |
660
|
|
|
|
|
|
|
# This file is part of Hash-Wrap |
661
|
|
|
|
|
|
|
# |
662
|
|
|
|
|
|
|
# This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory. |
663
|
|
|
|
|
|
|
# |
664
|
|
|
|
|
|
|
# This is free software, licensed under: |
665
|
|
|
|
|
|
|
# |
666
|
|
|
|
|
|
|
# The GNU General Public License, Version 3, June 2007 |
667
|
|
|
|
|
|
|
# |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
__END__ |