File Coverage

blib/lib/Mars/Meta.pm
Criterion Covered Total %
statement 111 111 100.0
branch 16 20 80.0
condition 11 14 78.5
subroutine 23 23 100.0
pod 10 14 71.4
total 171 182 93.9


line stmt bran cond sub pod time code
1             package Mars::Meta;
2              
3 6     6   87 use 5.018;
  6         16  
4              
5 6     6   26 use strict;
  6         6  
  6         97  
6 6     6   22 use warnings;
  6         8  
  6         143  
7              
8 6     6   25 use base 'Mars::Kind';
  6         9  
  6         2145  
9              
10             # METHODS
11              
12             sub attr {
13 2     2 1 8 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       13 return $data->{$name} ? 1 : 0;
20             }
21              
22             sub attrs {
23 3     3 1 6 my ($self) = @_;
24              
25 3         6 my $name = $self->{name};
26 3         5 my @attrs = attrs_resolver($name);
27              
28 3         4 for my $base (@{$self->bases}) {
  3         11  
29 9         12 push @attrs, attrs_resolver($base);
30             }
31              
32 3         3 for my $role (@{$self->roles}) {
  3         18  
33 6         10 push @attrs, attrs_resolver($role);
34             }
35              
36 3         4 my %seen;
37 3   50     33 return $self->{attrs} ||= [grep !$seen{$_}++, @attrs];
38             }
39              
40             sub attrs_resolver {
41 18     18 0 25 my ($name) = @_;
42              
43 6     6   37 no strict 'refs';
  6         9  
  6         1579  
44              
45 18 100 100     16 if (${"${name}::META"} && $${"${name}::META"}{ATTR}) {
  18         44  
  12         27  
46 9         9 return (keys %{$${"${name}::META"}{ATTR}});
  9         9  
  9         32  
47             }
48             else {
49 9         11 return ();
50             }
51             }
52              
53             sub base {
54 2     2 1 7 my ($self, $name) = @_;
55              
56 2 50       4 return 0 if !$name;
57              
58 2         3 my $data = {map +($_,$_), @{$self->bases}};
  2         3  
59              
60 2 100       11 return $data->{$name} ? 1 : 0;
61             }
62              
63             sub bases {
64 48     48 1 699 my ($self) = @_;
65              
66 48         60 my $name = $self->{name};
67 48         64 my @bases = bases_resolver($name);
68              
69 48         77 for my $base (@bases) {
70 187         236 push @bases, bases_resolver($base);
71             }
72              
73 48         51 my %seen;
74 48   100     338 return $self->{bases} ||= [grep !$seen{$_}++, @bases];
75             }
76              
77             sub bases_resolver {
78 235     235 0 256 my ($name) = @_;
79              
80 6     6   37 no strict 'refs';
  6         11  
  6         428  
81              
82 235         211 return (@{"${name}::ISA"});
  235         532  
83             }
84              
85             sub data {
86 1     1 1 5 my ($self) = @_;
87              
88 1         2 my $name = $self->{name};
89              
90 6     6   38 no strict 'refs';
  6         10  
  6         1597  
91              
92 1         1 return ${"${name}::META"};
  1         5  
93             }
94              
95             sub new {
96 50     50 1 4305 my ($self, @args) = @_;
97              
98 50         119 return $self->BLESS(@args);
99             }
100              
101             sub role {
102 33     33 1 57 my ($self, $name) = @_;
103              
104 33 50       60 return 0 if !$name;
105              
106 33         34 my $data = {map +($_,$_), @{$self->roles}};
  33         52  
107              
108 33 100       189 return $data->{$name} ? 1 : 0;
109             }
110              
111             sub roles {
112 38     38 1 53 my ($self) = @_;
113              
114 38         60 my $name = $self->{name};
115 38         54 my @roles = roles_resolver($name);
116              
117 38         46 for my $base (@{$self->bases}) {
  38         56  
118 102         137 push @roles, roles_resolver($base);
119             }
120              
121 38         49 for my $role (@roles) {
122 87         119 push @roles, roles_resolver($role);
123             }
124              
125 38         39 my %seen;
126 38   50     285 return $self->{roles} ||= [grep !$seen{$_}++, @roles];
127             }
128              
129             sub roles_resolver {
130 227     227 0 260 my ($name) = @_;
131              
132 6     6   42 no strict 'refs';
  6         10  
  6         1548  
133              
134 227 100 100     226 if (${"${name}::META"} && $${"${name}::META"}{ROLE}) {
  227         512  
  93         246  
135 45         48 return (keys %{$${"${name}::META"}{ROLE}});
  45         365  
  45         158  
136             }
137             else {
138 182         248 return ();
139             }
140             }
141              
142             sub sub {
143 2     2 1 8 my ($self, $name) = @_;
144              
145 2 50       4 return 0 if !$name;
146              
147 2         3 my $data = {map +($_,$_), @{$self->subs}};
  2         3  
148              
149 2 100       18 return $data->{$name} ? 1 : 0;
150             }
151              
152             sub subs {
153 3     3 1 7 my ($self) = @_;
154              
155 3         4 my $name = $self->{name};
156 3         3 my @subs = subs_resolver($name);
157              
158 3         7 for my $base (@{$self->bases}) {
  3         6  
159 9         15 push @subs, subs_resolver($base);
160             }
161              
162 3         5 my %seen;
163 3   50     82 return $self->{subs} ||= [grep !$seen{$_}++, @subs];
164             }
165              
166             sub subs_resolver {
167 12     12 0 14 my ($name) = @_;
168              
169 6     6   35 no strict 'refs';
  6         9  
  6         866  
170              
171             return (
172 216         361 grep *{"${name}::$_"}{"CODE"},
173 12         13 grep /^[_a-zA-Z]\w*$/, keys %{"${name}::"}
  12         117  
174             );
175             }
176              
177             1;
178              
179              
180              
181             =head1 NAME
182              
183             Mars::Meta - Class Metadata
184              
185             =cut
186              
187             =head1 ABSTRACT
188              
189             Class Metadata for Perl 5
190              
191             =cut
192              
193             =head1 SYNOPSIS
194              
195             package Person;
196              
197             use Mars::Class;
198              
199             attr 'fname';
200             attr 'lname';
201              
202             package Identity;
203              
204             use Mars::Role;
205              
206             attr 'id';
207             attr 'login';
208             attr 'password';
209              
210             sub EXPORT {
211             # explicitly declare routines to be consumed
212             ['id', 'login', 'password']
213             }
214              
215             package Authenticable;
216              
217             use Mars::Role;
218              
219             sub authenticate {
220             return true;
221             }
222              
223             sub AUDIT {
224             my ($self, $from) = @_;
225             # ensure the caller has a login and password when consumed
226             die "${from} missing the login attribute" if !$from->can('login');
227             die "${from} missing the password attribute" if !$from->can('password');
228             }
229              
230             sub EXPORT {
231             # explicitly declare routines to be consumed
232             ['authenticate']
233             }
234              
235             package User;
236              
237             use Mars::Class;
238              
239             base 'Person';
240              
241             with 'Identity';
242              
243             attr 'email';
244              
245             test 'Authenticable';
246              
247             sub valid {
248             my ($self) = @_;
249             return $self->login && $self->password ? true : false;
250             }
251              
252             package main;
253              
254             my $user = User->new(
255             fname => 'Elliot',
256             lname => 'Alderson',
257             );
258              
259             my $meta = $user->meta;
260              
261             # bless({name => 'User'}, 'Mars::Meta')
262              
263             =cut
264              
265             =head1 DESCRIPTION
266              
267             This package provides configuration information for L derived classes,
268             roles, and interfaces.
269              
270             =cut
271              
272             =head1 METHODS
273              
274             This package provides the following methods:
275              
276             =cut
277              
278             =head2 attr
279              
280             attr(Str $name) (Bool)
281              
282             The attr method returns true or false if the package referenced has the
283             attribute accessor named.
284              
285             I>
286              
287             =over 4
288              
289             =item attr example 1
290              
291             # given: synopsis
292              
293             package main;
294              
295             my $attr = $meta->attr('email');
296              
297             # 1
298              
299             =back
300              
301             =over 4
302              
303             =item attr example 2
304              
305             # given: synopsis
306              
307             package main;
308              
309             my $attr = $meta->attr('username');
310              
311             # 0
312              
313             =back
314              
315             =cut
316              
317             =head2 attrs
318              
319             attrs() (ArrayRef)
320              
321             The attrs method returns all of the attributes composed into the package
322             referenced.
323              
324             I>
325              
326             =over 4
327              
328             =item attrs example 1
329              
330             # given: synopsis
331              
332             package main;
333              
334             my $attrs = $meta->attrs;
335              
336             # [
337             # 'email',
338             # 'fname',
339             # 'id',
340             # 'lname',
341             # 'login',
342             # 'password',
343             # ]
344              
345             =back
346              
347             =cut
348              
349             =head2 base
350              
351             base(Str $name) (Bool)
352              
353             The base method returns true or false if the package referenced has inherited
354             the package named.
355              
356             I>
357              
358             =over 4
359              
360             =item base example 1
361              
362             # given: synopsis
363              
364             package main;
365              
366             my $base = $meta->base('Person');
367              
368             # 1
369              
370             =back
371              
372             =over 4
373              
374             =item base example 2
375              
376             # given: synopsis
377              
378             package main;
379              
380             my $base = $meta->base('Student');
381              
382             # 0
383              
384             =back
385              
386             =cut
387              
388             =head2 bases
389              
390             bases() (ArrayRef)
391              
392             The bases method returns returns all of the packages inherited by the package
393             referenced.
394              
395             I>
396              
397             =over 4
398              
399             =item bases example 1
400              
401             # given: synopsis
402              
403             package main;
404              
405             my $bases = $meta->bases;
406              
407             # [
408             # 'Person',
409             # 'Mars::Kind::Class',
410             # 'Mars::Kind',
411             # ]
412              
413             =back
414              
415             =cut
416              
417             =head2 data
418              
419             data() (HashRef)
420              
421             The data method returns a data structure representing the shallow configuration
422             for the package referenced.
423              
424             I>
425              
426             =over 4
427              
428             =item data example 1
429              
430             # given: synopsis
431              
432             package main;
433              
434             my $data = $meta->data;
435              
436             # {
437             # 'ATTR' => {
438             # 'email' => [
439             # 'email'
440             # ]
441             # },
442             # 'BASE' => {
443             # 'Person' => [
444             # 'Person'
445             # ]
446             # },
447             # 'ROLE' => {
448             # 'Authenticable' => [
449             # 'Authenticable'
450             # ],
451             # 'Identity' => [
452             # 'Identity'
453             # ]
454             # }
455             # }
456              
457             =back
458              
459             =cut
460              
461             =head2 new
462              
463             new(Any %args | HashRef $args) (Object)
464              
465             The new method returns a new instance of this package.
466              
467             I>
468              
469             =over 4
470              
471             =item new example 1
472              
473             # given: synopsis
474              
475             package main;
476              
477             my $meta = Mars::Meta->new(name => 'User');
478              
479             # bless({name => 'User'}, 'Mars::Meta')
480              
481             =back
482              
483             =over 4
484              
485             =item new example 2
486              
487             # given: synopsis
488              
489             package main;
490              
491             my $meta = Mars::Meta->new({name => 'User'});
492              
493             # bless({name => 'User'}, 'Mars::Meta')
494              
495             =back
496              
497             =cut
498              
499             =head2 role
500              
501             role(Str $name) (Bool)
502              
503             The role method returns true or false if the package referenced has consumed
504             the role named.
505              
506             I>
507              
508             =over 4
509              
510             =item role example 1
511              
512             # given: synopsis
513              
514             package main;
515              
516             my $role = $meta->role('Identity');
517              
518             # 1
519              
520             =back
521              
522             =over 4
523              
524             =item role example 2
525              
526             # given: synopsis
527              
528             package main;
529              
530             my $role = $meta->role('Builder');
531              
532             # 0
533              
534             =back
535              
536             =cut
537              
538             =head2 roles
539              
540             roles() (ArrayRef)
541              
542             The roles method returns all of the roles composed into the package referenced.
543              
544             I>
545              
546             =over 4
547              
548             =item roles example 1
549              
550             # given: synopsis
551              
552             package main;
553              
554             my $roles = $meta->roles;
555              
556             # [
557             # 'Identity',
558             # 'Authenticable'
559             # ]
560              
561             =back
562              
563             =cut
564              
565             =head2 sub
566              
567             sub(Str $name) (Bool)
568              
569             The sub method returns true or false if the package referenced has the
570             subroutine named on the package directly, or any of its superclasses.
571              
572             I>
573              
574             =over 4
575              
576             =item sub example 1
577              
578             # given: synopsis
579              
580             package main;
581              
582             my $sub = $meta->sub('authenticate');
583              
584             # 1
585              
586             =back
587              
588             =over 4
589              
590             =item sub example 2
591              
592             # given: synopsis
593              
594             package main;
595              
596             my $sub = $meta->sub('authorize');
597              
598             # 0
599              
600             =back
601              
602             =cut
603              
604             =head2 subs
605              
606             subs() (ArrayRef)
607              
608             The subs method returns all of the subroutines composed into the package
609             referenced.
610              
611             I>
612              
613             =over 4
614              
615             =item subs example 1
616              
617             # given: synopsis
618              
619             package main;
620              
621             my $subs = $meta->subs;
622              
623             # [
624             # 'attr', ...,
625             # 'base',
626             # 'email',
627             # 'false',
628             # 'fname', ...,
629             # 'id',
630             # 'lname',
631             # 'login',
632             # 'new', ...,
633             # 'role',
634             # 'test',
635             # 'true',
636             # 'with', ...,
637             # ]
638              
639             =back
640              
641             =cut
642              
643             =head1 AUTHORS
644              
645             Awncorp, C
646              
647             =cut