Last active
February 24, 2019 05:27
-
-
Save bokutin/5947585 to your computer and use it in GitHub Desktop.
またきちゃいましたバリデーション期。。。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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