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__ |