Fix bad prereqs and make sure we explicitly ask for module versions we need
[gitmo/MooseX-Params-Validate.git] / lib / MooseX / Params / Validate.pm
CommitLineData
d9d1529d 1package MooseX::Params::Validate;
2
3use strict;
4use warnings;
5
6use Carp 'confess';
7use Devel::Caller 'caller_cv';
954fa4ca 8use Scalar::Util 'blessed', 'refaddr', 'reftype';
d9d1529d 9
bde73fcc 10use Moose 0.58 ();
11use Moose::Util::TypeConstraints
12 qw( find_type_constraint class_type role_type );
13use Params::Validate 0.88 ();
d9d1529d 14use Sub::Exporter -setup => {
15 exports => [
16 qw( validated_hash validated_list pos_validated_list validate validatep )
17 ],
18 groups => {
19 default => [qw( validated_hash validated_list pos_validated_list )],
20 deprecated => [qw( validate validatep )],
21 },
22};
23
d9d1529d 24my %CACHED_SPECS;
25
26sub validated_hash {
27 my ( $args, %spec ) = @_;
28
29 my $cache_key = _cache_key( \%spec );
30
df3c7e86 31 my $allow_extra = delete $spec{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
32
d9d1529d 33 if ( exists $CACHED_SPECS{$cache_key} ) {
34 ( ref $CACHED_SPECS{$cache_key} eq 'HASH' )
35 || confess
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} };
39 }
40 else {
41 my $should_cache = delete $spec{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
42
43 $spec{$_} = _convert_to_param_validate_spec( $spec{$_} )
44 foreach keys %spec;
45
46 $CACHED_SPECS{$cache_key} = \%spec
47 if $should_cache;
48 }
49
50 my $instance;
51 $instance = shift @$args if blessed $args->[0];
52
a2433d98 53 my %args
954fa4ca 54 = @$args == 1
55 && ref $args->[0]
56 && reftype( $args->[0] ) eq 'HASH' ? %{ $args->[0] } : @$args;
d9d1529d 57
58 $args{$_} = $spec{$_}{constraint}->coerce( $args{$_} )
59 for grep { $spec{$_}{coerce} && exists $args{$_} } keys %spec;
60
61 %args = Params::Validate::validate_with(
df3c7e86 62 params => \%args,
63 spec => \%spec,
64 allow_extra => $allow_extra,
65 called => _caller_name(),
d9d1529d 66 );
67
68 return ( ( defined $instance ? $instance : () ), %args );
69}
70
71*validate = \&validated_hash;
72
73sub validated_list {
74 my ( $args, @spec ) = @_;
75
76 my %spec = @spec;
77
78 my $cache_key = _cache_key( \%spec );
79
df3c7e86 80 my $allow_extra = delete $spec{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
81
d9d1529d 82 my @ordered_spec;
83 if ( exists $CACHED_SPECS{$cache_key} ) {
84 ( ref $CACHED_SPECS{$cache_key} eq 'ARRAY' )
85 || confess
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] };
90 }
91 else {
92 my $should_cache = delete $spec{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
93
94 @ordered_spec = grep { exists $spec{$_} } @spec;
95
96 $spec{$_} = _convert_to_param_validate_spec( $spec{$_} )
97 foreach keys %spec;
98
99 $CACHED_SPECS{$cache_key} = [ \%spec, \@ordered_spec ]
100 if $should_cache;
101 }
102
103 my $instance;
104 $instance = shift @$args if blessed $args->[0];
105
a2433d98 106 my %args
954fa4ca 107 = @$args == 1
108 && ref $args->[0]
109 && reftype( $args->[0] ) eq 'HASH' ? %{ $args->[0] } : @$args;
d9d1529d 110
111 $args{$_} = $spec{$_}{constraint}->coerce( $args{$_} )
112 for grep { $spec{$_}{coerce} && exists $args{$_} } keys %spec;
113
114 %args = Params::Validate::validate_with(
df3c7e86 115 params => \%args,
116 spec => \%spec,
117 allow_extra => $allow_extra,
118 called => _caller_name(),
d9d1529d 119 );
120
121 return (
122 ( defined $instance ? $instance : () ),
123 @args{@ordered_spec}
124 );
125}
126
127*validatep = \&validated_list;
128
129sub pos_validated_list {
130 my $args = shift;
131
132 my @spec;
133 push @spec, shift while ref $_[0];
134
135 my %extra = @_;
136
137 my $cache_key = _cache_key( \%extra );
138
df3c7e86 139 my $allow_extra = delete $extra{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
140
d9d1529d 141 my @pv_spec;
142 if ( exists $CACHED_SPECS{$cache_key} ) {
143 ( ref $CACHED_SPECS{$cache_key} eq 'ARRAY' )
144 || confess
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} };
148 }
149 else {
150 my $should_cache = exists $extra{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
151
152 # prepare the parameters ...
153 @pv_spec = map { _convert_to_param_validate_spec($_) } @spec;
154
155 $CACHED_SPECS{$cache_key} = \@pv_spec
156 if $should_cache;
157 }
158
ddc128be 159 my @args = @$args;
d9d1529d 160
161 $args[$_] = $pv_spec[$_]{constraint}->coerce( $args[$_] )
162 for grep { $pv_spec[$_] && $pv_spec[$_]{coerce} } 0 .. $#args;
163
164 @args = Params::Validate::validate_with(
df3c7e86 165 params => \@args,
166 spec => \@pv_spec,
167 allow_extra => $allow_extra,
168 called => _caller_name(),
d9d1529d 169 );
170
171 return @args;
172}
173
174sub _cache_key {
175 my $spec = shift;
176
177 if ( exists $spec->{MX_PARAMS_VALIDATE_CACHE_KEY} ) {
178 return delete $spec->{MX_PARAMS_VALIDATE_CACHE_KEY};
179 }
180 else {
181 return refaddr( caller_cv(2) );
182 }
183}
184
185sub _convert_to_param_validate_spec {
186 my ($spec) = @_;
187 my %pv_spec;
188
189 $pv_spec{optional} = $spec->{optional}
190 if exists $spec->{optional};
191
192 $pv_spec{default} = $spec->{default}
193 if exists $spec->{default};
194
195 $pv_spec{coerce} = $spec->{coerce}
196 if exists $spec->{coerce};
197
198 my $constraint;
199 if ( defined $spec->{isa} ) {
200 $constraint
201 = _is_tc( $spec->{isa} )
202 || Moose::Util::TypeConstraints::find_or_parse_type_constraint(
203 $spec->{isa} )
204 || class_type( $spec->{isa} );
205 }
206 elsif ( defined $spec->{does} ) {
207 $constraint
208 = _is_tc( $spec->{isa} )
209 || find_type_constraint( $spec->{does} )
210 || role_type( $spec->{does} );
211 }
212
213 $pv_spec{callbacks} = $spec->{callbacks}
214 if exists $spec->{callbacks};
215
216 if ($constraint) {
217 $pv_spec{constraint} = $constraint;
218
219 $pv_spec{callbacks}
220 { 'checking type constraint for ' . $constraint->name }
221 = sub { $constraint->check( $_[0] ) };
222 }
223
224 delete $pv_spec{coerce}
225 unless $pv_spec{constraint} && $pv_spec{constraint}->has_coercion;
226
227 return \%pv_spec;
228}
229
230sub _is_tc {
231 my $maybe_tc = shift;
232
233 return $maybe_tc
234 if defined $maybe_tc
235 && blessed $maybe_tc
236 && $maybe_tc->isa('Moose::Meta::TypeConstraint');
237}
238
239sub _caller_name {
240 my $depth = shift || 0;
241
242 return ( caller( 2 + $depth ) )[3];
243}
244
2451;
246
c8c6d6c9 247# ABSTRACT: an extension of Params::Validate using Moose's types
248
d9d1529d 249__END__
250
251=pod
252
d9d1529d 253=head1 SYNOPSIS
254
255 package Foo;
256 use Moose;
257 use MooseX::Params::Validate;
258
259 sub foo {
260 my ( $self, %params ) = validated_hash(
261 \@_,
262 bar => { isa => 'Str', default => 'Moose' },
263 );
f5e350c1 264 return "Hooray for $params{bar}!";
d9d1529d 265 }
266
267 sub bar {
268 my $self = shift;
269 my ( $foo, $baz, $gorch ) = validated_list(
270 \@_,
271 foo => { isa => 'Foo' },
272 baz => { isa => 'ArrayRef | HashRef', optional => 1 },
273 gorch => { isa => 'ArrayRef[Int]', optional => 1 }
274 );
275 [ $foo, $baz, $gorch ];
276 }
277
278=head1 DESCRIPTION
279
280This module fills a gap in Moose by adding method parameter validation
281to Moose. This is just one of many developing options, it should not
282be considered the "official" one by any means though.
283
284You might also want to explore C<MooseX::Method::Signatures> and
f5e350c1 285C<MooseX::Declare>.
d9d1529d 286
287=head1 CAVEATS
288
f5e350c1 289It is not possible to introspect the method parameter specs; they are
d9d1529d 290created as needed when the method is called and cached for subsequent
291calls.
292
293=head1 EXPORTS
294
295=over 4
296
297=item B<validated_hash( \@_, %parameter_spec )>
298
f5e350c1 299This behaves similarly to the standard Params::Validate C<validate>
d9d1529d 300function and returns the captured values in a HASH. The one exception
f5e350c1 301is where if it spots an instance in the C<@_>, then it will handle
d9d1529d 302it appropriately (unlike Params::Validate which forces you to shift
303you C<$self> first).
304
c8399757 305The values in C<@_> can either be a set of name-value pairs or a single hash
306reference.
307
d9d1529d 308The C<%parameter_spec> accepts the following options:
309
310=over 4
311
312=item I<isa>
313
314The C<isa> option can be either; class name, Moose type constraint
315name or an anon Moose type constraint.
316
317=item I<does>
318
319The C<does> option can be either; role name or an anon Moose type
320constraint.
321
322=item I<default>
323
324This is the default value to be used if the value is not supplied.
325
326=item I<optional>
327
328As with Params::Validate, all options are considered required unless
329otherwise specified. This option is passed directly to
330Params::Validate.
331
332=item I<coerce>
333
334If this is true and the parameter has a type constraint which has
335coercions, then the coercion will be called for this parameter. If the
336type does have coercions, then this parameter is ignored.
337
338=back
339
340This function is also available under its old name, C<validate>.
341
342=item B<validated_list( \@_, %parameter_spec )>
343
344The C<%parameter_spec> accepts the same options as above, but returns
345the parameters as positional values instead of a HASH. This is best
346explained by example:
347
348 sub foo {
349 my ( $self, $foo, $bar ) = validated_list(
350 \@_,
351 foo => { isa => 'Foo' },
352 bar => { isa => 'Bar' },
353 );
354 $foo->baz($bar);
355 }
356
357We capture the order in which you defined the parameters and then
358return them as a list in the same order. If a param is marked optional
359and not included, then it will be set to C<undef>.
360
c8399757 361The values in C<@_> can either be a set of name-value pairs or a single hash
362reference.
363
d9d1529d 364Like C<validated_hash>, if it spots an object instance as the first
365parameter of C<@_>, it will handle it appropriately, returning it as
366the first argument.
367
368This function is also available under its old name, C<validatep>.
369
370=item B<pos_validated_list( \@_, $spec, $spec, ... )>
371
372This function validates a list of positional parameters. Each C<$spec>
373should validate one of the parameters in the list:
374
375 sub foo {
376 my $self = shift;
377 my ( $foo, $bar ) = pos_validated_list(
378 \@_,
379 { isa => 'Foo' },
380 { isa => 'Bar' },
381 );
382
383 ...
384 }
385
386Unlike the other functions, this function I<cannot> find C<$self> in
387the argument list. Make sure to shift it off yourself before doing
388validation.
389
ddc128be 390The values in C<@_> must be a list of values. You cannot pass the values as an
391array reference, because this cannot be distinguished from passing one value
392which is itself an array reference.
c8399757 393
d9d1529d 394If a parameter is marked as optional and is not present, it will
395simply not be returned.
396
397If you want to pass in any of the cache control parameters described
398below, simply pass them after the list of parameter validation specs:
399
400 sub foo {
401 my $self = shift;
402 my ( $foo, $bar ) = pos_validated_list(
403 \@_,
404 { isa => 'Foo' },
405 { isa => 'Bar' },
406 MX_PARAMS_VALIDATE_NO_CACHE => 1,
407 );
408
409 ...
410 }
411
412=back
413
df3c7e86 414=head1 ALLOWING EXTRA PARAMETERS
415
416By default, any parameters not mentioned in the parameter spec cause this
417module to throw an error. However, you can have have this module simply ignore
418them by setting C<MX_PARAMS_VALIDATE_ALLOW_EXTRA> to a true value when calling
419a validation subroutine.
420
421When calling C<validated_hash> or C<pos_validated_list> the extra parameters
422are simply returned in the hash or list as appropriate. However, when you call
423C<validated_list> the extra parameters will not be returned at all. You can
424get them by looking at the original value of C<@_>.
425
d9d1529d 426=head1 EXPORTS
427
428By default, this module exports the C<validated_hash>,
429C<validated_list>, and C<pos_validated_list>.
430
431If you would prefer to import the now deprecated functions C<validate>
432and C<validatep> instead, you can use the C<:deprecated> tag to import
433them.
434
435=head1 IMPORTANT NOTE ON CACHING
436
df3c7e86 437When a validation subroutine is called the first time, the parameter spec is
438prepared and cached to avoid unnecessary regeneration. It uses the fully
439qualified name of the subroutine (package + subname) as the cache key. In
44099.999% of the use cases for this module, that will be the right thing to do.
d9d1529d 441
442However, I have (ab)used this module occasionally to handle dynamic
443sets of parameters. In this special use case you can do a couple
444things to better control the caching behavior.
445
446=over 4
447
448=item *
449
450Passing in the C<MX_PARAMS_VALIDATE_NO_CACHE> flag in the parameter
451spec this will prevent the parameter spec from being cached.
452
453 sub foo {
454 my ( $self, %params ) = validated_hash(
455 \@_,
456 foo => { isa => 'Foo' },
457 MX_PARAMS_VALIDATE_NO_CACHE => 1,
458 );
459
460 }
461
462=item *
463
464Passing in C<MX_PARAMS_VALIDATE_CACHE_KEY> with a value to be used as
465the cache key will bypass the normal cache key generation.
466
467 sub foo {
468 my ( $self, %params ) = validated_hash(
469 \@_,
470 foo => { isa => 'Foo' },
471 MX_PARAMS_VALIDATE_CACHE_KEY => 'foo-42',
472 );
473
474 }
475
476=back
477
3a905ae7 478=head1 MAINTAINER
d9d1529d 479
480Dave Rolsky E<lt>autarch@urth.orgE<gt>
481
c8c6d6c9 482=head1 BUGS
d9d1529d 483
c8c6d6c9 484Please submit bugs to the CPAN RT system at
485http://rt.cpan.org/NoAuth/ReportBug.html?Queue=moosex-params-validate or via
486email at bug-moosex-params-validate@rt.cpan.org.
d9d1529d 487
488=cut