line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Data::Validator; |
2
|
20
|
|
|
20
|
|
850297
|
use 5.008_001; |
|
20
|
|
|
|
|
85
|
|
|
20
|
|
|
|
|
1522
|
|
3
|
20
|
|
|
20
|
|
26662
|
use Mouse; |
|
20
|
|
|
|
|
1048457
|
|
|
20
|
|
|
|
|
188
|
|
4
|
20
|
|
|
20
|
|
14790
|
use Mouse::Util::TypeConstraints (); |
|
20
|
|
|
|
|
56
|
|
|
20
|
|
|
|
|
354
|
|
5
|
20
|
|
|
20
|
|
114
|
use Mouse::Util (); |
|
20
|
|
|
|
|
42
|
|
|
20
|
|
|
|
|
313
|
|
6
|
20
|
|
|
20
|
|
107
|
use Carp (); |
|
20
|
|
|
|
|
40
|
|
|
20
|
|
|
|
|
2025
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '1.07'; |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
*_isa_tc = \&Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint; |
11
|
|
|
|
|
|
|
*_does_tc = \&Mouse::Util::TypeConstraints::find_or_create_does_type_constraint; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
has rules => ( |
14
|
|
|
|
|
|
|
is => 'ro', |
15
|
|
|
|
|
|
|
isa => 'ArrayRef', |
16
|
|
|
|
|
|
|
required => 1, |
17
|
|
|
|
|
|
|
); |
18
|
|
|
|
|
|
|
|
19
|
20
|
|
|
20
|
|
114
|
no Mouse; |
|
20
|
|
|
|
|
39
|
|
|
20
|
|
|
|
|
105
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my %rule_attrs = map { $_ => undef }qw( |
22
|
|
|
|
|
|
|
isa does coerce |
23
|
|
|
|
|
|
|
default optional |
24
|
|
|
|
|
|
|
xor |
25
|
|
|
|
|
|
|
documentation |
26
|
|
|
|
|
|
|
); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub BUILDARGS { |
29
|
35
|
|
|
35
|
1
|
21006
|
my($class, @mapping) = @_; |
30
|
|
|
|
|
|
|
|
31
|
35
|
|
|
|
|
83
|
my %xor; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
my @rules; |
34
|
35
|
|
|
|
|
223
|
while(my($name, $rule_ref) = splice @mapping, 0, 2) { |
35
|
57
|
|
|
|
|
97
|
my %rule; |
36
|
57
|
100
|
|
|
|
251
|
if(!Mouse::Util::TypeConstraints::HashRef($rule_ref)) { |
37
|
22
|
|
|
|
|
81
|
%rule = (isa => $rule_ref); |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
else { |
40
|
35
|
|
|
|
|
45
|
%rule = %{$rule_ref} |
|
35
|
|
|
|
|
129
|
|
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# validate the rule |
44
|
57
|
|
|
|
|
101
|
my $used = 0; |
45
|
57
|
|
|
|
|
272
|
foreach my $attr(keys %rule_attrs) { |
46
|
399
|
100
|
|
|
|
849
|
exists($rule{$attr}) and $used++; |
47
|
|
|
|
|
|
|
} |
48
|
57
|
100
|
|
|
|
276
|
if($used < keys %rule) { |
49
|
1
|
|
|
|
|
6
|
my @unknowns = grep { not exists $rule_attrs{$_} } sort keys %rule; |
|
2
|
|
|
|
|
8
|
|
50
|
1
|
|
|
|
|
10
|
Carp::croak("Wrong definition for '$name':" |
51
|
|
|
|
|
|
|
. ' Unknown attributes: ' |
52
|
|
|
|
|
|
|
. Mouse::Util::quoted_english_list(@unknowns) ); |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# setup the rule |
56
|
56
|
100
|
|
|
|
183
|
if(defined $rule{xor}) { |
57
|
2
|
|
|
|
|
7
|
my @xors = Mouse::Util::TypeConstraints::ArrayRef($rule{xor}) |
58
|
4
|
100
|
|
|
|
29
|
? @{$rule{xor}} |
59
|
|
|
|
|
|
|
: ($rule{xor}); |
60
|
4
|
|
|
|
|
16
|
$xor{$name} = $rule{xor} = \@xors; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
56
|
100
|
|
|
|
167
|
if(defined $rule{isa}) { |
64
|
35
|
|
|
|
|
209
|
$rule{type} = _isa_tc(delete $rule{isa}); |
65
|
|
|
|
|
|
|
} |
66
|
56
|
100
|
|
|
|
745
|
if(defined $rule{does}) { |
67
|
1
|
50
|
|
|
|
242
|
defined($rule{type}) |
68
|
|
|
|
|
|
|
and Carp::croak("Wrong definition for '$name':" |
69
|
|
|
|
|
|
|
. q{ You cannot use 'isa' and 'does' together}); |
70
|
0
|
|
|
|
|
0
|
$rule{type} = _does_tc(delete $rule{does}); |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
55
|
100
|
100
|
|
|
359
|
if(defined $rule{type} && not defined $rule{coerce}) { |
74
|
33
|
|
|
|
|
167
|
$rule{coerce} = $rule{type}->has_coercion; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
55
|
|
|
|
|
114
|
$rule{name} = $name; |
78
|
|
|
|
|
|
|
|
79
|
55
|
|
|
|
|
270
|
push @rules, \%rule; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# to check xor first and only once, move xor configuration into front rules |
83
|
33
|
100
|
|
|
|
138
|
if(%xor) { |
84
|
4
|
|
|
|
|
10
|
my %byname = map { $_->{name} => $_ } @rules; |
|
13
|
|
|
|
|
41
|
|
85
|
4
|
|
|
|
|
22
|
while(my($this, $others) = each %xor) { |
86
|
4
|
|
|
|
|
22
|
foreach my $other_name(@{$others}) { |
|
4
|
|
|
|
|
13
|
|
87
|
8
|
|
66
|
|
|
209
|
my $other_rule = $byname{$other_name} |
88
|
|
|
|
|
|
|
|| Carp::croak("Wrong definition for '$this':" |
89
|
|
|
|
|
|
|
. " Unknown parameter name '$other_name'" |
90
|
|
|
|
|
|
|
. " specified as exclusive-or"); |
91
|
|
|
|
|
|
|
|
92
|
7
|
|
50
|
|
|
9
|
push @{$other_rule->{xor} ||= []}, $this; |
|
7
|
|
|
|
|
54
|
|
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
32
|
|
|
|
|
409
|
return { rules => \@rules }; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub with { |
101
|
20
|
|
|
20
|
1
|
64
|
my($self, @roles) = @_; |
102
|
20
|
|
|
|
|
48
|
foreach my $role(@roles) { |
103
|
27
|
50
|
|
|
|
166
|
next if ref $role; |
104
|
27
|
|
|
|
|
186
|
$role = Mouse::Util::load_first_existing_class( |
105
|
|
|
|
|
|
|
__PACKAGE__ . '::Role::' . $role, |
106
|
|
|
|
|
|
|
$role, |
107
|
|
|
|
|
|
|
); |
108
|
|
|
|
|
|
|
} |
109
|
20
|
|
|
|
|
1562
|
Mouse::Util::apply_all_roles($self, @roles); |
110
|
20
|
|
|
|
|
57498
|
return $self; |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub find_rule { |
114
|
3
|
|
|
3
|
1
|
1279
|
my($self, $name) = @_; |
115
|
3
|
|
|
|
|
5
|
foreach my $rule(@{$self->rules}) { |
|
3
|
|
|
|
|
13
|
|
116
|
3
|
100
|
|
|
|
22
|
return $rule if $rule->{name} eq $name; |
117
|
|
|
|
|
|
|
} |
118
|
1
|
|
|
|
|
5
|
return undef; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub validate { |
122
|
109
|
|
|
109
|
1
|
115695
|
my $self = shift; |
123
|
109
|
|
|
|
|
612
|
my $args = $self->initialize(@_); |
124
|
|
|
|
|
|
|
|
125
|
109
|
|
|
|
|
217
|
my %skip; |
126
|
|
|
|
|
|
|
my @errors; |
127
|
0
|
|
|
|
|
0
|
my @missing; |
128
|
109
|
|
|
|
|
165
|
my $nargs = scalar keys %{$args}; |
|
109
|
|
|
|
|
329
|
|
129
|
109
|
|
|
|
|
169
|
my $used = 0; |
130
|
109
|
|
|
|
|
330
|
my $rules = $self->rules; |
131
|
109
|
|
|
|
|
176
|
RULE: foreach my $rule(@{ $rules }) { |
|
109
|
|
|
|
|
342
|
|
132
|
205
|
|
|
|
|
405
|
my $name = $rule->{name}; |
133
|
205
|
100
|
|
|
|
596
|
next RULE if exists $skip{$name}; |
134
|
|
|
|
|
|
|
|
135
|
192
|
100
|
|
|
|
870
|
if(exists $args->{$name}) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
136
|
|
|
|
|
|
|
|
137
|
123
|
100
|
|
|
|
406
|
if(exists $rule->{type}) { |
138
|
97
|
|
|
|
|
417
|
my $err = $self->apply_type_constraint($rule, $args, $name); |
139
|
97
|
100
|
|
|
|
791
|
if($err) { |
140
|
22
|
|
|
|
|
82
|
push @errors, $self->make_error( |
141
|
|
|
|
|
|
|
type => 'InvalidValue', |
142
|
|
|
|
|
|
|
message => $err, |
143
|
|
|
|
|
|
|
name => $name, |
144
|
|
|
|
|
|
|
); |
145
|
22
|
|
|
|
|
77
|
next RULE; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
101
|
100
|
|
|
|
305
|
if($rule->{xor}) { |
150
|
|
|
|
|
|
|
# checks conflicts with exclusive arguments |
151
|
10
|
|
|
|
|
18
|
foreach my $other_name( @{ $rule->{xor} } ) { |
|
10
|
|
|
|
|
25
|
|
152
|
18
|
100
|
|
|
|
48
|
if(exists $args->{$other_name}) { |
153
|
4
|
|
|
|
|
34
|
push @errors, $self->make_error( |
154
|
|
|
|
|
|
|
type => 'ExclusiveParameter', |
155
|
|
|
|
|
|
|
message => "Exclusive parameters passed together:" |
156
|
|
|
|
|
|
|
. " '$name' v.s. '$other_name'", |
157
|
|
|
|
|
|
|
name => $name, |
158
|
|
|
|
|
|
|
conflict=> $other_name, |
159
|
|
|
|
|
|
|
); |
160
|
|
|
|
|
|
|
} |
161
|
18
|
|
|
|
|
52
|
$skip{$other_name}++; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
} |
164
|
101
|
|
|
|
|
461
|
$used++; |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
elsif(exists $rule->{default}) { |
167
|
28
|
|
|
|
|
50
|
my $default = $rule->{default}; |
168
|
28
|
100
|
|
|
|
174
|
$args->{$name} = Mouse::Util::TypeConstraints::CodeRef($default) |
169
|
|
|
|
|
|
|
? $default->($self, $rule, $args) |
170
|
|
|
|
|
|
|
: $default; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
elsif(!$rule->{optional}) { |
173
|
35
|
|
|
|
|
101
|
push @missing, $rule; |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
|
178
|
109
|
100
|
|
|
|
353
|
if(@missing) { |
179
|
27
|
|
|
|
|
60
|
MISSING: foreach my $rule(@missing) { |
180
|
35
|
|
|
|
|
76
|
my $name = $rule->{name}; |
181
|
35
|
100
|
|
|
|
147
|
next if exists $skip{$name}; |
182
|
|
|
|
|
|
|
|
183
|
32
|
|
|
|
|
52
|
my @xors; |
184
|
32
|
100
|
|
|
|
128
|
if($rule->{xor}) { |
185
|
4
|
|
|
|
|
7
|
foreach my $other_name(@{$rule->{xor}}) { |
|
4
|
|
|
|
|
12
|
|
186
|
4
|
100
|
|
|
|
22
|
next MISSING if exists $args->{$other_name}; |
187
|
1
|
|
|
|
|
4
|
push @xors, $other_name; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
} |
190
|
29
|
100
|
|
|
|
169
|
my $real_missing = @xors |
191
|
|
|
|
|
|
|
? sprintf(q{'%s' (or %s)}, |
192
|
|
|
|
|
|
|
$name, Mouse::Util::quoted_english_list(@xors) ) |
193
|
|
|
|
|
|
|
: sprintf(q{'%s'}, $name); |
194
|
29
|
|
|
|
|
206
|
push @errors, $self->make_error( |
195
|
|
|
|
|
|
|
type => 'MissingParameter', |
196
|
|
|
|
|
|
|
message => "Missing parameter: $real_missing", |
197
|
|
|
|
|
|
|
name => $name, |
198
|
|
|
|
|
|
|
); |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
|
203
|
109
|
100
|
|
|
|
306
|
if($used < $nargs) { |
204
|
37
|
|
|
|
|
189
|
my %unknowns = $self->unknown_parameters($rules, $args); |
205
|
37
|
100
|
|
|
|
141
|
if(keys %unknowns) { |
206
|
7
|
|
|
|
|
42
|
foreach my $name( sort keys %unknowns ) { |
207
|
11
|
|
|
|
|
51
|
push @errors, $self->make_error( |
208
|
|
|
|
|
|
|
type => 'UnknownParameter', |
209
|
|
|
|
|
|
|
message => "Unknown parameter: '$name'", |
210
|
|
|
|
|
|
|
name => $name, |
211
|
|
|
|
|
|
|
); |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# make it restricted |
217
|
109
|
|
|
|
|
273
|
&Internals::SvREADONLY($args, 1); |
218
|
|
|
|
|
|
|
|
219
|
109
|
100
|
|
|
|
483
|
if(@errors) { |
220
|
50
|
|
|
|
|
183
|
$args = $self->found_errors($args, @errors); |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
64
|
|
|
|
|
285
|
return $args; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
__PACKAGE__->meta->add_method( initialize => \&Mouse::Object::BUILDARGS ); |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
sub unknown_parameters { |
229
|
37
|
|
|
37
|
0
|
74
|
my($self, $rules, $args) = @_; |
230
|
37
|
|
|
|
|
61
|
my %knowns = map { $_->{name} => undef } @{$rules}; |
|
61
|
|
|
|
|
236
|
|
|
37
|
|
|
|
|
79
|
|
231
|
73
|
100
|
|
|
|
276
|
return map { |
232
|
37
|
|
|
|
|
91
|
!exists $knowns{$_} |
233
|
|
|
|
|
|
|
? ($_ => delete $args->{$_}) |
234
|
|
|
|
|
|
|
: () |
235
|
37
|
|
|
|
|
75
|
} keys %{$args}; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
sub found_errors { |
239
|
45
|
|
|
45
|
0
|
104
|
my($self, $args, @errors) = @_; |
240
|
45
|
|
|
|
|
89
|
my $msg = ''; |
241
|
45
|
|
|
|
|
81
|
foreach my $e(@errors) { |
242
|
60
|
|
|
|
|
192
|
$msg .= $e->{message} . "\n"; |
243
|
|
|
|
|
|
|
} |
244
|
45
|
|
|
|
|
204
|
$self->throw_error($msg . '... found'); |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
sub make_error { |
248
|
66
|
|
|
66
|
0
|
260
|
my($self, %e) = @_; |
249
|
66
|
|
|
|
|
231
|
return \%e; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
sub throw_error { |
253
|
44
|
|
|
44
|
0
|
77
|
my($self, $message) = @_; |
254
|
44
|
|
|
|
|
184
|
local $Carp::CarpLevel = $Carp::CarpLevel + 2; # &throw_error + &validate |
255
|
44
|
|
|
|
|
7143
|
confess($message); |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub apply_type_constraint { |
259
|
97
|
|
|
97
|
0
|
173
|
my($self, $rule, $args, $name) = @_; |
260
|
97
|
|
|
|
|
168
|
my $tc = $rule->{type}; |
261
|
97
|
100
|
|
|
|
657
|
return '' if $tc->check($args->{$name}); |
262
|
|
|
|
|
|
|
|
263
|
23
|
100
|
|
|
|
87
|
if($rule->{coerce}) { |
264
|
2
|
|
|
|
|
13
|
my $value = $tc->coerce($args->{$name}); |
265
|
2
|
100
|
|
|
|
101
|
if($tc->check($value)) { |
266
|
1
|
|
|
|
|
3
|
$args->{$name} = $value; # commit |
267
|
1
|
|
|
|
|
3
|
return ''; |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
22
|
|
|
|
|
166
|
return "Invalid value for '$rule->{name}': " |
272
|
|
|
|
|
|
|
. $tc->get_message($args->{$name}); |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
276
|
|
|
|
|
|
|
__END__ |