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 $VERSION = '0.14';
23 our $AUTHORITY = 'cpan:STEVAN';
28 my ( $args, %spec ) = @_;
30 my $cache_key = _cache_key( \%spec );
32 my $allow_extra = delete $spec{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
34 if ( exists $CACHED_SPECS{$cache_key} ) {
35 ( ref $CACHED_SPECS{$cache_key} eq 'HASH' )
37 "I was expecting a HASH-ref in the cached $cache_key parameter"
38 . " spec, you are doing something funky, stop it!";
39 %spec = %{ $CACHED_SPECS{$cache_key} };
42 my $should_cache = delete $spec{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
44 $spec{$_} = _convert_to_param_validate_spec( $spec{$_} )
47 $CACHED_SPECS{$cache_key} = \%spec
52 $instance = shift @$args if blessed $args->[0];
56 $args{$_} = $spec{$_}{constraint}->coerce( $args{$_} )
57 for grep { $spec{$_}{coerce} && exists $args{$_} } keys %spec;
59 %args = Params::Validate::validate_with(
62 allow_extra => $allow_extra,
63 called => _caller_name(),
66 return ( ( defined $instance ? $instance : () ), %args );
69 *validate = \&validated_hash;
72 my ( $args, @spec ) = @_;
76 my $cache_key = _cache_key( \%spec );
78 my $allow_extra = delete $spec{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
81 if ( exists $CACHED_SPECS{$cache_key} ) {
82 ( ref $CACHED_SPECS{$cache_key} eq 'ARRAY' )
84 "I was expecting a ARRAY-ref in the cached $cache_key parameter"
85 . " spec, you are doing something funky, stop it!";
86 %spec = %{ $CACHED_SPECS{$cache_key}->[0] };
87 @ordered_spec = @{ $CACHED_SPECS{$cache_key}->[1] };
90 my $should_cache = delete $spec{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
92 @ordered_spec = grep { exists $spec{$_} } @spec;
94 $spec{$_} = _convert_to_param_validate_spec( $spec{$_} )
97 $CACHED_SPECS{$cache_key} = [ \%spec, \@ordered_spec ]
102 $instance = shift @$args if blessed $args->[0];
106 $args{$_} = $spec{$_}{constraint}->coerce( $args{$_} )
107 for grep { $spec{$_}{coerce} && exists $args{$_} } keys %spec;
109 %args = Params::Validate::validate_with(
112 allow_extra => $allow_extra,
113 called => _caller_name(),
117 ( defined $instance ? $instance : () ),
122 *validatep = \&validated_list;
124 sub pos_validated_list {
128 push @spec, shift while ref $_[0];
132 my $cache_key = _cache_key( \%extra );
134 my $allow_extra = delete $extra{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
137 if ( exists $CACHED_SPECS{$cache_key} ) {
138 ( ref $CACHED_SPECS{$cache_key} eq 'ARRAY' )
140 "I was expecting an ARRAY-ref in the cached $cache_key parameter"
141 . " spec, you are doing something funky, stop it!";
142 @pv_spec = @{ $CACHED_SPECS{$cache_key} };
145 my $should_cache = exists $extra{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
147 # prepare the parameters ...
148 @pv_spec = map { _convert_to_param_validate_spec($_) } @spec;
150 $CACHED_SPECS{$cache_key} = \@pv_spec
156 $args[$_] = $pv_spec[$_]{constraint}->coerce( $args[$_] )
157 for grep { $pv_spec[$_] && $pv_spec[$_]{coerce} } 0 .. $#args;
159 @args = Params::Validate::validate_with(
162 allow_extra => $allow_extra,
163 called => _caller_name(),
172 if ( exists $spec->{MX_PARAMS_VALIDATE_CACHE_KEY} ) {
173 return delete $spec->{MX_PARAMS_VALIDATE_CACHE_KEY};
176 return refaddr( caller_cv(2) );
180 sub _convert_to_param_validate_spec {
184 $pv_spec{optional} = $spec->{optional}
185 if exists $spec->{optional};
187 $pv_spec{default} = $spec->{default}
188 if exists $spec->{default};
190 $pv_spec{coerce} = $spec->{coerce}
191 if exists $spec->{coerce};
194 if ( defined $spec->{isa} ) {
196 = _is_tc( $spec->{isa} )
197 || Moose::Util::TypeConstraints::find_or_parse_type_constraint(
199 || class_type( $spec->{isa} );
201 elsif ( defined $spec->{does} ) {
203 = _is_tc( $spec->{isa} )
204 || find_type_constraint( $spec->{does} )
205 || role_type( $spec->{does} );
208 $pv_spec{callbacks} = $spec->{callbacks}
209 if exists $spec->{callbacks};
212 $pv_spec{constraint} = $constraint;
215 { 'checking type constraint for ' . $constraint->name }
216 = sub { $constraint->check( $_[0] ) };
219 delete $pv_spec{coerce}
220 unless $pv_spec{constraint} && $pv_spec{constraint}->has_coercion;
226 my $maybe_tc = shift;
231 && $maybe_tc->isa('Moose::Meta::TypeConstraint');
235 my $depth = shift || 0;
237 return ( caller( 2 + $depth ) )[3];
242 # ABSTRACT: an extension of Params::Validate using Moose's types
252 use MooseX::Params::Validate;
255 my ( $self, %params ) = validated_hash(
257 bar => { isa => 'Str', default => 'Moose' },
259 return "Hooray for $params{bar}!";
264 my ( $foo, $baz, $gorch ) = validated_list(
266 foo => { isa => 'Foo' },
267 baz => { isa => 'ArrayRef | HashRef', optional => 1 },
268 gorch => { isa => 'ArrayRef[Int]', optional => 1 }
270 [ $foo, $baz, $gorch ];
275 This module fills a gap in Moose by adding method parameter validation
276 to Moose. This is just one of many developing options, it should not
277 be considered the "official" one by any means though.
279 You might also want to explore C<MooseX::Method::Signatures> and
284 It is not possible to introspect the method parameter specs; they are
285 created as needed when the method is called and cached for subsequent
292 =item B<validated_hash( \@_, %parameter_spec )>
294 This behaves similarly to the standard Params::Validate C<validate>
295 function and returns the captured values in a HASH. The one exception
296 is where if it spots an instance in the C<@_>, then it will handle
297 it appropriately (unlike Params::Validate which forces you to shift
300 The C<%parameter_spec> accepts the following options:
306 The C<isa> option can be either; class name, Moose type constraint
307 name or an anon Moose type constraint.
311 The C<does> option can be either; role name or an anon Moose type
316 This is the default value to be used if the value is not supplied.
320 As with Params::Validate, all options are considered required unless
321 otherwise specified. This option is passed directly to
326 If this is true and the parameter has a type constraint which has
327 coercions, then the coercion will be called for this parameter. If the
328 type does have coercions, then this parameter is ignored.
332 This function is also available under its old name, C<validate>.
334 =item B<validated_list( \@_, %parameter_spec )>
336 The C<%parameter_spec> accepts the same options as above, but returns
337 the parameters as positional values instead of a HASH. This is best
338 explained by example:
341 my ( $self, $foo, $bar ) = validated_list(
343 foo => { isa => 'Foo' },
344 bar => { isa => 'Bar' },
349 We capture the order in which you defined the parameters and then
350 return them as a list in the same order. If a param is marked optional
351 and not included, then it will be set to C<undef>.
353 Like C<validated_hash>, if it spots an object instance as the first
354 parameter of C<@_>, it will handle it appropriately, returning it as
357 This function is also available under its old name, C<validatep>.
359 =item B<pos_validated_list( \@_, $spec, $spec, ... )>
361 This function validates a list of positional parameters. Each C<$spec>
362 should validate one of the parameters in the list:
366 my ( $foo, $bar ) = pos_validated_list(
375 Unlike the other functions, this function I<cannot> find C<$self> in
376 the argument list. Make sure to shift it off yourself before doing
379 If a parameter is marked as optional and is not present, it will
380 simply not be returned.
382 If you want to pass in any of the cache control parameters described
383 below, simply pass them after the list of parameter validation specs:
387 my ( $foo, $bar ) = pos_validated_list(
391 MX_PARAMS_VALIDATE_NO_CACHE => 1,
399 =head1 ALLOWING EXTRA PARAMETERS
401 By default, any parameters not mentioned in the parameter spec cause this
402 module to throw an error. However, you can have have this module simply ignore
403 them by setting C<MX_PARAMS_VALIDATE_ALLOW_EXTRA> to a true value when calling
404 a validation subroutine.
406 When calling C<validated_hash> or C<pos_validated_list> the extra parameters
407 are simply returned in the hash or list as appropriate. However, when you call
408 C<validated_list> the extra parameters will not be returned at all. You can
409 get them by looking at the original value of C<@_>.
413 By default, this module exports the C<validated_hash>,
414 C<validated_list>, and C<pos_validated_list>.
416 If you would prefer to import the now deprecated functions C<validate>
417 and C<validatep> instead, you can use the C<:deprecated> tag to import
420 =head1 IMPORTANT NOTE ON CACHING
422 When a validation subroutine is called the first time, the parameter spec is
423 prepared and cached to avoid unnecessary regeneration. It uses the fully
424 qualified name of the subroutine (package + subname) as the cache key. In
425 99.999% of the use cases for this module, that will be the right thing to do.
427 However, I have (ab)used this module occasionally to handle dynamic
428 sets of parameters. In this special use case you can do a couple
429 things to better control the caching behavior.
435 Passing in the C<MX_PARAMS_VALIDATE_NO_CACHE> flag in the parameter
436 spec this will prevent the parameter spec from being cached.
439 my ( $self, %params ) = validated_hash(
441 foo => { isa => 'Foo' },
442 MX_PARAMS_VALIDATE_NO_CACHE => 1,
449 Passing in C<MX_PARAMS_VALIDATE_CACHE_KEY> with a value to be used as
450 the cache key will bypass the normal cache key generation.
453 my ( $self, %params ) = validated_hash(
455 foo => { isa => 'Foo' },
456 MX_PARAMS_VALIDATE_CACHE_KEY => 'foo-42',
465 Stevan Little E<lt>stevan.little@iinteractive.comE<gt>
467 Dave Rolsky E<lt>autarch@urth.orgE<gt>
471 Please submit bugs to the CPAN RT system at
472 http://rt.cpan.org/NoAuth/ReportBug.html?Queue=moosex-params-validate or via
473 email at bug-moosex-params-validate@rt.cpan.org.