root/lang/perl/FormValidator-LazyWay/trunk/lib/FormValidator/LazyWay.pm @ 26746

Revision 26746, 13.9 kB (checked in by vkgtaro, 5 years ago)

Checking in changes prior to tagging of version 0.03. Changelog diff is:

Index: Changes
===================================================================
--- Changes (リビジョン 26631)
+++ Changes (作業コピー)
@@ -1,5 +1,9 @@

Revision history for Data-FormValidateEm?


+0.03 2008-12-15T14:49:54+09:00
+ - edited depedencies module list at Makefile.PL.
+ be grateful for your test. thanks!
+

0.02 2008-12-13T18:34:00+09:00

  • added encoding for pod.


Line 
1package FormValidator::LazyWay;
2
3use strict;
4use warnings;
5
6use base qw/Class::Accessor::Fast/;
7use FormValidator::LazyWay::Rule;
8use FormValidator::LazyWay::Message;
9use FormValidator::LazyWay::Fix;
10use FormValidator::LazyWay::Filter;
11use FormValidator::LazyWay::Utils;
12use FormValidator::LazyWay::Result;
13use Carp;
14use Data::Dumper;
15use Data::Visitor::Encode;
16
17our $VERSION = '0.03';
18
19__PACKAGE__->mk_accessors(qw/config unicode rule message fix filter/);
20
21sub new {
22    my $class = shift;
23
24    my $args;
25    if ( ref $_[0] eq 'HASH' ) {
26        $args = shift;
27    }
28    else {
29        my %args = @_;
30        $args = \%args;
31    }
32
33    croak 'you must set config' unless $args->{config};
34
35    my $self = bless $args, $class;
36
37    if ( $self->unicode || $self->{config}->{unicode} ) {
38        my $dev = Data::Visitor::Encode->new();
39        $self->config( $dev->decode('utf8', $self->config) );
40    }
41
42    my $rule = FormValidator::LazyWay::Rule->new( config => $self->config );
43    my $fix  = FormValidator::LazyWay::Fix->new( config => $self->config );
44    my $filter  = FormValidator::LazyWay::Filter->new( config => $self->config );
45    my $message = FormValidator::LazyWay::Message->new(
46        config  => $self->config,
47        rule    => $rule
48    );
49
50    $self->{rule}    = $rule;
51    $self->{fix}     = $fix;
52    $self->{filter}  = $filter;
53    $self->{message} = $message;
54
55    return $self;
56}
57
58sub label {
59    my $self    = shift;
60    my $lang    = $self->message->lang;
61    return $self->message->labels->{ $lang } ;
62}
63
64sub check {
65    my $self    = shift;
66    my $input   = shift;
67    my %profile = %{ shift || {} };
68    my $storage = {
69        error_message => {} ,
70        valid   => FormValidator::LazyWay::Utils::get_input_as_hash($input),
71        missing => [],
72        unknown => [],
73        invalid => {},
74    };
75
76    FormValidator::LazyWay::Utils::check_profile_syntax( \%profile );
77
78    my @methods = (
79
80        # profileを扱いやすい型にコンバート
81        '_conv_profile',
82
83        '_set_dependencies',
84
85        '_set_dependency_groups',
86
87        # langをプロフィールにセット
88        '_set_lang',
89
90        # デフォルトセット
91        '_set_default',
92
93        # 空白のフィールドのキーを消去
94        '_remove_empty_fields',
95
96        # マージが設定された項目を storage にセット
97        '_merge',
98
99        # 未定義のフィールドをセット、そしてvalidから消去
100        '_set_unknown',
101
102        # filter ループ
103        '_filter',
104
105        # missingのフィールドをセット、そしてvalidから消去
106        '_check_required_fields',
107
108        # invalidチェックループ
109        '_validation_block',
110
111        # fiexed ループ
112        '_fixed',
113
114    );
115
116    for my $method (@methods) {
117        $self->$method( $storage, \%profile );
118    }
119
120    $storage->{has_missing} = scalar @{$storage->{missing}} ? 1 : 0 ;
121    $storage->{has_invalid} = scalar keys %{$storage->{invalid}} ? 1 : 0 ;
122    $storage->{has_error}   = ( $storage->{has_missing} || $storage->{has_invalid} ) ? 1 : 0 ;
123    $storage->{success}   = ( $storage->{has_missing} || $storage->{has_invalid} ) ? 0 : 1 ;
124
125    return FormValidator::LazyWay::Result->new($storage);
126}
127
128sub _set_error_message_for_display {
129    my $self           = shift;
130    my $storage        = shift;
131    my $error_messages = shift;
132    my $lang           = shift;
133    my $result         = {};
134
135    foreach my $field ( keys %{$error_messages} ) {
136
137        local $" = ',';
138        my $tmp = "@{$error_messages->{$field}}";
139        my $label = $self->message->labels->{ $lang }{ $field } || $field;
140        my $mes = $self->message->base_message->{ $lang }{invalid} ;
141        $mes =~ s/__rule__/$tmp/g;
142        $mes =~ s/__field__/$label/g;
143
144        $result->{$field} = $mes;
145    }
146
147    # setting missing error message
148    if ( scalar @{ $storage->{missing} } ) {
149        for my $field ( @{ $storage->{missing} } ) {
150            my $label = $self->message->labels->{ $lang }{ $field } || $field;
151            my $mes = $self->message->base_message->{ $lang }{missing} ;
152            $mes =~ s/__field__/$label/g;
153            $result->{$field} = $mes;
154        }
155    }
156
157    $storage->{error_message} = $result;
158
159}
160
161sub _append_error_message {
162    my $self           = shift;
163    my $lang           = shift;
164    my $level          = shift;
165    my $field          = shift;
166    my $storage        = shift;
167    my $label          = shift;
168    my $error_messages = shift;
169    my $regex          = shift;
170
171    $storage->{invalid}{$field}{$label} = 1;
172
173    unless ( exists $error_messages->{$field} ) {
174        $error_messages->{$field} = [];
175    }
176
177    my $key = $regex || $field ;
178    push @{ $error_messages->{$field} },
179        $self->message->get(
180        { lang => $lang, field => $key , label => $label, level => $level }
181        );
182
183}
184
185sub _merge {
186    my $self           = shift;
187    my $storage        = shift;
188    my $profile        = shift;
189
190    my @fields = keys %{ $storage->{valid} } ;
191
192    return unless $self->config->{setting}->{merge};
193
194    for my $key ( keys %{$self->config->{setting}->{merge}} ) {
195        my $field = $self->config->{setting}->{merge}->{$key};
196        if ( ref $field eq 'HASH'
197                 && $field->{fields}
198                     && $field->{fields} ) {
199            my @values = map { $storage->{valid}->{$_} } @{ $field->{fields} };
200            $storage->{valid}->{$key} = sprintf($field->{format}, @values);
201        }
202    }
203}
204
205sub _filter {
206    my $self           = shift;
207    my $storage        = shift;
208    my $profile        = shift;
209
210    my @fields = keys %{ $storage->{valid} } ;
211
212    for my $field (@fields) {
213        my $level = $profile->{level}{$field} || 'strict';
214        $storage->{valid}{$field} = $self->filter->parse($storage->{valid}{$field}, $level, $field);
215    }
216}
217
218sub _fixed {
219    my $self           = shift;
220    my $storage        = shift;
221    my $profile        = shift;
222
223    my @fields = keys %{ $storage->{valid} } ;
224
225    for my $field (@fields) {
226        my $level = $profile->{level}{$field} || 'strict';
227        $storage->{valid}{$field} = $self->fix->parse($storage->{valid}{$field}, $level, $field);
228    }
229}
230
231sub _validation_block {
232    my $self           = shift;
233    my $storage        = shift;
234    my $profile        = shift;
235    my $error_messages = {};
236
237    my @fields = keys %{ $profile->{required} } ;
238    push @fields , keys %{ $profile->{optional} } ;
239
240    for my $field (@fields) {
241        my $is_invalid = 0;
242        my $level = $profile->{level}{$field} || 'strict';
243
244        # missing , empty optional
245        next unless exists $storage->{valid}{$field};
246
247        # bad logic... $level may change to regex_map
248        my $regex = '';
249        my $validators = $self->_get_validator_methods( $field, \$level , \$regex );
250        VALIDATE:
251        for my $validator ( @{$validators} ) {
252
253            my $stash = $profile->{stash}->{$field};
254
255            if ( ref $storage->{valid}{$field} eq 'ARRAY' ) {
256
257            CHECK_ARRAYS:
258                for my $value ( @{ $storage->{valid}{$field} } ) {
259                    if ( $validator->{method}->($value, $stash) ) {
260                        # OK
261                        next CHECK_ARRAYS;
262                    }
263                    else {
264                        $self->_append_error_message( $profile->{lang},
265                            $level, $field, $storage,
266                            $validator->{label},
267                            $error_messages , $regex );
268                        $is_invalid++;
269                        last CHECK_ARRAYS;
270                    }
271                }
272
273                # 配列をやめる。
274                if ( !$profile->{want_array}{ $field } ) {
275                    $storage->{valid}{$field} = $storage->{valid}{$field}[0];
276                    last VALIDATE;
277                }
278
279            }
280            else {
281                my $value = $storage->{valid}{$field};
282                if ( $validator->{method}->( $value, $stash ) ) {
283                    # return alwasy array ref when want_array is seted.
284                    if ( $profile->{want_array}{$field} ) {
285                        $storage->{valid}{$field} = [];
286                        push @{ $storage->{valid}{$field} }, $value;
287
288                    }
289                }
290                else {
291                    $self->_append_error_message( $profile->{lang}, $level, $field, $storage, $validator->{label}, $error_messages , $regex );
292                    $is_invalid++;
293                }
294            }
295
296        }
297        delete $storage->{valid}{$field} if $is_invalid;
298    }
299
300    $self->_set_error_message_for_display( $storage, $error_messages , $profile->{lang} );
301}
302
303sub _get_validator_methods {
304    my $self  = shift;
305    my $field = shift;
306    my $level = shift;
307    my $regex = shift;
308
309    my $validators = $self->rule->setting->{$$level}{$field};
310
311    if ( !defined $validators ) {
312
313        # 正規表現にfieldがマッチしたら適応
314        foreach my $regexp ( keys %{ $self->rule->setting->{regex_map} } )
315        {
316            if ( $field =~ qr/$regexp/ ) {
317                $validators = $self->rule->setting->{regex_map}{$regexp};
318                $$level      = 'regex_map';
319                $$regex      = $regexp;
320                last;
321            }
322        }
323
324        # 検証モジュールがセットされてないよ。
325        croak 'you should set ' . $$level . ':' . $field . ' validate method'
326            unless $validators;
327    }
328
329    return $validators;
330}
331
332sub _set_dependencies {
333    my $self    = shift;
334    my $storage = shift;
335    my $profile = shift;
336    return 1 unless defined $profile->{dependencies};
337
338    foreach my $field ( keys %{ $profile->{dependencies} } ) {
339        if ( $storage->{valid}{$field} ) {
340            for my $dependency  ( @{ $profile->{dependencies}{$field} } ) {
341                $profile->{required}{$dependency} = 1;
342            }
343        }
344    }
345   
346    return 1;
347}
348sub _set_dependency_groups {
349    my $self    = shift;
350    my $storage = shift;
351    my $profile = shift;
352    return 1 unless defined $profile->{dependency_groups};
353
354
355    # check dependency groups
356    # the presence of any member makes them all required
357    for my $group (values %{ $profile->{dependency_groups} }) {
358       my $require_all = 0;
359       for my $field ( FormValidator::LazyWay::Utils::arrayify($group)) {
360            $require_all = 1 if $storage->{valid}{$field};
361       }
362       if ($require_all) {
363            map { $profile->{required}{$_} = 1 }  FormValidator::LazyWay::Utils::arrayify($group);
364       }
365    }
366
367
368
369}
370
371sub _check_required_fields {
372    my $self    = shift;
373    my $storage = shift;
374    my $profile = shift;
375
376    for my $field ( keys %{ $profile->{required} } ) {
377        push @{ $storage->{missing} }, $field
378            unless exists $storage->{valid}{$field};
379        delete $storage->{valid}{$field}
380            unless exists $storage->{valid}{$field};
381    }
382
383    return 1;
384}
385
386sub _set_lang {
387    my $self    = shift;
388    my $storage = shift;
389    my $profile = shift;
390
391    $profile->{lang} = $profile->{lang} || $self->message->lang;
392}
393
394sub _conv_profile {
395    my $self        = shift;
396    my $storage     = shift;
397    my $profile     = shift;
398    my %new_profile = ();
399    %{ $new_profile{required} } = map { $_ => 1 }
400        FormValidator::LazyWay::Utils::arrayify( $profile->{required} );
401    %{ $new_profile{optional} } = map { $_ => 1 }
402        FormValidator::LazyWay::Utils::arrayify( $profile->{optional} );
403    %{ $new_profile{want_array} } = map { $_ => 1 }
404        FormValidator::LazyWay::Utils::arrayify( $profile->{want_array} );
405
406    $new_profile{stash} = $profile->{stash};
407
408    %{$profile} = ( %{$profile}, %new_profile );
409
410    return 1;
411}
412
413sub _set_unknown {
414    my $self    = shift;
415    my $storage = shift;
416    my $profile = shift;
417
418    @{ $storage->{unknown} } = grep {
419        not(   exists $profile->{optional}{$_}
420            or exists $profile->{required}{$_} )
421    } keys %{ $storage->{valid} };
422
423    # and remove them from the list
424    for my $field ( @{ $storage->{unknown} } ) {
425        delete $storage->{valid}{$field};
426    }
427
428    return 1;
429}
430
431sub _set_default {
432    my $self    = shift;
433    my $storage = shift;
434    my $profile = shift;
435
436    # get from profile
437    my $defaults = $profile->{defaults} || {};
438    foreach my $field ( %{ $defaults } ) {
439        $storage->{valid}{$field} ||= $defaults->{$field};
440    }
441
442    # get from config file
443    if ( defined $self->rule->defaults ) {
444        foreach my $field ( keys %{ $self->rule->defaults } ) {
445            $storage->{valid}{$field} ||= $self->rule->defaults->{$field};
446        }
447    }
448
449
450    return 1;
451}
452
453sub _remove_empty_fields {
454    my $self    = shift;
455    my $storage = shift;
456    $storage->{valid} = FormValidator::LazyWay::Utils::remove_empty_fields(
457        $storage->{valid} );
458
459    return 1;
460}
461
462sub add_custom_invalid {
463    my $self = shift;
464    my $form = shift;
465    my $key  = shift;
466    my $message
467        = $self->{messages}{config}{messages}{ $form->lang }{custom_invalid}
468        {$key} || $key;
469    $form->custom_invalid( $key, $message );
470}
471
4721;
473
474__END__
475
476=head1 NAME
477
478FormValidator::LazyWay - Yet Another Form Validator
479
480=head1 SYNOPSIS
481
482  my $fv = FormValidator::LazyWay->new( $config );
483  my $cgi = new CGI;
484  my $res
485    = $fv->check( $cgi , {
486        required => [qw/email password/], });
487
488  if ( $res->has_error ) {
489        print Dumper $res->error_message;
490  }
491  else {
492        # OK!
493        print Dumper $res->valid;
494  }
495
496=head1 DESCRIPTION
497
498THIS MODULE IS UNDER DEVELOPMENT. SPECIFICATION MAY CHANGE.
499
500This validator's scope is not a form but an application. why?? I do not like a validator much which scope is a form because
501I have to write rule per form. that make me tired some.
502
503There is one more cool aim for this validator. this validator does error message staff very well. This validator come with rule message  :-)
504
505well I am not good at explain all about detail in English , so I will write some code to explain one by one.
506
507
508=head1 AUTHOR
509
510Tomohiro Teranishi <tomohiro.teranishi@gmail.com>
511
512Daisuke Komatsu <vkg.taro@gmail.com>
513
514=cut
515
Note: See TracBrowser for help on using the browser.