• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

lib/H06-Feb-2017-2,9991,301

t/H06-Feb-2017-1,098971

ChangesH A D06-Feb-20172.8 KiB6653

INSTALLH A D06-Feb-20171.1 KiB4426

LICENSEH A D06-Feb-201711.2 KiB208172

MANIFESTH A D06-Feb-2017376 2322

MANIFEST.SKIPH A D06-Feb-2017232 1918

META.jsonH A D06-Feb-20171.3 KiB5351

META.ymlH A D06-Feb-2017793 2827

Makefile.PLH A D06-Feb-20171.1 KiB5039

READMEH A D06-Feb-201743.3 KiB963791

README.mdH A D06-Feb-201741.2 KiB959763

SIGNATUREH A D06-Feb-20171.9 KiB4437

dist.iniH A D06-Feb-2017149 86

README

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

README.md

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