• このエントリーをはてなブックマークに追加


概要

バリデーションには Data::Validator を.

まず { isa => ArrayRef } なルールで大枠だけバリデーションを通して,要素の内容に関する他のパラメータから要素の型を決定.

改めて { isa => ArrayRef[FooType] } なルールに NoThrow な role をつけて(そのパラメータだけ)もう一度バリデーション.

has_errors であれば,その要素のうち,is_FooType でないものを拾う.

こんな感じ.

具体的な

次のような型を定義して:

### MyTypes.pm
package MyTypes;
use strict;
use warnings;
use MouseX::Types -declare => [qw/
    MySpecialTypeA
    MySpecialTypeB
/];
use MouseX::Types::Mouse qw/Str/;
 
subtype MySpecialTypeA,
    as Str,
    where { /^1111-\d\d\d\d-2222$/ };
 
subtype MySpecialTypeB,
    as Str,
    where { /^9999-\d\d\d\d-8888$/ };
 
1;

次のようなデータを与えたときに:

__DATA__
 
@@ a_ok
1111-0000-2222
1111-1111-2222
1111-1234-2222
1111-9999-2222
 
@@ a_ng
1111-0000-2222
1111-0000-2221
1111-00000-2222
1111-1234-2222
1111-aaaa-2222
1110-0000-2222
1111-9999-2222
 
@@ b_ok
9999-0000-8888
9999-1111-8888
9999-1234-8888
9999-9999-8888
 
@@ b_ng
9999-0000-8888
1111-0000-8888
9999-11111-8888
9999-1234-8888
9999-9999-8888
9999-9876-8887
 
@@ i_ok
0
12
3456
000000
123456
987654
999999
 
@@ i_ng
0
12
3456
789O
abcd
1111-0000-2222
9999-0000-8888
000000
123456
65432I
9876S4
1Z3456
999999

次のような結果を得られるような:

perl -I. foo.pl
2014-02-04T11:25:57 [INFO] data "a_ok" is valid at foo.pl line 49
2014-02-04T11:25:57 [WARN] data "a_ng" is invalid at foo.pl line 53
[
  '1111-0000-2221',
  '1111-00000-2222',
  '1111-aaaa-2222',
  '1110-0000-2222'
]
2014-02-04T11:25:57 [INFO] data "b_ok" is valid at foo.pl line 49
2014-02-04T11:25:57 [WARN] data "b_ng" is invalid at foo.pl line 53
[
  '1111-0000-8888',
  '9999-11111-8888',
  '9999-9876-8887'
]
2014-02-04T11:25:57 [INFO] data "i_ok" is valid at foo.pl line 49
2014-02-04T11:25:57 [WARN] data "i_ng" is invalid at foo.pl line 53
[
  '789O',
  'abcd',
  '1111-0000-2222',
  '9999-0000-8888',
  '65432I',
  '9876S4',
  '1Z3456'
]

Perl のコードを愚直に書いてみただけです.

use strict;
use warnings;
use MouseX::Types::Mouse qw/Int Str ArrayRef/;
use MyTypes qw/MySpecialTypeA MySpecialTypeB/;
use Data::Validator;
use Data::Section::Simple qw/get_data_section/;
use Data::Dumper;
use Log::Minimal;
 
$Data::Dumper::Terse = 1;
 
sub d { Dumper( @_ ) }
 
sub v { Data::Validator->new( @_ ) }
 
sub data { split /\n/, get_data_section( @_ ) }
 
sub foo {
    my (%args) = @_;
    my ($v0, $v1);
    my @data;
    my @invalid;
    my $data_type;
    my %data_type_map = (
        'a' => MySpecialTypeA,
        'b' => MySpecialTypeB,
    );
 
    $v0 = v(
        type => { isa => Str },
        data => { isa => Str },
    );
    %args = %{ $v0->validate( \%args ) };
 
    $data_type = $data_type_map{ $args{type} } || Int;
    @data = data( $args{data} );
 
    $v1 = v(
        data => { isa => ArrayRef[$data_type] },
    )->with(qw/NoThrow AllowExtra/);
    $v1->validate( \%args );
    if ( $v1->has_errors ) {
        my $pkg = $data_type->{package_defined_in} || 'MouseX::Types::Mouse';
        my $n = ($data_type->{name} =~ /::([^:]+)$/)[-1] || $data_type->{name};
        my $is_type = \&{"$pkg\::is_$n"};
        @invalid = grep !$is_type->($_), @data;
    }
 
    unless ( @invalid ) {
        infof 'data "%s" is valid', $args{data};
    }
    else {
        warnf 'data "%s" is invalid', $args{data};
        print d \@invalid;
    }
}
 
sub main {
    foo( type => 'a', data => 'a_ok' );
    foo( type => 'a', data => 'a_ng' );
    foo( type => 'b', data => 'b_ok' );
    foo( type => 'b', data => 'b_ng' );
    foo( type => 'i', data => 'i_ok' );
    foo( type => 'i', data => 'i_ng' );
}
 
main();
__DATA__
 
@@ a_ok
1111-0000-2222
1111-1111-2222
1111-1234-2222
1111-9999-2222
 
@@ a_ng
1111-0000-2222
1111-0000-2221
1111-00000-2222
1111-1234-2222
1111-aaaa-2222
1110-0000-2222
1111-9999-2222
 
@@ b_ok
9999-0000-8888
9999-1111-8888
9999-1234-8888
9999-9999-8888
 
@@ b_ng
9999-0000-8888
1111-0000-8888
9999-11111-8888
9999-1234-8888
9999-9999-8888
9999-9876-8887
 
@@ i_ok
0
12
3456
000000
123456
987654
999999
 
@@ i_ng
0
12
3456
789O
abcd
1111-0000-2222
9999-0000-8888
000000
123456
65432I
9876S4
1Z3456
999999