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