File Coverage

blib/lib/Params/Validate/Dependencies.pm
Criterion Covered Total %
statement 88 88 100.0
branch 21 22 95.4
condition 16 21 76.1
subroutine 24 24 100.0
pod 2 2 100.0
total 151 157 96.1


line stmt bran cond sub pod time code
1             package Params::Validate::Dependencies;
2              
3 9     9   3845 use strict;
  9         20  
  9         205  
4 9     9   37 use warnings;
  9         19  
  9         199  
5              
6 9     9   2563 use Clone qw(clone);
  9         14421  
  9         440  
7 9     9   3207 use Params::Validate (); # don't import yet
  9         49945  
  9         192  
8 9     9   2979 use Params::Validate::Dependencies::Documenter;
  9         37  
  9         272  
9 9     9   52 use Scalar::Util qw(blessed);
  9         19  
  9         343  
10 9     9   3066 use PadWalker qw(closed_over);
  9         3864  
  9         463  
11              
12 9     9   54 use base qw(Exporter);
  9         19  
  9         664  
13              
14 9     9   50 use vars qw($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS $DOC);
  9         22  
  9         1068  
15              
16             $VERSION = '1.32';
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 9     9   50 no strict 'refs';
  9         16  
  9         213  
29 9     9   42 no warnings 'redefine';
  9         17  
  9         7243  
30             my $orig = \&{$sub};
31             *{$sub} = sub {
32 37     37   4683 local *__ANON__ = $sub;
33 37         127 _validate_factory_args(@_);
34 36         116 $orig->(@_);
35             };
36             }
37              
38             sub import {
39             # import all of P::V except validate()
40 12     12   61 Params::Validate->import(grep { $_ ne 'validate' } @Params::Validate::EXPORT_OK);
  192         1078  
41             # now export all that P::V would have exported, plus *_of
42 12         1785 __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 18     18 1 713 my $sub = shift;
112 18 100 66     203 die(__PACKAGE__."::document: object $sub is wrong type\n")
113             unless(blessed($sub) && $sub->can('_document'));
114 17         63 $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 44     44 1 10875 my @args = @{shift()};
  44         114  
125 44         79 my $pv_spec;
126 44 100 66     286 if(ref($_[0]) && ref($_[0]) =~ /HASH/) {
127 21         45 $pv_spec = shift;
128             }
129 44         94 my @coderefs = @_;
130              
131 44         117 my %rval = @args;
132             # P::V::validate may alter it by applying defaults
133 44 100       481 %rval = Params::Validate::validate(@args, $pv_spec) if($pv_spec);
134              
135 42         127 foreach (@coderefs) {
136 41 100       149 die("code-ref checking failed\n") unless($_->({@args}));
137             }
138              
139 23 50       174 return wantarray ? %rval : \%rval;
140             }
141              
142             =head2 exclusively
143              
144             Takes a single subref as its only argument (this would normally be the
145             results of one of the *_of functions), and returns a code-ref which
146             returns true if the hashref it is given only contains fields
147             mentioned in the original function or any of its children. For example
148             ...
149              
150             validate(@_,
151             exclusively(
152             any_of(
153             qw(alpha beta gamma),
154             all_of(qw(bar baz)),
155             )
156             )
157             );
158              
159             will not tolerate arguments such as:
160              
161             bar => ...,
162             baz => ...,
163             sheep => ...
164              
165             because sheep aren't mentioned in the 'any_of' and 'all_of's. Internally
166             this uses the auto-documenter interface to interrogate the child sub. This
167             means that if you want to use C with third-party extensions
168             then they must support auto-documentation.
169              
170             This function is not exported by default but can be.
171              
172             =cut
173              
174             sub exclusively {
175             my @options = @_;
176             my $childsub = shift;
177             _bless_right_class(
178             sub {
179             my $documentation = document($childsub);
180             if($Params::Validate::Dependencies::DOC) {
181             return "exclusively ($documentation)";
182             }
183              
184             my @strings = map {
185             s/\\'/'/g; $_
186             } $documentation =~ /
187             '
188             (
189             (?:\\'|[^'])+
190             )
191             '
192             /xg;
193              
194             my %params = %{shift()};
195             foreach my $param (keys %params) {
196             return 0 if(!grep { $param eq $_ } @strings);
197             }
198             return 1;
199             }
200             );
201             }
202              
203             =head2 none_of
204              
205             Returns a code-ref which checks that the hashref it receives matches
206             none of the options given.
207              
208             You might want to use it thus:
209              
210             all_of(
211             'alpha',
212             none_of(qw(bar baz))
213             )
214              
215             to validate that 'alpha' must *not* be accompanied by 'bar' or 'baz'.
216              
217             =cut
218              
219             sub none_of {
220             my @options = @_;
221             _bless_right_class(_mk_autodoc(sub { _count_of(\@options, 0)->(@_) }));
222             }
223              
224             =head2 one_of
225              
226             Returns a code-ref which checks that the hashref it receives matches
227             only one of the options given.
228              
229             =cut
230              
231             sub one_of {
232             my @options = @_;
233             _bless_right_class(_mk_autodoc(sub { _count_of(\@options, 1)->(@_) }));
234             }
235              
236             =head2 any_of
237              
238             Returns a code-ref which checks that the hashref it receives matches
239             one or more of the options given.
240              
241             =cut
242              
243             sub any_of {
244             my @options = @_;
245             _bless_right_class(_mk_autodoc(sub {
246             my %params = %{shift()};
247             foreach my $option (@options) {
248             return 1 if(!ref($option) && exists($params{$option}));
249             return 1 if(ref($option) && $option->(\%params));
250             }
251             return 0;
252             }));
253             }
254              
255             =head2 all_of
256              
257             Returns a code-ref which checks that the hashref it receives matches
258             all of the options given.
259              
260             =cut
261              
262             sub all_of {
263             my @options = @_;
264              
265             _bless_right_class(_mk_autodoc(sub { _count_of(\@options, $#options + 1)->(@_) }));
266             }
267              
268             # {none,one,all}_of are thin wrappers around this
269             sub _count_of {
270 53     53   90 my @options = @{shift()};
  53         130  
271 53         94 my $desired_count = shift;
272             sub {
273 53     53   85 my %params = %{shift()};
  53         158  
274 53         100 my $matches = 0;
275 53         98 foreach my $option (@options) {
276             $matches++ if(
277 119 100 66     547 (!ref($option) && exists($params{$option})) ||
      100        
      66        
278             (ref($option) && $option->(\%params))
279             );
280             }
281 53         382 return ($matches == $desired_count);
282             }
283 53         233 }
284              
285             sub _mk_autodoc {
286 35     35   72 my $sub = shift;
287             sub {
288 114 100   114   419 if($DOC) {
289 44         679 return $DOC->_doc_me(list => (closed_over($sub))[0]->{'@options'});
290             }
291 70         156 $sub->(@_);
292             }
293 35         145 }
294              
295             sub _bless_right_class {
296 36     36   205 my($sub, $class) = (shift(), (caller(1))[3]);
297 36         198 (my $subname = $class) =~ s/.*:://;
298 9     9   62 no strict 'refs';
  9         17  
  9         1788  
299 36 100       75 unless(@{"${class}::ISA"}) {
  36         240  
300 23         50 @{"${class}::ISA"} = ('Params::Validate::Dependencies::Documenter');
  23         204  
301 23     50   79 *{"${class}::name"} = sub { $subname };
  23         98  
  50         194  
302 23 100   34   72 *{"${class}::join_with"} = sub { $subname eq 'all_of' ? 'and' : 'or' };
  23         82  
  34         146  
303             }
304 36         226 bless $sub, $class;
305             }
306              
307             sub _validate_factory_args {
308 37     37   105 my @options = @_;
309 37         200 my $sub = (caller(1))[3];
310             die("$sub takes only SCALARs, code-refs, and Params::Validate::Dependencies::* objects\n")
311             if(grep {
312 37 100 66     93 ref($_) && ref($_) !~ /CODE/ &&
  87 100 100     613  
313             !(blessed($_) && $_->isa('Params::Validate::Dependencies::Documenter'))
314             } @options);
315             }
316              
317             =head1 LIES
318              
319             Some of the above is incorrect. If you really want to know what's
320             going on, look at L.
321              
322             =head1 BUGS, LIMITATIONS, and FEEDBACK
323              
324             I like to know who's using my code. All comments, including constructive
325             criticism, are welcome.
326              
327             Please report any bugs either by email or using L
328             or at L.
329              
330             Any incompatibility with Params::Validate will be considered to be a bug,
331             with the exception of minor differences in error messages.
332              
333             Bug reports should contain enough detail that I can replicate the
334             problem and write a test. The best bug reports have those details
335             in the form of a .t file. If you also include a patch I will love
336             you for ever.
337              
338             =head1 SEE ALSO
339              
340             L
341              
342             L
343              
344             =head1 SOURCE CODE REPOSITORY
345              
346             L
347              
348             L
349              
350             =head1 COPYRIGHT and LICENCE
351              
352             Copyright 2016 David Cantrell EFE
353              
354             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.
355              
356             =head1 CONSPIRACY
357              
358             This module is also free-as-in-mason.
359              
360             =cut
361              
362             1;