File Coverage

blib/lib/Mars/Meta.pm
Criterion Covered Total %
statement 117 117 100.0
branch 16 20 80.0
condition 11 14 78.5
subroutine 23 23 100.0
pod 10 14 71.4
total 177 188 94.1


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