1 package MooseX::Params::Validate;
7 use Devel::Caller 'caller_cv';
8 use Scalar::Util 'blessed', 'refaddr';
10 use Moose::Util::TypeConstraints qw( find_type_constraint class_type role_type );
11 use Params::Validate ();
12 use Sub::Exporter -setup => {
14 qw( validated_hash validated_list pos_validated_list validate validatep )
17 default => [qw( validated_hash validated_list pos_validated_list )],
18 deprecated => [qw( validate validatep )],
22 our $AUTHORITY = 'cpan:STEVAN';
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];
55 $args{$_} = $spec{$_}{constraint}->coerce( $args{$_} )
56 for grep { $spec{$_}{coerce} && exists $args{$_} } keys %spec;
58 %args = Params::Validate::validate_with(
61 allow_extra => $allow_extra,
62 called => _caller_name(),
65 return ( ( defined $instance ? $instance : () ), %args );
68 *validate = \&validated_hash;
71 my ( $args, @spec ) = @_;
75 my $cache_key = _cache_key( \%spec );
77 my $allow_extra = delete $spec{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
80 if ( exists $CACHED_SPECS{$cache_key} ) {
81 ( ref $CACHED_SPECS{$cache_key} eq 'ARRAY' )
83 "I was expecting a ARRAY-ref in the cached $cache_key parameter"
84 . " spec, you are doing something funky, stop it!";
85 %spec = %{ $CACHED_SPECS{$cache_key}->[0] };
86 @ordered_spec = @{ $CACHED_SPECS{$cache_key}->[1] };
89 my $should_cache = delete $spec{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
91 @ordered_spec = grep { exists $spec{$_} } @spec;
93 $spec{$_} = _convert_to_param_validate_spec( $spec{$_} )
96 $CACHED_SPECS{$cache_key} = [ \%spec, \@ordered_spec ]
101 $instance = shift @$args if blessed $args->[0];
105 $args{$_} = $spec{$_}{constraint}->coerce( $args{$_} )
106 for grep { $spec{$_}{coerce} && exists $args{$_} } keys %spec;
108 %args = Params::Validate::validate_with(
111 allow_extra => $allow_extra,
112 called => _caller_name(),
116 ( defined $instance ? $instance : () ),
121 *validatep = \&validated_list;
123 sub pos_validated_list {
127 push @spec, shift while ref $_[0];
131 my $cache_key = _cache_key( \%extra );
133 my $allow_extra = delete $extra{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
136 if ( exists $CACHED_SPECS{$cache_key} ) {
137 ( ref $CACHED_SPECS{$cache_key} eq 'ARRAY' )
139 "I was expecting an ARRAY-ref in the cached $cache_key parameter"
140 . " spec, you are doing something funky, stop it!";
141 @pv_spec = @{ $CACHED_SPECS{$cache_key} };
144 my $should_cache = exists $extra{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
146 # prepare the parameters ...
147 @pv_spec = map { _convert_to_param_validate_spec($_) } @spec;
149 $CACHED_SPECS{$cache_key} = \@pv_spec
155 $args[$_] = $pv_spec[$_]{constraint}->coerce( $args[$_] )
156 for grep { $pv_spec[$_] && $pv_spec[$_]{coerce} } 0 .. $#args;
158 @args = Params::Validate::validate_with(
161 allow_extra => $allow_extra,
162 called => _caller_name(),
171 if ( exists $spec->{MX_PARAMS_VALIDATE_CACHE_KEY} ) {
172 return delete $spec->{MX_PARAMS_VALIDATE_CACHE_KEY};
175 return refaddr( caller_cv(2) );
179 sub _convert_to_param_validate_spec {
183 $pv_spec{optional} = $spec->{optional}
184 if exists $spec->{optional};
186 $pv_spec{default} = $spec->{default}
187 if exists $spec->{default};
189 $pv_spec{coerce} = $spec->{coerce}
190 if exists $spec->{coerce};
193 if ( defined $spec->{isa} ) {
195 = _is_tc( $spec->{isa} )
196 || Moose::Util::TypeConstraints::find_or_parse_type_constraint(
198 || class_type( $spec->{isa} );
200 elsif ( defined $spec->{does} ) {
202 = _is_tc( $spec->{isa} )
203 || find_type_constraint( $spec->{does} )
204 || role_type( $spec->{does} );
207 $pv_spec{callbacks} = $spec->{callbacks}
208 if exists $spec->{callbacks};
211 $pv_spec{constraint} = $constraint;
214 { 'checking type constraint for ' . $constraint->name }
215 = sub { $constraint->check( $_[0] ) };
218 delete $pv_spec{coerce}
219 unless $pv_spec{constraint} && $pv_spec{constraint}->has_coercion;
225 my $maybe_tc = shift;
230 && $maybe_tc->isa('Moose::Meta::TypeConstraint');
234 my $depth = shift || 0;
236 return ( caller( 2 + $depth ) )[3];
241 # ABSTRACT: an extension of Params::Validate using Moose's types
251 use MooseX::Params::Validate;
254 my ( $self, %params ) = validated_hash(
256 bar => { isa => 'Str', default => 'Moose' },
258 return "Hooray for $params{bar}!";
263 my ( $foo, $baz, $gorch ) = validated_list(
265 foo => { isa => 'Foo' },
266 baz => { isa => 'ArrayRef | HashRef', optional => 1 },
267 gorch => { isa => 'ArrayRef[Int]', optional => 1 }
269 [ $foo, $baz, $gorch ];
274 This module fills a gap in Moose by adding method parameter validation
275 to Moose. This is just one of many developing options, it should not
276 be considered the "official" one by any means though.
278 You might also want to explore C<MooseX::Method::Signatures> and
283 It is not possible to introspect the method parameter specs; they are
284 created as needed when the method is called and cached for subsequent
291 =item B<validated_hash( \@_, %parameter_spec )>
293 This behaves similarly to the standard Params::Validate C<validate>
294 function and returns the captured values in a HASH. The one exception
295 is where if it spots an instance in the C<@_>, then it will handle
296 it appropriately (unlike Params::Validate which forces you to shift
299 The C<%parameter_spec> accepts the following options:
305 The C<isa> option can be either; class name, Moose type constraint
306 name or an anon Moose type constraint.
310 The C<does> option can be either; role name or an anon Moose type
315 This is the default value to be used if the value is not supplied.
319 As with Params::Validate, all options are considered required unless
320 otherwise specified. This option is passed directly to
325 If this is true and the parameter has a type constraint which has
326 coercions, then the coercion will be called for this parameter. If the
327 type does have coercions, then this parameter is ignored.
331 This function is also available under its old name, C<validate>.
333 =item B<validated_list( \@_, %parameter_spec )>
335 The C<%parameter_spec> accepts the same options as above, but returns
336 the parameters as positional values instead of a HASH. This is best
337 explained by example:
340 my ( $self, $foo, $bar ) = validated_list(
342 foo => { isa => 'Foo' },
343 bar => { isa => 'Bar' },
348 We capture the order in which you defined the parameters and then
349 return them as a list in the same order. If a param is marked optional
350 and not included, then it will be set to C<undef>.
352 Like C<validated_hash>, if it spots an object instance as the first
353 parameter of C<@_>, it will handle it appropriately, returning it as
356 This function is also available under its old name, C<validatep>.
358 =item B<pos_validated_list( \@_, $spec, $spec, ... )>
360 This function validates a list of positional parameters. Each C<$spec>
361 should validate one of the parameters in the list:
365 my ( $foo, $bar ) = pos_validated_list(
374 Unlike the other functions, this function I<cannot> find C<$self> in
375 the argument list. Make sure to shift it off yourself before doing
378 If a parameter is marked as optional and is not present, it will
379 simply not be returned.
381 If you want to pass in any of the cache control parameters described
382 below, simply pass them after the list of parameter validation specs:
386 my ( $foo, $bar ) = pos_validated_list(
390 MX_PARAMS_VALIDATE_NO_CACHE => 1,
398 =head1 ALLOWING EXTRA PARAMETERS
400 By default, any parameters not mentioned in the parameter spec cause this
401 module to throw an error. However, you can have have this module simply ignore
402 them by setting C<MX_PARAMS_VALIDATE_ALLOW_EXTRA> to a true value when calling
403 a validation subroutine.
405 When calling C<validated_hash> or C<pos_validated_list> the extra parameters
406 are simply returned in the hash or list as appropriate. However, when you call
407 C<validated_list> the extra parameters will not be returned at all. You can
408 get them by looking at the original value of C<@_>.
412 By default, this module exports the C<validated_hash>,
413 C<validated_list>, and C<pos_validated_list>.
415 If you would prefer to import the now deprecated functions C<validate>
416 and C<validatep> instead, you can use the C<:deprecated> tag to import
419 =head1 IMPORTANT NOTE ON CACHING
421 When a validation subroutine is called the first time, the parameter spec is
422 prepared and cached to avoid unnecessary regeneration. It uses the fully
423 qualified name of the subroutine (package + subname) as the cache key. In
424 99.999% of the use cases for this module, that will be the right thing to do.
426 However, I have (ab)used this module occasionally to handle dynamic
427 sets of parameters. In this special use case you can do a couple
428 things to better control the caching behavior.
434 Passing in the C<MX_PARAMS_VALIDATE_NO_CACHE> flag in the parameter
435 spec this will prevent the parameter spec from being cached.
438 my ( $self, %params ) = validated_hash(
440 foo => { isa => 'Foo' },
441 MX_PARAMS_VALIDATE_NO_CACHE => 1,
448 Passing in C<MX_PARAMS_VALIDATE_CACHE_KEY> with a value to be used as
449 the cache key will bypass the normal cache key generation.
452 my ( $self, %params ) = validated_hash(
454 foo => { isa => 'Foo' },
455 MX_PARAMS_VALIDATE_CACHE_KEY => 'foo-42',
464 Dave Rolsky E<lt>autarch@urth.orgE<gt>
468 Please submit bugs to the CPAN RT system at
469 http://rt.cpan.org/NoAuth/ReportBug.html?Queue=moosex-params-validate or via
470 email at bug-moosex-params-validate@rt.cpan.org.