File Coverage

blib/lib/Mars/Meta.pm
Criterion Covered Total %
statement 159 166 95.7
branch 29 40 72.5
condition 14 19 73.6
subroutine 29 29 100.0
pod 13 18 72.2
total 244 272 89.7


line stmt bran cond sub pod time code
1             package Mars::Meta;
2              
3 8     8   152 use 5.018;
  8         24  
4              
5 8     8   77 use strict;
  8         12  
  8         164  
6 8     8   30 use warnings;
  8         20  
  8         234  
7              
8 8     8   32 use base 'Mars::Kind';
  8         12  
  8         2627  
9              
10             # METHODS
11              
12             sub attr {
13 2     2 1 7 my ($self, $name) = @_;
14              
15 2 50       4 return 0 if !$name;
16              
17 2         3 my $data = {map +($_,$_), @{$self->attrs}};
  2         4  
18              
19 2 100       12 return $data->{$name} ? 1 : 0;
20             }
21              
22             sub attrs {
23 3     3 1 6 my ($self) = @_;
24              
25 3 50       5 if ($self->{attrs}) {
26 0         0 return $self->{attrs};
27             }
28              
29 3         4 my $name = $self->{name};
30 3         4 my @attrs = attrs_resolver($name);
31              
32 3         3 for my $base (@{$self->bases}) {
  3         6  
33 9         13 push @attrs, attrs_resolver($base);
34             }
35              
36 3         3 for my $role (@{$self->roles}) {
  3         6  
37 6         8 push @attrs, attrs_resolver($role);
38             }
39              
40 3         3 my %seen;
41 3   50     37 return $self->{attrs} ||= [grep !$seen{$_}++, @attrs];
42             }
43              
44             sub attrs_resolver {
45 19     19 0 22 my ($name) = @_;
46              
47 8     8   52 no strict 'refs';
  8         13  
  8         2502  
48              
49 19 100 100     18 if (${"${name}::META"} && $${"${name}::META"}{ATTR}) {
  19         44  
  13         35  
50             return (sort {
51 9         11 $${"${name}::META"}{ATTR}{$a}[0] <=> $${"${name}::META"}{ATTR}{$b}[0]
  9         40  
  9         26  
52 10         10 } keys %{$${"${name}::META"}{ATTR}});
  10         8  
  10         31  
53             }
54             else {
55 9         14 return ();
56             }
57             }
58              
59             sub base {
60 2     2 1 9 my ($self, $name) = @_;
61              
62 2 50       4 return 0 if !$name;
63              
64 2         2 my $data = {map +($_,$_), @{$self->bases}};
  2         4  
65              
66 2 100       10 return $data->{$name} ? 1 : 0;
67             }
68              
69             sub bases {
70 150     150 1 761 my ($self) = @_;
71              
72 150 100       283 if ($self->{bases}) {
73 4         8 return $self->{bases};
74             }
75              
76 146         190 my $name = $self->{name};
77 146         216 my @bases = bases_resolver($name);
78              
79 146         229 for my $base (@bases) {
80 518         602 push @bases, bases_resolver($base);
81             }
82              
83 146         164 my %seen;
84 146   50     1045 return $self->{bases} ||= [grep !$seen{$_}++, @bases];
85             }
86              
87             sub bases_resolver {
88 665     665 0 729 my ($name) = @_;
89              
90 8     8   46 no strict 'refs';
  8         19  
  8         567  
91              
92 665         605 return (@{"${name}::ISA"});
  665         1547  
93             }
94              
95             sub data {
96 1     1 1 3 my ($self) = @_;
97              
98 1         3 my $name = $self->{name};
99              
100 8     8   45 no strict 'refs';
  8         15  
  8         687  
101              
102 1         1 return ${"${name}::META"};
  1         4  
103             }
104              
105             sub local {
106 4     4 1 16 my ($self, $type) = @_;
107              
108 4 50       7 return if !$type;
109              
110 4         5 my $name = $self->{name};
111              
112 8     8   52 no strict 'refs';
  8         13  
  8         2286  
113              
114 4 50       13 return if !int grep $type eq $_, qw(attrs bases mixins roles subs);
115              
116 4         8 my $function = "${type}_resolver";
117              
118 4         4 return [&{"${function}"}($name)];
  4         9  
119             }
120              
121             sub mixin {
122 2     2 1 7 my ($self, $name) = @_;
123              
124 2 50       5 return 0 if !$name;
125              
126 2         2 my $data = {map +($_,$_), @{$self->mixins}};
  2         4  
127              
128 2 100       10 return $data->{$name} ? 1 : 0;
129             }
130              
131             sub mixins {
132 3     3 1 6 my ($self) = @_;
133              
134 3 50       9 if ($self->{mixins}) {
135 0         0 return $self->{mixins};
136             }
137              
138 3         4 my $name = $self->{name};
139 3         6 my @mixins = mixins_resolver($name);
140              
141 3         5 for my $mixin (@mixins) {
142 3         4 push @mixins, mixins_resolver($mixin);
143             }
144              
145 3         3 for my $base (@{$self->bases}) {
  3         5  
146 9         14 push @mixins, mixins_resolver($base);
147             }
148              
149 3         3 my %seen;
150 3   50     29 return $self->{mixins} ||= [grep !$seen{$_}++, @mixins];
151             }
152              
153             sub mixins_resolver {
154 18     18 0 22 my ($name) = @_;
155              
156 8     8   52 no strict 'refs';
  8         12  
  8         3271  
157              
158 18 100 100     17 if (${"${name}::META"} && $${"${name}::META"}{MIXIN}) {
  18         40  
  12         27  
159             return (map +($_, mixins_resolver($_)), sort {
160 0         0 $${"${name}::META"}{MIXIN}{$a}[0] <=> $${"${name}::META"}{MIXIN}{$b}[0]
  0         0  
  0         0  
161 3         3 } keys %{$${"${name}::META"}{MIXIN}});
  3         3  
  3         11  
162             }
163             else {
164 15         29 return ();
165             }
166             }
167              
168             sub new {
169 157     157 1 12910 my ($self, @args) = @_;
170              
171 157         369 return $self->BLESS(@args);
172             }
173              
174             sub role {
175 44     44 1 93 my ($self, $name) = @_;
176              
177 44 50       95 return 0 if !$name;
178              
179 44         49 my $data = {map +($_,$_), @{$self->roles}};
  44         96  
180              
181 44 100       255 return $data->{$name} ? 1 : 0;
182             }
183              
184             sub roles {
185 137     137 1 198 my ($self) = @_;
186              
187 137 50       297 if ($self->{roles}) {
188 0         0 return $self->{roles};
189             }
190              
191 137         193 my $name = $self->{name};
192 137         224 my @roles = roles_resolver($name);
193              
194 137         219 for my $role (@roles) {
195 213         265 push @roles, roles_resolver($role);
196             }
197              
198 137         147 for my $base (@{$self->bases}) {
  137         271  
199 346         447 push @roles, roles_resolver($base);
200             }
201              
202 137         164 my %seen;
203 137   50     807 return $self->{roles} ||= [grep !$seen{$_}++, @roles];
204             }
205              
206             sub roles_resolver {
207 912     912 0 1057 my ($name) = @_;
208              
209 8     8   49 no strict 'refs';
  8         15  
  8         2470  
210              
211 912 100 100     839 if (${"${name}::META"} && $${"${name}::META"}{ROLE}) {
  912         2130  
  462         1052  
212             return (map +($_, roles_resolver($_)), sort {
213 103         127 $${"${name}::META"}{ROLE}{$a}[0] <=> $${"${name}::META"}{ROLE}{$b}[0]
  103         184  
  103         341  
214 122         145 } keys %{$${"${name}::META"}{ROLE}});
  122         120  
  122         472  
215             }
216             else {
217 790         1322 return ();
218             }
219             }
220              
221             sub sub {
222 2     2 1 7 my ($self, $name) = @_;
223              
224 2 50       9 return 0 if !$name;
225              
226 2         3 my $data = {map +($_,$_), @{$self->subs}};
  2         4  
227              
228 2 100       20 return $data->{$name} ? 1 : 0;
229             }
230              
231             sub subs {
232 3     3 1 5 my ($self) = @_;
233              
234 3 50       6 if ($self->{subs}) {
235 0         0 return $self->{subs};
236             }
237              
238 3         4 my $name = $self->{name};
239 3         4 my @subs = subs_resolver($name);
240              
241 3         7 for my $base (@{$self->bases}) {
  3         22  
242 9         12 push @subs, subs_resolver($base);
243             }
244              
245 3         4 my %seen;
246 3   50     111 return $self->{subs} ||= [grep !$seen{$_}++, @subs];
247             }
248              
249             sub subs_resolver {
250 13     13 0 18 my ($name) = @_;
251              
252 8     8   50 no strict 'refs';
  8         21  
  8         1361  
253              
254             return (
255 275         445 grep *{"${name}::$_"}{"CODE"},
256 13         12 grep /^[_a-zA-Z]\w*$/, keys %{"${name}::"}
  13         142  
257             );
258             }
259              
260             1;
261              
262              
263              
264             =head1 NAME
265              
266             Mars::Meta - Class Metadata
267              
268             =cut
269              
270             =head1 ABSTRACT
271              
272             Class Metadata for Perl 5
273              
274             =cut
275              
276             =head1 SYNOPSIS
277              
278             package Person;
279              
280             use Mars::Class;
281              
282             attr 'fname';
283             attr 'lname';
284              
285             package Identity;
286              
287             use Mars::Role;
288              
289             attr 'id';
290             attr 'login';
291             attr 'password';
292              
293             sub EXPORT {
294             # explicitly declare routines to be consumed
295             ['id', 'login', 'password']
296             }
297              
298             package Authenticable;
299              
300             use Mars::Role;
301              
302             sub authenticate {
303             return true;
304             }
305              
306             sub AUDIT {
307             my ($self, $from) = @_;
308             # ensure the caller has a login and password when consumed
309             die "${from} missing the login attribute" if !$from->can('login');
310             die "${from} missing the password attribute" if !$from->can('password');
311             }
312              
313             sub EXPORT {
314             # explicitly declare routines to be consumed
315             ['authenticate']
316             }
317              
318             package User;
319              
320             use Mars::Class;
321              
322             base 'Person';
323              
324             with 'Identity';
325              
326             attr 'email';
327              
328             test 'Authenticable';
329              
330             sub valid {
331             my ($self) = @_;
332             return $self->login && $self->password ? true : false;
333             }
334              
335             package main;
336              
337             my $user = User->new(
338             fname => 'Elliot',
339             lname => 'Alderson',
340             );
341              
342             my $meta = $user->meta;
343              
344             # bless({name => 'User'}, 'Mars::Meta')
345              
346             =cut
347              
348             =head1 DESCRIPTION
349              
350             This package provides configuration information for L derived classes,
351             roles, and interfaces.
352              
353             =cut
354              
355             =head1 METHODS
356              
357             This package provides the following methods:
358              
359             =cut
360              
361             =head2 attr
362              
363             attr(Str $name) (Bool)
364              
365             The attr method returns true or false if the package referenced has the
366             attribute accessor named.
367              
368             I>
369              
370             =over 4
371              
372             =item attr example 1
373              
374             # given: synopsis
375              
376             package main;
377              
378             my $attr = $meta->attr('email');
379              
380             # 1
381              
382             =back
383              
384             =over 4
385              
386             =item attr example 2
387              
388             # given: synopsis
389              
390             package main;
391              
392             my $attr = $meta->attr('username');
393              
394             # 0
395              
396             =back
397              
398             =cut
399              
400             =head2 attrs
401              
402             attrs() (ArrayRef)
403              
404             The attrs method returns all of the attributes composed into the package
405             referenced.
406              
407             I>
408              
409             =over 4
410              
411             =item attrs example 1
412              
413             # given: synopsis
414              
415             package main;
416              
417             my $attrs = $meta->attrs;
418              
419             # [
420             # 'email',
421             # 'fname',
422             # 'id',
423             # 'lname',
424             # 'login',
425             # 'password',
426             # ]
427              
428             =back
429              
430             =cut
431              
432             =head2 base
433              
434             base(Str $name) (Bool)
435              
436             The base method returns true or false if the package referenced has inherited
437             the package named.
438              
439             I>
440              
441             =over 4
442              
443             =item base example 1
444              
445             # given: synopsis
446              
447             package main;
448              
449             my $base = $meta->base('Person');
450              
451             # 1
452              
453             =back
454              
455             =over 4
456              
457             =item base example 2
458              
459             # given: synopsis
460              
461             package main;
462              
463             my $base = $meta->base('Student');
464              
465             # 0
466              
467             =back
468              
469             =cut
470              
471             =head2 bases
472              
473             bases() (ArrayRef)
474              
475             The bases method returns returns all of the packages inherited by the package
476             referenced.
477              
478             I>
479              
480             =over 4
481              
482             =item bases example 1
483              
484             # given: synopsis
485              
486             package main;
487              
488             my $bases = $meta->bases;
489              
490             # [
491             # 'Person',
492             # 'Mars::Kind::Class',
493             # 'Mars::Kind',
494             # ]
495              
496             =back
497              
498             =cut
499              
500             =head2 data
501              
502             data() (HashRef)
503              
504             The data method returns a data structure representing the shallow configuration
505             for the package referenced.
506              
507             I>
508              
509             =over 4
510              
511             =item data example 1
512              
513             # given: synopsis
514              
515             package main;
516              
517             my $data = $meta->data;
518              
519             # {
520             # 'ATTR' => {
521             # 'email' => [
522             # 'email'
523             # ]
524             # },
525             # 'BASE' => {
526             # 'Person' => [
527             # 'Person'
528             # ]
529             # },
530             # 'ROLE' => {
531             # 'Authenticable' => [
532             # 'Authenticable'
533             # ],
534             # 'Identity' => [
535             # 'Identity'
536             # ]
537             # }
538             # }
539              
540             =back
541              
542             =cut
543              
544             =head2 local
545              
546             local(Str $type) (Any)
547              
548             The local method returns the names of properties defined in the package
549             directly (not inherited) for the property type specified. The C<$type> provided
550             can be either C, C, C, C, or C.
551              
552             I>
553              
554             =over 4
555              
556             =item local example 1
557              
558             # given: synopsis
559              
560             package main;
561              
562             my $attrs = $meta->local('attrs');
563              
564             # [...]
565              
566             =back
567              
568             =over 4
569              
570             =item local example 2
571              
572             # given: synopsis
573              
574             package main;
575              
576             my $bases = $meta->local('bases');
577              
578             # [...]
579              
580             =back
581              
582             =cut
583              
584             =over 4
585              
586             =item local example 3
587              
588             # given: synopsis
589              
590             package main;
591              
592             my $mixins = $meta->local('mixins');
593              
594             # [...]
595              
596             =back
597              
598             =cut
599              
600             =over 4
601              
602             =item local example 4
603              
604             # given: synopsis
605              
606             package main;
607              
608             my $roles = $meta->local('roles');
609              
610             # [...]
611              
612             =back
613              
614             =cut
615              
616             =over 4
617              
618             =item local example 5
619              
620             # given: synopsis
621              
622             package main;
623              
624             my $subs = $meta->local('subs');
625              
626             # [...]
627              
628             =back
629              
630             =cut
631              
632             =head2 mixin
633              
634             mixin(Str $name) (Bool)
635              
636             The mixin method returns true or false if the package referenced has consumed
637             the mixin named.
638              
639             I>
640              
641             =over 4
642              
643             =item mixin example 1
644              
645             # given: synopsis
646              
647             package main;
648              
649             my $mixin = $meta->mixin('Novice');
650              
651             # 1
652              
653             =back
654              
655             =over 4
656              
657             =item mixin example 2
658              
659             # given: synopsis
660              
661             package main;
662              
663             my $mixin = $meta->mixin('Intermediate');
664              
665             # 0
666              
667             =back
668              
669             =cut
670              
671             =head2 mixins
672              
673             mixins() (ArrayRef)
674              
675             The mixins method returns all of the mixins composed into the package
676             referenced.
677              
678             I>
679              
680             =over 4
681              
682             =item mixins example 1
683              
684             # given: synopsis
685              
686             package main;
687              
688             my $mixins = $meta->mixins;
689              
690             # [
691             # 'Novice',
692             # ]
693              
694             =back
695              
696             =cut
697              
698             =head2 new
699              
700             new(Any %args | HashRef $args) (Object)
701              
702             The new method returns a new instance of this package.
703              
704             I>
705              
706             =over 4
707              
708             =item new example 1
709              
710             # given: synopsis
711              
712             package main;
713              
714             my $meta = Mars::Meta->new(name => 'User');
715              
716             # bless({name => 'User'}, 'Mars::Meta')
717              
718             =back
719              
720             =over 4
721              
722             =item new example 2
723              
724             # given: synopsis
725              
726             package main;
727              
728             my $meta = Mars::Meta->new({name => 'User'});
729              
730             # bless({name => 'User'}, 'Mars::Meta')
731              
732             =back
733              
734             =cut
735              
736             =head2 role
737              
738             role(Str $name) (Bool)
739              
740             The role method returns true or false if the package referenced has consumed
741             the role named.
742              
743             I>
744              
745             =over 4
746              
747             =item role example 1
748              
749             # given: synopsis
750              
751             package main;
752              
753             my $role = $meta->role('Identity');
754              
755             # 1
756              
757             =back
758              
759             =over 4
760              
761             =item role example 2
762              
763             # given: synopsis
764              
765             package main;
766              
767             my $role = $meta->role('Builder');
768              
769             # 0
770              
771             =back
772              
773             =cut
774              
775             =head2 roles
776              
777             roles() (ArrayRef)
778              
779             The roles method returns all of the roles composed into the package referenced.
780              
781             I>
782              
783             =over 4
784              
785             =item roles example 1
786              
787             # given: synopsis
788              
789             package main;
790              
791             my $roles = $meta->roles;
792              
793             # [
794             # 'Identity',
795             # 'Authenticable'
796             # ]
797              
798             =back
799              
800             =cut
801              
802             =head2 sub
803              
804             sub(Str $name) (Bool)
805              
806             The sub method returns true or false if the package referenced has the
807             subroutine named on the package directly, or any of its superclasses.
808              
809             I>
810              
811             =over 4
812              
813             =item sub example 1
814              
815             # given: synopsis
816              
817             package main;
818              
819             my $sub = $meta->sub('authenticate');
820              
821             # 1
822              
823             =back
824              
825             =over 4
826              
827             =item sub example 2
828              
829             # given: synopsis
830              
831             package main;
832              
833             my $sub = $meta->sub('authorize');
834              
835             # 0
836              
837             =back
838              
839             =cut
840              
841             =head2 subs
842              
843             subs() (ArrayRef)
844              
845             The subs method returns all of the subroutines composed into the package
846             referenced.
847              
848             I>
849              
850             =over 4
851              
852             =item subs example 1
853              
854             # given: synopsis
855              
856             package main;
857              
858             my $subs = $meta->subs;
859              
860             # [
861             # 'attr', ...,
862             # 'base',
863             # 'email',
864             # 'false',
865             # 'fname', ...,
866             # 'id',
867             # 'lname',
868             # 'login',
869             # 'new', ...,
870             # 'role',
871             # 'test',
872             # 'true',
873             # 'with', ...,
874             # ]
875              
876             =back
877              
878             =cut
879              
880             =head1 AUTHORS
881              
882             Awncorp, C
883              
884             =cut