line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
=head1 NAME |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
Declare::Constraints::Simple - Declarative Validation of Data Structures |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=cut |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
package Declare::Constraints::Simple; |
8
|
13
|
|
|
13
|
|
427426
|
use warnings; |
|
13
|
|
|
|
|
32
|
|
|
13
|
|
|
|
|
460
|
|
9
|
13
|
|
|
13
|
|
76
|
use strict; |
|
13
|
|
|
|
|
27
|
|
|
13
|
|
|
|
|
661
|
|
10
|
|
|
|
|
|
|
|
11
|
13
|
|
|
13
|
|
83
|
use base 'Declare::Constraints::Simple::Library::Exportable'; |
|
13
|
|
|
|
|
26
|
|
|
13
|
|
|
|
|
10108
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our $VERSION = 0.03; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use Declare::Constraints::Simple-All; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my $profile = IsHashRef( |
20
|
|
|
|
|
|
|
-keys => HasLength, |
21
|
|
|
|
|
|
|
-values => IsArrayRef( IsObject )); |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
my $result1 = $profile->(undef); |
24
|
|
|
|
|
|
|
print $result1->message, "\n"; # 'Not a HashRef' |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my $result2 = $profile->({foo => [23]}); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
print $result2->message, "\n"; # 'Not an Object' |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
print $result2->path, "\n"; |
31
|
|
|
|
|
|
|
# 'IsHashRef[val foo].IsArrayRef[0].IsObject' |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head1 DESCRIPTION |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
The main purpose of this module is to provide an easy way to build a |
36
|
|
|
|
|
|
|
profile to validate a data structure. It does this by giving you a set of |
37
|
|
|
|
|
|
|
declarative keywords in the importing namespace. |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head1 USAGE |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
This is just a brief intro. For details read the documents mentioned in |
42
|
|
|
|
|
|
|
L. |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head2 Constraint Import |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
use Declare::Constraints::Simple-All; |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
The above command imports all constraint generators in the library into |
49
|
|
|
|
|
|
|
the current namespace. If you want only a selection, use C: |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
use Declare::Constraints::Simple |
52
|
|
|
|
|
|
|
Only => qw(IsInt Matches And); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
You can find all constraints (and constraint-like generators, like |
55
|
|
|
|
|
|
|
operators. In fact, C above is an operator. They're both implemented |
56
|
|
|
|
|
|
|
equally, so the distinction is a merely philosophical one) documented in |
57
|
|
|
|
|
|
|
the L pod. In that document you |
58
|
|
|
|
|
|
|
will also find the exact parameters for their usage, so this here is just |
59
|
|
|
|
|
|
|
a brief Intro and not a coverage of all possibilities. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head2 Building a Profile |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
You can use these constraints by building a tree that describes what data |
64
|
|
|
|
|
|
|
structure you expect. Every constraint can be used as sub-constraint, as |
65
|
|
|
|
|
|
|
parent, if it accepts other constraints, or stand-alone. If you'd just |
66
|
|
|
|
|
|
|
say |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
my $check = IsInt; |
69
|
|
|
|
|
|
|
print "yes!\n" if $check->(23); |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
it will work too. This also allows predefining tree segments, and nesting |
72
|
|
|
|
|
|
|
them: |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
my $id_to_objects = IsArrayRef(IsObject); |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
Here C<$id_to_objects> would give it's OK on an array reference |
77
|
|
|
|
|
|
|
containing a list of objects. But what if we now decide that we actually |
78
|
|
|
|
|
|
|
want a hashref containing two lists of objects? Behold: |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
my $object_lists = |
81
|
|
|
|
|
|
|
IsHashRef( HasAllKeys( qw(good bad) ), |
82
|
|
|
|
|
|
|
OnHashKeys( good => $id_to_objects, |
83
|
|
|
|
|
|
|
bad => $id_to_objects )); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
As you can see, constraints like C and C allow you |
86
|
|
|
|
|
|
|
to apply constraints to their keys and values. With this, you can step |
87
|
|
|
|
|
|
|
down in the data structure. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=head2 Applying a Profile to a Data Structure |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
Constraints return just code references that can be applied to one value |
92
|
|
|
|
|
|
|
(and only one value) like this: |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
my $result = $object_lists->($value); |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
After this call C<$result> contains a |
97
|
|
|
|
|
|
|
L object. The first think one wants |
98
|
|
|
|
|
|
|
to know is if the validation succeeded: |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
if ($result->is_valid) { ... } |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
This is pretty straight forward. To shorten things the result object also |
103
|
|
|
|
|
|
|
Ls it's Cean context. This means you can alternatively |
104
|
|
|
|
|
|
|
just say |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
if ($result) { ... } |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
However, if the result indicates a invalid data structure, we have a few |
109
|
|
|
|
|
|
|
options to find out what went wrong. There's a human parsable message in |
110
|
|
|
|
|
|
|
the C accessor. You can override these by forcing it to a |
111
|
|
|
|
|
|
|
message in a subtree with the C declaration. The C |
112
|
|
|
|
|
|
|
contains the name of the chain of constraints up to the point of failure. |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
You can use the C accessor for a joined string path representing |
115
|
|
|
|
|
|
|
the stack. |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=head2 Creating your own Libraries |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
You can declare a package as a library with |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
use Declare::Constraints::Simple-Library; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
which will install the base class and helper methods to define |
124
|
|
|
|
|
|
|
constraints. For a complete list read the documentation in |
125
|
|
|
|
|
|
|
L. You can use other |
126
|
|
|
|
|
|
|
libraries as base classes to include their constraints in your export |
127
|
|
|
|
|
|
|
possibilities. This means that with a package setup like |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
package MyLibrary; |
130
|
|
|
|
|
|
|
use warnings; |
131
|
|
|
|
|
|
|
use strict; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
use Declare::Constraints::Simple-Library; |
134
|
|
|
|
|
|
|
use base 'Declare::Constraints::Simple::Library'; |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
constraint 'MyConstraint', |
137
|
|
|
|
|
|
|
sub { return _result(($_[0] >= 12), 'Value too small') }; |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
1; |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
you can do |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
use MyLibrary-All; |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
and have all constraints, from the default library and yours from above, |
146
|
|
|
|
|
|
|
installed into your requesting namespace. You can override a constraint |
147
|
|
|
|
|
|
|
just by redeclaring it in a subclass. |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head2 Scoping |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Sometimes you want to validate parts of a data structure depending on |
152
|
|
|
|
|
|
|
another part of it. As of version 2.0 you can declare scopes and store |
153
|
|
|
|
|
|
|
results in them. Here is a complete example: |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
my $constraint = |
156
|
|
|
|
|
|
|
Scope('foo', |
157
|
|
|
|
|
|
|
And( |
158
|
|
|
|
|
|
|
HasAllKeys( qw(cmd data) ), |
159
|
|
|
|
|
|
|
OnHashKeys( |
160
|
|
|
|
|
|
|
cmd => Or( SetResult('foo', 'cmd_a', |
161
|
|
|
|
|
|
|
IsEq('FOO_A')), |
162
|
|
|
|
|
|
|
SetResult('foo', 'cmd_b', |
163
|
|
|
|
|
|
|
IsEq('FOO_B')) ), |
164
|
|
|
|
|
|
|
data => Or( And( IsValid('foo', 'cmd_a'), |
165
|
|
|
|
|
|
|
IsArrayRef( IsInt )), |
166
|
|
|
|
|
|
|
And( IsValid('foo', 'cmd_b'), |
167
|
|
|
|
|
|
|
IsRegex )) ))); |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
This profile would accept a hash references with the keys C and |
170
|
|
|
|
|
|
|
C. If C is set to C, then C has to be an array |
171
|
|
|
|
|
|
|
ref of integers. But if C is set to C, a regular expression |
172
|
|
|
|
|
|
|
is expected. |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=head1 SEE ALSO |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
L, |
177
|
|
|
|
|
|
|
L, |
178
|
|
|
|
|
|
|
L, |
179
|
|
|
|
|
|
|
L |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=head1 REQUIRES |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
L, L, L, L, |
184
|
|
|
|
|
|
|
L and L (for build). |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
=head1 TODO |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=over |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=item * |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
Examples. |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=item * |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
A list of questions that might come up, together with their answers. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=item * |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
A C constraint that takes a code reference. |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=item * |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
Create stack objects that stringify to the current form, but can hold |
205
|
|
|
|
|
|
|
more data. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=item * |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
Give the C constraint the ability to get the generated |
210
|
|
|
|
|
|
|
constraint inserted in the message. A possibility would be to replace |
211
|
|
|
|
|
|
|
__Value__ and __Message__. It might also accept code references, which |
212
|
|
|
|
|
|
|
return strings. |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=item * |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
Allow the C constraint to accept further constraints. One |
217
|
|
|
|
|
|
|
might like to check, for example, the refaddr of a closure. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=item * |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
A C constraint that takes a regex and can apply other |
222
|
|
|
|
|
|
|
constraints to the matches. |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=item * |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
??? |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=item * |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Profit. |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=back |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head1 INSTALLATION |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
perl Makefile.PL |
237
|
|
|
|
|
|
|
make |
238
|
|
|
|
|
|
|
make test |
239
|
|
|
|
|
|
|
make install |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
For details read L. |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=head1 AUTHOR |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
Robert 'phaylon' Sedlacek Cphaylon@dunkelheit.atE> |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
This module is free software, you can redistribute it and/or modify it |
250
|
|
|
|
|
|
|
under the same terms as perl itself. |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=cut |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
1; |