File Coverage

blib/lib/Brannigan.pm
Criterion Covered Total %
statement 38 39 97.4
branch 8 12 66.6
condition 7 18 38.8
subroutine 8 8 100.0
pod 4 4 100.0
total 65 81 80.2


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