Skip to content

Instantly share code, notes, and snippets.

@bokutin
Last active February 24, 2019 05:27
Show Gist options
  • Save bokutin/5947585 to your computer and use it in GitHub Desktop.
Save bokutin/5947585 to your computer and use it in GitHub Desktop.
またきちゃいましたバリデーション期。。。
#!/usr/bin/env perl
use sanity 'sane';
my $ok = {
var1 => 'foo',
var2 => 'bar',
var3 => 'baz',
items => [
{ subvar1 => 'foo' , subvar2 => 'bar' , } ,
{ subvar1 => 'foo' , subvar2 => 'bar' , } ,
],
};
my $ok1 = {
var1 => 'foo',
var2 => 'bar',
var3 => 'baz',
items =>
{ subvar1 => 'foo' , subvar2 => 'bar' , } ,
};
my $ng = {
var1 => 'foo',
var2 => '',
var3 => '',
items => [
{ subvar1 => 'foo' , subvar2 => 'bar' , } ,
{ subvar1 => 'foo' , subvar2 => '' , } ,
{ subvar1 => '' , subvar2 => 'foo' , } ,
],
};
my $ng2 = {
var1 => 'foo',
var2 => 'bar',
var3 => 'baz',
items => [
{ subvar1 => 'foo' , subvar2 => 'bar' , } ,
{ subvar1 => 'foo' , subvar2 => '' , } ,
{ subvar1 => '' , subvar2 => 'foo' , } ,
],
};
use Data::Domain qw(:all);
use YAML::Syck qw(Dump);
sub module_dd {
my $domain = Struct(
var1 => String(-min => 1),
var2 => String(-min => 1),
var3 => String(-min => 1),
items => List(
-all => Struct(
subvar1 => String(-min => 1),
subvar2 => String(-min => 1),
),
-max_size => 2,
),
);
#die Dump($domain);
say "--> Data::Domain";
say "==> Data::Domain x \$ok";
say " "x4,$_ for split /\n/, Dump($domain->inspect($ok));
say "==> Data::Domain x \$ng";
say " "x4,$_ for split /\n/, Dump($domain->inspect($ng));
say "==> 感想";
say " "x4,$_ for "理想的だと思われる。";
}
use Test::Deep qw(:all);
sub module_td {
my $str = code( sub { local $_ = $_[0]; defined and length } );
my $expected = {
var1 => $str,
var2 => $str,
var3 => $str,
items => array_each(
{ subvar1 => $str, subvar2 => $str },
),
};
say "--> Test::Deep";
say "==> Test::Deep x \$ok";
{
my ($ok, $stack) = cmp_details($ok, $expected);
say " "x4,$_ for split /\n/, Dump([$ok, $stack]);
}
say "==> Test::Deep x \$ng";
{
my ($ok, $stack) = cmp_details($ng, $expected);
say " "x4,$_ for split /\n/, Dump([$ok, $stack]);
}
say "==> 感想";
say " "x4,$_ for
"確かにdetailではあるが、これを高いレベルでエラーメッセージにするのは難しそうだ。",
"items数のチェックが出来ていない。";
}
use Data::Validator::Recursive;
use Moose::Util::TypeConstraints;
sub module_dvr {
my $str = subtype( 'Str' => where { length($_) } );
my $rule = Data::Validator::Recursive->new(
var1 => $str,
var2 => $str,
var3 => $str,
items => {
isa => 'ArrayRef',
#rule => [
# '[0]' => $str,
# '[1]' => $str,
#],
#rule => [
# isa => 'HashRef',
# rule => [
# subvar1 => $str,
# subvar2 => $str,
# ],
#],
#Single parameters to new() must be a HASH ref at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/Data/Validator.pm line 119.
# Data::Validator::validate('Mouse::Meta::Class::__ANON__::1=HASH(0x7fe4f25a8ca8)', 'ARRAY(0x7fe4f202d048)') called at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/Data/Validator/Recursive.pm line 67
# Data::Validator::Recursive::validate('Data::Validator::Recursive=HASH(0x7fe4f25a0448)', 'ARRAY(0x7fe4f202d048)', 'items') called at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/Data/Validator/Recursive.pm line 88
# Data::Validator::Recursive::validate('Data::Validator::Recursive=HASH(0x7fe4f259bd88)', 'HASH(0x7fe4f2267390)') called at validate-deep-01.pl line 104
# main::module_dvr() called at validate-deep-01.pl line 119
# main::run() called at validate-deep-01.pl line 122
# Mouse::Object::BUILDARGS([]); になっちゃって、だめっぽい。。。
},
);
say "--> Data::Validator::Recursive";
say "==> Data::Validator::Recursive x \$ok";
{
my $params = $rule->validate($ok);
say " "x4,$_ for split /\n/, Dump([$params, $rule->error]);
}
say "==> Data::Validator::Recursive x \$ng";
{
my $params = $rule->validate($ng);
say " "x4,$_ for split /\n/, Dump([$params, $rule->error]);
}
say "==> 感想";
say " "x4,$_ for "ArrayRefの中のHashRefにruleを当てるのは、無理っぽい?";
}
use MooseX::Params::Validate;
use MooseX::Types::Moose qw(Str Int HashRef ArrayRef);
use MooseX::Types::Structured qw(Dict Tuple Optional);
sub method_mts {
my $str = subtype( 'Str' => where { length($_) } );
my $item = Dict[
subvar1 => $str,
subvar2 => $str,
];
my @spec = (
var1 => { isa => $str },
var2 => { isa => $str },
var3 => { isa => $str },
items => { isa => ArrayRef[$item] },
);
say "--> MooseX::Types::Structured";
say "==> MooseX::Types::Structured x \$ok";
eval { my %p = validated_hash([$ok], @spec) };
say " "x4,$_ for split /\n/, $@;
say "==> MooseX::Types::Structured x \$ng";
eval { my %p = validated_hash([$ng], @spec) };
say " "x4,$_ for split /\n/, $@;
say "==> MooseX::Types::Structured x \$ng2";
eval { my %p = validated_hash([$ng2], @spec) };
say " "x4,$_ for split /\n/, $@;
say "==> 感想";
say " "x4,$_ for "チェックは出来るが、Test::Deepと同じく、高レベルなメッセージにするのは難しそうだ。";
}
use Data::Rx;
sub method_rx {
my $success = {
type => '//rec',
required => {
var1 => { type => '//str', length => { min => 1 } },
var2 => { type => '//str', length => { min => 1 } },
var3 => { type => '//str', length => { min => 1 } },
items => {
type => '//arr',
contents => {
type => '//rec',
required => {
subvar1 => { type => '//str', length => { min => 1 } },
subvar2 => { type => '//str', length => { min => 1 } },
},
},
},
},
};
my $rx = Data::Rx->new;
my $schema = $rx->make_schema($success);
my $ret;
$ret = eval { $schema->assert_valid($ok) };
say " "x4,$_ for split /\n/, Dump([$ret, \$@]);
$ret = eval { $schema->assert_valid($ng) };
say " "x4,$_ for split /\n/, Dump([$ret, \$@]);
}
use Data::Verifier::Nested;
sub method_verifier {
my $dv = Data::Verifier::Nested->new(
#filters => [qw(trim)],
profile => {
var1 => { type => 'Str', required => 1 },
var2 => { type => 'Str', required => 1 },
var3 => { type => 'Str', required => 1 },
},
);
my $results = $dv->verify($ok);
say " "x4,$_ for split /\n/, Dump([$results->success, $results->missings, $results->invalids]);
}
use Validation::Class::Simple;
sub method_vc {
my $rules = Validation::Class::Simple->new(
fields => {
var1 => { required => 1 },
var2 => { required => 1 },
var3 => { required => 1 },
#items => { multiple => 1 },
},
);
# Parameter values must be strings, arrays of strings, or hashrefs whose values are any of the previously mentioned values, i.e. an array with nested structures is illegal
$rules->params->add($ok);
say "--> Validation::Class";
my $ret = $rules->validate;
say "--> Validation::Class x \$ok";
say " "x4,$_ for split /\n/, Dump([$ret, $rules->errors_to_string]);
}
use HTML::FormHandler;
sub method_hfh {
my $form = HTML::FormHandler->new(
#name => 'user_form',
field_list => [
var1 => { type => 'Text', required => 1 },
var2 => { type => 'Text', required => 1 },
var3 => { type => 'Text', required => 1 },
items => { type => 'Repeatable' },
'items.subvar1' => { type => 'Text', required => 1 },
'items.subvar2' => { type => 'Text', required => 1 },
],
);
say "--> HTML::FormHandler";
my $ret;
local $YAML::Syck::ImplicitUnicode = 1;
say "--> HTML::FormHandler x \$ok";
$ret = $form->process( params => $ok );
say " "x4,$_ for split /\n/, Dump([$ret, [$form->errors]]);
say "--> HTML::FormHandler x \$ng";
$ret = $form->process( params => $ng );
say " "x4,$_ for split /\n/, Dump([$ret, [$form->errors]]);
say " "x4,$_ for split /\n/, Dump([$ret, [ map { join(": ", $_->full_name, $_->all_errors ) } $form->error_fields ]]);
say "--> HTML::FormHandler x \$ok1";
$ret = $form->process( params => $ok1 );
say " "x4,$_ for split /\n/, Dump([$ret, [$form->errors]]);
# すばらしい!
}
sub run {
binmode STDOUT, ":utf8";
my $spacer = sub { say "" };
do { eval { $_->() }; say $@ if $@; say "" } for (
\&module_dd,
\&module_td,
\&module_dvr,
\&method_mts,
\&method_rx,
\&method_verifier,
\&method_vc,
\&method_hfh,
);
}
run(@ARGV);
__END__
バリデーションのモジュールを幾つか試してみる。
* JSON PRCのような、多階層になっているデータにも使えること。
* 上の場合、最初のエラーで例外にならずに、適切なエラーが吐けるか確かめること。
* .t でも使えて、偽のときに、便利な表示が得られて、捗ること。
が、とりあえずの評価基準。
モジュール
Data::Domain
作者さま!!
http://www.bach-cantatas.com/Bio/Dami-Laurent.htm
Data::FormValidator
元祖。
多階層が出来ない。
Data::Rx
さすがrjbs様製。
多階層もサポートしていて、ちゃんと動く。
2008〜かららしくよく練られている気がする。
多言語対応もすばらしす。
ただ、手軽にエラーメッセージを埋め込めない。
コードでのチェックをする場合は、Typeを定義する必要がある。
Data::Sah
開発中。
コンセプトは素晴らしそうだけど、まだ実用できなさそう?
Data::Validator::Recursive
多階層のチェックはできけど、高レベルなメッセージはつらいかも。
Data::Verifier::Nested
Data::Validator::Recursive と同じ問題。
FormValidator::Nested
多階層のチェックもサポートしてそう!
http://cpansearch.perl.org/src/CHIBA/FormValidator-Nested-0.07/t/06_nested.t
FormValidator::Simpleライクなのかな。。。
http://www.slideshare.net/nihen/form-validator-nested
FormValidator::LazyWay
未調査。
https://github.com/vkgtaro/p5-formvalidator-lazyway
HTML::FormHandler
items.1.subvar1 という、CGI::Expandっぽいものだけでなく
普通に多階層もサポートしているらしい。
http://search.cpan.org/~gshank/HTML-FormHandler-0.40026/lib/HTML/FormHandler.pm#params
JSON::Schema
未調査。
Kwalify
http://www.kuwata-lab.com/kwalify/ruby/users-guide.01.html
Rxに近そう。
ただ手軽にエラーメッセージを埋め込めないのは同様そう。
Params::Validate
多階層のサポートは無いと思う。
Specio
今のところは、最初のエラーで例外だと思う。
Validation::Class
非常に素晴らしいコンセプトだと思うのだが。。。
Mooseではないのだけど、独特なコード。ロード時間がちょっと気になるのと、バリデーションがかなり遅い。
field のハッシュキーから担う Directive を引くコストがちょっと気になる。
キャッシュを挟めばすぐ速くなりそうではあるけど。。。
value に [ { key1 => 'var1' } ] はだめっぽい。
超偏見入り選好
Data::Domain
Perlのコードで、さくさくバリデーションを書ける。
エラーメッセージも埋め込める。
速度も問題ない。
バリデーションのルールも足せ、Data::Domain::CodeRefという拙作モジュールで
コードリファレンスで手軽にバリデーションも置いていけている。
Test::InDomainも便利。
ここまで書けると、MooseのTypesと別なものが積み上がっていってしまのが、ちょっと気になる。
Data::Rx
schemaがデータなので、とても綺麗。 https://github.com/rjbs/rx/tree/master/spec/schemata
多言語対応なのも良いと思う。
エラーメッセージの追加が手軽ではないので、テンプレート側で担うかどうかで、この点の評価が分かれるかも。
Data::Rx::Type::MooseTC, Data::Rx::Type::PCRE というモジュールがあるけど、どちらもエラーメッセージは埋め込めなさそう。
言語中立なスペックを書くには、とても良さそう。
HTML::FormHandler
formでなくとも、有力な気がした。
たしかに重量級ではあるが、hackable。
フィルタのサポート、ローカライゼーション、MooseのTypesが使える、Mooseのmetaでイントロスペクションなども考えられる。
ここまで書けると、MooseのTypesと別なものが積み上がっていってしまのが、ちょっと気になる。
Test::InHFH的なものがあると良いかもしれない。
% perl validate-deep-01.pl
--> Data::Domain
==> Data::Domain x $ok
--- ~
==> Data::Domain x $ng
---
items: "List: more than 2 its"
var2: "String: smaller than minimum '1'"
var3: "String: smaller than minimum '1'"
==> 感想
理想的だと思われる。
--> Test::Deep
==> Test::Deep x $ok
---
- 1
- !!perl/hash:Test::Deep::Stack
Stack: []
==> Test::Deep x $ng
---
- 0
- !!perl/hash:Test::Deep::Stack
Stack:
-
exp: !!perl/hash:Test::Deep::Hash
val: &2
items: !!perl/hash:Test::Deep::ArrayEach
val:
subvar1: &1 !!perl/hash:Test::Deep::Code
code: !!perl/code: '{ "DUMMY" }'
subvar2: *1
var1: *1
var2: *1
var3: *1
got: &3
items:
-
subvar1: foo
subvar2: bar
-
subvar1: foo
subvar2: ''
-
subvar1: ''
subvar2: foo
var1: foo
var2: ''
var3: ''
-
exp: !!perl/hash:Test::Deep::HashElements
val: *2
got: *3
index: var3
-
diag: ~
exp: *1
got: ''
==> 感想
確かにdetailではあるが、これを高いレベルでエラーメッセージにするのは難しそうだ。
items数のチェックが出来ていない。
--> Data::Validator::Recursive
==> Data::Validator::Recursive x $ok
---
-
items:
-
subvar1: foo
subvar2: bar
-
subvar1: foo
subvar2: bar
var1: foo
var2: bar
var3: baz
==> Data::Validator::Recursive x $ng
---
- ~
-
message: "'var2' is InvalidValue"
name: var2
type: InvalidValue
==> 感想
ArrayRefの中のHashRefにruleを当てるのは、無理っぽい?
--> MooseX::Types::Structured
==> MooseX::Types::Structured x $ok
==> MooseX::Types::Structured x $ng
The 'var3' parameter ("") to (eval) did not pass the 'checking type constraint for __ANON__' callback
at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/MooseX/Params/Validate.pm line 67.
MooseX::Params::Validate::validated_hash('ARRAY(0x7f895eaa9610)', 'var1', 'HASH(0x7f895eaa7d98)', 'var2', 'HASH(0x7f895b8373e0)', 'var3', 'HASH(0x7f895eadd5a8)', 'items', 'HASH(0x7f895eaacaf0)', ...) called at validate-deep-01.pl line 162
eval {...} called at validate-deep-01.pl line 162
main::method_mts() called at validate-deep-01.pl line 271
eval {...} called at validate-deep-01.pl line 271
main::run() called at validate-deep-01.pl line 283
==> MooseX::Types::Structured x $ng2
The 'items' parameter ("ARRAY(0x7f895ca7e750)") to (eval) did not pass the 'checking type constraint for ArrayRef[MooseX::Types::Structured::Dict[subvar1,__ANON__,subvar2,__ANON__]]' callback
at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/MooseX/Params/Validate.pm line 67.
MooseX::Params::Validate::validated_hash('ARRAY(0x7f895eadd6f8)', 'var1', 'HASH(0x7f895eaa7d98)', 'var2', 'HASH(0x7f895b8373e0)', 'var3', 'HASH(0x7f895eadd5a8)', 'items', 'HASH(0x7f895eaacaf0)', ...) called at validate-deep-01.pl line 166
eval {...} called at validate-deep-01.pl line 166
main::method_mts() called at validate-deep-01.pl line 271
eval {...} called at validate-deep-01.pl line 271
main::run() called at validate-deep-01.pl line 283
==> 感想
チェックは出来るが、Test::Deepと同じく、高レベルなメッセージにするのは難しそうだ。
---
- 1
- !!perl/ref
=: ''
---
- ~
- !!perl/ref
=: !!perl/hash:Data::Rx::FailureSet
failures:
- !!perl/hash:Data::Rx::Failure
rx: &1 !!perl/hash:Data::Rx
handler:
tag:codesimply.com,2008:rx/core/all: Data::Rx::CoreType::all
tag:codesimply.com,2008:rx/core/any: Data::Rx::CoreType::any
tag:codesimply.com,2008:rx/core/arr: Data::Rx::CoreType::arr
tag:codesimply.com,2008:rx/core/bool: Data::Rx::CoreType::bool
tag:codesimply.com,2008:rx/core/def: Data::Rx::CoreType::def
tag:codesimply.com,2008:rx/core/fail: Data::Rx::CoreType::fail
tag:codesimply.com,2008:rx/core/int: Data::Rx::CoreType::int
tag:codesimply.com,2008:rx/core/map: Data::Rx::CoreType::map
tag:codesimply.com,2008:rx/core/nil: Data::Rx::CoreType::nil
tag:codesimply.com,2008:rx/core/num: Data::Rx::CoreType::num
tag:codesimply.com,2008:rx/core/one: Data::Rx::CoreType::one
tag:codesimply.com,2008:rx/core/rec: Data::Rx::CoreType::rec
tag:codesimply.com,2008:rx/core/seq: Data::Rx::CoreType::seq
tag:codesimply.com,2008:rx/core/str: Data::Rx::CoreType::str
prefix:
"": tag:codesimply.com,2008:rx/core/
.meta: tag:codesimply.com,2008:rx/meta/
sort_keys: ''
struct:
-
error:
- length
message: length of value is outside allowed range
type: //str
value: ''
-
check_path:
-
- required
- key
-
- var3
- key
data_path:
-
- var3
- key
type: //rec
- !!perl/hash:Data::Rx::Failure
rx: *1
struct:
-
error:
- length
message: length of value is outside allowed range
type: //str
value: ''
-
check_path:
-
- required
- key
-
- var2
- key
data_path:
-
- var2
- key
type: //rec
- !!perl/hash:Data::Rx::Failure
rx: *1
struct:
-
error:
- length
message: length of value is outside allowed range
type: //str
value: ''
-
check_path:
-
- required
- key
-
- subvar2
- key
data_path:
-
- subvar2
- key
type: //rec
-
check_path:
-
- contents
- key
data_path:
-
- 1
- index
type: //arr
- &2
check_path:
-
- required
- key
-
- items
- key
data_path:
-
- items
- key
type: //rec
- !!perl/hash:Data::Rx::Failure
rx: *1
struct:
-
error:
- length
message: length of value is outside allowed range
type: //str
value: ''
-
check_path:
-
- required
- key
-
- subvar1
- key
data_path:
-
- subvar1
- key
type: //rec
-
check_path:
-
- contents
- key
data_path:
-
- 2
- index
type: //arr
- *2
Can only collapse HASH refs at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/Data/Verifier/Nested.pm line 55.
Parameter values must be strings, arrays of strings, or hashrefs whose values are any of the previously mentioned values, i.e. an array with nested structures is illegal at /Users/bokutin/perl5/perlbrew/perls/perl-5.16.2/lib/site_perl/5.16.2/Validation/Class/Params.pm line 44.
Validation::Class::Params::add('Validation::Class::Params=HASH(0x7f895eb18d58)', 'HASH(0x7f895ba68790)') called at validate-deep-01.pl line 227
main::method_vc() called at validate-deep-01.pl line 271
eval {...} called at validate-deep-01.pl line 271
main::run() called at validate-deep-01.pl line 283
--> HTML::FormHandler
--> HTML::FormHandler x $ok
---
- 1
- []
--> HTML::FormHandler x $ng
---
- ''
-
- Var2を入力してください。
- Var3を入力してください。
- Subvar2を入力してください。
- Subvar1を入力してください。
---
- ''
-
- "var2: Var2を入力してください。"
- "var3: Var3を入力してください。"
- "items.1.subvar2: Subvar2を入力してください。"
- "items.2.subvar1: Subvar1を入力してください。"
--> HTML::FormHandler x $ok1
---
- 1
- []
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment