File Coverage

blib/lib/Validation/Class.pm
Criterion Covered Total %
statement 184 204 90.2
branch 38 72 52.7
condition 33 68 48.5
subroutine 53 61 86.8
pod 15 32 46.8
total 323 437 73.9


line stmt bran cond sub pod time code
1             # ABSTRACT: Powerful Data Validation Framework
2              
3             package Validation::Class;
4              
5 109     109   6410772 use 5.10.0;
  109         1279  
6 109     109   667 use strict;
  109         225  
  109         2690  
7 109     109   580 use warnings;
  109         270  
  109         3284  
8              
9 109     109   45106 use Module::Find;
  109         140864  
  109         7480  
10              
11 109     109   39616 use Validation::Class::Util '!has';
  109         314  
  109         700  
12 109     109   40966 use Clone 'clone';
  109         230420  
  109         5717  
13 109     109   811 use Exporter ();
  109         240  
  109         2247  
14              
15 109     109   65803 use Validation::Class::Prototype;
  109         502  
  109         16469  
16              
17             our $VERSION = '7.900059'; # VERSION
18              
19             our @ISA = qw(Exporter);
20             our @EXPORT = qw(
21              
22             adopt
23             adt
24             attribute
25             bld
26             build
27             dir
28             directive
29             doc
30             document
31             ens
32             ensure
33             fld
34             field
35             flt
36             filter
37             has
38             load
39             msg
40             message
41             mth
42             method
43             mxn
44             mixin
45             pro
46             profile
47             set
48              
49             );
50              
51             sub return_class_proto {
52              
53 858   66 858 0 3135 my $class = shift || caller(2);
54              
55 858   66     2621 return prototype_registry->get($class) || do {
56              
57             # build new prototype class
58              
59             my $proto = Validation::Class::Prototype->new(
60             package => $class
61             );
62              
63 109     109   918 no strict 'refs';
  109         262  
  109         4707  
64 109     109   708 no warnings 'redefine';
  109         267  
  109         41220  
65              
66             # respect foreign constructors (such as $class->new) if found
67              
68             my $new = $class->can("new") ?
69             "initialize_validator" : "new"
70             ;
71              
72             # injected into every derived class (override if necessary)
73              
74 153     153   19129 *{"$class\::$new"} = sub { goto \&$new };
75 143     143   8107 *{"$class\::proto"} = sub { goto \&prototype };
76 163     163   5798 *{"$class\::prototype"} = sub { goto \&prototype };
77              
78             # inject prototype class aliases unless exist
79              
80             my @aliases = $proto->proxy_methods;
81              
82             foreach my $alias (@aliases) {
83              
84             next if $class->can($alias);
85              
86             # slight-of-hand
87              
88             $proto->set_method($alias, sub {
89              
90 476     476   21740 shift @_;
91              
92 476         2115 $proto->$alias(@_);
93              
94             });
95              
96             }
97              
98             # inject wrapped prototype class aliases unless exist
99              
100             my @wrapped_aliases = $proto->proxy_methods_wrapped;
101              
102             foreach my $alias (@wrapped_aliases) {
103              
104             next if $class->can($alias);
105              
106             # slight-of-hand
107              
108             $proto->set_method($alias, sub {
109              
110 250     250   29132 my $self = shift @_;
111              
112 250         1173 $proto->$alias($self, @_);
113              
114             });
115              
116             }
117              
118             # cache prototype
119             prototype_registry->add($class => $proto);
120              
121             $proto; # return-once
122              
123             };
124              
125             }
126              
127             sub configure_class_proto {
128              
129 250     250 0 529 my $configuration_routine = pop;
130              
131 250 50       846 return unless "CODE" eq ref $configuration_routine;
132              
133 109     109   886 no strict 'refs';
  109         315  
  109         237502  
134              
135 250         637 my $proto = return_class_proto shift;
136              
137 250         815 $configuration_routine->($proto);
138              
139 249         760 return $proto;
140              
141             }
142              
143             sub import {
144              
145 146   33 146   10292 my $caller = caller(0) || caller(1);
146              
147 146         992 strict->import;
148 146         1534 warnings->import;
149              
150 146         20444 __PACKAGE__->export_to_level(1, @_);
151              
152 146         658 return return_class_proto $caller # provision prototype when used
153              
154             }
155              
156             sub initialize_validator {
157              
158 167     167 0 388 my $self = shift;
159 167         708 my $proto = $self->prototype;
160              
161 167         847 my $arguments = $proto->build_args(@_);
162              
163             # provision a validation class configuration
164              
165 167         894 $proto->snapshot;
166              
167             # override prototype attributes if requested
168              
169 167 100       858 if (defined($arguments->{fields})) {
170 64         285 my $fields = delete $arguments->{fields};
171 64         277 $proto->fields->clear->add($fields);
172             }
173              
174 167 100       899 if (defined($arguments->{params})) {
175 78         225 my $params = delete $arguments->{params};
176 78         338 $proto->params->clear->add(clone $params);
177             }
178              
179             # process attribute assignments
180              
181 167         1012 my $proxy_methods = { map { $_ => 1 } ($proto->proxy_methods) } ;
  5177         9887  
182              
183 167         867 while (my($name, $value) = each (%{$arguments})) {
  269         1280  
184              
185             $self->$name($value) if
186              
187             $self->can($name) &&
188             $proto->fields->has($name) ||
189 102 100 100     599 $proto->attributes->has($name) || $proxy_methods->{$name}
      100        
      100        
190              
191             ;
192              
193             }
194              
195             # process builders
196              
197 167         842 foreach my $builder ($proto->builders->list) {
198              
199 4         19 $builder->($self, $arguments);
200              
201             }
202              
203             # initialize prototype
204              
205 167         992 $proto->normalize($self);
206              
207             # ready-set-go !!!
208              
209 166         886 return $self;
210              
211             }
212              
213              
214              
215              
216 0     0 0 0 sub adt { goto &adopt } sub adopt {
217              
218 3 50   3 1 32 my $package = shift if @_ == 4;
219              
220 3         7 my ($class, $type, $name) = @_;
221              
222 3         19 my $aliases = {
223             has => 'attribute',
224             dir => 'directive',
225             doc => 'document',
226             fld => 'field',
227             flt => 'filter',
228             msg => 'message',
229             mth => 'method',
230             mxn => 'mixin',
231             pro => 'profile'
232             };
233              
234 3         6 my $keywords = { map { $_ => $_ } values %{$aliases} };
  27         55  
  3         18  
235              
236 3   33     18 $type = $keywords->{$type} || $aliases->{$type};
237              
238 3 50 33     22 return unless $class && $name && $type;
      33        
239              
240 3         7 my $store = "${type}s";
241 3         7 my $config = prototype_registry->get($class)->configuration;
242 3         13 my $data = clone $config->$store->get($name);
243              
244 3 50       26 @_ = ($name => $data) and goto &$type;
245              
246 0         0 return;
247              
248             }
249              
250              
251 10     10 0 418 sub has { goto &attribute } sub attribute {
252              
253 14 100   14 1 207 my $package = shift if @_ == 3;
254              
255 14         52 my ($attributes, $default) = @_;
256              
257 14 50       42 return unless $attributes;
258              
259 14 50       50 $attributes = [$attributes] unless isa_arrayref $attributes;
260              
261             return configure_class_proto $package => sub {
262              
263 14     14   28 my ($proto) = @_;
264              
265 14         26 $proto->register_attribute($_ => $default) for @{$attributes};
  14         70  
266              
267 14         34 return $proto;
268              
269 14         86 };
270              
271             }
272              
273              
274 3     3 0 336 sub bld { goto &build } sub build {
275              
276 4 100   4 1 28 my $package = shift if @_ == 2;
277              
278 4         11 my ($code) = @_;
279              
280 4 50       18 return unless ("CODE" eq ref $code);
281              
282             return configure_class_proto $package => sub {
283              
284 4     4   10 my ($proto) = @_;
285              
286 4         21 $proto->register_builder($code);
287              
288 4         6 return $proto;
289              
290 4         20 };
291              
292             }
293              
294              
295 1     1 0 101 sub dir { goto &directive } sub directive {
296              
297 3 50   3 1 35 my $package = shift if @_ == 3;
298              
299 3         10 my ($name, $code) = @_;
300              
301 3 50 33     25 return unless ($name && $code);
302              
303             return configure_class_proto $package => sub {
304              
305 3     3   8 my ($proto) = @_;
306              
307 3         16 $proto->register_directive($name, $code);
308              
309 3         6 return $proto;
310              
311 3         21 };
312              
313             }
314              
315              
316 0     0 0 0 sub doc { goto &document } sub document {
317              
318 12 50   12 1 214 my $package = shift if @_ == 3;
319              
320 12         33 my ($name, $data) = @_;
321              
322 12   50     40 $data ||= {};
323              
324 12 50 33     74 return unless ($name && $data);
325              
326             return configure_class_proto $package => sub {
327              
328 12     12   48 my ($proto) = @_;
329              
330 12         58 $proto->register_document($name, $data);
331              
332 12         19 return $proto;
333              
334 12         79 };
335              
336             };
337              
338              
339              
340 0     0 0 0 sub ens { goto &ensure } sub ensure {
341              
342 2 50   2 1 28 my $package = shift if @_ == 3;
343              
344 2         6 my ($name, $data) = @_;
345              
346 2   50     5 $data ||= {};
347              
348 2 50 33     10 return unless ($name && $data);
349              
350             return configure_class_proto $package => sub {
351              
352 2     2   5 my ($proto) = @_;
353              
354 2         10 $proto->register_ensure($name, $data);
355              
356 2         4 return $proto;
357              
358 2         9 };
359              
360             }
361              
362              
363 33     33 0 6581 sub fld { goto &field } sub field {
364              
365 150 50   150 1 12448 my $package = shift if @_ == 3;
366              
367 150         444 my ($name, $data) = @_;
368              
369 150   100     465 $data ||= {};
370              
371 150 50 33     820 return unless ($name && $data);
372              
373             return configure_class_proto $package => sub {
374              
375 150     150   357 my ($proto) = @_;
376              
377 150         610 $proto->register_field($name, $data);
378              
379 150         274 return $proto;
380              
381 150         862 };
382              
383             }
384              
385              
386 0     0 0 0 sub flt { goto &filter } sub filter {
387              
388 1 50   1 1 99 my $package = shift if @_ == 3;
389              
390 1         4 my ($name, $code) = @_;
391              
392 1 50 33     10 return unless ($name && $code);
393              
394             return configure_class_proto $package => sub {
395              
396 1     1   4 my ($proto) = @_;
397              
398 1         4 $proto->register_filter($name, $code);
399              
400 1         3 return $proto;
401              
402 1         7 };
403              
404             }
405              
406              
407 14     14 0 10361 sub set { goto &load } sub load {
408              
409 18     18 1 155 my $package;
410             my $data;
411              
412             # handle different types of invocations
413              
414             # 1 - load({})
415             # 2+ - load(a => b)
416             # 2+ - package->load({})
417             # 3+ - package->load(a => b)
418              
419             # --
420              
421             # load({})
422              
423 18 100       90 if (@_ == 1) {
    50          
    0          
424              
425 5 50       29 if ("HASH" eq ref $_[0]) {
426              
427 5         17 $data = shift;
428              
429             }
430              
431             }
432              
433             # load(a => b)
434             # package->load({})
435              
436             elsif (@_ == 2) {
437              
438 13 50       45 if ("HASH" eq ref $_[-1]) {
439              
440 0         0 $package = shift;
441 0         0 $data = shift;
442              
443             }
444              
445             else {
446              
447 13         59 $data = {@_};
448              
449             }
450              
451             }
452              
453             # load(a => b)
454             # package->load(a => b)
455              
456             elsif (@_ >= 3) {
457              
458 0 0       0 if (@_ % 2) {
459              
460 0         0 $package = shift;
461 0         0 $data = {@_};
462              
463             }
464              
465             else {
466              
467 0         0 $data = {@_};
468              
469             }
470              
471             }
472              
473             return configure_class_proto $package => sub {
474              
475 18     18   53 my ($proto) = @_;
476              
477 18         98 $proto->register_settings($data);
478              
479 17         40 return $proto;
480              
481 18         128 };
482              
483             }
484              
485              
486 0     0 0 0 sub msg { goto &message } sub message {
487              
488 0 0   0 1 0 my $package = shift if @_ == 3;
489              
490 0         0 my ($name, $template) = @_;
491              
492 0 0 0     0 return unless ($name && $template);
493              
494             return configure_class_proto $package => sub {
495              
496 0     0   0 my ($proto) = @_;
497              
498 0         0 $proto->register_message($name, $template);
499              
500 0         0 return $proto;
501              
502 0         0 };
503              
504             }
505              
506              
507 10     10 0 573 sub mth { goto &method } sub method {
508              
509 16 50   16 1 137 my $package = shift if @_ == 3;
510              
511 16         55 my ($name, $data) = @_;
512              
513 16 50 33     105 return unless ($name && $data);
514              
515             return configure_class_proto $package => sub {
516              
517 16     16   51 my ($proto) = @_;
518              
519 16         91 $proto->register_method($name, $data);
520              
521 16         32 return $proto;
522              
523 16         95 };
524              
525             }
526              
527              
528 2     2 0 24 sub mxn { goto &mixin } sub mixin {
529              
530 19 50   19 1 1091 my $package = shift if @_ == 3;
531              
532 19         67 my ($name, $data) = @_;
533              
534 19   50     62 $data ||= {};
535              
536 19 50 33     123 return unless ($name && $data);
537              
538             return configure_class_proto $package => sub {
539              
540 19     19   55 my ($proto) = @_;
541              
542 19         81 $proto->register_mixin($name, $data);
543              
544 19         36 return $proto;
545              
546 19         131 };
547              
548             }
549              
550              
551             sub new {
552              
553 152     152 1 439 my $class = shift;
554              
555 152   33     935 $class = ref $class || $class;
556              
557 152         590 my $proto = return_class_proto $class;
558              
559 152         541 my $self = bless {}, $class;
560              
561 152         720 initialize_validator $self, @_;
562              
563 151         651 return $self;
564              
565             }
566              
567              
568 4     4 0 56 sub pro { goto &profile } sub profile {
569              
570 11 50   11 1 122 my $package = shift if @_ == 3;
571              
572 11         30 my ($name, $code) = @_;
573              
574 11 50 33     67 return unless ($name && $code);
575              
576             return configure_class_proto $package => sub {
577              
578 11     11   34 my ($proto) = @_;
579              
580 11         65 $proto->register_profile($name, $code);
581              
582 11         20 return $proto;
583              
584 11         60 };
585              
586             }
587              
588              
589 0     0 0 0 sub proto { goto &prototype } sub prototype {
590              
591 310     310 1 8241 my ($self) = pop @_;
592              
593 310   66     1708 return return_class_proto ref $self || $self;
594              
595             }
596              
597              
598             1;
599              
600             __END__