File Coverage

blib/lib/FormValidator/Simple.pm
Criterion Covered Total %
statement 105 110 95.4
branch 20 26 76.9
condition 9 19 47.3
subroutine 23 23 100.0
pod 1 8 12.5
total 158 186 84.9


line stmt bran cond sub pod time code
1             package FormValidator::Simple;
2 22     22   1053661 use strict;
  22         53  
  22         954  
3 22     22   125 use base qw/Class::Accessor::Fast Class::Data::Inheritable Class::Data::Accessor/;
  22         39  
  22         22938  
4 22     22   239782 use Class::Inspector;
  22         115266  
  22         967  
5 22     22   25233 use UNIVERSAL::require;
  22         60138  
  22         953  
6 22     22   806 use Scalar::Util qw/blessed/;
  22         46  
  22         2596  
7 22     22   21720 use FormValidator::Simple::Results;
  22         89  
  22         219  
8 22     22   869 use FormValidator::Simple::Exception;
  22         47  
  22         352  
9 22     22   15806 use FormValidator::Simple::Data;
  22         64  
  22         290  
10 22     22   14287 use FormValidator::Simple::Profile;
  22         130  
  22         300  
11 22     22   717 use FormValidator::Simple::Validator;
  22         46  
  22         120  
12 22     22   563 use FormValidator::Simple::Constants;
  22         46  
  22         1311  
13 22     22   15990 use FormValidator::Simple::Messages;
  22         78  
  22         260  
14              
15             our $VERSION = '0.29';
16              
17             __PACKAGE__->mk_classaccessors(qw/data prof results/);
18             __PACKAGE__->mk_classaccessor( messages => FormValidator::Simple::Messages->new );
19              
20             sub import {
21 22     22   233 my $class = shift;
22 22         301 foreach my $plugin (@_) {
23 2         4 my $plugin_class;
24 2 100       12 if ($plugin =~ /^\+(.*)/) {
25 1         4 $plugin_class = $1;
26             } else {
27 1         5 $plugin_class = "FormValidator::Simple::Plugin::$plugin";
28             }
29 2         10 $class->load_plugin($plugin_class);
30             }
31             }
32              
33             sub load_plugin {
34 2     2 0 6 my ($proto, $plugin) = @_;
35 2   33     14 my $class = ref $proto || $proto;
36 2 50       18 unless (Class::Inspector->installed($plugin)) {
37 0         0 FormValidator::Simple::Exception->throw(
38             qq/$plugin isn't installed./
39             );
40             }
41 2         206 $plugin->require;
42 2 50       392 if ($@) {
43 0         0 FormValidator::Simple::Exception->throw(
44             qq/Couldn't require "$plugin", "$@"./
45             );
46             }
47             {
48 22     22   7759 no strict 'refs';
  22         51  
  22         31626  
  2         4  
49 2         43 push @FormValidator::Simple::Validator::ISA, $plugin;
50             }
51             }
52              
53             sub set_option {
54 56     56 0 112 my $class = shift;
55 56         369 while ( my ($key, $val) = splice @_, 0, 2 ) {
56 3         31 FormValidator::Simple::Validator->options->{$key} = $val;
57             }
58             }
59              
60             sub set_messages {
61 4     4 0 662 my ($proto, $file) = @_;
62 4   66     30 my $class = ref $proto || $proto;
63 4 100       30 if (blessed $proto) {
64 1         8 $proto->messages(FormValidator::Simple::Messages->new)->load($file);
65 1 50       4 if ($proto->results) {
66 1         9 $proto->results->message($proto->messages);
67             } else {
68 0         0 $proto->results( FormValidator::Simple::Results->new(
69             messages => $proto->messages,
70             ) );
71             }
72             } else {
73 3         26 $class->messages->load($file);
74             }
75             }
76              
77             sub set_message_decode_from {
78 1     1 0 13 my ($self, $decode_from) = @_;
79 1         7 $self->messages->decode_from($decode_from);
80             }
81              
82             sub set_message_format {
83 2     2 0 8655 my ($proto, $format) = @_;
84 2   50     13 $format ||= '';
85 2         14 $proto->messages->format($format);
86             }
87              
88             sub new {
89 55     55 1 5408 my $proto = shift;
90 55   33     283 my $class = ref $proto || $proto;
91 55         181 my $self = bless { }, $class;
92 55         197 $self->_init(@_);
93 55         448 return $self;
94             }
95              
96             sub _init {
97 55     55   126 my ($self, @args) = @_;
98 55         107 my $class = ref $self;
99 55         231 $class->set_option(@args);
100 55         285 $self->results( FormValidator::Simple::Results->new(
101             messages => $self->messages,
102             ) );
103             }
104              
105             sub set_invalid {
106 1     1 0 3 my ($self, $name, $type) = @_;
107 1 50       6 unless (ref $self) {
108 0         0 FormValidator::Simple::Exception->throw(
109             qq/set_invalid is instance method./
110             );
111             }
112 1 50 33     9 unless ($name && $type) {
113 0         0 FormValidator::Simple::Exception->throw(
114             qq/set_invalid needs two arguments./
115             );
116             }
117 1         6 $self->results->set_result($name, $type, FALSE);
118             }
119              
120             sub check {
121 55     55 0 2370706 my ($proto, $input, $prof, $options) = @_;
122 55   50     374 $options ||= {};
123 55 100       453 my $self = blessed $proto ? $proto : $proto->new(%$options);
124              
125 55         384 my $data = FormValidator::Simple::Data->new($input);
126 55         555 my $prof_setting = FormValidator::Simple::Profile->new($prof);
127              
128 55         207 my $profile_iterator = $prof_setting->iterator;
129              
130             PROFILE:
131 55         308 while ( my $profile = $profile_iterator->next ) {
132              
133 101         290 my $name = $profile->name;
134 101         574 my $keys = $profile->keys;
135 101         547 my $constraints = $profile->constraints;
136              
137 101         642 my $params = $data->param($keys);
138              
139 101         316 $self->results->register($name);
140              
141 101 100       2294 $self->results->record($name)->data( @$params == 1 ? $params->[0] : '');
142              
143 101         2005 my $constraint_iterator = $constraints->iterator;
144 101 100       305 if ( scalar @$params == 1 ) {
145 79 100 66     488 unless ( defined $params->[0] && $params->[0] ne '' ) {
146 10 50       38 if ( $constraints->needs_blank_check ) {
147 10         80 $self->results->record($name)->is_blank( TRUE );
148             }
149 10         127 next PROFILE;
150             }
151             }
152              
153             CONSTRAINT:
154 91         350 while ( my $constraint = $constraint_iterator->next ) {
155              
156 109         374 my ($result, $data) = $constraint->check($params);
157              
158 109         336 $self->results->set_result($name, $constraint->name, $result);
159              
160 109 100       2462 $self->results->record($name)->data($data) if $data;
161             }
162              
163             }
164 55         671 return $self->results;
165             }
166              
167             1;
168              
169             =head1 NAME
170              
171             FormValidator::Simple - validation with simple chains of constraints
172              
173             =head1 SYNOPSIS
174              
175             my $query = CGI->new;
176             $query->param( param1 => 'ABCD' );
177             $query->param( param2 => 12345 );
178             $query->param( mail1 => 'lyo.kato@gmail.com' );
179             $query->param( mail2 => 'lyo.kato@gmail.com' );
180             $query->param( year => 2005 );
181             $query->param( month => 11 );
182             $query->param( day => 27 );
183              
184             my $result = FormValidator::Simple->check( $query => [
185             param1 => ['NOT_BLANK', 'ASCII', ['LENGTH', 2, 5]],
186             param2 => ['NOT_BLANK', 'INT' ],
187             mail1 => ['NOT_BLANK', 'EMAIL_LOOSE'],
188             mail2 => ['NOT_BLANK', 'EMAIL_LOOSE'],
189             { mails => ['mail1', 'mail2' ] } => ['DUPLICATION'],
190             { date => ['year', 'month', 'day'] } => ['DATE'],
191             ] );
192              
193             if ( $result->has_error ) {
194             my $tt = Template->new({ INCLUDE_PATH => './tmpl' });
195             $tt->process('template.html', { result => $result });
196             }
197              
198             template example
199              
200             [% IF result.has_error %]
201            

Found Input Error

202            
203              
204             [% IF result.missing('param1') %]
205            
  • param1 is blank.
  • 206             [% END %]
    207              
    208             [% IF result.invalid('param1') %]
    209            
  • param1 is invalid.
  • 210             [% END %]
    211              
    212             [% IF result.invalid('param1', 'ASCII') %]
    213            
  • param1 needs ascii code.
  • 214             [% END %]
    215              
    216             [% IF result.invalid('param1', 'LENGTH') %]
    217            
  • input into param1 with characters that's length should be between two and five.
  • 218             [% END %]
    219              
    220            
    221             [% END %]
    222              
    223             example2
    224              
    225             [% IF result.has_error %]
    226            
    227             [% FOREACH key IN result.error %]
    228             [% FOREACH type IN result.error(key) %]
    229            
  • invalid: [% key %] - [% type %]
  • 230             [% END %]
    231             [% END %]
    232            
    233             [% END %]
    234              
    235             =head1 DESCRIPTION
    236              
    237             This module provides you a sweet way of form data validation with simple constraints chains.
    238             You can write constraints on single line for each input data.
    239              
    240             This idea is based on Sledge::Plugin::Validator, and most of validation code is borrowed from this plugin.
    241              
    242             (Sledge is a MVC web application framework: http://sl.edge.jp [Japanese] )
    243              
    244             The result object this module returns behaves like L.
    245              
    246             =head1 HOW TO SET PROFILE
    247              
    248             FormValidator::Simple->check( $q => [
    249             #profile
    250             ] );
    251              
    252             Use 'check' method.
    253              
    254             A hash reference includes input data, or an object of some class that has a method named 'param', for example L, is needed as first argument.
    255              
    256             And set profile as array reference into second argument. Profile consists of some pairs of input data and constraints.
    257              
    258             my $q = CGI->new;
    259             $q->param( param1 => 'hoge' );
    260              
    261             FormValidator::Simple->check( $q => [
    262             param1 => [ ['NOT_BLANK'], ['LENGTH', 4, 10] ],
    263             ] );
    264              
    265             In this case, param1 is the name of a form element. and the array ref "[ ['NOT_BLANK']... ]" is a constraints chain.
    266              
    267             Write constraints chain as arrayref, and you can set some constraints into it. In the last example, two constraints
    268             'NOT_BLANK', and 'LENGTH' are set. Each constraints is should be set as arrayref, but in case the constraint has no
    269             argument, it can be written as scalar text.
    270              
    271             FormValidator::Simple->check( $q => [
    272             param1 => [ 'NOT_BLANK', ['LENGTH', 4, 10] ],
    273             ] );
    274              
    275             Now, in this sample 'NOT_BLANK' constraint is not an arrayref, but 'LENGTH' isn't. Because 'LENGTH' has two arguments, 4 and 10.
    276              
    277             =head2 MULTIPLE DATA VALIDATION
    278              
    279             When you want to check about multiple input data, do like this.
    280              
    281             my $q = CGI->new;
    282             $q->param( mail1 => 'lyo.kato@gmail.com' );
    283             $q->param( mail2 => 'lyo.kato@gmail.com' );
    284              
    285             my $result = FormValidator::Simple->check( $q => [
    286             { mails => ['mail1', 'mail2'] } => [ 'DUPLICATION' ],
    287             ] )
    288              
    289             [% IF result.invalid('mails') %]
    290            

    mail1 and mail2 aren't same.

    291             [% END %]
    292              
    293             and here's an another example.
    294              
    295             my $q = CGI->new;
    296             $q->param( year => 2005 );
    297             $q->param( month => 12 );
    298             $q->param( day => 27 );
    299              
    300             my $result = FormValidator::Simple->check( $q => [
    301             { date => ['year', 'month', 'day'] } => [ 'DATE' ],
    302             ] );
    303              
    304             [% IF result.invalid('date') %]
    305            

    Set correct date.

    306             [% END %]
    307              
    308             =head2 FLEXIBLE VALIDATION
    309              
    310             my $valid = FormValidator::Simple->new();
    311              
    312             $valid->check( $q => [
    313             param1 => [qw/NOT_BLANK ASCII/, [qw/LENGTH 4 10/] ],
    314             ] );
    315              
    316             $valid->check( $q => [
    317             param2 => [qw/NOT_BLANK/],
    318             ] );
    319              
    320             my $results = $valid->results;
    321              
    322             if ( found some error... ) {
    323             $results->set_invalid('param3' => 'MY_ERROR');
    324             }
    325              
    326             template example
    327              
    328             [% IF results.invalid('param1') %]
    329             ...
    330             [% END %]
    331             [% IF results.invalid('param2') %]
    332             ...
    333             [% END %]
    334             [% IF results.invalid('param3', 'MY_ERROR') %]
    335             ...
    336             [% END %]
    337              
    338             =head1 HOW TO SET OPTIONS
    339              
    340             Option setting is needed by some validation, especially in plugins.
    341              
    342             You can set them in two ways.
    343              
    344             FormValidator::Simple->set_option(
    345             dbic_base_class => 'MyProj::Model::DBIC',
    346             charset => 'euc',
    347             );
    348              
    349             or
    350              
    351             $valid = FormValidator::Simple->new(
    352             dbic_base_class => 'MyProj::Model::DBIC',
    353             charset => 'euc',
    354             );
    355              
    356             $valid->check(...)
    357              
    358             =head1 VALIDATION COMMANDS
    359              
    360             You can use follow variety validations.
    361             and each validations can be used as negative validation with 'NOT_' prefix.
    362              
    363             FormValidator::Simple->check( $q => [
    364             param1 => [ 'INT', ['LENGTH', 4, 10] ],
    365             param2 => [ 'NOT_INT', ['NOT_LENGTH', 4, 10] ],
    366             ] );
    367              
    368             =over 4
    369              
    370             =item SP
    371              
    372             check if the data has space or not.
    373              
    374             =item INT
    375              
    376             check if the data is integer or not.
    377              
    378             =item UINT
    379              
    380             unsigined integer check.
    381             for example, if -1234 is input, the validation judges it invalid.
    382              
    383             =item DECIMAL
    384              
    385             $q->param( 'num1' => '123.45678' );
    386              
    387             my $result = FormValidator::Simple->check( $q => [
    388             num1 => [ ['DECIMAL', 3, 5] ],
    389             ] );
    390              
    391             each numbers (3,5) mean maximum digits before/after '.'
    392              
    393             =item ASCII
    394              
    395             check is the data consists of only ascii code.
    396              
    397             =item LENGTH
    398              
    399             check the length of the data.
    400              
    401             my $result = FormValidator::Simple->check( $q => [
    402             param1 => [ ['LENGTH', 4] ],
    403             ] );
    404              
    405             check if the length of the data is 4 or not.
    406              
    407             my $result = FormValidator::Simple->check( $q => [
    408             param1 => [ ['LENGTH', 4, 10] ],
    409             ] );
    410              
    411             when you set two arguments, it checks if the length of data is in
    412             the range between 4 and 10.
    413              
    414             =item HTTP_URL
    415              
    416             verify it is a http(s)-url
    417              
    418             my $result = FormValidator::Simple->check( $q => [
    419             param1 => [ 'HTTP_URL' ],
    420             ] );
    421              
    422             =item SELECTED_AT_LEAST
    423              
    424             verify the quantity of selected parameters is counted over allowed minimum.
    425              
    426             Music
    427             Movie
    428             Game
    429              
    430             my $result = FormValidator::Simple->check( $q => [
    431             hobby => ['NOT_BLANK', ['SELECTED_AT_LEAST', 2] ],
    432             ] );
    433              
    434             =item REGEX
    435              
    436             check with regular expression.
    437              
    438             my $result = FormValidator::Simple->check( $q => [
    439             param1 => [ ['REGEX', qr/^hoge$/ ] ],
    440             ] );
    441              
    442             =item DUPLICATION
    443              
    444             check if the two data are same or not.
    445              
    446             my $result = FormValidator::Simple->check( $q => [
    447             { duplication_check => ['param1', 'param2'] } => [ 'DUPLICATION' ],
    448             ] );
    449              
    450             =item EMAIL
    451              
    452             check with L.
    453              
    454             =item EMAIL_MX
    455              
    456             check with L, including mx check.
    457              
    458             =item EMAIL_LOOSE
    459              
    460             check with L.
    461              
    462             =item EMAIL_LOOSE_MX
    463              
    464             check with L, including mx check.
    465              
    466             =item DATE
    467              
    468             check with L
    469              
    470             my $result = FormValidator::Simple->check( $q => [
    471             { date => [qw/year month day/] } => [ 'DATE' ]
    472             ] );
    473              
    474             =item TIME
    475              
    476             check with L
    477              
    478             my $result = FormValidator::Simple->check( $q => [
    479             { time => [qw/hour min sec/] } => ['TIME'],
    480             ] );
    481              
    482             =item DATETIME
    483              
    484             check with L
    485              
    486             my $result = FormValidator::Simple->check( $q => [
    487             { datetime => [qw/year month day hour min sec/] } => ['DATETIME']
    488             ] );
    489              
    490             =item DATETIME_STRPTIME
    491              
    492             check with L.
    493              
    494             my $q = CGI->new;
    495             $q->param( datetime => '2006-04-26T19:09:21+0900' );
    496              
    497             my $result = FormValidator::Simple->check( $q => [
    498             datetime => [ [ 'DATETIME_STRPTIME', '%Y-%m-%dT%T%z' ] ],
    499             ] );
    500              
    501             =item DATETIME_FORMAT
    502              
    503             check with DateTime::Format::***. for example, L,
    504             L, L and etc.
    505              
    506             my $q = CGI->new;
    507             $q->param( datetime => '2004-04-26 19:09:21' );
    508              
    509             my $result = FormValidator::Simple->check( $q => [
    510             datetime => [ [qw/DATETIME_FORMAT MySQL/] ],
    511             ] );
    512              
    513             =item GREATER_THAN
    514              
    515             numeric comparison
    516              
    517             my $result = FormValidator::Simple->check( $q => [
    518             age => [ ['GREATER_THAN', 25] ],
    519             ] );
    520              
    521             =item LESS_THAN
    522              
    523             numeric comparison
    524              
    525             my $result = FormValidator::Simple->check( $q => [
    526             age => [ ['LESS_THAN', 25] ],
    527             ] );
    528              
    529             =item EQUAL_TO
    530              
    531             numeric comparison
    532              
    533             my $result = FormValidator::Simple->check( $q => [
    534             age => [ ['EQUAL_TO', 25] ],
    535             ] );
    536              
    537             =item BETWEEN
    538              
    539             numeric comparison
    540              
    541             my $result = FormValidator::Simple->check( $q => [
    542             age => [ ['BETWEEN', 20, 25] ],
    543             ] );
    544              
    545             =item ANY
    546              
    547             check if there is not blank data in multiple data.
    548              
    549             my $result = FormValidator::Simple->check( $q => [
    550             { some_data => [qw/param1 param2 param3/] } => ['ANY']
    551             ] );
    552              
    553             =item IN_ARRAY
    554              
    555             check if the food ordered is in menu
    556              
    557             my $result = FormValidator::Simple->check( $q => [
    558             food => [ ['IN_ARRAY', qw/noodle soba spaghetti/] ],
    559             ] };
    560              
    561             =back
    562              
    563             =head1 HOW TO LOAD PLUGINS
    564              
    565             use FormValidator::Simple qw/Japanese CreditCard/;
    566              
    567             L, L are loaded.
    568              
    569             or use 'load_plugin' method.
    570              
    571             use FormValidator::Simple;
    572             FormValidator::Simple->load_plugin('FormValidator::Simple::Plugin::CreditCard');
    573              
    574             If you want to load plugin which name isn't in FormValidator::Simple::Plugin namespace, use +.
    575              
    576             use FormValidator::Simple qw/+MyApp::ValidatorPlugin/;
    577              
    578             =head1 MESSAGE HANDLING
    579              
    580             You can custom your own message with key and type.
    581              
    582             [% IF result.has_error %]
    583             [% FOREACH key IN result.error %]
    584             [% FOREACH type IN result.error(key) %]
    585            

    error message:[% type %] - [% key %]

    586             [% END %]
    587             [% END %]
    588             [% END %]
    589              
    590             And you can also set messages configuration before.
    591             You can prepare configuration as hash reference.
    592              
    593             FormValidator::Simple->set_messages( {
    594             action1 => {
    595             name => {
    596             NOT_BLANK => 'input name!',
    597             LENGTH => 'input name (length should be between 0 and 10)!',
    598             },
    599             email => {
    600             DEFAULT => 'input correct email address!',
    601             },
    602             },
    603             } );
    604              
    605             or a YAML file.
    606              
    607             # messages.yml
    608             DEFAULT:
    609             name:
    610             DEFAULT: name is invalid!
    611             action1:
    612             name:
    613             NOT_BLANK: input name!
    614             LENGTH: input name(length should be between 0 and 10)!
    615             email:
    616             DEFAULT: input correct email address!
    617             action2:
    618             name:
    619             DEFAULT: ...
    620            
    621             # in your perl-script, set the file's path.
    622             FormValidator::Simple->set_messages('messages.yml');
    623              
    624             DEFAULT is a special type.
    625             If it can't find setting for indicated validation-type, it uses message set for DEFAULT.
    626              
    627             after setting, execute check(),
    628              
    629             my $result = FormValidator::Simple->check( $q => [
    630             name => [qw/NOT_BLANK/, [qw/LENGTH 0 10/] ],
    631             email => [qw/NOT_BLANK EMAIL_LOOSE/, [qw/LENGTH 0 20/] ],
    632             ] );
    633              
    634             # matching result and messages for indicated action.
    635             my $messages = $result->messages('action1');
    636              
    637             foreach my $message ( @$messages ) {
    638             print $message, "\n";
    639             }
    640              
    641             # or you can get messages as hash style.
    642             # each fieldname is the key
    643             my $field_messages = $result->field_messages('action1');
    644             if ($field_messages->{name}) {
    645             foreach my $message ( @{ $field_messages->{name} } ) {
    646             print $message, "\n";
    647             }
    648             }
    649              
    650             When it can't find indicated action, name, and type, it searches proper message from DEFAULT action.
    651             If in template file,
    652              
    653             [% IF result.has_error %]
    654             [% FOREACH msg IN result.messages('action1') %]
    655            

    [% msg %]

    656             [% END %]
    657             [% END %]
    658              
    659             you can set each message format.
    660              
    661             FormValidator::Simple->set_message_format('

    %s

    ');
    662             my $result = FormValidator::Simple->check( $q => [
    663             ...profile
    664             ] );
    665              
    666             [% IF result.has_error %]
    667             [% result.messages('action1').join("\n") %]
    668             [% END %]
    669              
    670             =head1 RESULT HANDLING
    671              
    672             See L
    673              
    674             =head1 FLAGGED UTF-8
    675              
    676             If you set encoding like follows, it automatically decode the
    677             result messages.
    678              
    679             FormValidtor::Simple->set_mesasges_decode_from('utf-8');
    680              
    681             =head1 SEE ALSO
    682              
    683             L
    684              
    685             http://sl.edge.jp/ (Japanese)
    686              
    687             http://sourceforge.jp/projects/sledge
    688              
    689             =head1 AUTHOR
    690              
    691             Lyo Kato Elyo.kato@gmail.comE
    692              
    693             =head1 COPYRIGHT AND LICENSE
    694              
    695             This library is free software.
    696             You can redistribute it and/or modify it under the same terms as perl itself.
    697              
    698             =cut
    699