Practice of Programming

プログラム とか Linuxとかの話題

Catalyst::Plugin::FormValidator::Simple::Autoのパッチ

DPLICATIONとデフォルトのバリデーション、DBIC_UNIQUE 対応。DBIC_UNIQUEは微妙な感じ。

--- ../Catalyst-Plugin-FormValidator-Simple-Auto-0.15/./lib/Catalyst/Plugin/FormValidator/Simple/Auto.pm	2007-04-10 10:57:23.000000000 +0900
+++ ./lib/Catalyst/Plugin/FormValidator/Simple/Auto.pm	2007-12-12 03:13:17.000000000 +0900
@@ -6,6 +6,7 @@
 use Catalyst::Exception;
 use UNIVERSAL::isa;
 use YAML;
+use Clone ();
 use FormValidator::Simple;
 
 our $VERSION = '0.15';
@@ -90,6 +91,63 @@
         - rule: NOT_BLANK
           message: param1 is required!
 
+=head1 OTHER SYNTAX
+
+The followings are a bit difference from normal way.
+
+=head2 DEFAULT RULE
+
+If you want to use same validation rule to same param.
+You can write the follwing.
+
+    # profiles.yml
+    DEFAULT:
+      password:
+        - rule: NOT_BLANK
+          message: password is required!!!
+        - rule: INT
+          message: password must be integer!!!
+      param1:
+        - rule: NOT_BLANK
+          message: param1 is required!
+
+This C<DEFAULT> is not action name.
+This name is reserved to define common rule.
+And write as the following.
+
+    action1:
+      _USE_DEFAULT_
+        - password
+        - param1
+
+This C<_USE_DEFAULT_> is not param name.
+In it, you can specify which common rule you use.
+
+=head2 COMBINATION CHECK
+
+For example, checking wheter 2 password is same or not.
+Write the following.
+
+    # profiles.yml
+    confirm:
+      password:
+        - rule: DUPLICATION
+          with: [password, password2]
+          message: 2 password must be same.
+     date:
+        - rule: DATE
+          with: [y, m, d]
+          message: y, m, d not date
+
+=head2 DBIC_UNIQUE
+
+You need FormValidator::Simple::Plugin::DBIC_UNIQUE.
+
+    # profiles.yml
+    confirm:
+      - rule: [DBIC_UNIQUE, 'Bookmark', 'Bookmark', 'url']
+        message: URL is unique
+
 =head1 METHODS
 
 =head2 validator_profile
@@ -138,22 +196,47 @@
 
         $config->{profiles} = $profiles;
     }
+   # use Tie::Trace qw/:all/;
+   # watch my $profiles_multi;
+    my $profiles_multi = $config->{profiles_multi} = {};
 
     my $messages;
-    my $profiles = $config->{profiles};
+
+    use Tie::Trace qw/:all/;
+    my $profiles;
+    $profiles = $config->{profiles};
+    my $default_validation = Clone::clone $profiles->{DEFAULT};
+
     for my $action ( keys %{ $profiles || {} } ) {
         my $profile = $profiles->{$action} || {};
 
+        if(my $use_default = delete $profile->{$c->config->{default_string} || '_USE_DEFAULT_'}){
+            foreach my $param (@$use_default) {
+                my $rules = $profile->{$param};
+                $profile->{$param} = $default_validation->{$param};
+                if (defined $rules and ref $rules) {
+                    push @{$profile->{$param}}, @$rules;
+                }
+            }
+        }
+
         for my $param ( keys %$profile ) {
             my $rules = $profile->{$param} || [];
 
             my $i = 0;
             for my $rule (@$rules) {
+
                 if ( ref $rule eq 'HASH' and defined $rule->{rule} ) {
                     my $rule_name = ref $rule->{rule} eq 'ARRAY' ? $rule->{rule}[0] : $rule->{rule};
                     $messages->{$action}{$param} ||= {};
                     $messages->{$action}{$param}{ $rule_name } = $rule->{message} if defined $rule->{message};
-                    $rule = $rule->{rule};
+                    if ( exists $rule->{with} and my @with = @{$rule->{with}} ) {
+                        push @{$profiles_multi->{$action} ||= []}, { $param => \@with } => $rule->{rule};
+                        splice @$rules, $i, 1;
+                        $i--;
+                    } else {
+                        $rule = $rule->{rule};
+                    }
                 }
                 elsif (ref $rule eq 'HASH' and defined $rule->{self_rule} ) {
                     $messages->{$action}{$param} ||= {};
@@ -162,6 +245,7 @@
                 }
                 $i++;
             }
+            # delete $profile->{$param} unless @$rules;
         }
     }
 
@@ -185,14 +269,47 @@
 sub prepare {
     my $c = shift->NEXT::prepare(@_);
 
-    if ( my $profile = $c->config->{validator}{profiles}{ $c->action->reverse } ) {
-        $c->validator_profile( $c->action->reverse );
-        $c->form(%$profile);
+    my $action = $c->action->reverse;
+    my @profile = $c->_fvs_profile($action);
+    if ( @profile ) {
+        $c->validator_profile( $action );
+        $c->form(@profile);
     }
 
     $c
 }
 
+my %_fvs_profile;
+
+use Data::Dumper;
+
+sub _fvs_profile {
+    my($c, $action) = @_;
+
+    return @{Clone::clone $_fvs_profile{$action}} if defined $_fvs_profile{$action};
+
+    my @profile = (%{$c->config->{validator}{profiles}{ $action } || {} }, @{$c->config->{validator}{profiles_multi}{$action} || []});
+
+    if (@profile) {
+        my $profile_max = $#profile;
+        for (my $i = 0; $i < $profile_max; $i +=2) {
+            my $rules = $profile[$i + 1];
+            unless (ref $rules) {
+                $rules = $profile[$i + 1] = [$profile[$i + 1]];
+            }
+
+            foreach my $rule (@$rules) {
+                next if ref $rule ne 'ARRAY' or $rule->[0] ne 'DBIC_UNIQUE' or ($rule->[0] eq 'DBIC_UNIQUE' and ref $rule->[1]);
+                $rule->[1] = $c->model($rule->[1])->resultset($rule->[2]);
+                splice @$rule, 2, 1;
+            }
+        }
+    }
+    $_fvs_profile{$action} = \@profile;
+
+    return @profile;
+}
+
 =head2 forward
 
 =cut
@@ -204,12 +321,15 @@
     local $NEXT::NEXT{ $c, 'forward' };
 
     my $res;
-    if ( my $profile = $c->config->{validator}{profiles}{ $action } ) {
+    my @profile = $c->_fvs_profile($action);
+
+    if ( @profile ) {
+
         # first time validation
         if (not $c->validator_profile) {
             $c->{validator_profile} = $action;
 
-            $c->form(%$profile);
+            $c->form(@profile);
             $res = $c->NEXT::forward(@_);
         }
         else {
@@ -217,7 +337,7 @@
             local $c->{validator} = FormValidator::Simple->new;
             local $c->{validator_profile} = $action;
 
-            $c->form(%$profile);
+            $c->form(@profile);
             $res = $c->NEXT::forward(@_);
         }
     }
--- ../Catalyst-Plugin-FormValidator-Simple-Auto-0.15/./t/03_store_profile.t	2007-04-08 02:02:32.000000000 +0900
+++ ./t/03_store_profile.t	2007-12-07 00:11:55.000000000 +0900
@@ -17,6 +17,10 @@
             profiles => {
                 action1 => {
                     param1 => ['NOT_BLANK', 'ASCII'],
+		    password => [{
+			rule => 'DUPLICATION',
+			with => [qw/password1 password2/],
+		    }]
                 },
             },
         },
--- ../Catalyst-Plugin-FormValidator-Simple-Auto-0.15/./t/04_bundle_message.t	2007-04-10 10:55:33.000000000 +0900
+++ ./t/04_bundle_message.t	2007-12-07 00:11:55.000000000 +0900
@@ -15,11 +15,19 @@
         name      => 'TestApp',
         validator => {
             profiles => {
+		DEFAULT  => {
+		    password1 => [ { rule => 'NOT_BLANK', message => 'NOT_BLANK_P1!!!'} ],
+		    password2 => [ { rule => 'NOT_BLANK', message => 'NOT_BLANK_P2!!!'} ],
+		},
                 action1 => {
+		    _USE_DEFAULT_ => [qw/password1 password2/],
                     param1 => [
                         { rule => 'NOT_BLANK', message => 'NOT_BLANK!!!' },
                         { rule => 'ASCII',     message => 'ASCII!!!' },
                     ],
+		    password => [
+                        { rule => 'DUPLICATION', with => [qw/password1 password2/] ,message => 'DUPLICATION!!!' },
+		    ],
                 },
                 action2_submit => { param1 => [ 'NOT_BLANK', 'ASCII' ], },
                 action3        => {
@@ -34,7 +42,13 @@
         my ( $self, $c ) = @_;
 
         if ($c->form->has_error) {
-            $c->res->body( $c->form->message->get( $c->validator_profile, 'param1', $c->form->error('param1') ) );
+#            $c->res->body( join ",", map $c->form->message->get( $c->validator_profile, $_, $c->form->error($_) ), qw/pram1 password/ );
+
+	    my @msgs;
+	    foreach my $p (sort $c->form->error){
+		push @msgs, $c->form->message->get( $c->validator_profile, $p, $c->form->error($p));
+	    }
+            $c->res->body(join ",", @msgs);
         }
         else {
             $c->res->body('no errors');
@@ -80,12 +94,12 @@
 
 # action driven validation
 ok( my $res = request('/action1'), 'request ok' );
-is( $res->content, 'NOT_BLANK!!!', 'is NOT_BLANK error');
+is( $res->content, 'NOT_BLANK!!!,NOT_BLANK_P1!!!,NOT_BLANK_P2!!!', 'is NOT_BLANK error');
 
-ok( $res = request('/action1?param1=aaa bbb'), 'request ok' );
+ok( $res = request('/action1?param1=aaa bbb&password1=123&password2=123'), 'request ok' );
 is( $res->content, 'ASCII!!!', 'is ASCII error');
 
-ok( $res = request('/action1?param1=aaa'), 'request ok' );
+ok( $res = request('/action1?param1=aaa&password1=b&password2=b'), 'request ok' );
 is( $res->content, 'no errors', 'is no errors');
 
 
--- ../Catalyst-Plugin-FormValidator-Simple-Auto-0.15/./t/02_basic.t	2007-02-18 12:55:55.000000000 +0900
+++ ./t/02_basic.t	2007-12-07 09:14:14.000000000 +0900
@@ -15,11 +15,28 @@
         name => 'TestApp',
         validator => {
             profiles => {
+		DEFAULT => {
+                    password1 => ['NOT_BLANK', 'INT'],
+                    password2 => ['NOT_BLANK', 'INT'],
+		    date      => [
+				  {
+				      rule => 'DATE',
+				      with => [qw/y m d/],
+				  },
+				  ],
+		},
                 action1 => {
+		    _USE_DEFAULT_ => [qw/password1 password2 date/],
                     param1 => ['NOT_BLANK', 'ASCII'],
+                    password  => [{rule => 'DUPLICATION', with => ['password1', 'password2'] }],
                 },
                 action2_submit => {
+		    _USE_DEFAULT_ => [qw/password1 password2/],
                     param1 => ['NOT_BLANK', 'ASCII'],
+                    password  => [{
+                                  rule => 'DUPLICATION',
+                                  with => ['password1', 'password2']
+                                 }]
                 },
             },
         },
@@ -30,7 +47,7 @@
         my ( $self, $c ) = @_;
 
         if ($c->form->has_error) {
-            $c->res->body( $c->form->error('param1') );
+            $c->res->body( join ",", map $c->form->error($_), sort $c->form->error);
         }
         else {
             $c->res->body('no errors');
@@ -52,7 +69,7 @@
         my ( $self, $c ) = @_;
 
         if ($c->form->has_error) {
-            $c->res->body( $c->form->error('param1') );
+            $c->res->body( join ",", map $c->form->error($_), sort $c->form->error);
         }
         else {
             $c->res->body('no errors');
@@ -61,29 +78,32 @@
 }
 
 use Catalyst::Test 'TestApp';
-use Test::More tests => 14;
+use Test::More tests => 16;
 
 use HTTP::Request::Common;
 
 # action driven validation
 ok( my $res = request('/action1'), 'request ok' );
-is( $res->content, 'NOT_BLANK', 'is NOT_BLANK error');
+is( $res->content, 'DATE,NOT_BLANK,NOT_BLANK,NOT_BLANK', 'is NOT_BLANK error');
 
-ok( $res = request('/action1?param1=aaa bbb'), 'request ok' );
-is( $res->content, 'ASCII', 'is ASCII error');
+ok( $res = request('/action1?param1=aaa bbb&password1=a&password2=a'), 'request ok' );
+is( $res->content, 'DATE,ASCII,INT,INT', 'is ASCII & INT error');
 
-ok( $res = request('/action1?param1=aaa'), 'request ok' );
+ok( $res = request('/action1?param1=aaa&password1=123&password2=1234&y=2007&m=13&d=31'), 'request ok' );
+is( $res->content, 'DATE,DUPLICATION', 'is DATE/DUPLICATION error');
+
+ok( $res = request('/action1?param1=aaa&password1=1234&password2=1234&y=2007&m=12&d=31'), 'request ok' );
 is( $res->content, 'no errors', 'is no errors');
 
 
 # forward driven validation
-ok( $res = request(POST '/action2', [ param1 => '' ]), 'request ok' );
-is( $res->content, 'NOT_BLANK', 'is NOT_BLANK error');
+ok( $res = request(POST '/action2', [ param1 => '', password1 => '', password2 => '' ]), 'request ok' );
+is( $res->content, 'NOT_BLANK,NOT_BLANK,NOT_BLANK', 'is NOT_BLANK error');
 
-ok( $res = request(POST '/action2', [ param1 => 'aaa bbb' ]), 'request ok' );
-is( $res->content, 'ASCII', 'is ASCII error');
+ok( $res = request(POST '/action2', [ param1 => 'aaa bbb', password1 => 'abc', password2=> 'abc' ]), 'request ok' );
+is( $res->content, 'ASCII,INT,INT', 'is ASCII/INT error');
 
-ok( $res = request(POST '/action2', [ param1 => 'ab' ]), 'request ok' );
+ok( $res = request(POST '/action2', [ param1 => 'ab', password1 => 123, password2=> 123 ]), 'request ok' );
 is( $res->content, 'no errors', 'is no errors');
 
 ok( $res = request('/action2'), 'request ok' );