| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Brannigan; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# ABSTRACT: Comprehensive, flexible system for validating and parsing input, mainly targeted at web applications. |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $VERSION = "1.100001"; |
|
6
|
|
|
|
|
|
|
$VERSION = eval $VERSION; |
|
7
|
|
|
|
|
|
|
|
|
8
|
3
|
|
|
3
|
|
40629
|
use warnings; |
|
|
3
|
|
|
|
|
4
|
|
|
|
3
|
|
|
|
|
84
|
|
|
9
|
3
|
|
|
3
|
|
9
|
use strict; |
|
|
3
|
|
|
|
|
4
|
|
|
|
3
|
|
|
|
|
49
|
|
|
10
|
3
|
|
|
3
|
|
1120
|
use Brannigan::Tree; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
1232
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 NAME |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
Brannigan - Comprehensive, flexible system for validating and parsing input, mainly targeted at web applications. |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
use Brannigan; |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
my %scheme1 = ( name => 'scheme1', params => ... ); |
|
21
|
|
|
|
|
|
|
my %scheme2 = ( name => 'scheme2', params => ... ); |
|
22
|
|
|
|
|
|
|
my %scheme3 = ( name => 'scheme3', params => ... ); |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# use the OO interface |
|
25
|
|
|
|
|
|
|
my $b = Brannigan->new(\%scheme1, \%scheme2); |
|
26
|
|
|
|
|
|
|
$b->add_scheme(\%scheme3); |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
my $parsed = $b->process('scheme1', \%params); |
|
29
|
|
|
|
|
|
|
if ($parsed->{_rejects}) { |
|
30
|
|
|
|
|
|
|
die $parsed->{_rejects}; |
|
31
|
|
|
|
|
|
|
} else { |
|
32
|
|
|
|
|
|
|
return $parsed; |
|
33
|
|
|
|
|
|
|
} |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# Or use the functional interface |
|
36
|
|
|
|
|
|
|
my $parsed = Brannigan::process(\%scheme1, \%params); |
|
37
|
|
|
|
|
|
|
if ($parsed->{_rejects}) { |
|
38
|
|
|
|
|
|
|
die $parsed->{_rejects}; |
|
39
|
|
|
|
|
|
|
} else { |
|
40
|
|
|
|
|
|
|
return $parsed; |
|
41
|
|
|
|
|
|
|
} |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
For a more comprehensive example, see L"MANUAL"> in this document |
|
44
|
|
|
|
|
|
|
or the L document. |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
Brannigan is an attempt to ease the pain of collecting, validating and |
|
49
|
|
|
|
|
|
|
parsing input parameters in web applications. It's designed to answer both of |
|
50
|
|
|
|
|
|
|
the main problems that web applications face: |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=over 2 |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=item * Simple user input |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
Brannigan can validate and parse simple, "flat", user input, possibly |
|
57
|
|
|
|
|
|
|
coming from web forms. |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=item * Complex data structures |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
Brannigan can validate and parse complex data structures, possibly |
|
62
|
|
|
|
|
|
|
deserialized from JSON or XML data sent to web services and APIs. |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=back |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Brannigan's approach to data validation is as follows: define a structure |
|
67
|
|
|
|
|
|
|
of parameters and their needed validations, and let the module automatically |
|
68
|
|
|
|
|
|
|
examine input parameters against this structure. Brannigan provides you |
|
69
|
|
|
|
|
|
|
with common validation methods that are used everywhere, and also allows |
|
70
|
|
|
|
|
|
|
you to create custom validations easily. This structure also defines how, |
|
71
|
|
|
|
|
|
|
if at all, the input should be parsed. This is akin to schema-based |
|
72
|
|
|
|
|
|
|
validations such as XSD, but much more functional, and most of all |
|
73
|
|
|
|
|
|
|
flexible. |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
Check the next section for an example of such a structure. I call this |
|
76
|
|
|
|
|
|
|
structure a validation/parsing scheme. Schemes can inherit all the properties |
|
77
|
|
|
|
|
|
|
of other schemes, which allows you to be much more flexible in certain |
|
78
|
|
|
|
|
|
|
situations. Imagine you have a blogging application. A base scheme might |
|
79
|
|
|
|
|
|
|
define all validations and parsing needed to create a new blog post from |
|
80
|
|
|
|
|
|
|
a user's input. When editing a post, however, some parameters that were |
|
81
|
|
|
|
|
|
|
required when creating the post might not be required now (so you can |
|
82
|
|
|
|
|
|
|
just use older values), and maybe new parameters are introduced. Inheritance |
|
83
|
|
|
|
|
|
|
helps you avoid repeating yourself. You can another scheme which gets |
|
84
|
|
|
|
|
|
|
all the properties of the base scheme, only changing whatever it is needs |
|
85
|
|
|
|
|
|
|
changing (and possibly adding specific properties that don't exist in |
|
86
|
|
|
|
|
|
|
the base scheme). |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=head1 MANUAL |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
In the following manual, we will look at the following example. It is based |
|
91
|
|
|
|
|
|
|
on L, but should be fairly understandable for non-Catalyst users. |
|
92
|
|
|
|
|
|
|
Do not be alarmed by the size of this, this is only because it displays |
|
93
|
|
|
|
|
|
|
basically every aspect of Brannigan. |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
This example uses L, but should be pretty self explanatory. It's |
|
96
|
|
|
|
|
|
|
fairly complex, since it details pretty much all of the available Brannigan |
|
97
|
|
|
|
|
|
|
functionality, so don't be alarmed by the size of this thing. |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
package MyApp::Controller::Post; |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
use strict; |
|
102
|
|
|
|
|
|
|
use warnings; |
|
103
|
|
|
|
|
|
|
use Brannigan; |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# create a new Brannigan object with two validation/parsing schemes: |
|
106
|
|
|
|
|
|
|
my $b = Brannigan->new({ |
|
107
|
|
|
|
|
|
|
name => 'post', |
|
108
|
|
|
|
|
|
|
ignore_missing => 1, |
|
109
|
|
|
|
|
|
|
params => { |
|
110
|
|
|
|
|
|
|
subject => { |
|
111
|
|
|
|
|
|
|
required => 1, |
|
112
|
|
|
|
|
|
|
length_between => [3, 40], |
|
113
|
|
|
|
|
|
|
}, |
|
114
|
|
|
|
|
|
|
text => { |
|
115
|
|
|
|
|
|
|
required => 1, |
|
116
|
|
|
|
|
|
|
min_length => 10, |
|
117
|
|
|
|
|
|
|
validate => sub { |
|
118
|
|
|
|
|
|
|
my $value = shift; |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
return undef unless $value; |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
return $value =~ m/^lorem ipsum/ ? 1 : undef; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
}, |
|
125
|
|
|
|
|
|
|
day => { |
|
126
|
|
|
|
|
|
|
required => 0, |
|
127
|
|
|
|
|
|
|
integer => 1, |
|
128
|
|
|
|
|
|
|
value_between => [1, 31], |
|
129
|
|
|
|
|
|
|
}, |
|
130
|
|
|
|
|
|
|
mon => { |
|
131
|
|
|
|
|
|
|
required => 0, |
|
132
|
|
|
|
|
|
|
integer => 1, |
|
133
|
|
|
|
|
|
|
value_between => [1, 12], |
|
134
|
|
|
|
|
|
|
}, |
|
135
|
|
|
|
|
|
|
year => { |
|
136
|
|
|
|
|
|
|
required => 0, |
|
137
|
|
|
|
|
|
|
integer => 1, |
|
138
|
|
|
|
|
|
|
value_between => [1900, 2900], |
|
139
|
|
|
|
|
|
|
}, |
|
140
|
|
|
|
|
|
|
section => { |
|
141
|
|
|
|
|
|
|
required => 1, |
|
142
|
|
|
|
|
|
|
integer => 1, |
|
143
|
|
|
|
|
|
|
value_between => [1, 3], |
|
144
|
|
|
|
|
|
|
parse => sub { |
|
145
|
|
|
|
|
|
|
my $val = shift; |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
my $ret = $val == 1 ? 'reviews' : |
|
148
|
|
|
|
|
|
|
$val == 2 ? 'receips' : |
|
149
|
|
|
|
|
|
|
'general'; |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
return { section => $ret }; |
|
152
|
|
|
|
|
|
|
}, |
|
153
|
|
|
|
|
|
|
}, |
|
154
|
|
|
|
|
|
|
id => { |
|
155
|
|
|
|
|
|
|
required => 1, |
|
156
|
|
|
|
|
|
|
exact_length => 10, |
|
157
|
|
|
|
|
|
|
value_between => [1000000000, 2000000000], |
|
158
|
|
|
|
|
|
|
}, |
|
159
|
|
|
|
|
|
|
'/^picture_(\d+)$/' => { |
|
160
|
|
|
|
|
|
|
length_between => [3, 100], |
|
161
|
|
|
|
|
|
|
validate => sub { |
|
162
|
|
|
|
|
|
|
my ($value, $num) = @_; |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
... |
|
165
|
|
|
|
|
|
|
}, |
|
166
|
|
|
|
|
|
|
}, |
|
167
|
|
|
|
|
|
|
picture_1 => { |
|
168
|
|
|
|
|
|
|
default => 'http://www.example.com/avatar.png', |
|
169
|
|
|
|
|
|
|
}, |
|
170
|
|
|
|
|
|
|
array_of_ints => { |
|
171
|
|
|
|
|
|
|
array => 1, |
|
172
|
|
|
|
|
|
|
min_length => 3, |
|
173
|
|
|
|
|
|
|
values => { |
|
174
|
|
|
|
|
|
|
integer => 1, |
|
175
|
|
|
|
|
|
|
}, |
|
176
|
|
|
|
|
|
|
}, |
|
177
|
|
|
|
|
|
|
hash_of_langs => { |
|
178
|
|
|
|
|
|
|
hash => 1, |
|
179
|
|
|
|
|
|
|
keys => { |
|
180
|
|
|
|
|
|
|
_all => { |
|
181
|
|
|
|
|
|
|
exact_length => 10, |
|
182
|
|
|
|
|
|
|
}, |
|
183
|
|
|
|
|
|
|
en => { |
|
184
|
|
|
|
|
|
|
required => 1, |
|
185
|
|
|
|
|
|
|
}, |
|
186
|
|
|
|
|
|
|
}, |
|
187
|
|
|
|
|
|
|
}, |
|
188
|
|
|
|
|
|
|
}, |
|
189
|
|
|
|
|
|
|
groups => { |
|
190
|
|
|
|
|
|
|
date => { |
|
191
|
|
|
|
|
|
|
params => [qw/year mon day/], |
|
192
|
|
|
|
|
|
|
parse => sub { |
|
193
|
|
|
|
|
|
|
my ($year, $mon, $day) = @_; |
|
194
|
|
|
|
|
|
|
return undef unless $year && $mon && $day; |
|
195
|
|
|
|
|
|
|
return { date => $year.'-'.$mon.'-'.$day }; |
|
196
|
|
|
|
|
|
|
}, |
|
197
|
|
|
|
|
|
|
}, |
|
198
|
|
|
|
|
|
|
tags => { |
|
199
|
|
|
|
|
|
|
regex => '/^tags_(en|he|fr)$/', |
|
200
|
|
|
|
|
|
|
forbid_words => ['bad_word', 'very_bad_word'], |
|
201
|
|
|
|
|
|
|
parse => sub { |
|
202
|
|
|
|
|
|
|
return { tags => \@_ }; |
|
203
|
|
|
|
|
|
|
}, |
|
204
|
|
|
|
|
|
|
}, |
|
205
|
|
|
|
|
|
|
}, |
|
206
|
|
|
|
|
|
|
}, { |
|
207
|
|
|
|
|
|
|
name => 'edit_post', |
|
208
|
|
|
|
|
|
|
inherits_from => 'post', |
|
209
|
|
|
|
|
|
|
params => { |
|
210
|
|
|
|
|
|
|
subject => { |
|
211
|
|
|
|
|
|
|
required => 0, # subject is no longer required |
|
212
|
|
|
|
|
|
|
}, |
|
213
|
|
|
|
|
|
|
id => { |
|
214
|
|
|
|
|
|
|
forbidden => 1, |
|
215
|
|
|
|
|
|
|
}, |
|
216
|
|
|
|
|
|
|
}, |
|
217
|
|
|
|
|
|
|
}); |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# create the custom 'forbid_words' validation method |
|
220
|
|
|
|
|
|
|
$b->custom_validation('forbid_words', sub { |
|
221
|
|
|
|
|
|
|
my $value = shift; |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
foreach (@_) { |
|
224
|
|
|
|
|
|
|
return 0 if $value =~ m/$_/; |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
return 1; |
|
228
|
|
|
|
|
|
|
}); |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# post a new blog post |
|
231
|
|
|
|
|
|
|
sub new_post : Local { |
|
232
|
|
|
|
|
|
|
my ($self, $c) = @_; |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# get input parameters hash-ref |
|
235
|
|
|
|
|
|
|
my $params = $c->request->params; |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# process the parameters |
|
238
|
|
|
|
|
|
|
my $parsed_params = $b->process('post', $params); |
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
if ($parsed_params->{_rejects}) { |
|
241
|
|
|
|
|
|
|
die $c->list_errors($parsed_params); |
|
242
|
|
|
|
|
|
|
} else { |
|
243
|
|
|
|
|
|
|
$c->model('DB::BlogPost')->create($parsed_params); |
|
244
|
|
|
|
|
|
|
} |
|
245
|
|
|
|
|
|
|
} |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# edit a blog post |
|
248
|
|
|
|
|
|
|
sub edit_post : Local { |
|
249
|
|
|
|
|
|
|
my ($self, $c, $id) = @_; |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
my $params = $b->process('edit_posts', $c->req->params); |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
if ($params->{_rejects}) { |
|
254
|
|
|
|
|
|
|
die $c->list_errors($params); |
|
255
|
|
|
|
|
|
|
} else { |
|
256
|
|
|
|
|
|
|
$c->model('DB::BlogPosts')->find($id)->update($params); |
|
257
|
|
|
|
|
|
|
} |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
=head2 HOW BRANNIGAN WORKS |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
In essence, Brannigan works in three stages (which all boil down to one |
|
263
|
|
|
|
|
|
|
single command): |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=over |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=item * Input stage and preparation |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
Brannigan receives a hash-ref of input parameters, or a hash-ref based |
|
270
|
|
|
|
|
|
|
data structure, and the name of a scheme to validate against. Brannigan |
|
271
|
|
|
|
|
|
|
then loads the scheme and prepares it (by merging it with inherited schemes) |
|
272
|
|
|
|
|
|
|
for later processing. |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=item * Data validation |
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
Brannigan invokes all validation methods defined in the scheme on the |
|
277
|
|
|
|
|
|
|
input data, and generates a hash-ref of rejected parameters. For every |
|
278
|
|
|
|
|
|
|
parameter in this hash-ref, a list of failed validations is created in an |
|
279
|
|
|
|
|
|
|
array-ref. |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=item * Data parsing |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
Regardless of the previous stage, every parsing method defined in the scheme |
|
284
|
|
|
|
|
|
|
is applied on the relevant data. The data resulting from these parsing |
|
285
|
|
|
|
|
|
|
methods, along with the values of all input parameters for which no parsing |
|
286
|
|
|
|
|
|
|
methods were defined, is returned to the user in a hash-ref. This hash-ref |
|
287
|
|
|
|
|
|
|
also includes a _rejects key whose value is the rejects hash created in |
|
288
|
|
|
|
|
|
|
the previous stage. |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
The reason I say this stage isn't dependant on the previous stage is |
|
291
|
|
|
|
|
|
|
simple. First of all, it's possible no parameters failed validation, but |
|
292
|
|
|
|
|
|
|
the truth is this stage doesn't care if a parameter failed validation. It |
|
293
|
|
|
|
|
|
|
will still parse it and return it to the user, and no errors are ever |
|
294
|
|
|
|
|
|
|
raised by Brannigan. It is the developer's (i.e. you) job to decide what |
|
295
|
|
|
|
|
|
|
to do in case rejects are present. |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=back |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=head2 HOW SCHEMES LOOK |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
The validation/parsing scheme defines the structure of the data you're |
|
302
|
|
|
|
|
|
|
expecting to receive, along with information about the way it should be |
|
303
|
|
|
|
|
|
|
validated and parsed. Schemes are created by passing them to the Brannigan |
|
304
|
|
|
|
|
|
|
constructor. You can pass as many schemes as you like, and these schemes |
|
305
|
|
|
|
|
|
|
can inherit from one another. You can create the Brannigan object that |
|
306
|
|
|
|
|
|
|
gets these schemes wherever you want. Maybe in a controller of your web |
|
307
|
|
|
|
|
|
|
app that will directly use this object to validate and parse input it |
|
308
|
|
|
|
|
|
|
gets, or maybe in a special validation class that will hold all schemes. |
|
309
|
|
|
|
|
|
|
It doesn't matter where, as long as you make the object available for |
|
310
|
|
|
|
|
|
|
your application. |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
A scheme is a hash-ref based data structure that has the following keys: |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=over |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=item * name |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
Defines the name of the scheme. Required. |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=item * ignore_missing |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
Boolean value indicating whether input parameters that are not referenced |
|
323
|
|
|
|
|
|
|
in the scheme should be added to the parsed output or not. Optional, |
|
324
|
|
|
|
|
|
|
defaults to false (i.e. parameters missing from the scheme will be added |
|
325
|
|
|
|
|
|
|
to the output as-is). You might find it is probably a good idea to turn |
|
326
|
|
|
|
|
|
|
this on, so any input parameters you're not expecting to receive from users |
|
327
|
|
|
|
|
|
|
are ignored. |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
=item * inherits_from |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
Either a scalar naming a different scheme or an array-ref of scheme names. |
|
332
|
|
|
|
|
|
|
The new scheme will inherit all the properties of the scheme(s) defined |
|
333
|
|
|
|
|
|
|
by this key. If an array-ref is provided, the scheme will inherit their |
|
334
|
|
|
|
|
|
|
properties in the order they are defined. See the L"CAVEATS"> section for some |
|
335
|
|
|
|
|
|
|
"heads-up" about inheritance. |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=item * params |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
The params key is the most important part of the scheme, as it defines |
|
340
|
|
|
|
|
|
|
the expected input. This key takes a hash-ref containing the names of |
|
341
|
|
|
|
|
|
|
input parameters. Every such name (i.e. key) in itself is also a hash-ref. |
|
342
|
|
|
|
|
|
|
This hash-ref defines the necessary validation methods to assert for this |
|
343
|
|
|
|
|
|
|
parameter, and optionally a 'parse' and 'default' method. The idea is this: use the name |
|
344
|
|
|
|
|
|
|
of the validation method as the key, and the appropriate values for this |
|
345
|
|
|
|
|
|
|
method as the value of this key. For example, if a certain parameter, let's |
|
346
|
|
|
|
|
|
|
say 'subject', must be between 3 to 10 characters long, then your scheme |
|
347
|
|
|
|
|
|
|
will contain: |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
subject => { |
|
350
|
|
|
|
|
|
|
length_between => [3, 10] |
|
351
|
|
|
|
|
|
|
} |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
The 'subject' parameter's value (from the user input), along with both of |
|
354
|
|
|
|
|
|
|
the values defined above (3 and 10) will be passed to the C validation |
|
355
|
|
|
|
|
|
|
method. Now, suppose a certain subject sent to your app failed the |
|
356
|
|
|
|
|
|
|
C validation; then the rejects hash-ref described |
|
357
|
|
|
|
|
|
|
earlier will have something like this: |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
subject => ['length_between(3, 10)'] |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
Notice the values of the C validation method were added |
|
362
|
|
|
|
|
|
|
to the string, so you can easily know why the parameter failed the validation. |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
B Aside for the built-in validation methods |
|
365
|
|
|
|
|
|
|
that come with Brannigan, a custom validation method can be defined for |
|
366
|
|
|
|
|
|
|
each parameter. This is done by adding a 'validate' key to the parameter, |
|
367
|
|
|
|
|
|
|
and an anonymous subroutine as the value. As with built-in methods, the |
|
368
|
|
|
|
|
|
|
parameter's value will be automatically sent to this method. So, for |
|
369
|
|
|
|
|
|
|
example, if the subject parameter from above must start with the words |
|
370
|
|
|
|
|
|
|
'lorem ipsum', then we can define the subject parameter like so: |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
subject => { |
|
373
|
|
|
|
|
|
|
length_between => [3, 10], |
|
374
|
|
|
|
|
|
|
validate => sub { |
|
375
|
|
|
|
|
|
|
my $value = shift; |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
return $value =~ m/^lorem ipsum/ ? 1 : 0; |
|
378
|
|
|
|
|
|
|
} |
|
379
|
|
|
|
|
|
|
} |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
Custom validation methods, just like built-in ones, are expected to return |
|
382
|
|
|
|
|
|
|
a true value if the parameter passed the validation, or a false value |
|
383
|
|
|
|
|
|
|
otherwise. If a parameter failed a custom validation method, then 'validate' |
|
384
|
|
|
|
|
|
|
will be added to the list of failed validations for this parameter. So, |
|
385
|
|
|
|
|
|
|
in our 'subject' example, the rejects hash-ref will have something like this: |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
subject => ['length_between(3, 10)', 'validate'] |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
B For your convenience, Brannigan allows you to set default |
|
390
|
|
|
|
|
|
|
values for parameters that are not required (so, if you set a default |
|
391
|
|
|
|
|
|
|
value for a parameter, don't add the C validation method to |
|
392
|
|
|
|
|
|
|
it). There are two ways to add a default value: either directly, or |
|
393
|
|
|
|
|
|
|
through an anonymous subroutine (just like the custom validation method). |
|
394
|
|
|
|
|
|
|
For example, maybe we'd like the 'subject' parameter to have a default |
|
395
|
|
|
|
|
|
|
value of 'lorem ipsum dolor sit amet'. Then we can have the following definition: |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
subject => { |
|
398
|
|
|
|
|
|
|
length_between => [3, 10], |
|
399
|
|
|
|
|
|
|
validate => sub { |
|
400
|
|
|
|
|
|
|
my $value = shift; |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
return $value =~ m/^lorem ipsum/ ? 1 : 0; |
|
403
|
|
|
|
|
|
|
}, |
|
404
|
|
|
|
|
|
|
default => 'lorem ipsum dolor sit amet' |
|
405
|
|
|
|
|
|
|
} |
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
Alternatively, you can give a parameter a generated default value by using |
|
408
|
|
|
|
|
|
|
an anonymous subroutine, like so: |
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
subject => { |
|
411
|
|
|
|
|
|
|
length_between => [3, 10], |
|
412
|
|
|
|
|
|
|
validate => sub { |
|
413
|
|
|
|
|
|
|
my $value = shift; |
|
414
|
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
return $value =~ m/^lorem ipsum/ ? 1 : 0; |
|
416
|
|
|
|
|
|
|
}, |
|
417
|
|
|
|
|
|
|
default => sub { |
|
418
|
|
|
|
|
|
|
return int(rand(100000000)); |
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
} |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
Notice that default values are added to missing parameters only at the |
|
423
|
|
|
|
|
|
|
parsing stage (i.e. stage 3 - after the validation stage), so validation |
|
424
|
|
|
|
|
|
|
methods do not apply to default values. |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
B It is more than possible that the way input parameters are passed to your |
|
427
|
|
|
|
|
|
|
application will not be exactly the way you'll eventually use them. That's |
|
428
|
|
|
|
|
|
|
where parsing methods can come in handy. Brannigan doesn't have any |
|
429
|
|
|
|
|
|
|
built-in parsing methods (obviously), so you must create these by yourself, |
|
430
|
|
|
|
|
|
|
just like custom validation methods. All you need to do is add a 'parse' |
|
431
|
|
|
|
|
|
|
key to the parameter's definition, with an anonymous subroutine. This |
|
432
|
|
|
|
|
|
|
subroutine also receives the value of the parameter automatically, |
|
433
|
|
|
|
|
|
|
and is expected to return a hash-ref of key-value pairs. You will probably |
|
434
|
|
|
|
|
|
|
find it that most of the time this hash-ref will only contain one key-value |
|
435
|
|
|
|
|
|
|
pair, and that the key will probably just be the name of the parameter. But |
|
436
|
|
|
|
|
|
|
note that when a parse method exists, Brannigan makes absolutely no assumptions |
|
437
|
|
|
|
|
|
|
of what else to do with that parameter, so you must tell it exactly how to |
|
438
|
|
|
|
|
|
|
return it. After all parameters were parsed by Brannigan, all these little hash-refs are |
|
439
|
|
|
|
|
|
|
merged into one hash-ref that is returned to the caller. If a parse |
|
440
|
|
|
|
|
|
|
method doesn't exist for a paramter, Brannigan will simply add it "as-is" |
|
441
|
|
|
|
|
|
|
to the resulting hash-ref. Returning to our subject example (which we |
|
442
|
|
|
|
|
|
|
defined must start with 'lorem ipsum'), let's say we want to substitute |
|
443
|
|
|
|
|
|
|
'lorem ipsum' with 'effing awesome' before using this parameter. Then the |
|
444
|
|
|
|
|
|
|
subject definition will now look like this: |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
subject => { |
|
447
|
|
|
|
|
|
|
length_between => [3, 10], |
|
448
|
|
|
|
|
|
|
validate => sub { |
|
449
|
|
|
|
|
|
|
my $value = shift; |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
return $value =~ m/^lorem ipsum/ ? 1 : 0; |
|
452
|
|
|
|
|
|
|
}, |
|
453
|
|
|
|
|
|
|
default => 'lorem ipsum dolor sit amet', |
|
454
|
|
|
|
|
|
|
parse => sub { |
|
455
|
|
|
|
|
|
|
my $value = shift; |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
$value =~ s/^lorem ipsum/effing awesome/; |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
return { subject => $value }; |
|
460
|
|
|
|
|
|
|
} |
|
461
|
|
|
|
|
|
|
} |
|
462
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
If you're still not sure what happens when no parse method exists, then |
|
464
|
|
|
|
|
|
|
you can imagine Brannigan uses the following default parse method: |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
param => { |
|
467
|
|
|
|
|
|
|
parse => sub { |
|
468
|
|
|
|
|
|
|
my $value = shift; |
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
return { param => $value }; |
|
471
|
|
|
|
|
|
|
} |
|
472
|
|
|
|
|
|
|
} |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
B As of version 0.3, parameter names can also be regular expressions in the |
|
475
|
|
|
|
|
|
|
form C<'/regex/'>. Sometimes you cannot know the names of all parameters passed |
|
476
|
|
|
|
|
|
|
to your app. For example, you might have a dynamic web form which starts with |
|
477
|
|
|
|
|
|
|
a single field called 'url_1', but your app allows your visitors to dynamically |
|
478
|
|
|
|
|
|
|
add more fields, such as 'url_2', 'url_3', etc. Regular expressions are |
|
479
|
|
|
|
|
|
|
handy in such situations. Your parameter key can be C<'/^url_(\d+)$/'>, and |
|
480
|
|
|
|
|
|
|
all such fields will be matched. Regex params have a special feature: if |
|
481
|
|
|
|
|
|
|
your regex uses capturing, then captured values will be passed to the |
|
482
|
|
|
|
|
|
|
custom C and C methods (in their order) after the parameter's |
|
483
|
|
|
|
|
|
|
value. For example: |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
'/^url_(\d+)$/' => { |
|
486
|
|
|
|
|
|
|
validate => sub { |
|
487
|
|
|
|
|
|
|
my ($value, $num) = @_; |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
# $num has the value captured by (\d+) in the regex |
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
return $value =~ m!^http://! ? 1 : undef; |
|
492
|
|
|
|
|
|
|
}, |
|
493
|
|
|
|
|
|
|
parse => sub { |
|
494
|
|
|
|
|
|
|
my ($value, $num) = @_; |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
return { urls => { $num => $value } }; |
|
497
|
|
|
|
|
|
|
}, |
|
498
|
|
|
|
|
|
|
} |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
Please note that a regex must be defined with a starting and trailing |
|
501
|
|
|
|
|
|
|
slash, in single quotes, otherwise it won't work. It is also important to |
|
502
|
|
|
|
|
|
|
note what happens when a parameter matches a regex rule (or perhaps rules), |
|
503
|
|
|
|
|
|
|
and also has a direct reference in the scheme. For example, let's say |
|
504
|
|
|
|
|
|
|
we have the following rules in our scheme: |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
'/^sub(ject|headline)$/' => { |
|
507
|
|
|
|
|
|
|
required => 1, |
|
508
|
|
|
|
|
|
|
length_between => [3, 10], |
|
509
|
|
|
|
|
|
|
}, |
|
510
|
|
|
|
|
|
|
subject => { |
|
511
|
|
|
|
|
|
|
required => 0, |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
When validating and parsing the 'subject' parameter, Brannigan will |
|
515
|
|
|
|
|
|
|
automatically merge both of these references to the subject parameter, |
|
516
|
|
|
|
|
|
|
giving preference to the direct reference, so the actual structure on |
|
517
|
|
|
|
|
|
|
which the parameter will be validated is as follows: |
|
518
|
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
subject => { |
|
520
|
|
|
|
|
|
|
required => 0, |
|
521
|
|
|
|
|
|
|
length_between => [3, 10], |
|
522
|
|
|
|
|
|
|
} |
|
523
|
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
If your parameter matches more than one regex rule, they will all be |
|
525
|
|
|
|
|
|
|
merged, but there's no way (yet) to ensure in which order these regex |
|
526
|
|
|
|
|
|
|
rules will be merged. |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
B As previously stated, Brannigan can also validate and parse a little more |
|
529
|
|
|
|
|
|
|
complex data structures. So, your parameter no longer has to be just a |
|
530
|
|
|
|
|
|
|
string or a number, but maybe a hash-ref or an array-ref. In the first |
|
531
|
|
|
|
|
|
|
case, you tell Brannigan the paramter is a hash-ref by adding a 'hash' |
|
532
|
|
|
|
|
|
|
key with a true value, and a 'keys' key with a hash-ref which is just |
|
533
|
|
|
|
|
|
|
like the 'params' hash-ref. For example, suppose you're receiving a 'name' |
|
534
|
|
|
|
|
|
|
parameter from the user as a hash-ref containing first and last names. |
|
535
|
|
|
|
|
|
|
That's how the 'name' parameter might be defined: |
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
name => { |
|
538
|
|
|
|
|
|
|
hash => 1, |
|
539
|
|
|
|
|
|
|
required => 1, |
|
540
|
|
|
|
|
|
|
keys => { |
|
541
|
|
|
|
|
|
|
first_name => { |
|
542
|
|
|
|
|
|
|
length_between => [3, 10], |
|
543
|
|
|
|
|
|
|
}, |
|
544
|
|
|
|
|
|
|
last_name => { |
|
545
|
|
|
|
|
|
|
required => 1, |
|
546
|
|
|
|
|
|
|
min_length => 3, |
|
547
|
|
|
|
|
|
|
}, |
|
548
|
|
|
|
|
|
|
} |
|
549
|
|
|
|
|
|
|
} |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
What are we seeing here? We see that the 'name' parameter must be a |
|
552
|
|
|
|
|
|
|
hash-ref, that it's required, and that it has two keys: first_name, whose |
|
553
|
|
|
|
|
|
|
length must be between 3 to 10 if it's present, and last_name, which must |
|
554
|
|
|
|
|
|
|
be 3 characters or more, and must be present. |
|
555
|
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
An array parameter, on the other hand, is a little different. Similar to hashes, |
|
557
|
|
|
|
|
|
|
you define the parameter as an array-ref with the 'array' key with a true |
|
558
|
|
|
|
|
|
|
value, and a 'values' key. This key has a hash-ref of validation and parse |
|
559
|
|
|
|
|
|
|
methods that will be applied to EVERY value inside this array. For example, |
|
560
|
|
|
|
|
|
|
suppose you're receiving a 'pictures' parameter from the user as an array-ref |
|
561
|
|
|
|
|
|
|
containing URLs to pictures on the web. That's how the 'pictures' parameter |
|
562
|
|
|
|
|
|
|
might be defined: |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
pictures => { |
|
565
|
|
|
|
|
|
|
array => 1, |
|
566
|
|
|
|
|
|
|
length_between => [1, 5], |
|
567
|
|
|
|
|
|
|
values => { |
|
568
|
|
|
|
|
|
|
min_length => 3, |
|
569
|
|
|
|
|
|
|
validate => sub { |
|
570
|
|
|
|
|
|
|
my $value = shift; |
|
571
|
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
return $value =~ m!^http://! ? 1 : 0; |
|
573
|
|
|
|
|
|
|
}, |
|
574
|
|
|
|
|
|
|
}, |
|
575
|
|
|
|
|
|
|
} |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
What are we seeing this time? We see that the 'pictures' parameter must |
|
578
|
|
|
|
|
|
|
be an array, with no less than one item (i.e. value) and no more than five |
|
579
|
|
|
|
|
|
|
items (notice that we're using the same C method from |
|
580
|
|
|
|
|
|
|
before, but in the context of an array, it doesn't validate against |
|
581
|
|
|
|
|
|
|
character count but item count). We also see that every value in the |
|
582
|
|
|
|
|
|
|
'pictures' array must have a minimum length of three (this time it is |
|
583
|
|
|
|
|
|
|
characterwise), and must match 'http://' in its beginning. |
|
584
|
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
Since complex data structures are supported, you can define default values |
|
586
|
|
|
|
|
|
|
for parameters that aren't just strings or numbers (or methods), for example: |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
complex_param => { |
|
589
|
|
|
|
|
|
|
hash => 1, |
|
590
|
|
|
|
|
|
|
keys => { |
|
591
|
|
|
|
|
|
|
... |
|
592
|
|
|
|
|
|
|
}, |
|
593
|
|
|
|
|
|
|
default => { key1 => 'def1', key2 => 'def2' } |
|
594
|
|
|
|
|
|
|
} |
|
595
|
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
What Brannigan returns for such structures when they fail validations is |
|
597
|
|
|
|
|
|
|
a little different than before. Instead of an array-ref of failed validations, |
|
598
|
|
|
|
|
|
|
Brannigan will return a hash-ref. This hash-ref might contain a '_self' key |
|
599
|
|
|
|
|
|
|
with an array-ref of validations that failed specifically on the 'pictures' |
|
600
|
|
|
|
|
|
|
parameter (such as the 'required' validation for the 'name' parameter or |
|
601
|
|
|
|
|
|
|
the 'length_between' validation for the 'pictures' parameter), and/or |
|
602
|
|
|
|
|
|
|
keys for each value in these structures that failed validation. If it's a |
|
603
|
|
|
|
|
|
|
hash, then the key will simply be the name of that key. If it's an array, |
|
604
|
|
|
|
|
|
|
it will be its index. For example, let's say the 'first_name' key under |
|
605
|
|
|
|
|
|
|
the 'name' parameter failed the C validation method, |
|
606
|
|
|
|
|
|
|
and that the 'last_name' key was not present (and hence failed the |
|
607
|
|
|
|
|
|
|
C validation). Also, let's say the 'pictures' parameter failed |
|
608
|
|
|
|
|
|
|
the C validation (for the sake of the argument, let's |
|
609
|
|
|
|
|
|
|
say it had 6 items instead of the maximum allowed 5), and that the 2nd |
|
610
|
|
|
|
|
|
|
item failed the C validation, and the 6th item failed the |
|
611
|
|
|
|
|
|
|
custom validate method. Then our rejects hash-ref will have something like |
|
612
|
|
|
|
|
|
|
this: |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
name => { |
|
615
|
|
|
|
|
|
|
first_name => ['length_between(3, 10)'], |
|
616
|
|
|
|
|
|
|
last_name => ['required(1)'], |
|
617
|
|
|
|
|
|
|
}, |
|
618
|
|
|
|
|
|
|
pictures => { |
|
619
|
|
|
|
|
|
|
_self => ['length_between(1, 5)'], |
|
620
|
|
|
|
|
|
|
1 => ['min_length(3)'], |
|
621
|
|
|
|
|
|
|
5 => ['validate'], |
|
622
|
|
|
|
|
|
|
} |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
Notice the '_self' key under 'pictures' and that the numbering of the |
|
625
|
|
|
|
|
|
|
items of the 'pictures' array starts at zero (obviously). |
|
626
|
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
The beauty of Brannigan's data structure support is that it's recursive. |
|
628
|
|
|
|
|
|
|
So, it's not that a parameter can be a hash-ref and that's it. Every key |
|
629
|
|
|
|
|
|
|
in that hash-ref might be in itself a hash-ref, and every key in that |
|
630
|
|
|
|
|
|
|
hash-ref might be an array-ref, and every value in that array-ref might |
|
631
|
|
|
|
|
|
|
be a hash-ref... well, you get the idea. How might that look like? Well, |
|
632
|
|
|
|
|
|
|
just take a look at this: |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
pictures => { |
|
635
|
|
|
|
|
|
|
array => 1, |
|
636
|
|
|
|
|
|
|
values => { |
|
637
|
|
|
|
|
|
|
hash => 1, |
|
638
|
|
|
|
|
|
|
keys => { |
|
639
|
|
|
|
|
|
|
filename => { |
|
640
|
|
|
|
|
|
|
min_length => 5, |
|
641
|
|
|
|
|
|
|
}, |
|
642
|
|
|
|
|
|
|
source => { |
|
643
|
|
|
|
|
|
|
hash => 1, |
|
644
|
|
|
|
|
|
|
keys => { |
|
645
|
|
|
|
|
|
|
website => { |
|
646
|
|
|
|
|
|
|
validate => sub { ... }, |
|
647
|
|
|
|
|
|
|
}, |
|
648
|
|
|
|
|
|
|
license => { |
|
649
|
|
|
|
|
|
|
one_of => [qw/GPL FDL CC/], |
|
650
|
|
|
|
|
|
|
}, |
|
651
|
|
|
|
|
|
|
}, |
|
652
|
|
|
|
|
|
|
}, |
|
653
|
|
|
|
|
|
|
}, |
|
654
|
|
|
|
|
|
|
}, |
|
655
|
|
|
|
|
|
|
} |
|
656
|
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
So, we have a pictures array that every value in it is a hash-ref with a |
|
658
|
|
|
|
|
|
|
filename key and a source key whose value is a hash-ref with a website |
|
659
|
|
|
|
|
|
|
key and a license key. |
|
660
|
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
B The _all "parameter" can be used in a scheme to define rules that apply |
|
662
|
|
|
|
|
|
|
to all of the parameters in a certain level. This can either be used directly |
|
663
|
|
|
|
|
|
|
in the 'params' key of the scheme, or in the 'keys' key of a hash parameter. |
|
664
|
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
_all => { |
|
666
|
|
|
|
|
|
|
required => 1 |
|
667
|
|
|
|
|
|
|
}, |
|
668
|
|
|
|
|
|
|
subject => { |
|
669
|
|
|
|
|
|
|
length_between => [3, 255] |
|
670
|
|
|
|
|
|
|
}, |
|
671
|
|
|
|
|
|
|
text => { |
|
672
|
|
|
|
|
|
|
min_length => 10 |
|
673
|
|
|
|
|
|
|
} |
|
674
|
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
In the above example, both 'subject' and 'text' receive the C |
|
676
|
|
|
|
|
|
|
validation methods. |
|
677
|
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
=item * groups |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
Groups are very useful to parse parameters that are somehow related |
|
681
|
|
|
|
|
|
|
together. This key takes a hash-ref containing the names of the groups |
|
682
|
|
|
|
|
|
|
(names are irrelevant, they're more for you). Every group will also take |
|
683
|
|
|
|
|
|
|
a hash-ref, with a rule defining which parameters are members of this group, |
|
684
|
|
|
|
|
|
|
and a parse method to use with these parameters (just like our custom |
|
685
|
|
|
|
|
|
|
parse method from the 'params' key). This parse method will |
|
686
|
|
|
|
|
|
|
automatically receive the values of all the parameters in the group, in |
|
687
|
|
|
|
|
|
|
the order they were defined. |
|
688
|
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
For example, suppose our app gets a user's birth date by using three web |
|
690
|
|
|
|
|
|
|
form fields: day, month and year. And suppose our app saves this date |
|
691
|
|
|
|
|
|
|
in a database in the format 'YYYY-MM-DD'. Then we can define a group, |
|
692
|
|
|
|
|
|
|
say 'date', that automatically does this. For example: |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
date => { |
|
695
|
|
|
|
|
|
|
params => [qw/year month day/], |
|
696
|
|
|
|
|
|
|
parse => sub { |
|
697
|
|
|
|
|
|
|
my ($year, $month, $day) = @_; |
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
$month = '0'.$month if $month < 10; |
|
700
|
|
|
|
|
|
|
$day = '0'.$day if $day < 10; |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
return { date => $year.'-'.$month.'-'.$day }; |
|
703
|
|
|
|
|
|
|
}, |
|
704
|
|
|
|
|
|
|
} |
|
705
|
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
Alternative to the 'params' key, you can define a 'regex' key that takes |
|
707
|
|
|
|
|
|
|
a regex. All parameters whose name matches this regex will be parsed as |
|
708
|
|
|
|
|
|
|
a group. As oppose to using regexes in the 'params' key of the scheme, |
|
709
|
|
|
|
|
|
|
captured values in the regexes will not be passed to the parse method, |
|
710
|
|
|
|
|
|
|
only the values of the parameters will. Also, please note that there's no |
|
711
|
|
|
|
|
|
|
way to know in which order the values will be provided when using regexes |
|
712
|
|
|
|
|
|
|
for groups. |
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
For example, let's say our app receives one or more URLs (to whatever |
|
715
|
|
|
|
|
|
|
type of resource) in the input, in parameters named 'url_1', 'url_2', |
|
716
|
|
|
|
|
|
|
'url_3' and so on, and that there's no limit on the number of such |
|
717
|
|
|
|
|
|
|
parameters we can receive. Now, suppose we want to create an array |
|
718
|
|
|
|
|
|
|
of all of these URLs, possibly to push it to a database. Then we can |
|
719
|
|
|
|
|
|
|
create a 'urls' group such as this: |
|
720
|
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
urls => { |
|
722
|
|
|
|
|
|
|
regex => '/^url_(\d+)$/', |
|
723
|
|
|
|
|
|
|
parse => sub { |
|
724
|
|
|
|
|
|
|
my @urls = @_; |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
return { urls => \@urls }; |
|
727
|
|
|
|
|
|
|
} |
|
728
|
|
|
|
|
|
|
} |
|
729
|
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=back |
|
731
|
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
=head2 BUILT-IN VALIDATION METHODS |
|
733
|
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
As mentioned earlier, Brannigan comes with a set of built-in validation |
|
735
|
|
|
|
|
|
|
methods which are most common and useful everywhere. For a list of all |
|
736
|
|
|
|
|
|
|
validation methods provided by Brannigan, check L. |
|
737
|
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
=head2 CROSS-SCHEME CUSTOM VALIDATION METHODS |
|
739
|
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
Custom C methods are nice, but when you want to use the same |
|
741
|
|
|
|
|
|
|
custom validation method in different places inside your scheme, or more |
|
742
|
|
|
|
|
|
|
likely in different schemes altogether, repeating the definition of each |
|
743
|
|
|
|
|
|
|
custom method in every place you want to use it is not very comfortable. |
|
744
|
|
|
|
|
|
|
Brannigan provides a simple mechanism to create custom, named validation |
|
745
|
|
|
|
|
|
|
methods that can be used across schemes as if they were internal methods. |
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
The process is simple: when creating your schemes, give the names of the |
|
748
|
|
|
|
|
|
|
custom validation methods and their relevant supplement values as with |
|
749
|
|
|
|
|
|
|
every built-in validation method. For example, suppose we want to create |
|
750
|
|
|
|
|
|
|
a custom validation method named 'forbid_words', that makes sure a certain |
|
751
|
|
|
|
|
|
|
text does not contain any words we don't like it to contain. Suppose this |
|
752
|
|
|
|
|
|
|
will be true for a parameter named 'text'. Then we define 'text' like so: |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
text => { |
|
755
|
|
|
|
|
|
|
required => 1, |
|
756
|
|
|
|
|
|
|
forbid_words => ['curse_word', 'bad_word', 'ugly_word'], |
|
757
|
|
|
|
|
|
|
} |
|
758
|
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
As you can see, we have provided the name of our custom method, and the words |
|
760
|
|
|
|
|
|
|
we want to forbid. Now we need to actually create this C |
|
761
|
|
|
|
|
|
|
method. We do this after we've created our Brannigan object, by using the |
|
762
|
|
|
|
|
|
|
C method, as in this example: |
|
763
|
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
$b->custom_validation('forbid_words', sub { |
|
765
|
|
|
|
|
|
|
my ($value, @forbidden) = @_; |
|
766
|
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
foreach (@forbidden) { |
|
768
|
|
|
|
|
|
|
return 0 if $value =~ m/$_/; |
|
769
|
|
|
|
|
|
|
} |
|
770
|
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
return 1; |
|
772
|
|
|
|
|
|
|
}); |
|
773
|
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
We give the C method the name of our new method, and |
|
775
|
|
|
|
|
|
|
an anonymous subroutine, just like in "local" custom validation methods. |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
And that's it. Now we can use the C validation method |
|
778
|
|
|
|
|
|
|
across our schemes. If a paremeter failed our custom method, it will be |
|
779
|
|
|
|
|
|
|
added to the rejects like built-in methods. So, if 'text' failed our new |
|
780
|
|
|
|
|
|
|
method, our rejects hash-ref will contain: |
|
781
|
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
text => [ 'forbid_words(curse_word, bad_word, ugly_word)' ] |
|
783
|
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
As an added bonus, you can use this mechanism to override Brannigan's |
|
785
|
|
|
|
|
|
|
built-in validations. Just give the name of the validation method you wish |
|
786
|
|
|
|
|
|
|
to override, along with the new code for this method. Brannigan gives |
|
787
|
|
|
|
|
|
|
precedence to cross-scheme custom validations, so your method will be used |
|
788
|
|
|
|
|
|
|
instead of the internal one. |
|
789
|
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
=head2 NOTES ABOUT PARSE METHODS |
|
791
|
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
As stated earlier, your C methods are expected to return a hash-ref |
|
793
|
|
|
|
|
|
|
of key-value pairs. Brannigan collects all of these key-value pairs |
|
794
|
|
|
|
|
|
|
and merges them into one big hash-ref (along with all the non-parsed |
|
795
|
|
|
|
|
|
|
parameters). |
|
796
|
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
Brannigan actually allows you to have your C methods be two-leveled. |
|
798
|
|
|
|
|
|
|
This means that a value in a key-value pair in itself can be a hash-ref |
|
799
|
|
|
|
|
|
|
or an array-ref. This allows you to use the same key in different places, |
|
800
|
|
|
|
|
|
|
and Brannigan will automatically aggregate all of these occurrences, just like |
|
801
|
|
|
|
|
|
|
in the first level. So, for example, suppose your scheme has a regex |
|
802
|
|
|
|
|
|
|
rule that matches parameters like 'tag_en' and 'tag_he'. Your parse |
|
803
|
|
|
|
|
|
|
method might return something like C<< { tags => { en => 'an english tag' } } >> |
|
804
|
|
|
|
|
|
|
when it matches the 'tag_en' parameter, and something like |
|
805
|
|
|
|
|
|
|
C<< { tags => { he => 'a hebrew tag' } } >> when it matches the 'tag_he' |
|
806
|
|
|
|
|
|
|
parameter. The resulting hash-ref from the process method will thus |
|
807
|
|
|
|
|
|
|
include C<< { tags => { en => 'an english tag', he => 'a hebrew tag' } } >>. |
|
808
|
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
Similarly, let's say your scheme has a regex rule that matches parameters |
|
810
|
|
|
|
|
|
|
like 'url_1', 'url_2', etc. Your parse method might return something like |
|
811
|
|
|
|
|
|
|
C<< { urls => [$url_1] } >> for 'url_1' and C<< { urls => [$url_2] } >> |
|
812
|
|
|
|
|
|
|
for 'url_2'. The resulting hash-ref in this case will be |
|
813
|
|
|
|
|
|
|
C<< { urls => [$url_1, $url_2] } >>. |
|
814
|
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
Take note however that only two-levels are supported, so don't go crazy |
|
816
|
|
|
|
|
|
|
with this. |
|
817
|
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
=head2 SO HOW DO I PROCESS INPUT? |
|
819
|
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
OK, so we have created our scheme(s), we know how schemes look and work, |
|
821
|
|
|
|
|
|
|
but what now? |
|
822
|
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
Well, that's the easy part. All you need to do is call the C |
|
824
|
|
|
|
|
|
|
method on the Brannigan object, passing it the name of the scheme to |
|
825
|
|
|
|
|
|
|
enforce and a hash-ref of the input parameters/data structure. This method |
|
826
|
|
|
|
|
|
|
will return a hash-ref back, with all the parameters after parsing. If any |
|
827
|
|
|
|
|
|
|
validations failed, this hash-ref will have a '_rejects' key, with the |
|
828
|
|
|
|
|
|
|
rejects hash-ref described earlier. Remember: Brannigan doesn't raise |
|
829
|
|
|
|
|
|
|
any errors. It's your job to decide what to do, and that's a good thing. |
|
830
|
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
Example schemes, input and output can be seen in L. |
|
832
|
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
|
834
|
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
=head2 new( \%scheme | @schemes ) |
|
836
|
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
Creates a new instance of Brannigan, with the provided scheme(s) (see |
|
838
|
|
|
|
|
|
|
L"HOW SCHEMES LOOK"> for more info on schemes). |
|
839
|
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
=cut |
|
841
|
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
sub new { |
|
843
|
2
|
|
|
2
|
1
|
137760
|
my $class = shift; |
|
844
|
|
|
|
|
|
|
|
|
845
|
2
|
|
|
|
|
6
|
return bless { map { $_->{name} => $_ } @_ }, $class; |
|
|
6
|
|
|
|
|
16
|
|
|
846
|
|
|
|
|
|
|
} |
|
847
|
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
=head1 OBJECT METHODS |
|
849
|
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
=head2 add_scheme( \%scheme | @schemes ) |
|
851
|
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
Adds one or more schemes to the object. Every scheme hash-ref should have |
|
853
|
|
|
|
|
|
|
a C key with the name of the scheme. Existing schemes will be overridden. |
|
854
|
|
|
|
|
|
|
Returns the object itself for chainability. |
|
855
|
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
=cut |
|
857
|
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
sub add_scheme { |
|
859
|
1
|
|
|
1
|
1
|
381
|
my $self = shift; |
|
860
|
|
|
|
|
|
|
|
|
861
|
1
|
|
|
|
|
3
|
foreach (@_) { |
|
862
|
1
|
|
|
|
|
3
|
$self->{$_->{name}} = $_; |
|
863
|
|
|
|
|
|
|
} |
|
864
|
|
|
|
|
|
|
|
|
865
|
1
|
|
|
|
|
2
|
return $self; |
|
866
|
|
|
|
|
|
|
} |
|
867
|
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
=head2 process( $scheme, \%params ) |
|
869
|
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
Receives the name of a scheme and a hash-ref of input parameters (or a data |
|
871
|
|
|
|
|
|
|
structure), and validates and parses these paremeters according to the |
|
872
|
|
|
|
|
|
|
scheme (see L"HOW SCHEMES LOOK"> for detailed information about this process). |
|
873
|
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
Returns a hash-ref of parsed parameters according to the parsing scheme, |
|
875
|
|
|
|
|
|
|
possibly containing a list of failed validations for each parameter. |
|
876
|
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
Actual processing is done by L. |
|
878
|
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
=head2 process( \%scheme, \%params ) |
|
880
|
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
Same as above, but takes a scheme hash-ref instead of a name hash-ref. That |
|
882
|
|
|
|
|
|
|
basically gives you a functional interface for Brannigan, so you don't have |
|
883
|
|
|
|
|
|
|
to go through the regular object oriented interface. The only downsides to this |
|
884
|
|
|
|
|
|
|
are that you cannot define custom validations using the C |
|
885
|
|
|
|
|
|
|
method (defined below) and that your scheme must be standalone (it cannot inherit |
|
886
|
|
|
|
|
|
|
from other schemes). Note that when directly passing a scheme, you don't need |
|
887
|
|
|
|
|
|
|
to give the scheme a name. |
|
888
|
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
=cut |
|
890
|
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
sub process { |
|
892
|
9
|
100
|
|
9
|
1
|
11946
|
if (ref $_[0] eq 'Brannigan') { |
|
893
|
8
|
|
|
|
|
14
|
my ($self, $scheme, $params) = @_; |
|
894
|
|
|
|
|
|
|
|
|
895
|
8
|
50
|
33
|
|
|
76
|
return unless $scheme && $params && ref $params eq 'HASH' && $self->{$scheme}; |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
896
|
8
|
|
|
|
|
26
|
$self->_build_tree($scheme, $self->{validations})->process($params); |
|
897
|
|
|
|
|
|
|
} else { |
|
898
|
1
|
|
|
|
|
4
|
Brannigan::Tree->new($_[0])->process($_[1]); |
|
899
|
|
|
|
|
|
|
} |
|
900
|
|
|
|
|
|
|
} |
|
901
|
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
=head2 custom_validation( $name, $code ) |
|
903
|
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
Receives the name of a custom validation method (C<$name>), and a reference to an |
|
905
|
|
|
|
|
|
|
anonymous subroutine (C<$code>), and creates a new validation method with |
|
906
|
|
|
|
|
|
|
that name and code, to be used across schemes in the Brannigan object as |
|
907
|
|
|
|
|
|
|
if they were internal methods. You can even use this to override internal |
|
908
|
|
|
|
|
|
|
validation methods, just give the name of the method you want to override |
|
909
|
|
|
|
|
|
|
and the new code. |
|
910
|
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
=cut |
|
912
|
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
sub custom_validation { |
|
914
|
2
|
|
|
2
|
1
|
661
|
my ($self, $name, $code) = @_; |
|
915
|
|
|
|
|
|
|
|
|
916
|
2
|
50
|
33
|
|
|
23
|
return unless $name && $code && ref $code eq 'CODE'; |
|
|
|
|
33
|
|
|
|
|
|
917
|
|
|
|
|
|
|
|
|
918
|
2
|
|
|
|
|
4
|
$self->{validations}->{$name} = $code; |
|
919
|
|
|
|
|
|
|
} |
|
920
|
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
############################ |
|
922
|
|
|
|
|
|
|
##### INTERNAL METHODS ##### |
|
923
|
|
|
|
|
|
|
############################ |
|
924
|
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
# _build_tree( $scheme, [ \%custom_validations ] ) |
|
926
|
|
|
|
|
|
|
# ------------------------------------------------ |
|
927
|
|
|
|
|
|
|
# Builds the final "tree" of validations and parsing methods to be performed |
|
928
|
|
|
|
|
|
|
# on the parameters hash during processing. Optionally receives a hash-ref |
|
929
|
|
|
|
|
|
|
# of cross-scheme custom validation methods defined in the Brannigan object |
|
930
|
|
|
|
|
|
|
# (see L"CROSS-SCHEME CUSTOM VALIDATION METHODS"> for more info). |
|
931
|
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
sub _build_tree { |
|
933
|
13
|
|
|
13
|
|
18
|
my ($self, $scheme, $customs) = @_; |
|
934
|
|
|
|
|
|
|
|
|
935
|
13
|
|
|
|
|
13
|
my @trees; |
|
936
|
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
# get a list of all schemes to inherit from |
|
938
|
13
|
100
|
66
|
|
|
62
|
my @schemes = $self->{$scheme}->{inherits_from} && ref $self->{$scheme}->{inherits_from} eq 'ARRAY' ? @{$self->{$scheme}->{inherits_from}} : $self->{$scheme}->{inherits_from} ? ($self->{$scheme}->{inherits_from}) : (); |
|
|
0
|
50
|
|
|
|
0
|
|
|
939
|
|
|
|
|
|
|
|
|
940
|
13
|
|
|
|
|
26
|
foreach (@schemes) { |
|
941
|
5
|
50
|
|
|
|
13
|
next unless $self->{$_}; |
|
942
|
5
|
|
|
|
|
20
|
push(@trees, $self->_build_tree($_)); |
|
943
|
|
|
|
|
|
|
} |
|
944
|
|
|
|
|
|
|
|
|
945
|
13
|
|
|
|
|
47
|
my $tree = Brannigan::Tree->new(@trees, $self->{$scheme}); |
|
946
|
13
|
|
|
|
|
20
|
$tree->{_custom_validations} = $customs; |
|
947
|
|
|
|
|
|
|
|
|
948
|
13
|
|
|
|
|
53
|
return $tree; |
|
949
|
|
|
|
|
|
|
} |
|
950
|
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
=head1 CAVEATS |
|
952
|
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
Brannigan is still in an early stage. Currently, no checks are made to |
|
954
|
|
|
|
|
|
|
validate the schemes built, so if you incorrectly define your schemes, |
|
955
|
|
|
|
|
|
|
Brannigan will not croak and processing will probably fail. Also, there |
|
956
|
|
|
|
|
|
|
is no support yet for recursive inheritance or any crazy inheritance |
|
957
|
|
|
|
|
|
|
situation. While deep inheritance is supported, it hasn't been tested |
|
958
|
|
|
|
|
|
|
extensively. Also bugs are popping up as I go along, so keep in mind that |
|
959
|
|
|
|
|
|
|
you might encounter bugs (and please report any if that happens). |
|
960
|
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
=head1 IDEAS FOR THE FUTURE |
|
962
|
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
The following list of ideas may or may not be implemented in future |
|
964
|
|
|
|
|
|
|
versions of Brannigan: |
|
965
|
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
=over |
|
967
|
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
=item * Cross-scheme custom parsing methods |
|
969
|
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
Add an option to define custom parse methods in the Brannigan object that |
|
971
|
|
|
|
|
|
|
can be used in the schemes as if they were built-in methods (cross-scheme |
|
972
|
|
|
|
|
|
|
custom validations are already supported, next up is parse methods). |
|
973
|
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
=item * Support for third-party validation methods |
|
975
|
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
Add support for loading validation methods defined in third-party modules |
|
977
|
|
|
|
|
|
|
(written like L) and using them in schemes as if they |
|
978
|
|
|
|
|
|
|
were built-in methods. |
|
979
|
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
=item * Validate schemes by yourself |
|
981
|
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
Have Brannigan use itself to validate the schemes it receives from the |
|
983
|
|
|
|
|
|
|
developers (i.e. users of this module). |
|
984
|
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
=item * Support loading schemes from JSON/XML |
|
986
|
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
Allow loading schemes from JSON/XML files or any other source. Does that |
|
988
|
|
|
|
|
|
|
make any sense? |
|
989
|
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
=item * Something to aid rejects traversal |
|
991
|
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
Find something that would make traversal of the rejects list easier or |
|
993
|
|
|
|
|
|
|
whatever. Plus, printing the name of the validation method and its supplement |
|
994
|
|
|
|
|
|
|
values in the rejects list isn't always a good idea. For example, if we |
|
995
|
|
|
|
|
|
|
use the C validation method with a big list of say 100 options, |
|
996
|
|
|
|
|
|
|
our rejects list will contain all these 100 options, and that's not nice. |
|
997
|
|
|
|
|
|
|
So, think about something there. |
|
998
|
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
=back |
|
1000
|
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
1002
|
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
L, L, L. |
|
1004
|
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
=head1 AUTHOR |
|
1006
|
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
Ido Perlmuter, C<< >> |
|
1008
|
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
=head1 BUGS |
|
1010
|
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through |
|
1012
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you'll |
|
1013
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
|
1014
|
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
=head1 SUPPORT |
|
1016
|
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
|
1018
|
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
perldoc Brannigan |
|
1020
|
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
You can also look for information at: |
|
1022
|
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
=over 4 |
|
1024
|
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
|
1026
|
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
L |
|
1028
|
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
|
1030
|
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
L |
|
1032
|
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
=item * CPAN Ratings |
|
1034
|
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
L |
|
1036
|
|
|
|
|
|
|
|
|
1037
|
|
|
|
|
|
|
=item * Search CPAN |
|
1038
|
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
L |
|
1040
|
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
=back |
|
1042
|
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
|
1044
|
|
|
|
|
|
|
|
|
1045
|
|
|
|
|
|
|
Brannigan was inspired by L (Al Newkirk) and the "Ketchup" jQuery |
|
1046
|
|
|
|
|
|
|
validation plugin (L). |
|
1047
|
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
|
1049
|
|
|
|
|
|
|
|
|
1050
|
|
|
|
|
|
|
Copyright 2017 Ido Perlmuter |
|
1051
|
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); |
|
1053
|
|
|
|
|
|
|
you may not use this file except in compliance with the License. |
|
1054
|
|
|
|
|
|
|
You may obtain a copy of the License at |
|
1055
|
|
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0 |
|
1057
|
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software |
|
1059
|
|
|
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS, |
|
1060
|
|
|
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
1061
|
|
|
|
|
|
|
See the License for the specific language governing permissions and |
|
1062
|
|
|
|
|
|
|
limitations under the License. |
|
1063
|
|
|
|
|
|
|
|
|
1064
|
|
|
|
|
|
|
=cut |
|
1065
|
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
1; |
|
1067
|
|
|
|
|
|
|
__END__ |