File Coverage

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


line stmt bran cond sub pod time code
1             # ABSTRACT: Powerful Data Validation Framework
2              
3             package Validation::Class;
4              
5 108     108   2152903 use 5.10.0;
  108         428  
6 108     108   565 use strict;
  108         209  
  108         2363  
7 108     108   531 use warnings;
  108         202  
  108         2962  
8              
9 108     108   70238 use Module::Find;
  108         121129  
  108         7826  
10              
11 108     108   50674 use Validation::Class::Util '!has';
  108         277  
  108         728  
12 108     108   63813 use Clone 'clone';
  108         280446  
  108         6057  
13 108     108   709 use Exporter ();
  108         210  
  108         3451  
14              
15 108     108   89139 use Validation::Class::Prototype;
  108         365  
  108         15551  
16              
17             our $VERSION = '7.900057'; # 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 847   66 847 0 3318 my $class = shift || caller(2);
54              
55 847   66     2850 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 108     108   701 no strict 'refs';
  108         205  
  108         3466  
64 108     108   602 no warnings 'redefine';
  108         237  
  108         40812  
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 151     151   18677 *{"$class\::$new"} = sub { goto \&$new };
75 141     141   5138 *{"$class\::proto"} = sub { goto \&prototype };
76 161     161   3008 *{"$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   12274 shift @_;
91              
92 476         2015 $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 245     245   16850 my $self = shift @_;
111              
112 245         1237 $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 247     247 0 437 my $configuration_routine = pop;
130              
131 247 50       809 return unless "CODE" eq ref $configuration_routine;
132              
133 108     108   601 no strict 'refs';
  108         225  
  108         233013  
134              
135 247         690 my $proto = return_class_proto shift;
136              
137 247         680 $configuration_routine->($proto);
138              
139 246         716 return $proto;
140              
141             }
142              
143             sub import {
144              
145 144   33 144   10254 my $caller = caller(0) || caller(1);
146              
147 144         931 strict->import;
148 144         1739 warnings->import;
149              
150 144         26394 __PACKAGE__->export_to_level(1, @_);
151              
152 144         498 return return_class_proto $caller # provision prototype when used
153              
154             }
155              
156             sub initialize_validator {
157              
158 165     165 0 366 my $self = shift;
159 165         695 my $proto = $self->prototype;
160              
161 165         952 my $arguments = $proto->build_args(@_);
162              
163             # provision a validation class configuration
164              
165 165         874 $proto->snapshot;
166              
167             # override prototype attributes if requested
168              
169 165 100       741 if (defined($arguments->{fields})) {
170 64         177 my $fields = delete $arguments->{fields};
171 64         278 $proto->fields->clear->add($fields);
172             }
173              
174 165 100       695 if (defined($arguments->{params})) {
175 76         200 my $params = delete $arguments->{params};
176 76         330 $proto->params->clear->add(clone $params);
177             }
178              
179             # process attribute assignments
180              
181 165         976 my $proxy_methods = { map { $_ => 1 } ($proto->proxy_methods) } ;
  5115         10282  
182              
183 165         924 while (my($name, $value) = each (%{$arguments})) {
  267         1198  
184              
185             $self->$name($value) if
186              
187             $self->can($name) &&
188             $proto->fields->has($name) ||
189 102 100 100     737 $proto->attributes->has($name) || $proxy_methods->{$name}
      100        
      66        
190              
191             ;
192              
193             }
194              
195             # process builders
196              
197 165         736 foreach my $builder ($proto->builders->list) {
198              
199 4         17 $builder->($self, $arguments);
200              
201             }
202              
203             # initialize prototype
204              
205 165         1412 $proto->normalize($self);
206              
207             # ready-set-go !!!
208              
209 164         1004 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 29 my $package = shift if @_ == 4;
219              
220 3         7 my ($class, $type, $name) = @_;
221              
222 3         20 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         4 my $keywords = { map { $_ => $_ } values %{$aliases} };
  27         53  
  3         9  
235              
236 3   33     12 $type = $keywords->{$type} || $aliases->{$type};
237              
238 3 50 33     22 return unless $class && $name && $type;
      33        
239              
240 3         6 my $store = "${type}s";
241 3         10 my $config = prototype_registry->get($class)->configuration;
242 3         13 my $data = clone $config->$store->get($name);
243              
244 3 50       29 @_ = ($name => $data) and goto &$type;
245              
246 0         0 return;
247              
248             }
249              
250              
251 10     10 0 103 sub has { goto &attribute } sub attribute {
252              
253 14 100   14 1 78 my $package = shift if @_ == 3;
254              
255 14         36 my ($attributes, $default) = @_;
256              
257 14 50       40 return unless $attributes;
258              
259 14 50       61 $attributes = [$attributes] unless isa_arrayref $attributes;
260              
261             return configure_class_proto $package => sub {
262              
263 14     14   39 my ($proto) = @_;
264              
265 14         24 $proto->register_attribute($_ => $default) for @{$attributes};
  14         87  
266              
267 14         29 return $proto;
268              
269 14         88 };
270              
271             }
272              
273              
274 3     3 0 336 sub bld { goto &build } sub build {
275              
276 4 100   4 1 29 my $package = shift if @_ == 2;
277              
278 4         9 my ($code) = @_;
279              
280 4 50       16 return unless ("CODE" eq ref $code);
281              
282             return configure_class_proto $package => sub {
283              
284 4     4   9 my ($proto) = @_;
285              
286 4         17 $proto->register_builder($code);
287              
288 4         6 return $proto;
289              
290 4         28 };
291              
292             }
293              
294              
295 1     1 0 14 sub dir { goto &directive } sub directive {
296              
297 3 50   3 1 30 my $package = shift if @_ == 3;
298              
299 3         9 my ($name, $code) = @_;
300              
301 3 50 33     20 return unless ($name && $code);
302              
303             return configure_class_proto $package => sub {
304              
305 3     3   7 my ($proto) = @_;
306              
307 3         22 $proto->register_directive($name, $code);
308              
309 3         6 return $proto;
310              
311 3         19 };
312              
313             }
314              
315              
316 0     0 0 0 sub doc { goto &document } sub document {
317              
318 12 50   12 1 179 my $package = shift if @_ == 3;
319              
320 12         39 my ($name, $data) = @_;
321              
322 12   50     37 $data ||= {};
323              
324 12 50 33     90 return unless ($name && $data);
325              
326             return configure_class_proto $package => sub {
327              
328 12     12   31 my ($proto) = @_;
329              
330 12         73 $proto->register_document($name, $data);
331              
332 12         22 return $proto;
333              
334 12         63 };
335              
336             };
337              
338              
339              
340 0     0 0 0 sub ens { goto &ensure } sub ensure {
341              
342 2 50   2 1 29 my $package = shift if @_ == 3;
343              
344 2         5 my ($name, $data) = @_;
345              
346 2   50     8 $data ||= {};
347              
348 2 50 33     14 return unless ($name && $data);
349              
350             return configure_class_proto $package => sub {
351              
352 2     2   5 my ($proto) = @_;
353              
354 2         11 $proto->register_ensure($name, $data);
355              
356 2         3 return $proto;
357              
358 2         13 };
359              
360             }
361              
362              
363 33     33 0 6378 sub fld { goto &field } sub field {
364              
365 148 50   148 1 6668 my $package = shift if @_ == 3;
366              
367 148         324 my ($name, $data) = @_;
368              
369 148   100     450 $data ||= {};
370              
371 148 50 33     949 return unless ($name && $data);
372              
373             return configure_class_proto $package => sub {
374              
375 148     148   266 my ($proto) = @_;
376              
377 148         634 $proto->register_field($name, $data);
378              
379 148         249 return $proto;
380              
381 148         848 };
382              
383             }
384              
385              
386 0     0 0 0 sub flt { goto &filter } sub filter {
387              
388 1 50   1 1 16 my $package = shift if @_ == 3;
389              
390 1         4 my ($name, $code) = @_;
391              
392 1 50 33     12 return unless ($name && $code);
393              
394             return configure_class_proto $package => sub {
395              
396 1     1   2 my ($proto) = @_;
397              
398 1         8 $proto->register_filter($name, $code);
399              
400 1         2 return $proto;
401              
402 1         8 };
403              
404             }
405              
406              
407 13     13 0 13239 sub set { goto &load } sub load {
408              
409 17     17 1 62 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 17 100       97 if (@_ == 1) {
    50          
    0          
424              
425 5 50       30 if ("HASH" eq ref $_[0]) {
426              
427 5         15 $data = shift;
428              
429             }
430              
431             }
432              
433             # load(a => b)
434             # package->load({})
435              
436             elsif (@_ == 2) {
437              
438 12 50       39 if ("HASH" eq ref $_[-1]) {
439              
440 0         0 $package = shift;
441 0         0 $data = shift;
442              
443             }
444              
445             else {
446              
447 12         47 $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 17     17   37 my ($proto) = @_;
476              
477 17         84 $proto->register_settings($data);
478              
479 16         34 return $proto;
480              
481 17         113 };
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 445 sub mth { goto &method } sub method {
508              
509 16 50   16 1 105 my $package = shift if @_ == 3;
510              
511 16         37 my ($name, $data) = @_;
512              
513 16 50 33     95 return unless ($name && $data);
514              
515             return configure_class_proto $package => sub {
516              
517 16     16   33 my ($proto) = @_;
518              
519 16         72 $proto->register_method($name, $data);
520              
521 16         25 return $proto;
522              
523 16         85 };
524              
525             }
526              
527              
528 2     2 0 26 sub mxn { goto &mixin } sub mixin {
529              
530 19 50   19 1 587 my $package = shift if @_ == 3;
531              
532 19         47 my ($name, $data) = @_;
533              
534 19   50     76 $data ||= {};
535              
536 19 50 33     172 return unless ($name && $data);
537              
538             return configure_class_proto $package => sub {
539              
540 19     19   39 my ($proto) = @_;
541              
542 19         100 $proto->register_mixin($name, $data);
543              
544 19         28 return $proto;
545              
546 19         138 };
547              
548             }
549              
550              
551             sub new {
552              
553 150     150 1 356 my $class = shift;
554              
555 150   33     1105 $class = ref $class || $class;
556              
557 150         547 my $proto = return_class_proto $class;
558              
559 150         439 my $self = bless {}, $class;
560              
561 150         653 initialize_validator $self, @_;
562              
563 149         601 return $self;
564              
565             }
566              
567              
568 4     4 0 58 sub pro { goto &profile } sub profile {
569              
570 11 50   11 1 92 my $package = shift if @_ == 3;
571              
572 11         23 my ($name, $code) = @_;
573              
574 11 50 33     72 return unless ($name && $code);
575              
576             return configure_class_proto $package => sub {
577              
578 11     11   23 my ($proto) = @_;
579              
580 11         49 $proto->register_profile($name, $code);
581              
582 11         17 return $proto;
583              
584 11         57 };
585              
586             }
587              
588              
589 0     0 0 0 sub proto { goto &prototype } sub prototype {
590              
591 306     306 1 10766 my ($self) = pop @_;
592              
593 306   66     1464 return return_class_proto ref $self || $self;
594              
595             }
596              
597              
598             1;
599              
600             __END__