1Validation::Class::CookUbsoeork(C3o)ntributed Perl DocumVeanltiadtaitoinon::Class::Cookbook(3)
2
3
4
6 Validation::Class::Cookbook - Recipes for Validation::Class
7
9 version 7.900057
10
12 The instructions contained in this documentation are also relevant for
13 configuring any class derived from Validation::Class. The validation
14 logic that follows is not specific to a particular use-case.
15
16 Parameter Handling
17 There are three ways to declare parameters you wish to have validated.
18 The first and most common approach is to supply the target parameters
19 to the validation class constructor:
20
21 use Validation::Class::Simple;
22
23 my $rules = Validation::Class::Simple->new(params => $params);
24
25 All input parameters are wrapped by the Validation::Class::Params
26 container which provides generic functionality for managing hashes.
27 Additionally you can declare parameters by using the params object
28 directly:
29
30 use Validation::Class::Simple;
31
32 my $rules = Validation::Class::Simple->new;
33
34 $rules->params->clear;
35
36 $rules->params->add(user => 'admin', pass => 's3cret');
37
38 printf "%s parameters were submitted", $rules->params->count;
39
40 Finally, any parameter which has corresponding validation rules that
41 has been declared in a validation class derived from Validation::Class
42 will have an accessor which can be used directly or as an argument to
43 the constructor:
44
45 package MyApp::Person;
46
47 use Validation::Class;
48
49 field 'name' => {
50 required => 1
51 };
52
53 package main;
54
55 my $rules = MyApp::Person->new(name => 'Egon Spangler');
56
57 $rules->name('Egon Spengler');
58
59 Validation Rules
60 Validation::Class comes with a complete standard set of validation
61 rules which allows you to easily describe the constraints and
62 operations that need to be performed per parameter.
63
64 Validation rules are referred to as fields, fields are named after the
65 parameters they expect to be matched against. A field is also a hashref
66 whose keys are called directives which correspond with the names of
67 classes in the directives namespace, and whose values are arguments
68 which control how directives carry-out their operations.
69
70 use Validation::Class::Simple;
71
72 my $rules = Validation::Class::Simple->new;
73
74 $rules->fields->clear;
75
76 $rules->fields->add(name => { required => 1, max_length => 255 });
77
78 Fields can be specified as an argument to the class constructor, or
79 managed directly using the Validation::Class::Fields container. Every
80 field is wrapped by the Validation::Class::Field container which
81 provides accessors for all core directives. Directives can be found
82 under the directives namespace, e.g. the required directive refers to
83 Validation::Class::Directive::Required. Please see
84 Validation::Class::Directives for a list of all core directives.
85
86 Flow Control
87 A good data validation tool is not simply checking input against
88 constraints, its also providing a means to easily handle different and
89 often complex data input scenarios.
90
91 The queue method allows you to designate and defer fields to be
92 validated. It also allows you to set fields that must be validated
93 regardless of what has been passed to the validate method. Additionally
94 it allows you to conditionally specify constraints:
95
96 use Validation::Class::Simple;
97
98 my $rules = Validation::Class::Simple->new;
99
100 $rules->queue('name'); # always validate the name parameter
101
102 $rules->queue('email', 'email2') if $rules->param('change_email');
103 $rules->queue('login', 'login2') if $rules->param('change_login');
104
105 # validate name
106 # validate email and email confirmation if change_email is true
107 # validate login and login confirmation if change_login is true
108
109 $rules->validate('password'); # additionally, validate password
110 $rules->clear_queue; # reset the queue when finished
111
112 Akin to the queue method is the stash method. At-times it is necessary
113 to break out of the box in order to design constraints that fit your
114 particular use-case. The stash method allows you to share arbitrary
115 objects with routines used by validation classes.
116
117 use Validation::Class::Simple;
118
119 my $rules = Validation::Class::Simple->new;
120
121 $rules->fields->add(
122 email => {
123 # email validation relies on a stashed object
124 validation => sub {
125 my ($self, $field, $params) = @_;
126 return 0 if ! my $dbo = $self->stash('dbo');
127 return 0 if ! $dbo->email_exists($field->value);
128 return 1;
129 }
130 }
131 );
132
133 # elsewhere in the program
134 $rules->stash(dbo => $database_object); # stash the database object
135
136 Error Handling
137 When validation fails, and it will, you need to be able to report what
138 failed and why. Validation::Class give you complete control over error
139 handling and messages. Errors can exist at the field-level and class-
140 level (errors not specific to a particular field). All errors are
141 wrapped in a Validation::Class::Errors container.
142
143 use Validation::Class::Simple;
144
145 my $rules = Validation::Class::Simple->new;
146
147 # print a comma separated list of class and field errors
148 print $rules->errors_to_string unless $rules->validate;
149
150 # print a newline separated list of class and field errors
151 print $rules->errors_to_string("\n") unless $rules->validate;
152
153 # print a comma separated list of class and upper-cased field errors
154 print $rules->errors_to_string(undef, sub{ ucfirst lc shift })
155
156 # print total number of errors at the class and field levels
157 print "Found %s errors", $rules->error_count;
158
159 # return a hashref of fields with errors
160 my $errors = $rules->error_fields;
161
162 # get errors for specific fields only
163 my @errors = $rules->get_errors('email', 'login');
164
165 Input Filtering
166 Filtering data is one fringe benefits of a good data validation
167 framework. The process is also known as scrubbing or sanitizing data.
168 The process ensures that the data being passed to the business logic
169 will be clean and consistent.
170
171 Filtering data is not as simple and straight-forward as it may seem
172 which is why it is necessary to think-through your applications
173 interactions before implementation.
174
175 Filtering is the process of applying transformations to the incoming
176 data. The problem with filtering is that it permanently alters the data
177 input and in the event of a failure could report inconsistent error
178 messages:
179
180 use Validation::Class::Simple;
181
182 my $rules = Validation::Class::Simple->new;
183
184 $rules->fields->add(
185 # even if the input is submitted as lowercase it will fail
186 # the filter is run as a pre-process by default
187 username => {
188 filters => ['uppercase'],
189 validation => sub {
190 return 0 if $_[1]->value =~ /[A-Z]/;
191 return 1;
192 }
193 }
194 );
195
196 When designing a system to filter data, it is always necessary to
197 differentiate pre-processing filters from post-processing filters.
198 Validation::Class provides a filtering directive which designates
199 certain fields to run filters in post-processing:
200
201 $rules->fields->add(
202 # if the input is submitted as lowercase it will pass
203 username => {
204 filters => ['uppercase'],
205 filtering => 'post',
206 validation => sub {
207 return 0 if $_[1]->value =~ /[A-Z]/;
208 return 1;
209 }
210 }
211 );
212
213 Handling Failures
214 A data validation framework exists to handle failures, it is its main
215 function and purpose, in-fact, the difference between a validation
216 framework and a type-constraint system is how it responds to errors.
217
218 When a type-constraint system finds an error it raises an exception.
219 Exception handling is the process of responding to the occurrence,
220 during computation, of exceptions (anomalous or exceptional
221 situations).
222
223 Typically the errors reported when an exception is raised includes a
224 dump of the program's state up until the point of the exception which
225 is apropos as exceptions are unexpected.
226
227 A data validation framework can also be thought-of as a type system but
228 one that is specifically designed to expect input errors and report
229 user-friendly error messages.
230
231 Validation::Class may encounter exceptions as programmers defined
232 validation rules which remain mutable. Validation::Class provides
233 attributes for determining how the validation engine reacts to
234 exceptions and validation failures:
235
236 use Validation::Class::Simple;
237
238 my $rules = Validation::Class::Simple->new(
239 ignore_failure => 1, # do not throw errors if validation fails
240 ignore_unknown => 0, # throw errors if unknown directives are found
241 report_failure => 0, # register errors if "method validations" fail
242 report_unknown => 0, # register errors if "unknown directives" are found
243 );
244
245 Data Validation
246 Once your fields are defined and you have your parameter rules
247 configured as desired you will like use the validate method to perform
248 all required operations. The validation operations occur in the
249 following order:
250
251 normalization (resetting fields, clearing existing errors, etc)
252 pre-processing (applying filters, etc)
253 validation (processing directives, etc)
254 post-processing (applying filters, etc)
255
256 What gets validated is determined by the state and arguments passed to
257 the validate method. The validate method determines what to validate in
258 the following order:
259
260 checks the validation queue for fields
261 checks arguments for regular expression objects and adds matching fields
262 validates fields with matching parameters if no fields are specified
263 validates all fields if no parameters are specified
264
265 It is also important to under what it means to declare a field as being
266 required. A field is a data validation rule matching a specific
267 parameter, A required field simply means that if-and-when a parameter
268 is submitted, it is required to have a value. It does not mean that a
269 field is always required to be validated.
270
271 Occasionally you may need to temporarily set a field as required or
272 not-required for a specific validation operation. This requirement is
273 referred to as the toggle function. The toggle function is enacted by
274 prefixing a field name with a plus or minus sign (+|-) when passed to
275 the validate method:
276
277 use Validation::Class::Simple;
278
279 my $rules = Validation::Class::Simple->new(fields => {...});
280
281 # meaning, email is always required to have a value
282 # however password and password2 can be submitted as empty strings
283 # but if password and password2 have values they will be validated
284 $rules->validate('+email', '-password', '-password2');
285
286 Here are a few examples and explanations of using the validate method:
287
288 use Validation::Class::Simple;
289
290 my $rules = Validation::Class::Simple->new(fields => {...});
291
292 unless ($rules->validate) {
293 # validate all fields with matching parameters
294 }
295
296 unless ($rules->validate) {
297 # validate all fields because no parameters were submitted
298 }
299
300 unless ($rules->validate(qr/^email/)) {
301 # validate all fields whose name being with email
302 # e.g. email, email2, email_update
303 }
304
305 unless ($rules->validate('login', 'password')) {
306 # validate the login and password specifically
307 # regardless of what parameters have been set
308 }
309
310 unless ($rules->validate({ user => 'login', pass => 'password' })) {
311 # map user and pass parameters to the appropriate fields as aliases
312 # and validate login and password fields using the aliases
313 }
314
316 This recipe displays the usage of keywords to configure a validation
317 class.
318
319 Problem
320 You want to know how to use the Validation::Class keywords to define a
321 validation class.
322
323 Solution
324 Use the keywords exported by Validation::Class to register validation
325 rules, templates, profiles, methods and filters.
326
327 Discussion
328 Your validation class can be thought of as your
329 data-model/input-firewall. The benefits this approach provides might
330 require you to change your perspective on parameter handling and
331 workflow. Typically when designing an application we tend to name
332 parameters arbitrarily and validate the same data at various stages
333 during a program's execution in various places in the application
334 stack. This approach is inefficient and prone to bugs and security
335 problems.
336
337 To get the most out of Validation::Class you should consider each
338 parameter hitting your application (individually) as a transmission
339 fitting a very specific criteria, yes, like a field in a data model.
340
341 Your validation rules will act as filters which will reject or accept
342 and format the transmission for use within your application, yes,
343 almost exactly like a firewall.
344
345 A validation class is defined as follows:
346
347 package MyApp::Person;
348
349 use Validation::Class;
350
351 # a validation rule template
352
353 mixin 'basic' => {
354 required => 1,
355 min_length => 1,
356 max_length => 255,
357 filters => ['lowercase', 'alphanumeric']
358 };
359
360 # a validation rule
361
362 field 'login' => {
363 mixin => 'basic',
364 label => 'user login',
365 error => 'login invalid',
366 validation => sub {
367
368 my ($self, $field, $params) = @_;
369
370 return $field->value eq 'admin' ? 1 : 0;
371
372 }
373 };
374
375 # a validation rule
376
377 field 'password' => {
378 mixin => 'basic',
379 label => 'user password',
380 error => 'password invalid',
381 validation => sub {
382
383 my ($self, $field, $params) = @_;
384
385 return $field->value eq 'pass' ? 1 : 0;
386
387 }
388 };
389
390 # a validation profile
391
392 profile 'registration' => sub {
393
394 my ($self, @args) = @_;
395
396 return $self->validate(qw(login password));
397
398 };
399
400 # an auto-validating method
401
402 method 'registers' => {
403
404 input => 'registration',
405 using => sub {
406
407 my ($self, @args) = shift;
408
409 # ... do something
410
411 }
412
413 };
414
415 1;
416
417 The fields defined will be used to validate the specified input
418 parameters. You specify the input parameters at/after instantiation,
419 parameters should take the form of a hashref of key/value pairs passed
420 to the params attribute, or attribute/value pairs. The following is an
421 example on using your validate class to validate input in various
422 scenarios:
423
424 # web app
425 package MyApp;
426
427 use MyApp::User;
428 use Misc::WebAppFramework;
429
430 get '/auth' => sub {
431
432 # get user input parameters
433 my $params = shift;
434
435 # initialize validation class and set input parameters
436 my $user = MyApp::User->new(params => $params);
437
438 unless ($user->registers) {
439
440 # print errors to browser unless validation is successful
441 return $user->errors_to_string;
442
443 }
444
445 return 'you have authenticated';
446
447 };
448
449 A field can have aliases, parameter names that if detected will be
450 mapped to the parameter name matching the field definition. Multiple
451 fields cannot have the same alias defined, such a configuration would
452 result in a runtime error.
453
454 use MyApp::User;
455
456 my $user = MyApp::User->new(params => $params);
457
458 unless ($user->validate) {
459
460 return $input->errors_to_string;
461
462 }
463
464 package MyApp::User;
465
466 field 'email' => {
467 ...,
468 alias => [
469 'emails',
470 'email_address',
471 'email_addresses'
472 ]
473
474 };
475
476 package main;
477
478 use MyApp::User;
479
480 my $user = MyApp::User->new(params => { email_address => '...' });
481
482 unless ($user->validate('email'){
483
484 return $user->errors_to_string;
485
486 }
487
488 # valid because email_address is an alias on the email field
489
491 This recipe displays methods of configuring your validation class to
492 cooperate with your pre-existing classes and object-system.
493
494 Problem
495 You want to know how to configure Validation::Class to cooperate with
496 pre-existing classes or object systems like Mo, Moo, Mouse, and Moose.
497
498 Solution
499 Use a combination of techniques such as excluding keywords exported by
500 Validation::Class and utilizing the initialize_validator method.
501
502 Discussion
503 Validation::Class will atuomatically inject a method name
504 `initialize_validator` if a pre-existing `new` method is dicovered
505 which allows you to execute certain validation class normalization
506 routines. When, the initialize_validator method is called is not
507 important, it is only important that it is called before your object is
508 used as a validation class object.
509
510 A validation class using Moose as an object system could be configured
511 as follows:
512
513 package MyApp::Person;
514
515 use Moose;
516 use Validation::Class qw(fld mxn);
517
518 # the order in which these frameworks are used is important
519 # loading Moose first ensures that the Moose::Object constructor
520 # has precendence
521
522 sub BUILD {
523
524 my ($self, $params) = @_;
525
526 $self->initialize_validator($params);
527
528 }
529
530 mxn 'basic' => {
531 required => 1,
532 min_length => 1,
533 max_length => 255,
534 filters => ['lowercase', 'alphanumeric']
535 };
536
537 fld 'login' => {
538 mixin => 'basic',
539 label => 'user login',
540 error => 'login invalid'
541 };
542
543 fld 'password' => {
544 mixin => 'basic',
545 label => 'user password',
546 error => 'password invalid'
547 };
548
549 has 'profile' => (
550 is => 'rw',
551 isa => 'MyApp::Person::Profile'
552 );
553
554 1;
555
557 This recipe describes how to define filtering in your validation class
558 rules.
559
560 Problem
561 You want to know how to define filters to sanatize and transform your
562 data although some transformations may need to occur after a successful
563 validation.
564
565 Solution
566 Data validation rules can be configured to apply filtering as both pre-
567 and-post processing operations.
568
569 Discussion
570 Validation::Class supports pre/post filtering but is configured to pre-
571 filter incoming data by default. This means that based upon the
572 filtering options supplied within the individual fields, filtering will
573 happen before validation (technically at instantiation and again just
574 before validation). As expected, this is configurable via the filtering
575 attribute.
576
577 A WORD OF CAUTION: Validation::Class is configured to pre-filter
578 incoming data which boosts application security and is best used with
579 passive filtering (e.g. converting character case - filtering which
580 only alters the input in predictable ways), versus aggressive filtering
581 (e.g. formatting a telephone number) which completely and permanently
582 changes the incoming data ... so much so that if the validation still
583 fails ... errors that are reported may not match the data that was
584 submitted.
585
586 If you're sure you'd rather employ aggressive filtering, I suggest
587 setting the filtering attribute to 'post' for post-filtering or setting
588 it to null and applying the filters manually by calling the
589 apply_filters() method.
590
592 This recipe describes how to separate validation logic between multiple
593 related classes.
594
595 Problem
596 You want to know how to define multiple validation classes and pass
597 input data and input parameters between them.
598
599 Solution
600 Use classes as validation domains, as a space to logically group
601 related validation rules, then use built-in methods to have multiple
602 validation classes validate in-concert.
603
604 Discussion
605 For larger applications where a single validation class might become
606 cluttered and inefficient, Validation::Class comes equipped to help you
607 separate your validation rules into separate classes.
608
609 The idea is that you'll end up with a main validation class (most
610 likely empty) that will simply serve as your point of entry into your
611 relative (child) classes. The following is an example of this:
612
613 package MyApp::User;
614
615 use Validation::Class;
616
617 field name => { ... };
618 field email => { ... };
619 field login => { ... };
620 field password => { ... };
621
622 package MyApp::Profile;
623
624 use Validation::Class;
625
626 field age => { ... };
627 field sex => { ... };
628 field birthday => { ... };
629
630 package MyApp;
631
632 use Validation::Class;
633
634 set classes => 1;
635
636 package main;
637
638 my $input = MyApp->new(params => $params);
639
640 my $user = $input->class('user');
641
642 my $profile = $input->class('profile');
643
644 1;
645
647 This recipe describes how to peek under the curtain and leverage the
648 framework for other purposes.
649
650 Problem
651 You want to know how to use your data validation classes to perform
652 other tasks programatically (e.g. generate documentation, etc).
653
654 Solution
655 By using the prototype class associated with your validation class you
656 can introspect it's configuration and perform additional tasks
657 programatically.
658
659 Discussion
660 Most users will never venture beyond the public API, but powerful
661 abilities await the more adventureous developer and this section was
662 written specifically for you. To assist you on along your journey, let
663 me explain exactly what happens when you define and instantiate a
664 validation class.
665
666 Classes are defined using keywords (field, mixin, filter, etc) which
667 register rule definitions on a cached class profile (of-sorts)
668 associated with the class which is being constructed. On instantiation,
669 the cached class profile is cloned then merged with any arguments
670 provided to the constructor, this means that even in a persistent
671 environment the original class profile is never altered.
672
673 To begin introspection, simply look into the attributes attached to the
674 class prototype, e.g. fields, mixins, filters, etc., the following
675 examples will give you an idea of how to use introspection to extend
676 your application code using Validation::Class.
677
678 Please keep in mind that Validation::Class is likely to already have
679 most of the functionalty you would need to introspect your codebase.
680 The following is an introspection design template that will work in
681 most cases:
682
683 package MyApp::Introspect;
684
685 use Validation::Class;
686
687 load classes => 'MyApp'; # load MyApp and all child classes
688
689 sub per_class {
690
691 my ($self, $code) = @_;
692
693 my %relatives = %{$self->proto->settings->{relatives}};
694
695 while (my($parent, $children) = each(%relatives)) {
696
697 while (my($nickname, $namespace) = each(%{$children})) {
698
699 # do something with each class
700 $code->($namespace);
701
702 }
703
704 }
705
706 }
707
708 sub per_field_per_class {
709
710 my ($self, $code) = @_;
711
712 $self->per_class(sub{
713
714 my $namespace = shift;
715
716 my $class = $namespace->new;
717
718 foreach my $field ($class->fields->values) {
719
720 # do something with each field in each class
721 $code->($class, $class->fields->{$field});
722
723 }
724
725 });
726
727 }
728
730 This recipe describes how to generate JSON objects which can be used to
731 validate user input in the web-browser (client-side).
732
733 Problem
734 You want to know how to make the most out of your data validation rules
735 by making your configuration available as JSON objects in the browser.
736
737 Solution
738 Using introspection, you can leverage the prototype class associated
739 with your validation class to generate JSON objects based on your
740 validation class configuration.
741
742 Discussion
743 In the context of a web-application, it is often best to perform the
744 initial input validation on the client (web-browser) before submitting
745 data to the server for further validation and processing. In the
746 following code we will generate javascript objects that match our
747 Validation::Class data models which we will then use with some js
748 library to validate form data, etc.
749
750 ... example validation class
751
752 package MyApp::Model;
753
754 use Validation::Class;
755 use Validation::Class::Plugin::JavascriptObjects;
756
757 mxn scrub => {
758 filters => ['trim', 'strip']
759 };
760
761 fld login => {
762 mixin => 'scrub'
763 email => 1,
764 required => 1,
765 alias => 'user',
766 };
767
768 fld password => {
769 mixin => 'scrub',
770 required => 1,
771 alias => 'pass',
772 min_length => 5,
773 min_symbols => 1,
774 min_alpha => 1,
775 min_digits => 1
776 };
777
778 ... in your webapp controller
779
780 get '/js/model' => sub {
781
782 my $model = MyApp::Model->new;
783
784 # generate the JS object
785 my $data = $model->plugin('javascript_objects')->render(
786 namespace => 'validate.model',
787 fields => [qw/email password/],
788 include => [qw/required email minlength maxlength/]
789 )
790
791 return print $data;
792
793 };
794
795 The output of the /js/model route should generate a javascript object
796 which looks similar to the following:
797
798 var validate = {
799 "model" : {
800 "email" : {
801 "minlength" : 3,
802 "required" : 1,
803 "maxlength" : 255
804 },
805 "password" : {
806 "minlength" : 5,
807 "required" : 1,
808 "maxlength" : 255
809 }
810 }
811 };
812
813 If its not obvious yet, we can now easily use this generated javascript
814 API with jQuery (or other client-side library) to validate form data,
815 etc.
816
817 <!DOCTYPE html>
818 <html>
819 <head>
820 <title>AUTH REQUIRED</title>
821 <script type="text/javascript" src="/js/jquery.js"></script>
822 <script type="text/javascript" src="/js/jquery.validate.js"></script>
823 <script type="text/javascript" src="/js/model"></script>
824 <script type="text/javascript">
825 $(document).ready(function() {
826 $("#form").validate({rules:validate.model});
827 });
828 </script>
829 </head>
830 <body>
831 <div>[% input.errors_to_string %]</div>
832 <form id="form" autocomplete="off" method="post" action="/">
833 <fieldset>
834 <legend><h2><strong>Halt</strong>, who goes there?</h2></legend>
835 <label for="email">Email</label><br/>
836 <input id="email" name="email" value="" /><br/>
837 <label for="password">Password</label><br/>
838 <input id="password" name="password" type="password" /><br/>
839 <br/><input type="submit" value="Submit" />
840 </fieldset>
841 </form>
842 </body>
843 </html>
844
846 Al Newkirk <anewkirk@ana.io>
847
849 This software is copyright (c) 2011 by Al Newkirk.
850
851 This is free software; you can redistribute it and/or modify it under
852 the same terms as the Perl 5 programming language system itself.
853
854
855
856perl v5.32.0 2020-07-28 Validation::Class::Cookbook(3)