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];
248 MooseX::Params::Validate - an extension of Params::Validate for using Moose's types
254 use MooseX::Params::Validate;
257 my ( $self, %params ) = validated_hash(
259 bar => { isa => 'Str', default => 'Moose' },
261 return "Hooray for $params{bar}!";
266 my ( $foo, $baz, $gorch ) = validated_list(
268 foo => { isa => 'Foo' },
269 baz => { isa => 'ArrayRef | HashRef', optional => 1 },
270 gorch => { isa => 'ArrayRef[Int]', optional => 1 }
272 [ $foo, $baz, $gorch ];
277 This module fills a gap in Moose by adding method parameter validation
278 to Moose. This is just one of many developing options, it should not
279 be considered the "official" one by any means though.
281 You might also want to explore C<MooseX::Method::Signatures> and
286 It is not possible to introspect the method parameter specs; they are
287 created as needed when the method is called and cached for subsequent
294 =item B<validated_hash( \@_, %parameter_spec )>
296 This behaves similarly to the standard Params::Validate C<validate>
297 function and returns the captured values in a HASH. The one exception
298 is where if it spots an instance in the C<@_>, then it will handle
299 it appropriately (unlike Params::Validate which forces you to shift
302 The C<%parameter_spec> accepts the following options:
308 The C<isa> option can be either; class name, Moose type constraint
309 name or an anon Moose type constraint.
313 The C<does> option can be either; role name or an anon Moose type
318 This is the default value to be used if the value is not supplied.
322 As with Params::Validate, all options are considered required unless
323 otherwise specified. This option is passed directly to
328 If this is true and the parameter has a type constraint which has
329 coercions, then the coercion will be called for this parameter. If the
330 type does have coercions, then this parameter is ignored.
334 This function is also available under its old name, C<validate>.
336 =item B<validated_list( \@_, %parameter_spec )>
338 The C<%parameter_spec> accepts the same options as above, but returns
339 the parameters as positional values instead of a HASH. This is best
340 explained by example:
343 my ( $self, $foo, $bar ) = validated_list(
345 foo => { isa => 'Foo' },
346 bar => { isa => 'Bar' },
351 We capture the order in which you defined the parameters and then
352 return them as a list in the same order. If a param is marked optional
353 and not included, then it will be set to C<undef>.
355 Like C<validated_hash>, if it spots an object instance as the first
356 parameter of C<@_>, it will handle it appropriately, returning it as
359 This function is also available under its old name, C<validatep>.
361 =item B<pos_validated_list( \@_, $spec, $spec, ... )>
363 This function validates a list of positional parameters. Each C<$spec>
364 should validate one of the parameters in the list:
368 my ( $foo, $bar ) = pos_validated_list(
377 Unlike the other functions, this function I<cannot> find C<$self> in
378 the argument list. Make sure to shift it off yourself before doing
381 If a parameter is marked as optional and is not present, it will
382 simply not be returned.
384 If you want to pass in any of the cache control parameters described
385 below, simply pass them after the list of parameter validation specs:
389 my ( $foo, $bar ) = pos_validated_list(
393 MX_PARAMS_VALIDATE_NO_CACHE => 1,
401 =head1 ALLOWING EXTRA PARAMETERS
403 By default, any parameters not mentioned in the parameter spec cause this
404 module to throw an error. However, you can have have this module simply ignore
405 them by setting C<MX_PARAMS_VALIDATE_ALLOW_EXTRA> to a true value when calling
406 a validation subroutine.
408 When calling C<validated_hash> or C<pos_validated_list> the extra parameters
409 are simply returned in the hash or list as appropriate. However, when you call
410 C<validated_list> the extra parameters will not be returned at all. You can
411 get them by looking at the original value of C<@_>.
415 By default, this module exports the C<validated_hash>,
416 C<validated_list>, and C<pos_validated_list>.
418 If you would prefer to import the now deprecated functions C<validate>
419 and C<validatep> instead, you can use the C<:deprecated> tag to import
422 =head1 IMPORTANT NOTE ON CACHING
424 When a validation subroutine is called the first time, the parameter spec is
425 prepared and cached to avoid unnecessary regeneration. It uses the fully
426 qualified name of the subroutine (package + subname) as the cache key. In
427 99.999% of the use cases for this module, that will be the right thing to do.
429 However, I have (ab)used this module occasionally to handle dynamic
430 sets of parameters. In this special use case you can do a couple
431 things to better control the caching behavior.
437 Passing in the C<MX_PARAMS_VALIDATE_NO_CACHE> flag in the parameter
438 spec this will prevent the parameter spec from being cached.
441 my ( $self, %params ) = validated_hash(
443 foo => { isa => 'Foo' },
444 MX_PARAMS_VALIDATE_NO_CACHE => 1,
451 Passing in C<MX_PARAMS_VALIDATE_CACHE_KEY> with a value to be used as
452 the cache key will bypass the normal cache key generation.
455 my ( $self, %params ) = validated_hash(
457 foo => { isa => 'Foo' },
458 MX_PARAMS_VALIDATE_CACHE_KEY => 'foo-42',
467 All complex software has bugs lurking in it, and this module is no
468 exception. If you find a bug please either email me, or add the bug to
473 Stevan Little E<lt>stevan.little@iinteractive.comE<gt>
475 Dave Rolsky E<lt>autarch@urth.orgE<gt>
477 =head1 COPYRIGHT AND LICENSE
479 Copyright 2007-2009 by Infinity Interactive, Inc.
481 L<http://www.iinteractive.com>
483 This library is free software; you can redistribute it and/or modify
484 it under the same terms as Perl itself.