File Coverage

blib/lib/MouseX/Params/Validate.pm
Criterion Covered Total %
statement 27 114 23.6
branch 0 64 0.0
condition 0 23 0.0
subroutine 9 17 52.9
pod 3 3 100.0
total 39 221 17.6


line stmt bran cond sub pod time code
1             package MouseX::Params::Validate;
2              
3             $MouseX::Params::Validate::VERSION = '0.08';
4              
5             =head1 NAME
6              
7             MouseX::Params::Validate - Extension of Params::Validate using Mouse's types.
8              
9             =head1 VERSION
10              
11             Version 0.08
12              
13             =cut
14              
15 3     3   20376 use 5.006;
  3         10  
  3         132  
16 3     3   13 use strict; use warnings;
  3     3   7  
  3         89  
  3         15  
  3         8  
  3         91  
17              
18 3     3   14 use Carp 'confess';
  3         3  
  3         301  
19 3     3   7366 use Devel::Caller 'caller_cv';
  3         9989  
  3         250  
20 3     3   25 use Scalar::Util 'blessed', 'refaddr', 'reftype';
  3         7  
  3         378  
21              
22 3     3   2250 use Mouse::Util::TypeConstraints qw( find_type_constraint class_type role_type );
  3         51460  
  3         23  
23 3     3   2643 use Params::Validate;
  3         25502  
  3         321  
24 3         52 use Sub::Exporter -setup =>
25             {
26             exports => [qw( validated_hash validated_list pos_validated_list )],
27             groups => {default => [qw( validated_hash validated_list pos_validated_list )]},
28 3     3   2330 };
  3         42094  
29              
30             my %CACHED_SPECS;
31              
32             =head1 DESCRIPTION
33              
34             Method parameter validation extension to Mouse.
35              
36             Borrowed code entirely from L and stripped Moose footprints.
37              
38             =head1 EXPORTS
39              
40             By default, this module exports the following:
41              
42             =over 3
43              
44             =item * C
45              
46             =item * C
47              
48             =item * C
49              
50             =back
51              
52             =head1 CAVEATS
53              
54             It isn't possible to introspect the method parameter specs they are created as
55             needed when the method is called and cached for subsequent calls.
56              
57             =head1 CACHING
58              
59             When a validation subroutine is called the first time, the parameter spec is
60             prepared & cached to avoid unnecessary regeneration. It uses the fully qualified
61             name of the subroutine (package +subname) as the cache key. In 99.999% of the use
62             cases for this module that will be the right thing to do. You can do a couple
63             things to better control the caching behavior.
64              
65             =over 2
66              
67             =item *
68              
69             Passing in the C flag in the parameter spec this will
70             prevent the parameter spec from being cached.
71              
72             =item *
73              
74             Passing in C with value to be used as the cache key
75             will bypass the normal cache key generation.
76              
77             =back
78              
79             =head1 METHODS
80              
81             =head2 B
82              
83             This behaves similarly to the standard L C function
84             and returns the captured values in a HASH. The one exception is where if it spots
85             an instance in the C<@_> ,then it will handle it appropriately.
86              
87             The values in C<@_> can either be a set of name-value pairs or a single hash
88             reference.
89              
90             The C<%parameter_spec> accepts the following options:
91              
92             =over 4
93              
94             =item I
95              
96             The C option can be either;class name, Mouse type constraint name or an anon
97             Mouse type constraint.
98              
99             =item I
100              
101             The C option can be either; role name or an anon Mouse type constraint.
102              
103             =item I
104              
105             This is the default value to be used if the value is not supplied.
106              
107             =item I
108              
109             As with L, all options are considered required unless otherwise
110             specified. This option is passed directly to L.
111              
112             =item I
113              
114             If this is true and the parameter has a type constraint which has coercions, then
115             the coercion will be called for this parameter.If the type does have coercions,
116             then this parameter is ignored.
117              
118             =back
119              
120             use Mouse;
121             use MouseX::Params::Validate;
122              
123             sub foo
124             {
125             my ($self, %params) = validated_hash(
126             \@_,
127             bar => {isa => 'Str', default => 'Mouse'},
128             );
129             ...
130             ...
131             }
132              
133             =cut
134              
135             sub validated_hash
136             {
137 0     0 1   my ($args, %spec) = @_;
138              
139 0           my $cache_key = _cache_key(\%spec);
140 0           my $allow_extra = delete $spec{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
141              
142 0 0         if (exists $CACHED_SPECS{$cache_key})
143             {
144 0 0         (ref($CACHED_SPECS{$cache_key}) eq 'HASH')
145             || confess("I was expecting a HASH-ref in the cached $cache_key parameter"
146             . " spec, you are doing something funky, stop it!");
147 0           %spec = %{$CACHED_SPECS{$cache_key}};
  0            
148             }
149             else
150             {
151 0 0         my $should_cache = delete $spec{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
152             $spec{$_} = _convert_to_param_validate_spec( $spec{$_} )
153 0           foreach keys %spec;
154 0 0         $CACHED_SPECS{$cache_key} = \%spec if $should_cache;
155             }
156              
157 0           my $instance;
158 0 0         $instance = shift @$args if blessed $args->[0];
159              
160 0           my %args
161             = @$args == 1
162             && ref $args->[0]
163 0 0 0       && reftype($args->[0]) eq 'HASH' ? %{$args->[0]} : @$args;
164              
165 0 0         $args{$_} = $spec{$_}{constraint}->coerce($args{$_})
166 0           for grep { $spec{$_}{coerce} && exists $args{$_} } keys %spec;
167              
168 0           %args = Params::Validate::validate_with(
169             params => \%args,
170             spec => \%spec,
171             allow_extra => $allow_extra,
172             called => _caller_name(),
173             );
174              
175 0 0         return ((defined $instance ? $instance : ()), %args);
176             }
177              
178             =head2 B
179              
180             The C<%parameter_spec> accepts the same options as above but returns the params
181             as positional values instead of a HASH.
182              
183             We capture the order in which you defined the parameters and then return them as
184             a list in the same order.If a param is marked optional and not included, then it
185             will be set to C.
186              
187             The values in C<@_> can either be a set of name-value pairs or a single hash
188             reference.
189              
190             Like C, if it spots an object instance as the first parameter of
191             C<@_> it will handle it appropriately, returning it as the first argument.
192              
193             use Mouse;
194             use MouseX::Params::Validate;
195              
196             sub foo
197             {
198             my ($self, $foo, $bar) = validated_list(
199             \@_,
200             foo => {isa => 'Foo'},
201             bar => {isa => 'Bar'},
202             );
203             ...
204             ...
205             }
206              
207             =cut
208              
209             sub validated_list
210             {
211 0     0 1   my ($args, @spec) = @_;
212              
213 0           my %spec = @spec;
214 0           my $cache_key = _cache_key(\%spec);
215 0           my $allow_extra = delete $spec{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
216              
217 0           my @ordered_spec;
218 0 0         if (exists $CACHED_SPECS{$cache_key})
219             {
220 0 0         (ref($CACHED_SPECS{$cache_key}) eq 'ARRAY')
221             || confess("I was expecting a ARRAY-ref in the cached $cache_key parameter"
222             . " spec, you are doing something funky, stop it!");
223 0           %spec = %{ $CACHED_SPECS{$cache_key}->[0] };
  0            
224 0           @ordered_spec = @{ $CACHED_SPECS{$cache_key}->[1] };
  0            
225             }
226             else
227             {
228 0 0         my $should_cache = delete $spec{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
229 0           @ordered_spec = grep { exists $spec{$_} } @spec;
  0            
230 0           $spec{$_} = _convert_to_param_validate_spec($spec{$_}) foreach keys %spec;
231 0 0         $CACHED_SPECS{$cache_key} = [\%spec, \@ordered_spec] if $should_cache;
232             }
233              
234 0           my $instance;
235 0 0         $instance = shift @$args if blessed $args->[0];
236              
237 0           my %args
238             = @$args == 1
239             && ref $args->[0]
240 0 0 0       && reftype( $args->[0] ) eq 'HASH' ? %{ $args->[0] } : @$args;
241              
242 0 0         $args{$_} = $spec{$_}{constraint}->coerce($args{$_})
243 0           for grep { $spec{$_}{coerce} && exists $args{$_} } keys %spec;
244              
245 0           %args = Params::Validate::validate_with(
246             params => \%args,
247             spec => \%spec,
248             allow_extra => $allow_extra,
249             called => _caller_name(),
250             );
251              
252             return (
253 0 0         (defined $instance ? $instance : ()),
254             @args{@ordered_spec}
255             );
256             }
257              
258             =head2 B
259              
260             This function validates a list of positional parameters. Each C<$spec> should
261             validate one of the parameters in the list.
262              
263             Unlike the other functions, this function I find C<$self> in the argument
264             list. Make sure to shift it off yourself before doing validation.
265              
266             The values in C<@_> must be a list of values. You cannot pass the values as an
267             array reference,because this cannot be distinguished from passing one value which
268             itself an array reference.
269              
270             If a parameter is marked as optional and is not present, it will simply not be
271             returned.
272              
273             If you want to pass in any of the cache control parameters described below,
274             simply pass them after the list of parameter validation specs.
275              
276             use Mouse;
277             use MouseX::Params::Validate;
278              
279             sub foo
280             {
281             my $self = shift;
282             my ($foo, $bar) = pos_validated_list(
283             \@_,
284             {isa => 'Foo'},
285             {isa => 'Bar'},
286             MX_PARAMS_VALIDATE_NO_CACHE => 1,
287             );
288             ...
289             ...
290             }
291              
292             =cut
293              
294             sub pos_validated_list
295             {
296 0     0 1   my $args = shift;
297              
298 0           my @spec;
299 0           push @spec, shift while ref $_[0];
300 0           my %extra = @_;
301 0           my $cache_key = _cache_key( \%extra );
302 0           my $allow_extra = delete $extra{MX_PARAMS_VALIDATE_ALLOW_EXTRA};
303              
304 0           my @pv_spec;
305 0 0         if (exists $CACHED_SPECS{$cache_key})
306             {
307 0 0         (ref($CACHED_SPECS{$cache_key}) eq 'ARRAY')
308             || confess("I was expecting an ARRAY-ref in the cached $cache_key parameter"
309             . " spec, you are doing something funky, stop it!");
310 0           @pv_spec = @{$CACHED_SPECS{$cache_key}};
  0            
311             }
312             else
313             {
314 0 0         my $should_cache = exists $extra{MX_PARAMS_VALIDATE_NO_CACHE} ? 0 : 1;
315 0           @pv_spec = map { _convert_to_param_validate_spec($_) } @spec;
  0            
316 0 0         $CACHED_SPECS{$cache_key} = \@pv_spec if $should_cache;
317             }
318              
319 0           my @args = @$args;
320 0 0         $args[$_] = $pv_spec[$_]{constraint}->coerce($args[$_])
321 0           for grep { $pv_spec[$_] && $pv_spec[$_]{coerce} } 0 .. $#args;
322              
323 0           @args = Params::Validate::validate_with(
324             params => \@args,
325             spec => \@pv_spec,
326             allow_extra => $allow_extra,
327             called => _caller_name(),
328             );
329              
330 0           return @args;
331             }
332              
333             sub _cache_key
334             {
335 0     0     my $spec = shift;
336              
337 0 0         if (exists $spec->{MX_PARAMS_VALIDATE_CACHE_KEY})
338             {
339 0           return delete $spec->{MX_PARAMS_VALIDATE_CACHE_KEY};
340             }
341             else
342             {
343 0           return refaddr(caller_cv(2));
344             }
345             }
346              
347             sub _convert_to_param_validate_spec
348             {
349 0     0     my $spec = shift;
350 0           my %pv_spec;
351              
352 0 0         $pv_spec{optional} = $spec->{optional}
353             if exists $spec->{optional};
354 0 0         $pv_spec{default} = $spec->{default}
355             if exists $spec->{default};
356 0 0         $pv_spec{coerce} = $spec->{coerce}
357             if exists $spec->{coerce};
358 0 0         $pv_spec{depends} = $spec->{depends}
359             if exists $spec->{depends};
360              
361 0           my $constraint;
362 0 0         if (defined $spec->{isa})
    0          
363             {
364 0   0       $constraint = _is_tc($spec->{isa})
365             || Mouse::Util::TypeConstraints::find_or_parse_type_constraint($spec->{isa})
366             || class_type($spec->{isa});
367             }
368             elsif (defined $spec->{does})
369             {
370 0   0       $constraint = _is_tc($spec->{isa})
371             || find_type_constraint($spec->{does})
372             || role_type($spec->{does});
373             }
374              
375 0 0         $pv_spec{callbacks} = $spec->{callbacks}
376             if exists $spec->{callbacks};
377              
378 0 0         if ($constraint)
379             {
380 0           $pv_spec{constraint} = $constraint;
381             $pv_spec{callbacks}{'checking type constraint for ' . $constraint->name}
382 0     0     = sub { $constraint->check($_[0]) };
  0            
383             }
384              
385 0 0 0       delete $pv_spec{coerce}
386             unless $pv_spec{constraint} && $pv_spec{constraint}->has_coercion;
387              
388 0           return \%pv_spec;
389             }
390              
391             sub _is_tc
392             {
393 0     0     my $maybe_tc = shift;
394              
395 0 0 0       return $maybe_tc
      0        
396             if defined $maybe_tc
397             && blessed $maybe_tc
398             && $maybe_tc->isa('Mouse::Meta::TypeConstraint');
399             }
400              
401             sub _caller_name
402             {
403 0   0 0     my $depth = shift || 0;
404 0           return (caller(2 + $depth))[3];
405             }
406              
407             =head1 AUTHOR
408              
409             Mohammad S Anwar, C<< >>
410              
411             =head1 REPOSITORY
412              
413             L
414              
415             =head1 CONTRIBUTORS
416              
417             Hans Staugaard (STAUGAARD)
418              
419             =head1 BUGS
420              
421             Please report any bugs or feature requests to C
422             rt.cpan.org>, or through the web interface at L.
423             I will be notified and then you'll automatically be notified of progress on your
424             bug as I make changes.
425              
426             =head1 SUPPORT
427              
428             You can find documentation for this module with the perldoc command.
429              
430             perldoc MouseX::Params::Validate
431              
432             You can also look for information at:
433              
434             =over 4
435              
436             =item * RT: CPAN's request tracker
437              
438             L
439              
440             =item * AnnoCPAN: Annotated CPAN documentation
441              
442             L
443              
444             =item * CPAN Ratings
445              
446             L
447              
448             =item * Search CPAN
449              
450             L
451              
452             =back
453              
454             =head1 ACKNOWLEDGEMENTS
455              
456             =over 2
457              
458             =item * Stevan Little (Author of L).
459              
460             =item * Dave Rolsky Eautarch@urth.orgE (Maintainer of L).
461              
462             =back
463              
464             =head1 LICENSE AND COPYRIGHT
465              
466             Copyright (C) 2011 - 2015 Mohammad S Anwar.
467              
468             This program is free software; you can redistribute it and/or modify it under
469             the terms of the the Artistic License (2.0). You may obtain a copy of the full
470             license at:
471              
472             L
473              
474             Any use, modification, and distribution of the Standard or Modified Versions is
475             governed by this Artistic License.By using, modifying or distributing the Package,
476             you accept this license. Do not use, modify, or distribute the Package, if you do
477             not accept this license.
478              
479             If your Modified Version has been derived from a Modified Version made by someone
480             other than you,you are nevertheless required to ensure that your Modified Version
481             complies with the requirements of this license.
482              
483             This license does not grant you the right to use any trademark, service mark,
484             tradename, or logo of the Copyright Holder.
485              
486             This license includes the non-exclusive, worldwide, free-of-charge patent license
487             to make, have made, use, offer to sell, sell, import and otherwise transfer the
488             Package with respect to any patent claims licensable by the Copyright Holder that
489             are necessarily infringed by the Package. If you institute patent litigation
490             (including a cross-claim or counterclaim) against any party alleging that the
491             Package constitutes direct or contributory patent infringement,then this Artistic
492             License to you shall terminate on the date that such litigation is filed.
493              
494             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND
495             CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
496             WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
497             NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS
498             REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT,
499             INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE
500             OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
501              
502             =cut
503              
504             1; # End of MouseX::Params::Validate