File Coverage

blib/lib/Params/Validate/Dependencies.pm
Criterion Covered Total %
statement 104 104 100.0
branch 32 34 94.1
condition 18 21 85.7
subroutine 25 25 100.0
pod 3 3 100.0
total 182 187 97.3


line stmt bran cond sub pod time code
1             package Params::Validate::Dependencies;
2              
3 11     11   65380 use strict;
  11         51  
  11         269  
4 11     11   48 use warnings;
  11         18  
  11         258  
5              
6 11     11   3440 use Clone qw(clone);
  11         20007  
  11         540  
7 11     11   4070 use Params::Validate (); # don't import yet
  11         68556  
  11         274  
8 11     11   3962 use Params::Validate::Dependencies::Documenter;
  11         29  
  11         280  
9 11     11   56 use Scalar::Util qw(blessed);
  11         20  
  11         364  
10 11     11   10066 use PadWalker qw(closed_over);
  11         5533  
  11         622  
11              
12 11     11   75 use base qw(Exporter);
  11         22  
  11         1475  
13              
14 11     11   67 use vars qw($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS $DOC);
  11         29  
  11         1305  
15              
16             $VERSION = '1.40';
17             $DOC = 0;
18              
19             # copy and update P::V's EXPORT* constants
20             my @_of = qw(any_of all_of none_of one_of);
21             @EXPORT = (@Params::Validate::EXPORT, @_of);
22             @EXPORT_OK = (@Params::Validate::EXPORT_OK, @_of, 'exclusively');
23             %EXPORT_TAGS = (%{clone(\%Params::Validate::EXPORT_TAGS)}, _of => \@_of);
24             push @{$EXPORT_TAGS{all}}, (@_of, 'exclusively');
25              
26             # because repeating the call to _validate_factory_args everywhere is BAD
27             foreach my $sub (@_of, 'exclusively') {
28 11     11   73 no strict 'refs';
  11         19  
  11         370  
29 11     11   49 no warnings 'redefine';
  11         25  
  11         13381  
30             my $orig = \&{$sub};
31             *{$sub} = sub {
32 41     41   4042 local *__ANON__ = $sub;
33 41         120 _validate_factory_args(@_);
34 40         116 $orig->(@_);
35             };
36             }
37              
38             sub import {
39             # import all of P::V except validate() and dvalidate_with()
40 14     14   133 Params::Validate->import(grep { ! /^validate(_with)?$/ } @Params::Validate::EXPORT_OK);
  224         1398  
41             # now export all that P::V would have exported, plus *_of
42 14         3856 __PACKAGE__->export_to_level(1, @_);
43             }
44              
45             =head1 NAME
46              
47             Params::Validate::Dependencies - check that the right combination of arguments is passed to a function
48              
49             =head1 DESCRIPTION
50              
51             Extends Params::Validate to make it easy to validate
52             that you have been passed the correct combinations of parameters.
53              
54             =head1 SYNOPSIS
55              
56             This example validates that sub 'foo's arguments are of the right types,
57             and that either we have at least one of alpha, beta and gamma, or
58             we have both of bar amd baz:
59              
60             use Params::Validate::Dependencies qw(:all);
61              
62             sub foo {
63             validate(@_,
64             {
65             alpha => { type => ARRAYREF, optional => 1 },
66             beta => { type => ARRAYREF, optional => 1 },
67             gamma => { type => ARRAYREF, optional => 1 },
68             bar => { type => SCALAR, optional => 1 },
69             baz => { type => SCALAR, optional => 1 },
70             },
71             any_of(
72             qw(alpha beta gamma),
73             all_of(qw(bar baz)),
74             )
75             );
76             }
77              
78             =head1 HOW IT WORKS
79              
80             Params::Validate::Dependencies extends Params::Validate's
81             validate() function to
82             support an arbitrary number of callbacks which are not associated
83             with any one parameter. All of those callbacks are run after
84             Params::Validate's normal validate() function.
85             If any of them return false, then validate() will die as normal.
86              
87             =head1 SUBROUTINES and EXPORTS
88              
89             All of the *_of functions are exported by default in addition to those
90             exported by default by Params::Validate. They are also available with the
91             tag ':_of' in case you want to use them without Params::Validate.
92             In that case you would load the module thus:
93              
94             use Params::Validate::Dependencies qw(:_of);
95              
96             All of the *_of functions take a list of scalars and code-refs and
97             return a code-ref (which is a closure over the list originally passed
98             to the function) suitable for use in validate() or in another *_of
99             function. All code-refs should take as their only argument a hashref
100             of parameters to check, returning true if the parameters are good
101             and false otherwise.
102              
103             =head2 document
104              
105             This takes a code-ref argument as generated by a tree of *_of calls,
106             and spits out some documentation of it. This function is not exported.
107              
108             =cut
109              
110             sub document {
111 38     38 1 714 my $sub = shift;
112 38 100 66     377 die(__PACKAGE__."::document: object $sub is wrong type\n")
113             unless(blessed($sub) && $sub->can('_document'));
114 32         191 $sub->_document();
115             }
116              
117             =head2 validate
118              
119             Overrides and extends Params::Validate's function of the same name.
120              
121             =cut
122              
123             sub validate (\@@) {
124 46     46 1 11320 my @args;
125              
126 46         140 my $p = shift;
127 46 50       131 if ( ref $p eq 'ARRAY' ) {
128             # First argument might have been a hash reference
129 46 100       71 @args = @{ ref $p->[0] ? [ %{ $p->[0] } ] : $p };
  46         157  
  1         5  
130             }
131              
132 46         82 my $pv_spec;
133 46 100 66     252 if(ref($_[0]) && ref($_[0]) =~ /HASH/) {
134 23         37 $pv_spec = shift;
135             }
136 46         110 my @coderefs = @_;
137              
138 46         142 my %rval = @args;
139             # P::V::validate may alter it by applying defaults
140 46 100       555 %rval = Params::Validate::validate(@args, $pv_spec) if($pv_spec);
141              
142 44         127 foreach (@coderefs) {
143 41 100       139 die 'code-ref checking failed: arguments were not ' . document($_) . "\n"
144             unless $_->({@args});
145             }
146              
147 25 100       209 return wantarray ? %rval : \%rval;
148             }
149              
150             =head2 validate_with
151              
152             Overrides and extends Params::Validate's function of the same name.
153              
154             The code-ref, or an array-ref of code-refs, are passed as the
155             extra 'dependencies' argument.
156              
157             =cut
158              
159             sub validate_with {
160 5     5 1 122 my %args = @_;
161 5         10 my $params = [ @{$args{params}} ];
  5         16  
162              
163 5 100       17 $args{dependencies} = [] unless defined $args{dependencies};
164 5         14 my $coderefs = delete $args{dependencies};
165 5 100       20 $coderefs = ref($coderefs) eq 'ARRAY' ? $coderefs : [ $coderefs ];
166              
167 5         115 my %rval = Params::Validate::validate_with(@_);
168              
169 5         29 foreach (@{$coderefs}) {
  5         14  
170 4 100       25 die('code-ref checking failed: arguments were not '.document($_)."\n") unless($_->({@{$params}}));
  4         23  
171             }
172              
173 4 50       49 return wantarray ? %rval : \%rval;
174             }
175              
176             =head2 exclusively
177              
178             Takes a single subref as its only argument (this would normally be the
179             results of one of the *_of functions), and returns a code-ref which
180             returns true if the hashref it is given only contains fields
181             mentioned in the original function or any of its children. For example
182             ...
183              
184             validate(@_,
185             exclusively(
186             any_of(
187             qw(alpha beta gamma),
188             all_of(qw(bar baz)),
189             )
190             )
191             );
192              
193             will not tolerate arguments such as:
194              
195             bar => ...,
196             baz => ...,
197             sheep => ...
198              
199             because sheep aren't mentioned in the 'any_of' and 'all_of's. Internally
200             this uses the auto-documenter interface to interrogate the child sub. This
201             means that if you want to use C with third-party extensions
202             then they must support auto-documentation.
203              
204             This function is not exported by default but can be.
205              
206             =cut
207              
208             sub exclusively {
209             my @options = @_;
210             my $childsub = shift;
211             _bless_right_class(
212             sub {
213             my $documentation = document($childsub);
214             if($Params::Validate::Dependencies::DOC) {
215             return "exclusively ($documentation)";
216             }
217              
218             my @strings = map {
219             s/\\'/'/g; $_
220             } $documentation =~ /
221             '
222             (
223             (?:\\'|[^'])+
224             )
225             '
226             /xg;
227              
228             my %params = %{shift()};
229             foreach my $param (keys %params) {
230             return 0 if(!grep { $param eq $_ } @strings);
231             }
232             return 1;
233             }
234             );
235             }
236              
237             =head2 none_of
238              
239             Returns a code-ref which checks that the hashref it receives matches
240             none of the options given.
241              
242             You might want to use it thus:
243              
244             all_of(
245             'alpha',
246             none_of(qw(bar baz))
247             )
248              
249             to validate that 'alpha' must *not* be accompanied by 'bar' or 'baz'.
250              
251             =cut
252              
253             sub none_of {
254             my @options = @_;
255             _bless_right_class(_mk_autodoc(sub { _count_of(\@options, 0)->(@_) }));
256             }
257              
258             =head2 one_of
259              
260             Returns a code-ref which checks that the hashref it receives matches
261             only one of the options given.
262              
263             =cut
264              
265             sub one_of {
266             my @options = @_;
267             _bless_right_class(_mk_autodoc(sub { _count_of(\@options, 1)->(@_) }));
268             }
269              
270             =head2 any_of
271              
272             Returns a code-ref which checks that the hashref it receives matches
273             one or more of the options given.
274              
275             =cut
276              
277             sub any_of {
278             my @options = @_;
279             _bless_right_class(_mk_autodoc(sub {
280             my %params = %{shift()};
281             foreach my $option (@options) {
282             return 1 if(!ref($option) && exists($params{$option}));
283             return 1 if(ref($option) && $option->(\%params));
284             }
285             return 0;
286             }));
287             }
288              
289             =head2 all_of
290              
291             Returns a code-ref which checks that the hashref it receives matches
292             all of the options given.
293              
294             =cut
295              
296             sub all_of {
297             my @options = @_;
298              
299             _bless_right_class(_mk_autodoc(sub { _count_of(\@options, $#options + 1)->(@_) }));
300             }
301              
302             # {none,one,all}_of are thin wrappers around this
303             sub _count_of {
304 57     57   90 my @options = @{shift()};
  57         135  
305 57         108 my $desired_count = shift;
306             sub {
307 57     57   89 my %params = %{shift()};
  57         181  
308 57         99 my $matches = 0;
309 57         94 foreach my $option (@options) {
310             $matches++ if(
311 125 100 100     526 (!ref($option) && exists($params{$option})) ||
      100        
      100        
312             (ref($option) && $option->(\%params))
313             );
314             }
315 57         407 return ($matches == $desired_count);
316             }
317 57         254 }
318              
319             sub _mk_autodoc {
320 39     39   81 my $sub = shift;
321             sub {
322 142 100   142   443 if($DOC) {
323 68         949 return $DOC->_doc_me(list => (closed_over($sub))[0]->{'@options'});
324             }
325 74         139 $sub->(@_);
326             }
327 39         168 }
328              
329             sub _bless_right_class {
330 40     40   200 my($sub, $class) = (shift(), (caller(1))[3]);
331 40         223 (my $subname = $class) =~ s/.*:://;
332 11     11   81 no strict 'refs';
  11         22  
  11         2390  
333 40 100       68 unless(@{"${class}::ISA"}) {
  40         265  
334 25         44 @{"${class}::ISA"} = ('Params::Validate::Dependencies::Documenter');
  25         504  
335 25     74   130 *{"${class}::name"} = sub { $subname };
  25         134  
  74         341  
336 25 100   55   99 *{"${class}::join_with"} = sub { $subname eq 'all_of' ? 'and' : 'or' };
  25         107  
  55         222  
337             }
338 40         238 bless $sub, $class;
339             }
340              
341             sub _validate_factory_args {
342 41     41   102 my @options = @_;
343 41         248 my $sub = (caller(1))[3];
344             die("$sub takes only SCALARs, code-refs, and Params::Validate::Dependencies::* objects\n")
345             if(grep {
346 41 100 66     97 ref($_) && ref($_) !~ /CODE/ &&
  93 100 100     524  
347             !(blessed($_) && $_->isa('Params::Validate::Dependencies::Documenter'))
348             } @options);
349             }
350              
351             =head1 LIES
352              
353             Some of the above is incorrect. If you really want to know what's
354             going on, look at L.
355              
356             =head1 BUGS, LIMITATIONS, and FEEDBACK
357              
358             I like to know who's using my code. All comments, including constructive
359             criticism, are welcome.
360              
361             Please report any bugs either by email or
362             at L.
363              
364             Any incompatibility with Params::Validate will be considered to be a bug,
365             with the exception of minor differences in error messages.
366              
367             Bug reports should contain enough detail that I can replicate the
368             problem and write a test. The best bug reports have those details
369             in the form of a .t file. If you also include a patch I will love
370             you for ever.
371              
372             =head1 SEE ALSO
373              
374             L
375              
376             L
377              
378             =head1 SOURCE CODE REPOSITORY
379              
380             L
381              
382             L
383              
384             =head1 COPYRIGHT and LICENCE
385              
386             Copyright 2016 David Cantrell EFE
387              
388             This software is free-as-in-speech software, and may be used, distributed, and modified under the terms of either the GNU General Public Licence version 2 or the Artistic Licence. It's up to you which one you use. The full text of the licences can be found in the files GPL2.txt and ARTISTIC.txt, respectively.
389              
390             =head1 CONSPIRACY
391              
392             This module is also free-as-in-mason.
393              
394             =cut
395              
396             1;