1 package MooseX::Params::Validate;
7 use Devel::Caller 'caller_cv';
8 use Scalar::Util 'blessed', 'refaddr', 'reftype';
11 use Moose::Util::TypeConstraints
12 qw( find_type_constraint class_type role_type );
13 use Params::Validate 0.88 ();
14 use Sub::Exporter -setup => {
16 qw( validated_hash validated_list pos_validated_list validate validatep )
19 default => [qw( validated_hash validated_list pos_validated_list )],
20 deprecated => [qw( validate validatep )],
27 my ( $args, %spec ) = @_;
29 my $cache_key = _cache_key( \%spec );
31 my $allow_extra = delete $spec{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
33 if ( exists $CACHED_SPECS{$cache_key} ) {
34 ( ref $CACHED_SPECS{$cache_key} eq 'HASH' )
36 "I was expecting a HASH-ref in the cached $cache_key parameter"
37 . " spec, you are doing something funky, stop it!";
38 %spec = %{ $CACHED_SPECS{$cache_key} };
41 my $should_cache = delete $spec{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
43 $spec{$_} = _convert_to_param_validate_spec( $spec{$_} )
46 $CACHED_SPECS{$cache_key} = \%spec
51 $instance = shift @$args if blessed $args->[0];
56 && reftype( $args->[0] ) eq 'HASH' ? %{ $args->[0] } : @$args;
58 $args{$_} = $spec{$_}{constraint}->coerce( $args{$_} )
59 for grep { $spec{$_}{coerce} && exists $args{$_} } keys %spec;
61 %args = Params::Validate::validate_with(
64 allow_extra => $allow_extra,
65 called => _caller_name(),
68 return ( ( defined $instance ? $instance : () ), %args );
71 *validate = \&validated_hash;
74 my ( $args, @spec ) = @_;
78 my $cache_key = _cache_key( \%spec );
80 my $allow_extra = delete $spec{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
83 if ( exists $CACHED_SPECS{$cache_key} ) {
84 ( ref $CACHED_SPECS{$cache_key} eq 'ARRAY' )
86 "I was expecting a ARRAY-ref in the cached $cache_key parameter"
87 . " spec, you are doing something funky, stop it!";
88 %spec = %{ $CACHED_SPECS{$cache_key}->[0] };
89 @ordered_spec = @{ $CACHED_SPECS{$cache_key}->[1] };
92 my $should_cache = delete $spec{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
94 @ordered_spec = grep { exists $spec{$_} } @spec;
96 $spec{$_} = _convert_to_param_validate_spec( $spec{$_} )
99 $CACHED_SPECS{$cache_key} = [ \%spec, \@ordered_spec ]
104 $instance = shift @$args if blessed $args->[0];
109 && reftype( $args->[0] ) eq 'HASH' ? %{ $args->[0] } : @$args;
111 $args{$_} = $spec{$_}{constraint}->coerce( $args{$_} )
112 for grep { $spec{$_}{coerce} && exists $args{$_} } keys %spec;
114 %args = Params::Validate::validate_with(
117 allow_extra => $allow_extra,
118 called => _caller_name(),
122 ( defined $instance ? $instance : () ),
127 *validatep = \&validated_list;
129 sub pos_validated_list {
133 push @spec, shift while ref $_[0];
137 my $cache_key = _cache_key( \%extra );
139 my $allow_extra = delete $extra{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
142 if ( exists $CACHED_SPECS{$cache_key} ) {
143 ( ref $CACHED_SPECS{$cache_key} eq 'ARRAY' )
145 "I was expecting an ARRAY-ref in the cached $cache_key parameter"
146 . " spec, you are doing something funky, stop it!";
147 @pv_spec = @{ $CACHED_SPECS{$cache_key} };
150 my $should_cache = exists $extra{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
152 # prepare the parameters ...
153 @pv_spec = map { _convert_to_param_validate_spec($_) } @spec;
155 $CACHED_SPECS{$cache_key} = \@pv_spec
161 $args[$_] = $pv_spec[$_]{constraint}->coerce( $args[$_] )
162 for grep { $pv_spec[$_] && $pv_spec[$_]{coerce} } 0 .. $#args;
164 @args = Params::Validate::validate_with(
167 allow_extra => $allow_extra,
168 called => _caller_name(),
177 if ( exists $spec->{MX_PARAMS_VALIDATE_CACHE_KEY} ) {
178 return delete $spec->{MX_PARAMS_VALIDATE_CACHE_KEY};
181 return refaddr( caller_cv(2) );
185 sub _convert_to_param_validate_spec {
189 $pv_spec{optional} = $spec->{optional}
190 if exists $spec->{optional};
192 $pv_spec{default} = $spec->{default}
193 if exists $spec->{default};
195 $pv_spec{coerce} = $spec->{coerce}
196 if exists $spec->{coerce};
199 if ( defined $spec->{isa} ) {
201 = _is_tc( $spec->{isa} )
202 || Moose::Util::TypeConstraints::find_or_parse_type_constraint(
204 || class_type( $spec->{isa} );
206 elsif ( defined $spec->{does} ) {
208 = _is_tc( $spec->{isa} )
209 || find_type_constraint( $spec->{does} )
210 || role_type( $spec->{does} );
213 $pv_spec{callbacks} = $spec->{callbacks}
214 if exists $spec->{callbacks};
217 $pv_spec{constraint} = $constraint;
220 { 'checking type constraint for ' . $constraint->name }
221 = sub { $constraint->check( $_[0] ) };
224 delete $pv_spec{coerce}
225 unless $pv_spec{constraint} && $pv_spec{constraint}->has_coercion;
231 my $maybe_tc = shift;
236 && $maybe_tc->isa('Moose::Meta::TypeConstraint');
240 my $depth = shift || 0;
242 return ( caller( 2 + $depth ) )[3];
247 # ABSTRACT: an extension of Params::Validate using Moose's types
257 use MooseX::Params::Validate;
260 my ( $self, %params ) = validated_hash(
262 bar => { isa => 'Str', default => 'Moose' },
264 return "Hooray for $params{bar}!";
269 my ( $foo, $baz, $gorch ) = validated_list(
271 foo => { isa => 'Foo' },
272 baz => { isa => 'ArrayRef | HashRef', optional => 1 },
273 gorch => { isa => 'ArrayRef[Int]', optional => 1 }
275 [ $foo, $baz, $gorch ];
280 This module fills a gap in Moose by adding method parameter validation
281 to Moose. This is just one of many developing options, it should not
282 be considered the "official" one by any means though.
284 You might also want to explore C<MooseX::Method::Signatures> and
289 It is not possible to introspect the method parameter specs; they are
290 created as needed when the method is called and cached for subsequent
297 =item B<validated_hash( \@_, %parameter_spec )>
299 This behaves similarly to the standard Params::Validate C<validate>
300 function and returns the captured values in a HASH. The one exception
301 is where if it spots an instance in the C<@_>, then it will handle
302 it appropriately (unlike Params::Validate which forces you to shift
305 The values in C<@_> can either be a set of name-value pairs or a single hash
308 The C<%parameter_spec> accepts the following options:
314 The C<isa> option can be either; class name, Moose type constraint
315 name or an anon Moose type constraint.
319 The C<does> option can be either; role name or an anon Moose type
324 This is the default value to be used if the value is not supplied.
328 As with Params::Validate, all options are considered required unless
329 otherwise specified. This option is passed directly to
334 If this is true and the parameter has a type constraint which has
335 coercions, then the coercion will be called for this parameter. If the
336 type does have coercions, then this parameter is ignored.
340 This function is also available under its old name, C<validate>.
342 =item B<validated_list( \@_, %parameter_spec )>
344 The C<%parameter_spec> accepts the same options as above, but returns
345 the parameters as positional values instead of a HASH. This is best
346 explained by example:
349 my ( $self, $foo, $bar ) = validated_list(
351 foo => { isa => 'Foo' },
352 bar => { isa => 'Bar' },
357 We capture the order in which you defined the parameters and then
358 return them as a list in the same order. If a param is marked optional
359 and not included, then it will be set to C<undef>.
361 The values in C<@_> can either be a set of name-value pairs or a single hash
364 Like C<validated_hash>, if it spots an object instance as the first
365 parameter of C<@_>, it will handle it appropriately, returning it as
368 This function is also available under its old name, C<validatep>.
370 =item B<pos_validated_list( \@_, $spec, $spec, ... )>
372 This function validates a list of positional parameters. Each C<$spec>
373 should validate one of the parameters in the list:
377 my ( $foo, $bar ) = pos_validated_list(
386 Unlike the other functions, this function I<cannot> find C<$self> in
387 the argument list. Make sure to shift it off yourself before doing
390 The values in C<@_> must be a list of values. You cannot pass the values as an
391 array reference, because this cannot be distinguished from passing one value
392 which is itself an array reference.
394 If a parameter is marked as optional and is not present, it will
395 simply not be returned.
397 If you want to pass in any of the cache control parameters described
398 below, simply pass them after the list of parameter validation specs:
402 my ( $foo, $bar ) = pos_validated_list(
406 MX_PARAMS_VALIDATE_NO_CACHE => 1,
414 =head1 ALLOWING EXTRA PARAMETERS
416 By default, any parameters not mentioned in the parameter spec cause this
417 module to throw an error. However, you can have this module simply ignore them
418 by setting C<MX_PARAMS_VALIDATE_ALLOW_EXTRA> to a true value when calling a
419 validation subroutine.
421 When calling C<validated_hash> or C<pos_validated_list> the extra parameters
422 are simply returned in the hash or list as appropriate. However, when you call
423 C<validated_list> the extra parameters will not be returned at all. You can
424 get them by looking at the original value of C<@_>.
428 By default, this module exports the C<validated_hash>,
429 C<validated_list>, and C<pos_validated_list>.
431 If you would prefer to import the now deprecated functions C<validate>
432 and C<validatep> instead, you can use the C<:deprecated> tag to import
435 =head1 IMPORTANT NOTE ON CACHING
437 When a validation subroutine is called the first time, the parameter spec is
438 prepared and cached to avoid unnecessary regeneration. It uses the fully
439 qualified name of the subroutine (package + subname) as the cache key. In
440 99.999% of the use cases for this module, that will be the right thing to do.
442 However, I have (ab)used this module occasionally to handle dynamic
443 sets of parameters. In this special use case you can do a couple
444 things to better control the caching behavior.
450 Passing in the C<MX_PARAMS_VALIDATE_NO_CACHE> flag in the parameter
451 spec this will prevent the parameter spec from being cached.
454 my ( $self, %params ) = validated_hash(
456 foo => { isa => 'Foo' },
457 MX_PARAMS_VALIDATE_NO_CACHE => 1,
464 Passing in C<MX_PARAMS_VALIDATE_CACHE_KEY> with a value to be used as
465 the cache key will bypass the normal cache key generation.
468 my ( $self, %params ) = validated_hash(
470 foo => { isa => 'Foo' },
471 MX_PARAMS_VALIDATE_CACHE_KEY => 'foo-42',
480 Dave Rolsky E<lt>autarch@urth.orgE<gt>
484 Please submit bugs to the CPAN RT system at
485 http://rt.cpan.org/NoAuth/ReportBug.html?Queue=moosex-params-validate or via
486 email at bug-moosex-params-validate@rt.cpan.org.