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 if ( exists $CACHED_SPECS{$cache_key} ) {
33 ( ref $CACHED_SPECS{$cache_key} eq 'HASH' )
35 "I was expecting a HASH-ref in the cached $cache_key parameter"
36 . " spec, you are doing something funky, stop it!";
37 %spec = %{ $CACHED_SPECS{$cache_key} };
40 my $should_cache = delete $spec{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
42 $spec{$_} = _convert_to_param_validate_spec( $spec{$_} )
45 $CACHED_SPECS{$cache_key} = \%spec
50 $instance = shift @$args if blessed $args->[0];
54 $args{$_} = $spec{$_}{constraint}->coerce( $args{$_} )
55 for grep { $spec{$_}{coerce} && exists $args{$_} } keys %spec;
57 %args = Params::Validate::validate_with(
60 called => _caller_name(),
63 return ( ( defined $instance ? $instance : () ), %args );
66 *validate = \&validated_hash;
69 my ( $args, @spec ) = @_;
73 my $cache_key = _cache_key( \%spec );
76 if ( exists $CACHED_SPECS{$cache_key} ) {
77 ( ref $CACHED_SPECS{$cache_key} eq 'ARRAY' )
79 "I was expecting a ARRAY-ref in the cached $cache_key parameter"
80 . " spec, you are doing something funky, stop it!";
81 %spec = %{ $CACHED_SPECS{$cache_key}->[0] };
82 @ordered_spec = @{ $CACHED_SPECS{$cache_key}->[1] };
85 my $should_cache = delete $spec{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
87 @ordered_spec = grep { exists $spec{$_} } @spec;
89 $spec{$_} = _convert_to_param_validate_spec( $spec{$_} )
92 $CACHED_SPECS{$cache_key} = [ \%spec, \@ordered_spec ]
97 $instance = shift @$args if blessed $args->[0];
101 $args{$_} = $spec{$_}{constraint}->coerce( $args{$_} )
102 for grep { $spec{$_}{coerce} && exists $args{$_} } keys %spec;
104 %args = Params::Validate::validate_with(
107 called => _caller_name(),
111 ( defined $instance ? $instance : () ),
116 *validatep = \&validated_list;
118 sub pos_validated_list {
122 push @spec, shift while ref $_[0];
126 my $cache_key = _cache_key( \%extra );
129 if ( exists $CACHED_SPECS{$cache_key} ) {
130 ( ref $CACHED_SPECS{$cache_key} eq 'ARRAY' )
132 "I was expecting an ARRAY-ref in the cached $cache_key parameter"
133 . " spec, you are doing something funky, stop it!";
134 @pv_spec = @{ $CACHED_SPECS{$cache_key} };
137 my $should_cache = exists $extra{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
139 # prepare the parameters ...
140 @pv_spec = map { _convert_to_param_validate_spec($_) } @spec;
142 $CACHED_SPECS{$cache_key} = \@pv_spec
148 $args[$_] = $pv_spec[$_]{constraint}->coerce( $args[$_] )
149 for grep { $pv_spec[$_] && $pv_spec[$_]{coerce} } 0 .. $#args;
151 @args = Params::Validate::validate_with(
154 called => _caller_name(),
163 if ( exists $spec->{MX_PARAMS_VALIDATE_CACHE_KEY} ) {
164 return delete $spec->{MX_PARAMS_VALIDATE_CACHE_KEY};
167 return refaddr( caller_cv(2) );
171 sub _convert_to_param_validate_spec {
175 $pv_spec{optional} = $spec->{optional}
176 if exists $spec->{optional};
178 $pv_spec{default} = $spec->{default}
179 if exists $spec->{default};
181 $pv_spec{coerce} = $spec->{coerce}
182 if exists $spec->{coerce};
185 if ( defined $spec->{isa} ) {
187 = _is_tc( $spec->{isa} )
188 || Moose::Util::TypeConstraints::find_or_parse_type_constraint(
190 || class_type( $spec->{isa} );
192 elsif ( defined $spec->{does} ) {
194 = _is_tc( $spec->{isa} )
195 || find_type_constraint( $spec->{does} )
196 || role_type( $spec->{does} );
199 $pv_spec{callbacks} = $spec->{callbacks}
200 if exists $spec->{callbacks};
203 $pv_spec{constraint} = $constraint;
206 { 'checking type constraint for ' . $constraint->name }
207 = sub { $constraint->check( $_[0] ) };
210 delete $pv_spec{coerce}
211 unless $pv_spec{constraint} && $pv_spec{constraint}->has_coercion;
217 my $maybe_tc = shift;
222 && $maybe_tc->isa('Moose::Meta::TypeConstraint');
226 my $depth = shift || 0;
228 return ( caller( 2 + $depth ) )[3];
239 MooseX::Params::Validate - an extension of Params::Validate for using Moose's types
245 use MooseX::Params::Validate;
248 my ( $self, %params ) = validated_hash(
250 bar => { isa => 'Str', default => 'Moose' },
252 return "Horray for $params{bar}!";
257 my ( $foo, $baz, $gorch ) = validated_list(
259 foo => { isa => 'Foo' },
260 baz => { isa => 'ArrayRef | HashRef', optional => 1 },
261 gorch => { isa => 'ArrayRef[Int]', optional => 1 }
263 [ $foo, $baz, $gorch ];
268 This module fills a gap in Moose by adding method parameter validation
269 to Moose. This is just one of many developing options, it should not
270 be considered the "official" one by any means though.
272 You might also want to explore C<MooseX::Method::Signatures> and
277 It is not possible to introspect the method parameter specs, they are
278 created as needed when the method is called and cached for subsequent
285 =item B<validated_hash( \@_, %parameter_spec )>
287 This behaves similar to the standard Params::Validate C<validate>
288 function and returns the captured values in a HASH. The one exception
289 being that if it spots an instance in the C<@_>, then it will handle
290 it appropriately (unlike Params::Validate which forces you to shift
293 The C<%parameter_spec> accepts the following options:
299 The C<isa> option can be either; class name, Moose type constraint
300 name or an anon Moose type constraint.
304 The C<does> option can be either; role name or an anon Moose type
309 This is the default value to be used if the value is not supplied.
313 As with Params::Validate, all options are considered required unless
314 otherwise specified. This option is passed directly to
319 If this is true and the parameter has a type constraint which has
320 coercions, then the coercion will be called for this parameter. If the
321 type does have coercions, then this parameter is ignored.
325 This function is also available under its old name, C<validate>.
327 =item B<validated_list( \@_, %parameter_spec )>
329 The C<%parameter_spec> accepts the same options as above, but returns
330 the parameters as positional values instead of a HASH. This is best
331 explained by example:
334 my ( $self, $foo, $bar ) = validated_list(
336 foo => { isa => 'Foo' },
337 bar => { isa => 'Bar' },
342 We capture the order in which you defined the parameters and then
343 return them as a list in the same order. If a param is marked optional
344 and not included, then it will be set to C<undef>.
346 Like C<validated_hash>, if it spots an object instance as the first
347 parameter of C<@_>, it will handle it appropriately, returning it as
350 This function is also available under its old name, C<validatep>.
352 =item B<pos_validated_list( \@_, $spec, $spec, ... )>
354 This function validates a list of positional parameters. Each C<$spec>
355 should validate one of the parameters in the list:
359 my ( $foo, $bar ) = pos_validated_list(
368 Unlike the other functions, this function I<cannot> find C<$self> in
369 the argument list. Make sure to shift it off yourself before doing
372 If a parameter is marked as optional and is not present, it will
373 simply not be returned.
375 If you want to pass in any of the cache control parameters described
376 below, simply pass them after the list of parameter validation specs:
380 my ( $foo, $bar ) = pos_validated_list(
384 MX_PARAMS_VALIDATE_NO_CACHE => 1,
394 By default, this module exports the C<validated_hash>,
395 C<validated_list>, and C<pos_validated_list>.
397 If you would prefer to import the now deprecated functions C<validate>
398 and C<validatep> instead, you can use the C<:deprecated> tag to import
401 =head1 IMPORTANT NOTE ON CACHING
403 When C<validate> or C<validatep> are called the first time, the
404 parameter spec is prepared and cached to avoid unnecessary
405 regeneration. It uses the fully qualified name of the subroutine
406 (package + subname) as the cache key. In 99.999% of the use cases for
407 this module, that will be the right thing to do.
409 However, I have (ab)used this module occasionally to handle dynamic
410 sets of parameters. In this special use case you can do a couple
411 things to better control the caching behavior.
417 Passing in the C<MX_PARAMS_VALIDATE_NO_CACHE> flag in the parameter
418 spec this will prevent the parameter spec from being cached.
421 my ( $self, %params ) = validated_hash(
423 foo => { isa => 'Foo' },
424 MX_PARAMS_VALIDATE_NO_CACHE => 1,
431 Passing in C<MX_PARAMS_VALIDATE_CACHE_KEY> with a value to be used as
432 the cache key will bypass the normal cache key generation.
435 my ( $self, %params ) = validated_hash(
437 foo => { isa => 'Foo' },
438 MX_PARAMS_VALIDATE_CACHE_KEY => 'foo-42',
447 All complex software has bugs lurking in it, and this module is no
448 exception. If you find a bug please either email me, or add the bug to
453 Stevan Little E<lt>stevan.little@iinteractive.comE<gt>
455 Dave Rolsky E<lt>autarch@urth.orgE<gt>
457 =head1 COPYRIGHT AND LICENSE
459 Copyright 2007-2009 by Infinity Interactive, Inc.
461 L<http://www.iinteractive.com>
463 This library is free software; you can redistribute it and/or modify
464 it under the same terms as Perl itself.