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   5297781 use 5.10.0;
  109         1071  
6 109     109   522 use strict;
  109         183  
  109         2175  
7 109     109   589 use warnings;
  109         190  
  109         2836  
8              
9 109     109   36214 use Module::Find;
  109         115742  
  109         6130  
10              
11 109     109   32480 use Validation::Class::Util '!has';
  109         267  
  109         590  
12 109     109   35463 use Clone 'clone';
  109         198218  
  109         5437  
13 109     109   677 use Exporter ();
  109         197  
  109         1828  
14              
15 109     109   54598 use Validation::Class::Prototype;
  109         351  
  109         13639  
16              
17             our $VERSION = '7.900058'; # 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 2648 my $class = shift || caller(2);
54              
55 858   66     2304 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   887 no strict 'refs';
  109         221  
  109         3459  
64 109     109   547 no warnings 'redefine';
  109         219  
  109         34496  
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   15339 *{"$class\::$new"} = sub { goto \&$new };
75 143     143   6142 *{"$class\::proto"} = sub { goto \&prototype };
76 163     163   5221 *{"$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   17110 shift @_;
91              
92 476         1853 $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   25184 my $self = shift @_;
111              
112 250         1027 $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 453 my $configuration_routine = pop;
130              
131 250 50       711 return unless "CODE" eq ref $configuration_routine;
132              
133 109     109   784 no strict 'refs';
  109         242  
  109         193408  
134              
135 250         569 my $proto = return_class_proto shift;
136              
137 250         655 $configuration_routine->($proto);
138              
139 249         653 return $proto;
140              
141             }
142              
143             sub import {
144              
145 146   33 146   9485 my $caller = caller(0) || caller(1);
146              
147 146         952 strict->import;
148 146         1517 warnings->import;
149              
150 146         17748 __PACKAGE__->export_to_level(1, @_);
151              
152 146         570 return return_class_proto $caller # provision prototype when used
153              
154             }
155              
156             sub initialize_validator {
157              
158 167     167 0 347 my $self = shift;
159 167         605 my $proto = $self->prototype;
160              
161 167         660 my $arguments = $proto->build_args(@_);
162              
163             # provision a validation class configuration
164              
165 167         666 $proto->snapshot;
166              
167             # override prototype attributes if requested
168              
169 167 100       646 if (defined($arguments->{fields})) {
170 64         176 my $fields = delete $arguments->{fields};
171 64         210 $proto->fields->clear->add($fields);
172             }
173              
174 167 100       725 if (defined($arguments->{params})) {
175 78         220 my $params = delete $arguments->{params};
176 78         287 $proto->params->clear->add(clone $params);
177             }
178              
179             # process attribute assignments
180              
181 167         866 my $proxy_methods = { map { $_ => 1 } ($proto->proxy_methods) } ;
  5177         8135  
182              
183 167         755 while (my($name, $value) = each (%{$arguments})) {
  269         995  
184              
185             $self->$name($value) if
186              
187             $self->can($name) &&
188             $proto->fields->has($name) ||
189 102 100 100     501 $proto->attributes->has($name) || $proxy_methods->{$name}
      100        
      100        
190              
191             ;
192              
193             }
194              
195             # process builders
196              
197 167         628 foreach my $builder ($proto->builders->list) {
198              
199 4         11 $builder->($self, $arguments);
200              
201             }
202              
203             # initialize prototype
204              
205 167         840 $proto->normalize($self);
206              
207             # ready-set-go !!!
208              
209 166         851 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 21 my $package = shift if @_ == 4;
219              
220 3         8 my ($class, $type, $name) = @_;
221              
222 3         15 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         44  
  3         8  
235              
236 3   33     10 $type = $keywords->{$type} || $aliases->{$type};
237              
238 3 50 33     13 return unless $class && $name && $type;
      33        
239              
240 3         6 my $store = "${type}s";
241 3         7 my $config = prototype_registry->get($class)->configuration;
242 3         17 my $data = clone $config->$store->get($name);
243              
244 3 50       23 @_ = ($name => $data) and goto &$type;
245              
246 0         0 return;
247              
248             }
249              
250              
251 10     10 0 366 sub has { goto &attribute } sub attribute {
252              
253 14 100   14 1 146 my $package = shift if @_ == 3;
254              
255 14         52 my ($attributes, $default) = @_;
256              
257 14 50       36 return unless $attributes;
258              
259 14 50       46 $attributes = [$attributes] unless isa_arrayref $attributes;
260              
261             return configure_class_proto $package => sub {
262              
263 14     14   52 my ($proto) = @_;
264              
265 14         29 $proto->register_attribute($_ => $default) for @{$attributes};
  14         61  
266              
267 14         26 return $proto;
268              
269 14         71 };
270              
271             }
272              
273              
274 3     3 0 285 sub bld { goto &build } sub build {
275              
276 4 100   4 1 25 my $package = shift if @_ == 2;
277              
278 4         8 my ($code) = @_;
279              
280 4 50       13 return unless ("CODE" eq ref $code);
281              
282             return configure_class_proto $package => sub {
283              
284 4     4   10 my ($proto) = @_;
285              
286 4         14 $proto->register_builder($code);
287              
288 4         6 return $proto;
289              
290 4         21 };
291              
292             }
293              
294              
295 1     1 0 79 sub dir { goto &directive } sub directive {
296              
297 3 50   3 1 26 my $package = shift if @_ == 3;
298              
299 3         8 my ($name, $code) = @_;
300              
301 3 50 33     14 return unless ($name && $code);
302              
303             return configure_class_proto $package => sub {
304              
305 3     3   6 my ($proto) = @_;
306              
307 3         12 $proto->register_directive($name, $code);
308              
309 3         6 return $proto;
310              
311 3         15 };
312              
313             }
314              
315              
316 0     0 0 0 sub doc { goto &document } sub document {
317              
318 12 50   12 1 167 my $package = shift if @_ == 3;
319              
320 12         37 my ($name, $data) = @_;
321              
322 12   50     36 $data ||= {};
323              
324 12 50 33     85 return unless ($name && $data);
325              
326             return configure_class_proto $package => sub {
327              
328 12     12   36 my ($proto) = @_;
329              
330 12         51 $proto->register_document($name, $data);
331              
332 12         20 return $proto;
333              
334 12         66 };
335              
336             };
337              
338              
339              
340 0     0 0 0 sub ens { goto &ensure } sub ensure {
341              
342 2 50   2 1 31 my $package = shift if @_ == 3;
343              
344 2         5 my ($name, $data) = @_;
345              
346 2   50     6 $data ||= {};
347              
348 2 50 33     10 return unless ($name && $data);
349              
350             return configure_class_proto $package => sub {
351              
352 2     2   4 my ($proto) = @_;
353              
354 2         18 $proto->register_ensure($name, $data);
355              
356 2         3 return $proto;
357              
358 2         11 };
359              
360             }
361              
362              
363 33     33 0 5803 sub fld { goto &field } sub field {
364              
365 150 50   150 1 10951 my $package = shift if @_ == 3;
366              
367 150         365 my ($name, $data) = @_;
368              
369 150   100     401 $data ||= {};
370              
371 150 50 33     658 return unless ($name && $data);
372              
373             return configure_class_proto $package => sub {
374              
375 150     150   284 my ($proto) = @_;
376              
377 150         518 $proto->register_field($name, $data);
378              
379 150         251 return $proto;
380              
381 150         719 };
382              
383             }
384              
385              
386 0     0 0 0 sub flt { goto &filter } sub filter {
387              
388 1 50   1 1 80 my $package = shift if @_ == 3;
389              
390 1         4 my ($name, $code) = @_;
391              
392 1 50 33     8 return unless ($name && $code);
393              
394             return configure_class_proto $package => sub {
395              
396 1     1   2 my ($proto) = @_;
397              
398 1         6 $proto->register_filter($name, $code);
399              
400 1         2 return $proto;
401              
402 1         7 };
403              
404             }
405              
406              
407 14     14 0 8884 sub set { goto &load } sub load {
408              
409 18     18 1 151 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       83 if (@_ == 1) {
    50          
    0          
424              
425 5 50       19 if ("HASH" eq ref $_[0]) {
426              
427 5         14 $data = shift;
428              
429             }
430              
431             }
432              
433             # load(a => b)
434             # package->load({})
435              
436             elsif (@_ == 2) {
437              
438 13 50       59 if ("HASH" eq ref $_[-1]) {
439              
440 0         0 $package = shift;
441 0         0 $data = shift;
442              
443             }
444              
445             else {
446              
447 13         45 $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   41 my ($proto) = @_;
476              
477 18         77 $proto->register_settings($data);
478              
479 17         41 return $proto;
480              
481 18         106 };
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 429 sub mth { goto &method } sub method {
508              
509 16 50   16 1 95 my $package = shift if @_ == 3;
510              
511 16         41 my ($name, $data) = @_;
512              
513 16 50 33     93 return unless ($name && $data);
514              
515             return configure_class_proto $package => sub {
516              
517 16     16   50 my ($proto) = @_;
518              
519 16         76 $proto->register_method($name, $data);
520              
521 16         24 return $proto;
522              
523 16         85 };
524              
525             }
526              
527              
528 2     2 0 21 sub mxn { goto &mixin } sub mixin {
529              
530 19 50   19 1 951 my $package = shift if @_ == 3;
531              
532 19         49 my ($name, $data) = @_;
533              
534 19   50     50 $data ||= {};
535              
536 19 50 33     93 return unless ($name && $data);
537              
538             return configure_class_proto $package => sub {
539              
540 19     19   43 my ($proto) = @_;
541              
542 19         65 $proto->register_mixin($name, $data);
543              
544 19         26 return $proto;
545              
546 19         95 };
547              
548             }
549              
550              
551             sub new {
552              
553 152     152 1 358 my $class = shift;
554              
555 152   33     752 $class = ref $class || $class;
556              
557 152         481 my $proto = return_class_proto $class;
558              
559 152         434 my $self = bless {}, $class;
560              
561 152         603 initialize_validator $self, @_;
562              
563 151         527 return $self;
564              
565             }
566              
567              
568 4     4 0 47 sub pro { goto &profile } sub profile {
569              
570 11 50   11 1 86 my $package = shift if @_ == 3;
571              
572 11         27 my ($name, $code) = @_;
573              
574 11 50 33     52 return unless ($name && $code);
575              
576             return configure_class_proto $package => sub {
577              
578 11     11   32 my ($proto) = @_;
579              
580 11         49 $proto->register_profile($name, $code);
581              
582 11         30 return $proto;
583              
584 11         50 };
585              
586             }
587              
588              
589 0     0 0 0 sub proto { goto &prototype } sub prototype {
590              
591 310     310 1 6534 my ($self) = pop @_;
592              
593 310   66     1128 return return_class_proto ref $self || $self;
594              
595             }
596              
597              
598             1;
599              
600             __END__