1Validation::Class::CookUbsoeork(C3o)ntributed Perl DocumVeanltiadtaitoinon::Class::Cookbook(3)
2
3
4

NAME

6       Validation::Class::Cookbook - Recipes for Validation::Class
7

VERSION

9       version 7.900057
10

GUIDED TOUR

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

BUILDING CLASSES

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

INTEGRATING CLASSES AND FRAMEWORKS

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

FILTERING DATA

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

DELEGATING VALIDATION

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

INTROSPECT AND EXTEND

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

CLIENT-SIDE VALIDATION

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

AUTHOR

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)
Impressum