root/lang/perl/Catalyst-Plugin-FormValidator-Lazy/trunk/lib/Catalyst/Plugin/FormValidator/Lazy.pm @ 2684

Revision 2684, 12.6 kB (checked in by tomyhero, 6 years ago)

lang/perl/Catalyst-Plugin-FormValidator?-Lazy : update version and ready to ship to CPAN

Line 
1package Catalyst::Plugin::FormValidator::Lazy ;
2
3use strict;
4use warnings;
5use NEXT;
6use UNIVERSAL::require;
7use Data::FormValidator;
8
9our $VERSION = '0.03';
10
11#{{{ setup
12sub setup {
13    my $c = shift;
14
15    my $conf = $c->config->{form_validator_lazy};
16    my $pkg = $conf->{method_pkg} ;
17    $pkg->require or die $@;
18
19    $conf->{_strict}
20        = _make_constraints( $pkg , $conf->{strict} , 'strict' );
21
22    $conf->{_loose}
23        = _make_constraints( $pkg , $conf->{loose} , 'loose' );
24
25    $conf->{_regexp}
26        = _make_constraint_regexp_map( $pkg , $conf->{regexp_map} );
27
28    $c->NEXT::setup( @_ );
29
30    return $c;
31}
32#}}}
33
34#{{{ prepare
35sub prepare {
36    my $c = shift;
37    $c = $c->NEXT::prepare(@_);
38    $c->{form} = Data::FormValidator->check( $c->request->parameters, {} );
39    return $c;
40}
41#}}}
42
43#{{{ has_dfv_error
44sub has_dfv_error {
45    my $c = shift;
46
47    if ( $c->form->has_invalid or $c->form->has_missing or exists $c->stash->{custom_invalid} ) {
48        return 1;
49    }
50    else {
51        return 0;
52    }
53}
54#}}}
55
56#{{{ form
57sub form {
58    my $c = shift;
59    if ( $_[0] ) {
60        my $form = $_[1] ? {@_} : $_[0];
61        my $conf = $c->config->{form_validator_lazy};
62        my $custom_parameters = undef;
63
64        if ( !$form->{constraints} ) {
65            my %constraints = %{ $conf->{_strict} };
66            $form->{constraints} = \%constraints;
67
68            if ( $form->{constraints_loose} ) {
69                for my $key ( @{ $form->{constraints_loose} } ) {
70                    $form->{constraints}{ $key } = $c->config->{_loose}{ $key };
71                }
72                delete $form->{constraints_loose};
73            }
74
75        }
76        if( !$form->{constraint_regexp_map} ) {
77            $form->{constraint_regexp_map} = $conf->{_regexp} ;
78        }
79
80        if( $form->{custom_parameters} ) {
81            $custom_parameters = $form->{custom_parameters} ;
82            delete $form->{custom_parameters} ;
83        }
84
85        $c->{form} =
86          Data::FormValidator->check( $custom_parameters || $c->request->parameters , $form );
87
88        $c->stash->{v} = $c->{form}{valid} ;
89        for my $key ( $c->{form}->invalid  ) {
90            $c->stash->{invalid}{ $key } = 1;
91        }
92
93        for my $key ( $c->{form}->missing  ) {
94            $c->stash->{missing}{ $key } = 1;
95        }
96    }
97    return $c->{form};
98}
99#}}}
100
101#{{{ dfv_push_invalid
102sub dfv_push_invalid {
103    my $c   = shift;
104    my $key = shift;
105   
106    if ( ref $key eq 'ARRAY') {
107        $c->stash->{custom_invalid} = {};
108        &_array2hashkey( $c->stash->{custom_invalid} , $key , 1 );
109    }
110    else {
111        $c->stash->{custom_invalid}{ $key } = 1;
112    }
113
114}
115#}}}
116
117#{{{ _array2hashkey
118sub _array2hashkey {
119    my $hash  = shift;
120    my $keys  = shift;
121    my $value = shift;
122
123    return if !scalar @{ $keys };
124
125    my $key = shift @{$keys};
126    $hash->{$key} = scalar @{ $keys } ? {} : $value;
127    _array2hashkey( $hash->{$key} , $keys , $value ) ;
128}
129#}}}
130
131#{{{ _make_constraint_regexp_map
132sub _make_constraint_regexp_map {
133    my $pkg  = shift;
134    my $data = shift;
135    my $constraints = {};
136    foreach my $key ( keys %{ $data } ) {
137             my $value = $data->{ $key } ;
138             if( ref $value eq 'ARRAY' ) {
139                my $method = $value->[0];
140                my @args = @{ $value };
141                shift @args;
142                my $sub =  $pkg . '::' . 'static' .  '_' .  $method  ;
143                $constraints->{ qr/$key/ }
144                    = sub {
145                        my $item = shift ;
146                        no strict;
147                        my $result =  $sub->( $item  ,@args );
148                        return $result;
149                      }
150                    ;
151             }
152             else {
153                $constraints->{ qr/$key/ } = qr/$value/;
154             }
155    }
156
157    return $constraints;
158}
159#}}}
160
161#{{{ _make_constraints
162sub _make_constraints {
163    my $pkg  = shift;
164    my $data = shift;
165    my $mode = shift;
166
167    my $constraints = {};
168
169    foreach my $key ( keys %{ $data } ) {
170        my $value = $data->{ $key };
171
172        if ( $value eq 'method' ) {
173            local *glob =  $pkg . '::' . $mode .  '_' .  $key;
174            $constraints->{ $key } = \&glob ;
175        }
176        elsif ( ref $value eq 'ARRAY' ) {
177            my $method = $value->[0];
178            my @args = @{ $value };
179            shift @args;
180            my $sub =  $pkg . '::' . 'static' .  '_' .  $method  ;
181            $constraints->{ $key }
182                = sub {
183                    my $item = shift ;
184                    no strict;
185                    my $result =  $sub->( $item  ,@args );
186                    return $result;
187                  }
188                ;
189        }
190        else {
191             $constraints->{ $key } = qr/$value/;
192        }
193    }
194    return $constraints;
195}
196#}}}
197
1981;
199
200=head1 NAME
201
202Catalyst::Plugin::FormValidator::Lazy - Catalyst FormValidator Plugin in Lazy way
203
204=head1 DESCRIPTION
205
206Instead of writting constraints in your controller source code , this plugin let you
207use config file. and more...
208
209=head1 SYNOPSYS
210
211 use Catalyst qw( FormValidator::Lazy );
212 
213 sub foo : Local {
214    my ( $s , $c ) = @_;
215    $c->form(
216        required            => [qw/user_name password monster/],
217        constraints_loose   => [qw/user_name/],
218        custom_parameters   => {
219                                    user_name => 'tomyhero',
220                                    password  => 'hi_mom',
221                                    monster   => 'doragon',
222                                },
223     );
224     
225     return if $c->has_dfv_error ;
226
227    # do something!
228 }
229 
230foo.tt
231
232    <td><input type="text" name="user_name"></td>
233    <td>&nbsp;[% IF invalid.user_name %]User Name Is Invalid [% END -%][% IF missing.user_name %]User Name is Missing [% END -%] </td>
234
235app.yml
236
237 form_validator_lazy :
238    method_pkg : 'TestApp::Constraints'
239    regexp_map :
240        '_id$' : '^\d+$'
241        '_cd$' :
242            - string
243    strict     :
244        user_name : method
245        password  : '^[a-zA-Z0-9]+$'
246        doragon   :
247            - string
248            - 10
249   loose       :
250        user_name : method
251        password  : '.+'
252
253TestApp/Constraints.pm
254
255 Package TestApp::Constraints;
256
257 sub strict_user_name {
258    my $value = shift;
259    return $value eq 'tomyhero' ? 1 : 0 ;
260 }
261
262 sub loose_user_name {
263    my $value = shift;
264    return 1 ;
265 }
266
267 sub static_string {
268    my $value  = shift;
269    my $length = shift;
270
271    return length $value <= $length ? 1 : 0 ;
272 }
273
274 1;
275
276=head1 LAZY WAY
277
278=head2 I want to forget about constraints.
279
280I am not a smart person who can think about many thing together. When I
281codeing controller I evern not want to think about constraints. I want
282to write constraints when I finish everything or when I finish design DB
283layout or whatever when I feel I want to work on constraints staff.
284
285that is why this plugin use config file to solve this problem.
286
287app.yml
288
289 form_validator_lazy :
290    strict     :
291        user_name : '^[a-zA-Z0-9]+$'
292        password  : '^[a-zA-Z0-9]+$'
293
294in your controller.
295
296    # even no constraints here , do not worry , it is ready!
297    $c->form(
298        required => [qw/user_name password/],
299    );
300
301=head2 I do not want config data is complicated.
302
303I like simple. When I think about too much I always get headache.
304I did not want to set constraints per controller like bellow.
305
306 controller_name_a:
307    user_name : '^[a-zA-Z0-9]+$'
308    password  : '^[a-zA-Z0-9]+$'
309 controlller_name_b:
310    user_name : '^[a-zA-Z0-9]+$'
311    password  : '^[a-zA-Z0-9]+$'
312
313When I design  a system , I named request parameter very carefully so
314that a parameter never contain different kind of validation . I mean below
315situation never happen.
316
317 # some case this
318 user_name => qr/^[a-zA-Z]+$/;
319 # other case this
320 user_name => qr/^[a-zA-Z0-9]+$/;
321
322But I realize some case we need to have 2 kind of validation for a key. like
323fazzy search...
324
325 form_validator_lazy :
326    strict     :
327        user_name : qr/^[a-zA-Z]+$/
328    loose      :
329        user_name : qr/^[a-zA-Z%]+$/
330
331that is why you can set strict and loose for your config file.  strict is
332default. When you want to use loose constraints then,
333
334 $c->form(
335    required => [qw/user_name/],
336    constraints_loose => [qw/user_name/],
337 );
338
339easy??
340
341=head2 I want to use method for constraints!!!
342
343Yeah , even I am lazy to create methods for constraints , I need them..
344We need to set which package containt the methods
345
346using config file.
347
348 form_validator_lazy :
349    method_pkg : 'TestApp::Constraints'
350
351How to write??
352
353    package TestApp::Constraints;
354   
355    sub strict_user_name {
356        my $user_name = shift;
357   
358        return $user_name eq 'tomyhero' ? 1 : 0 ;
359    }
360   
361    sub loose_user_name {
362        my $user_name = shift;
363        return $user_name =~ /^tom/ ? 1 : 0 ;
364    }
365
366    1;
367
368how to use it??
369the keyword 'method' automatically read method from package. and the method
370name is ${prefix}_${parameter_key_name} .
371
372app.yml
373
374 form_validator_lazy :
375    method_pkg : 'TestApp::Constraints'
376    strict :
377        user_name : method # TestApp::Constraints::strict_user_name
378    loose  :
379        user_name : method # TestApp::Constraints::loose_user_name
380   
381easy?
382
383=head2 Oh.. I do not want same function but different name
384
385If I follow strict_ and loose_ methods rule then I will end up writng like
386below methods.
387
388    package TestApp::Constraints;
389   
390    sub strict_user_id {
391        my $id = shift;
392        return $id =~ /^\d+$/ ? 1 : 0 ;
393    }
394
395    sub strict_goods_id {
396        my $id = shift;
397        return $id =~ /^\d+$/ ? 1 : 0 ;
398    }
399
400app.yml
401
402 form_validator_lazy :
403    method_pkg : 'TestApp::Constraints'
404    strict :
405        user_id : method
406        goods_id: method
407
408
409I hate this. So that I add static_ prefix method...
410how to use it??
411
412 app.yml
413
414 form_validator_lazy :
415    method_pkg : 'TestApp::Constraints'
416    strict :
417        user_id :
418            - number
419        goods_id:
420            - number
421
422
423    package TestApp::Constraints;
424   
425    sub static_number {
426        my $id = shift;
427        return $id =~ /^\d+$/ ? 1 : 0 ;
428    }
429
430Now , not really great but I think it OK.
431
432I forget to tell , static_ method can have arg(s).
433
434 form_validator_lazy :
435    method_pkg : 'TestApp::Constraints'
436    strict :
437        user_id :
438            - number
439            - 10
440        goods_id:
441            - number
442            - 3
443
444
445    package TestApp::Constraints;
446   
447    sub static_number {
448        my $id     = shift;
449        my $length = shift;
450        return 0 of length $id > $length ;
451        return $id =~ /^\d+$/ ? 1 : 0 ;
452    }
453
454I think this is nice.
455
456=head2 I even do not want to type parameter at config.
457
458Yeah , I am lazy to type even parameter key...
459
460app.yml
461
462 form_validator_lazy :
463    method_pkg : 'TestApp::Constraints'
464    strict :
465        user_id  : '^\d+$'
466        member_id: '^\d+$'
467        person_id: '^\d+$'
468        human_id : '^\d+$'
469
470like this situation you can do like this. So you can save even typeing parameters!
471
472 form_validator_lazy :
473    method_pkg : 'TestApp::Constraints'
474    regexp_map     :
475        '_id$' : '^\d+$'
476
477=head2 I am lazy to use two methods for error checking.
478
479Instead of this
480
481    if ( $c->form->has_invalid or $c->form->has_missing ) {
482        $c->detach('hi_mom');
483    }
484
485use this.
486
487    if( $c->has_dfv_error ) {
488        $c->detach('hi_mom');
489    }
490
491=head2 I want to customaize parameters!!!
492
493you can use custom_parameters hash key!
494
495    # get parameters from custom_parameters instead of $c->request->parameters
496    # Of course you do not need to set if you do not use it. this is option.
497    $c->form(
498        custom_parameters => { user_name => 'tomohiro' , password => 'hi_mom' },
499        required => [qw/user_name password/],
500    );
501
502=head2 I am lazy to retrive invalid or missing error key from array in some case.
503
504Desiners want to set error message at specific postision sometimes.
505And also your validated data is ready to use at $c->stash->{v}
506
507app.yml
508
509 form_validator_lazy :
510    method_pkg : 'TestApp::Constraints'
511
512foo.tt
513
514 <td><input type="text" name="user_name"></td>
515 <td>&nbsp;[% IF invalid.user_name %]User Name Is Invalid [% END -%][% IF missing.user_name %]User Name is Missing [% END -%] </td>
516
517=head2 I want to add custom errors.
518
519Yes you can.
520
521 $c->dfv_push_invalid( 'key_name' );
522
523or
524
525 $c->dfv_push_invalid( ['key1' , 'key2'] );
526
527and after this , $c->has_dfv_error will return 1 and also set
528
529 $c->stash->{custom_error}{key_name} = 1;
530 $c->stash->{custom_error}{key1}{key2} = 1,
531
532=head1 METHOD
533
534=head2 form
535
536Returns a L<Data::FormValidator::Results> object.
537
538=head2 has_dfv_error
539
540Having invalid or missing error or not.
541
542=head2 dfv_push_invalid
543
544You can add your custom error with this module.
545
546=head1 SEE ALSO
547
548L<Data::FormValidator>
549
550=head1 AUTHOR
551
552Tomohiro Teranishi C<tomohiro.teranishi@gmail.com>
553
554=cut
555
Note: See TracBrowser for help on using the browser.