-###----------------------------------------------------------------###
-
-1;
-
-
-__END__
-
-=head1 SYNOPSIS
-
- use CGI::Ex::Validate;
-
- ### THE SHORT
-
- my $errobj = CGI::Ex::Validate->new->validate($form, $val_hash);
-
- ### THE LONG
-
- my $form = CGI->new;
- # OR #
- my $form = CGI::Ex->new; # OR CGI::Ex->get_form;
- # OR #
- my $form = {key1 => 'val1', key2 => 'val2'};
-
-
- ### simplest
- my $val_hash = {
- username => {
- required => 1,
- max_len => 30,
- field => 'username',
- # field is optional in this case - will use key name
- },
- email => {
- required => 1,
- max_len => 100,
- },
- email2 => {
- validate_if => 'email',
- equals => 'email',
- },
- };
-
- ### ordered
- my $val_hash = {
- 'group order' => [qw(username email email2)],
- username => {required => 1, max_len => 30},
- email => ...,
- email2 => ...,
- };
-
- ### ordered again
- my $val_hash = {
- 'group fields' => [{
- field => 'username', # field is not optional in this case
- required => 1,
- max_len => 30,
- }, {
- field => 'email',
- required => 1,
- max_len => 100,
- }, {
- field => 'email2',
- validate_if => 'email',
- equals => 'email',
- }],
- };
-
-
- my $vob = CGI::Ex::Validate->new;
- my $errobj = $vob->validate($form, $val_hash);
- # OR #
- my $errobj = $vob->validate($form, "/somefile/somewhere.val"); # import config using yaml file
- # OR #
- my $errobj = $vob->validate($form, "/somefile/somewhere.pl"); # import config using perl file
- # OR #
- my $errobj = $vob->validate($form, "--- # a yaml document\n"); # import config using yaml str
-
-
- if ($errobj) {
- my $error_heading = $errobj->as_string; # OR "$errobj";
- my $error_list = $errobj->as_array; # ordered list of what when wrong
- my $error_hash = $errobj->as_hash; # hash of arrayrefs of errors
- } else {
- # form passed validation
- }
-
- ### will add an error for any form key not found in $val_hash
- my $vob = CGI::Ex::Validate->new({no_extra_keys => 1});
- my $errobj = $vob->validate($form, $val_hash);
-
-=head1 DESCRIPTION
-
-CGI::Ex::Validate is one of many validation modules. It aims to have
-all of the basic data validation functions, avoid adding all of the
-millions of possible types, while still giving the capability for the
-developer to add their own types.
-
-It also has full support for providing the same validation in javascript.
-It provides methods for attaching the javascript to existing forms.
-
-As opposed to other kitchen sync validation modules, CGI::Ex::Validate
-offers the simple types of validation, and makes it easy to add your
-own custom types.
-
-=head1 METHODS
-
-=over 4
-
-=item C<new>
-
-Used to instantiate the object. Arguments are either a hash, or hashref,
-or nothing at all. Keys of the hash become the keys of the object.
-
-=item C<get_validation>
-
-Given a filename or YAML string will return perl hash. If more than one
-group is contained in the file, it will return an arrayref of hashrefs.
-
- my $ref = $self->get_validation($file);
-
-=item C<get_validation_keys>
-
-Given a filename or YAML string or a validation hashref, will return all
-of the possible keys found in the validation hash. This can be used to
-check to see if extra items have been passed to validate. If a second
-argument contains a form hash is passed, get_validation_keys will only
-return the keys of groups that were validated.
-
- my $key_hashref = $self->get_validation_keys($val_hash);
-
-The values of the hash are the names of the fields.
-
-=item C<validate>
-
-Arguments are a form hashref or cgi object, a validation hashref or
-filename, and an optional what_was_validated arrayref (discussed
-further later on). If a CGI object is passed, CGI::Ex::get_form will
-be called on that object to turn it into a hashref. If a filename is
-given for the validation, get_validation will be called on that
-filename. If the what_was_validated_arrayref is passed - it will be
-populated (pushed) with the field hashes that were actually validated
-(anything that was skipped because of validate_if will not be in the
-array).
-
-If the form passes validation, validate will return undef. If it
-fails validation, it will return a CGI::Ex::Validate::Error object.
-If the 'raise_error' option has been set, validate will die
-with a CGI::Ex::validate::Error object as the value.
-
- my $err_obj = $self->validate($form, $val_hash);
-
- # OR #
-
- $self->{raise_error} = 1; # can also be listed in the val_hash
- eval { $self->validate($form, $val_hash) };
- if ($@) { my $err_obj = $@; }
-
-=item C<generate_js>
-
-Works with CGI::Ex::JSONDump, but can also work with JSON or YAML
-if desired (see L<JSON> or L<YAML>).
-
-Takes a validation hash, a form name, and an optional javascript uri
-path and returns Javascript that can be embedded on a page and will
-perform identical validations as the server side. The form name must be
-the name of the form that the validation will act upon - the name is
-used to register an onsubmit function. The javascript uri path is
-used to embed the locations of javascript source files included
-with the CGI::Ex distribution.
-
-The javascript uri path is highly dependent upon the server
-configuration and therefore must be configured manually. It may be
-passed to generate_js, or it may be specified in $JS_URI_PATH. There
-are two files included with this module that are needed -
-CGI/Ex/yaml_load.js and CGI/Ex/validate.js. When generating the js
-code, generate_js will look in $JS_URI_PATH_YAML and
-$JS_URI_PATH_VALIDATE. If either of these are not set, generate_js
-will default to "$JS_URI_PATH/CGI/Ex/yaml_load.js" and
-"$JS_URI_PATH/CGI/Ex/validate.js" (Note: yaml_load is only needed
-if the flags no_jsondump and no_json have been set).
-
- $self->generate_js($val_hash, 'my_form', "/cgi-bin/js")
-
- # would generate something like the following...
-
- <script src="/cgi-bin/js/CGI/Ex/validate.js"></script>
- ... more js follows ...
-
- $CGI::Ex::Validate::JS_URI_PATH = "/stock/js";
- $self->generate_js($val_hash, 'my_form')
-
- # would generate something like the following...
-
- <script src="/stock/js/CGI/Ex/validate.js"></script>
- ... more js follows ...
-
-Referencing yaml_load.js and validate.js can be done in any of
-several ways. They can be copied to or symlinked to a fixed location
-in the server's html directory. They can also be printed out by a cgi.
-The method C<-E<gt>print_js> has been provided in CGI::Ex for printing
-js files found in the perl hierarchy. See L<CGI::Ex> for more details.
-The $JS_URI_PATH of "/cgi-bin/js" could contain the following:
-
- #!/usr/bin/perl -w
-
- use strict;
- use CGI::Ex;
-
- ### path_info should contain something like /CGI/Ex/yaml_load.js
- my $info = $ENV{PATH_INFO} || '';
- die "Invalid path" if $info !~ m|^(/\w+)+.js$|;
- $info =~ s|^/+||;
-
- CGI::Ex->new->print_js($info);
- exit;
-
-The print_js method in CGI::Ex is designed to cache the javascript in
-the browser (caching is suggested as they are medium sized files).
-
-=item C<-E<gt>cgix>
-
-Returns a CGI::Ex object. Used internally.
-
-=back
-
-=head1 VALIDATION HASH
-
-The validation hash may be passed as a hashref or as a
-filename, or as a YAML document string. If it is a filename, it will
-be translated into a hash using the %EXT_HANDLER for the extension on
-the file. If there is no extension, it will use $DEFAULT_EXT as a
-default. CGI::Ex::Conf is used for the reading of files.
-
-Keys matching the regex m/^(general|group)\s+(\w+)$/ are reserved and
-are counted as GROUP OPTIONS. Other keys (if any, should be field names
-that need validation).
-
-If the GROUP OPTION 'group validate_if' is set, the validation will only
-be validated if the conditions are met. If 'group validate_if' is not
-specified, then the validation will proceed.
-
-Each of the items listed in the validation will be validated. The
-validation order is determined in one of three ways:
-
-=over 4
-
-=item Specify 'group fields' arrayref.
-
- # order will be (username, password, 'm/\w+_foo/', somethingelse)
- {
- 'group title' => "User Information",
- 'group fields' => [
- {field => 'username', required => 1},
- {field => 'password', required => 1},
- {field => 'm/\w+_foo/', required => 1},
- ],
- somethingelse => {required => 1},
- }
-
-=item Specify 'group order' arrayref.
-
- # order will be (username, password, 'm/\w+_foo/', somethingelse)
- {
- 'group title' => "User Information",
- 'group order' => [qw(username password), 'm/\w+_foo/'],
- username => {required => 1},
- password => {required => 1},
- 'm/\w+_foo/' => {required => 1},
- somethingelse => {required => 1},
- }
-
-=item Do nothing - use sorted order.
-
- # order will be ('m/\w+_foo/', password, somethingelse, username)
- {
- 'group title' => "User Information",
- username => {required => 1},
- password => {required => 1},
- 'm/\w+_foo/' => {required => 1},
- somethingelse => {required => 1},
- }
-
-=back
-
-Optionally the 'group fields' or the 'group order' may contain the
-word 'OR' as a special keyword. If the item preceding 'OR' fails
-validation the item after 'OR' will be tested instead. If the item
-preceding 'OR' passes validation the item after 'OR' will not be
-tested.
-
- 'group order' => [qw(zip OR postalcode state OR region)],
-
-Each individual field validation hashref will operate on the field contained
-in the 'field' key. This key may also be a regular expression in the
-form of 'm/somepattern/'. If a regular expression is used, all keys
-matching that pattern will be validated. If the field key is
-not specified, the key from the top level hash will be used.
-
- foobar => { # "foobar" is not used as key because field is specified
- field => 'real_key_name',
- required => 1,
- },
- real_key_name2 => {
- required => 1,
- },
-
-Each of the individual field validation hashrefs should contain the
-types listed in VALIDATION TYPES.
-
-=head1 VALIDATION TYPES
-
-This section lists the available validation types. Multiple instances
-of the same type may be used for some validation types by adding a
-number to the type (ie match, match2, match232, match_94). Multiple
-instances are validated in sorted order. Types that allow multiple
-values are:
-
- compare
- custom
- equals
- match
- max_in_set
- min_in_set
- replace
- required_if
- sql
- type
- validate_if
-
-=over 4
-
-=item C<validate_if>
-
-If validate_if is specified, the field will only be validated
-if the conditions are met. Works in JS.
-
- validate_if => {field => 'name', required => 1, max_len => 30}
- # Will only validate if the field "name" is present and is less than 30 chars.
-
- validate_if => 'name',
- # SAME as
- validate_if => {field => 'name', required => 1},
-
- validate_if => '! name',
- # SAME as
- validate_if => {field => 'name', max_in_set => '0 of name'},
-
- validate_if => {field => 'country', compare => "eq US"},
- # only if country's value is equal to US
-
- validate_if => {field => 'country', compare => "ne US"},
- # if country doesn't equal US
-
- validate_if => {field => 'password', match => 'm/^md5\([a-z0-9]{20}\)$/'},
- # if password looks like md5(12345678901234567890)
-
- {
- field => 'm/^(\w+)_pass/',
- validate_if => '$1_user',
- required => 1,
- }
- # will validate foo_pass only if foo_user was present.
-
-The validate_if may also contain an arrayref of validation items. So that
-multiple checks can be run. They will be run in order. validate_if will
-return true only if all options returned true.
-
- validate_if => ['email', 'phone', 'fax']
-
-Optionally, if validate_if is an arrayref, it may contain the word
-'OR' as a special keyword. If the item preceding 'OR' fails validation
-the item after 'OR' will be tested instead. If the item preceding 'OR'
-passes validation the item after 'OR' will not be tested.
-
- validate_if => [qw(zip OR postalcode)],
-
-=item C<required_if>
-
-Requires the form field if the condition is satisfied. The conditions
-available are the same as for validate_if. This is somewhat the same
-as saying:
-
- validate_if => 'some_condition',
- required => 1
-
- required_if => 'some_condition',
-
-If a regex is used for the field name, the required_if
-field will have any match patterns swapped in.
-
- {
- field => 'm/^(\w+)_pass/',
- required_if => '$1_user',
- }
-
-This example would require the "foobar_pass" field to be set
-if the "foobar_user" field was passed.
-
-=item C<required>
-
-Requires the form field to have some value. If the field is not present,
-no other checks will be run.
-
-=item C<min_values> and C<max_values>
-
-Allows for specifying the maximum number of form elements passed.
-max_values defaults to 1 (You must explicitly set it higher
-to allow more than one item by any given name).
-
-=item C<min_in_set> and C<max_in_set>
-
-Somewhat like min_values and max_values except that you specify the
-fields that participate in the count. Also - entries that are not
-defined or do not have length are not counted. An optional "of" can
-be placed after the number for human readability.
-
- min_in_set => "2 of foo bar baz",
- # two of the fields foo, bar or baz must be set
- # same as
- min_in_set => "2 foo bar baz",
- # same as
- min_in_set => "2 OF foo bar baz",
-
- validate_if => {field => 'whatever', max_in_set => '0 of whatever'},
- # only run validation if there were zero occurrences of whatever
-
-=item C<enum>
-
-Allows for checking whether an item matches a set of options. In perl
-the value may be passed as an arrayref. In the conf or in perl the
-value may be passed of the options joined with ||.
-
- {
- field => 'password_type',
- enum => 'plaintext||crypt||md5', # OR enum => [qw(plaintext crypt md5)],
- }
-
-=item C<equals>
-
-Allows for comparison of two form elements. Can have an optional !.
-
- {
- field => 'password',
- equals => 'password_verify',
- },
- {
- field => 'domain1',
- equals => '!domain2', # make sure the fields are not the same
- }
-
-=item C<min_len and max_len>
-
-Allows for check on the length of fields
-
- {
- field => 'site',
- min_len => 4,
- max_len => 100,
- }
-
-=item C<match>
-
-Allows for regular expression comparison. Multiple matches may
-be concatenated with ||. Available in JS.
-
- {
- field => 'my_ip',
- match => 'm/^\d{1,3}(\.\d{1,3})3$/',
- match_2 => '!/^0\./ || !/^192\./',
- }
-
-=item C<compare>
-
-Allows for custom comparisons. Available types are
->, <, >=, <=, !=, ==, gt, lt, ge, le, ne, and eq. Comparisons
-also work in the JS.
-
- {
- field => 'my_number',
- match => 'm/^\d+$/',
- compare1 => '> 100',
- compare2 => '< 255',
- compare3 => '!= 150',
- }
-
-=item C<sql>
-
-SQL query based - not available in JS. The database handle will be looked
-for in the value $self->{dbhs}->{foo} if sql_db_type is set to 'foo',
-otherwise it will default to $self->{dbh}. If $self->{dbhs}->{foo} or
-$self->{dbh} is a coderef - they will be called and should return a dbh.
-
- {
- field => 'username',
- sql => 'SELECT COUNT(*) FROM users WHERE username = ?',
- sql_error_if => 1, # default is 1 - set to 0 to negate result
- # sql_db_type => 'foo', # will look for a dbh under $self->{dbhs}->{foo}
- }
-
-=item C<custom>
-
-Custom value - not available in JS. Allows for extra programming types.
-May be either a boolean value predetermined before calling validate, or may be
-a coderef that will be called during validation. If coderef is called, it will
-be passed the field name, the form value for that name, and a reference to the
-field validation hash. If the custom type returns false the element fails
-validation and an error is added.
-
- {
- field => 'username',
- custom => sub {
- my ($key, $val, $type, $field_val_hash) = @_;
- # do something here
- return 0;
- },