File Coverage

blib/lib/MooseX/Params/Validate.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


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