File Coverage

blib/lib/Coat/Meta.pm
Criterion Covered Total %
statement 101 109 92.6
branch 42 50 84.0
condition 10 13 76.9
subroutine 22 25 88.0
pod 0 20 0.0
total 175 217 80.6


line stmt bran cond sub pod time code
1             package Coat::Meta;
2              
3 43     43   79613 use strict;
  43         80  
  43         2167  
4 43     43   234 use warnings;
  43         161  
  43         1298  
5 43     43   221 use Carp 'confess';
  43         79  
  43         2666  
6 43     43   234 use Scalar::Util 'reftype';
  43         72  
  43         49050  
7              
8             # This is the classes placeholder for attribute descriptions
9             my $CLASSES = {};
10              
11             # the root accessor: returns the whole data structure, all meta classes
12 0     0 0 0 sub classes { $CLASSES }
13              
14             # returns all attributes for the given class
15 289 100   289 0 1614 sub attributes { $CLASSES->{ $_[1] } || {} }
16              
17             # returns the meta-data for the given class
18             sub class
19             {
20 168     168 0 298 my ($self, $class) = @_;
21            
22 168   100     914 $CLASSES->{ $class } ||= {};
23            
24 168 100       775 $CLASSES->{'@!family'}{ $class } = []
25             unless defined $CLASSES->{'@!family'}{ $class };
26              
27 168         461 return $CLASSES->{ $class };
28             }
29              
30             # define an attribute for a class,
31             # takes care to propagate default values from parents to
32             # children
33             sub attribute
34             {
35 104     104 0 604 my ($self, $class, $attribute, $attr_desc) = @_;
36            
37             # the attribute description may already exist
38 104         359 my $desc = Coat::Meta->has( $class, $attribute );
39            
40             # we define the attribute for the class
41 104 100       316 if (@_ == 4) {
42 102 100       316 $desc = {} unless defined $desc;
43              
44             # default values for attribute description
45 102 100       513 $desc->{isa} = 'Any' unless exists $desc->{isa};
46 102 100       388 $desc->{is} = 'rw' unless exists $desc->{is};
47              
48             # if a trigger is set, must be a coderef
49 102 100       325 if (defined $attr_desc->{'trigger'}) {
50 3         6 my $trigger = $attr_desc->{'trigger'};
51 3 100 100     469 confess "The trigger option must be passed a code reference"
52             unless ref $trigger && (ref $trigger eq 'CODE');
53             }
54              
55             # check attribute description
56 100 100       324 if (defined $desc->{default}) {
57 6 50 66     37 if (( ref($desc->{default})) &&
58             ('CODE' ne reftype($desc->{default}))) {
59 0         0 confess "Default must be a code reference or a simple scalar for "
60             . "attribute '$attribute' : ".$desc->{default};
61             }
62             }
63              
64 100         140 return $CLASSES->{ $class }{ $attribute } = { %{$desc}, %{$attr_desc}};
  100         312  
  100         787  
65             }
66              
67             # we have to return the attribute description
68             # either from ourselves, or from our parents
69             else {
70 2 100       8 return $desc if defined $desc;
71 1         237 confess "Attribute $attribute was not previously declared ".
72             "for class $class";
73             }
74             }
75              
76             sub exists
77             {
78 113     113 0 205 my ($self, $class) = @_;
79 113         515 return exists $CLASSES->{ $class };
80             }
81              
82             # returns the default value for the given $class/$attr
83             sub attr_default($$) {
84 52     52 0 97 my( $self, $obj, $attr) = @_;
85 52         87 my $class = ref $obj;
86              
87 52         131 my $meta = Coat::Meta->has( $class, $attr );
88              
89 52         101 my $default = $meta->{'default'};
90 52 50       161 return undef unless defined $default;
91              
92 52 100       351 return (ref $default)
93             ? $default->($obj) # we have a CODE ref
94             : $default; # we have a plain scalar
95             }
96              
97             # this method looks for the attribute description in the whole hierarchy
98             # of the class, starting by the lowest leaf.
99             # returns the description or undef if not found.
100             sub has($$$);
101             sub has($$$)
102             {
103 646     646 0 3566 my ($self, $class, $attribute) = @_;
104              
105             # if the attribute is declared for us, it's ok
106 646 100       2518 return $CLASSES->{ $class }{ $attribute } if
107             exists $CLASSES->{ $class }{ $attribute };
108              
109             # else, we'll look inside each of our parents, recursively
110             # until we stop or find one ancestor with the atttribute
111 300         402 foreach my $parent (@{ Coat::Meta->parents( $class ) }) {
  300         680  
112 203         674 my $parent_attr = Coat::Meta->has( $parent, $attribute );
113 203 100       715 return $parent_attr if defined $parent_attr;
114             }
115              
116             # none found, the attribute is not supported by the family
117 210         443 return undef;
118             }
119              
120             # This will build the attributes for a class with all inherited attributes
121             sub all_attributes
122             {
123 120     120 0 245 my ($self, $class, $hash) = @_;
124 120 50       380 $hash = {} unless defined $hash;
125              
126 120         182 foreach my $parent (@{ Coat::Meta->family( $class ) }) {
  120         455  
127 169         229 $hash = { %{ $hash }, %{ Coat::Meta->attributes( $parent ) } };
  169         354  
  169         478  
128             }
129            
130 120         217 $hash = { %{ $hash }, %{ Coat::Meta->attributes( $class ) } };
  120         296  
  120         389  
131              
132 120         427 return $hash;
133             }
134              
135             sub is_family
136             {
137 0     0 0 0 my ($self, $class, $parent) = @_;
138 0         0 return grep /^$parent$/, @{$CLASSES->{'@!family'}{ $class }};
  0         0  
139             }
140              
141              
142             sub parents
143             {
144 463     463 0 688 my ($self, $class) = @_;
145 43     43   313 { no strict 'refs'; return \@{"${class}::ISA"}; }
  43         135  
  43         40531  
  463         529  
  463         517  
  463         2330  
146             }
147              
148             sub class_precedence_list {
149 12     12 0 18 my ($self, $class) = @_;
150 12 50       23 return if !$class;
151              
152 12         17 ( $class, map { $self->class_precedence_list($_) } @{$self->parents($class)} );
  7         21  
  12         30  
153             }
154              
155             sub linearized_isa {
156 5     5 0 10 my ($self, $class) = @_;
157 5         7 my %seen;
158 5         16 grep { !( $seen{$_}++ ) } $self->class_precedence_list($class);
  12         57  
159             }
160              
161             sub is_parent
162             {
163 0     0 0 0 my ($self, $class, $parent) = @_;
164 0         0 return grep /^$parent$/, @{ Coat::Meta->parents( $class ) };
  0         0  
165             }
166              
167             sub family {
168 140     140 0 249 my ($self, $class) = @_;
169 140   66     760 $CLASSES->{'@!family'}{ $class } ||= Coat::Meta->parents( $class );
170             }
171              
172             sub add_to_family {
173 150     150 0 288 my ($self, $class, $parent) = @_;
174            
175             # add the parent to the family if not already present
176 150 100       235 if (not grep /^$parent$/, @{$CLASSES->{'@!family'}{ $class }}) {
  150         1601  
177 122         188 push @{ $CLASSES->{'@!family'}{ $class } }, $parent;
  122         777  
178             }
179             }
180              
181             sub extends($$$);
182             sub extends($$$)
183             {
184 150     150 0 300 my ($self, $class, $parents) = @_;
185 150 50       492 $parents = [$parents] unless ref $parents;
186              
187             # init the family with parents if not exists
188 150 100       555 if (! defined $CLASSES->{'@!family'}{ $class } ) {
189 1         5 $CLASSES->{'@!family'}{ $class } = [];
190             }
191            
192             # loop on each parent, add it to family and do the same
193             # with recursion through its family
194 150         306 foreach my $parent (@$parents) {
195 150         207 foreach my $ancestor (@{ Coat::Meta->parents( $parent ) }) {
  150         434  
196 38         167 Coat::Meta->extends($class, $ancestor);
197             }
198             # we do it at the end, so we respect the order of ancestry
199 150         19507 Coat::Meta->add_to_family($class, $parent);
200             }
201             }
202              
203             sub modifiers
204             {
205 64     64 0 102 my ($self, $hook, $class, $method, $coderef) = @_;
206              
207             # init the method modifiers placeholder
208 64 100       363 $CLASSES->{'%!hooks'}{ $class }{ $hook }{ $method } = [] unless
209             defined $CLASSES->{'%!hooks'}{ $class }{$hook}{ $method };
210            
211             # wants to push a new coderef
212 64 100       105 if (defined $coderef) {
213 16         20 push @{ $CLASSES->{'%!hooks'}{ $class }{$hook}{ $method } }, $coderef;
  16         52  
214 16         46 return $coderef;
215             }
216              
217             # wants to get the hooks
218             else {
219 48   50     300 return $CLASSES->{'%!hooks'}{ $class }{$hook}{ $method } ||= [];
220             }
221             }
222              
223 21 50   21 0 77 sub around_modifiers { shift and Coat::Meta->modifiers('around', @_ ) }
224 23 50   23 0 114 sub after_modifiers { shift and Coat::Meta->modifiers('after', @_ ) }
225 20 50   20 0 101 sub before_modifiers { shift and Coat::Meta->modifiers('before', @_ ) }
226              
227             1;
228             __END__