From 3382deba9acb1c77e3cca464dc8f59801b62b1c5 Mon Sep 17 00:00:00 2001 From: Yorhel Date: Sun, 16 Mar 2025 15:39:10 +0100 Subject: [PATCH] Validate: Rename "scalar" to "accept_scalar" and add "accept_array" --- FU/Validate.pm | 33 ++++++++++++++++++++++++--------- t/validate.t | 10 +++++++++- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/FU/Validate.pm b/FU/Validate.pm index 8d8db91..6016b28 100644 --- a/FU/Validate.pm +++ b/FU/Validate.pm @@ -13,7 +13,8 @@ my %builtin = map +($_,1), qw/ default onerror trim - elems scalar sort unique + elems sort unique + accept_scalar accept_array keys values unknown missing func /; @@ -22,8 +23,9 @@ my %type_vals = map +($_,1), qw/scalar hash array any/; my %unknown_vals = map +($_,1), qw/remove reject pass/; my %missing_vals = map +($_,1), qw/create reject ignore/; my %implied_type = qw/ + accept_array scalar keys hash values hash unknown hash - elems array sort array unique array scalar array + elems array sort array unique array accept_scalar array /; my %sort_vals = ( str => sub($x,$y) { $x cmp $y }, @@ -151,6 +153,7 @@ sub _compile($schema, $custom, $rec, $top, $validations=$top->{validations}) { if ($builtin{$name}) { confess "Invalid value for 'missing': $val" if $name eq 'missing' && !$missing_vals{$val}; confess "Invalid value for 'unknown': $val" if $name eq 'unknown' && !$unknown_vals{$val}; + confess "Invalid value for 'accept_array': $val" if $name eq 'accept_array' && $val && $val ne 'first' && $val ne 'last'; $val = $sort_vals{$val} || confess "Unknown value for 'sort': $val" if $name eq 'sort' && ref $val ne 'CODE'; $top->{$name} = $val; next; @@ -273,6 +276,10 @@ sub _validate_input { my $type = $c->{type} // 'scalar'; + # accept_array (needs to be done before 'trim') + $_[1] = $_[1]->@* == 0 ? undef : $c->{accept_array} eq 'first' ? $_[1][0] : $_[1][ $#{$_[1]} ] + if $c->{accept_array} && ref $_[1] eq 'ARRAY'; + # trim (needs to be done before the 'default' test) $_[1] = trim $_[1] =~ s/\r//rg if defined $_[1] && !ref $_[1] && $type eq 'scalar' && (!exists $c->{trim} || $c->{trim}); @@ -305,8 +312,8 @@ sub _validate_input { } } elsif ($type eq 'array') { - $_[1] = [$_[1]] if $c->{scalar} && !ref $_[1]; - return { validation => 'type', expected => $c->{scalar} ? 'array or scalar' : 'array', got => lc ref $_[1] || 'scalar' } if ref $_[1] ne 'ARRAY'; + $_[1] = [$_[1]] if $c->{accept_scalar} && !ref $_[1]; + return { validation => 'type', expected => $c->{accept_scalar} ? 'array or scalar' : 'array', got => lc ref $_[1] || 'scalar' } if ref $_[1] ne 'ARRAY'; $_[1] = [$_[1]->@*]; # Create a shallow copy to prevent in-place modification. } elsif ($type eq 'any') { @@ -600,10 +607,10 @@ Failure is reported in a similar fashion to I: ] } -=item scalar => 0/1 +=item accept_scalar => 0/1 Implies C<< type => 'array' >>, this option will also permit the input to be a -scalar. In this case, the input is interpreted and returned as an array with +scalar. In that case, the input is interpreted and returned as an array with only one element. This option exists to make it easy to validate multi-value form inputs. For example, consider C in L: a parameter in a query string is decoded into an array if it is listed multiple @@ -613,16 +620,24 @@ times, a scalar if it only occcurs once. So we could either end up with: # OR: { a => [1, 3], b => 1 } -With the I option, we can accept both forms for C and normalize into -an array. The following schema definition can validate the above examples: +With the I option, we can accept both forms for C and +normalize into an array. The following schema definition can validate the above +examples: { type => 'hash', keys => { - a => { type => 'array', scalar => 1 }, + a => { type => 'array', accept_scalar => 1 }, b => { } } } +=item accept_array => false/'first'/'last' + +Implies C<< type => 'scalar' >>. Similar to I but normalizes in +the other direction: when the input is an array, only the first or last item is +extracted and the other elements are ignored. If the input is an empty array, +the value is taken to be C. + =item sort => $option Implies C<< type => 'array' >>, sort the array after validating its elements. diff --git a/t/validate.t b/t/validate.t index de37264..1481193 100644 --- a/t/validate.t +++ b/t/validate.t @@ -79,12 +79,20 @@ t { trim => 0 }, " Va\rl id \n ", " Va\rl id \n "; f {}, ' ', { validation => 'required' }, 'required value missing'; t { trim => 0 }, ' ', ' '; +# accept_array +t { default => undef, accept_array => 'first' }, [], undef; +t { default => undef, accept_array => 'first' }, [' x '], 'x'; +t { accept_array => 'first' }, [1,2,3], 1; +t { accept_array => 'last' }, [1,2,3], 3; +f { accept_array => 'first' }, [' ', 1], { validation => 'required' }, 'required value missing'; +f { accept_array => 'first' }, [], { validation => 'required' }, 'required value missing'; + # arrays f {}, [], { validation => 'type', expected => 'scalar', got => 'array' }, "invalid type, expected 'scalar' but got 'array'"; f { type => 'array' }, 1, { validation => 'type', expected => 'array', got => 'scalar' }, "invalid type, expected 'array' but got 'scalar'"; t { type => 'array' }, [], []; t { type => 'array' }, [undef,1,2,{}], [undef,1,2,{}]; -t { type => 'array', scalar => 1 }, 1, [1]; +t { type => 'array', accept_scalar => 1 }, 1, [1]; f { type => 'array', elems => {} }, [undef], { validation => 'elems', errors => [{ index => 0, validation => 'required' }] }, "[0]: required value missing"; t { type => 'array', elems => {} }, [' a '], ['a']; t { type => 'array', sort => 'str' }, [qw/20 100 3/], [qw/100 20 3/];