File Coverage

lib/Class/Dot/Meta/Class.pm
Criterion Covered Total %
statement 147 173 84.9
branch 38 48 79.1
condition 4 5 80.0
subroutine 37 41 90.2
pod 0 14 0.0
total 226 281 80.4


line stmt bran cond sub pod time code
1             # $Id$
2             # $Source$
3             # $Author$
4             # $HeadURL$
5             # $Revision$
6             # $Date$
7             package Class::Dot::Meta::Class;
8              
9 16     47   89978 use strict;
  16         32  
  16         623  
10 16     38   226 use warnings;
  16         35  
  16         658  
11 16     16   4626 use version;
  16         11032  
  16         95  
12 16     16   1232 use 5.00600;
  16         58  
  16         1235  
13              
14             our $VERSION = qv('2.0.0_15');
15             our $AUTHORITY = 'cpan:ASKSH';
16              
17 16     16   85 use Carp qw(carp croak confess);
  16         25  
  16         1130  
18 16     16   3204 use Params::Util qw(_ARRAYLIKE _HASHLIKE);
  16         41505  
  16         1432  
19 16     16   3580 use Class::Plugin::Util qw(require_class);
  16         39931  
  16         194  
20 16     16   844 use Scalar::Util qw(blessed);
  16         38  
  16         835  
21              
22 16     16   10240 use Class::Dot::Registry;
  16         45  
  16         2288  
23             our $REGISTRY = Class::Dot::Registry->new();
24              
25 16         119 use Class::Dot::Meta::Method qw(
26             install_sub_from_coderef
27             install_sub_from_class
28 16     16   1279 );
  16         37  
29 16     16   10852 use Class::Dot::Meta::Property;
  16         53  
  16         715  
30              
31 16     16   98 use Class::Dot::Devel::Sub::Name;
  16         97  
  16         73  
32              
33             # Try to load the mro module available in recent perl's.
34             if (!defined $INC{'mro.pm'}) {
35 16     16   85 no warnings 'all'; ## no critic
  16         31  
  16         10955  
36             eval 'require mro'; ## no critic
37             }
38              
39             my $COMMON_BASE_CLASS = 'Class::Dot::Object';
40              
41             # ------------------------------ CONSTRUCTOR --------------------------- #
42             sub new {
43 173     173 0 3175 my ($class, $options_ref) = @_;
44 173   100     690 $options_ref ||= { };
45            
46 173 50       432 if (! $options_ref->{property}) {
47 173         750 $options_ref->{property} = Class::Dot::Meta::Property->new();
48             }
49              
50 173         381 my $self = bless { %{$options_ref} }, $class;
  173         796  
51              
52 173 100       587 if (exists $options_ref->{for_class}) {
53 32         45 my %init_methods; # initial class methods to be installed.
54              
55             # Make the class inherit from the common object base class.
56 32         67 my $for_class = $options_ref->{for_class};
57 32         120 $self->append_superclasses_for($for_class, $COMMON_BASE_CLASS);
58              
59             # Create constructor and destructor methods.
60              
61 32 100       112 if (not $options_ref->{'-no_constructor'}) {
62 29         113 $init_methods{new} = $self->create_constructor($for_class);
63             }
64 32         137 $init_methods{DESTROY} = $self->create_destructor($for_class);
65              
66             # Install default methods to the new class.
67 32         159 while (my ($method_name, $method_ref) = each %init_methods) {
68 61         202 install_sub_from_coderef(
69             $method_ref => $for_class, $method_name
70             );
71             }
72             }
73            
74 173         510 return $self;
75             }
76              
77             # ------------------------------ ATTRIBUTES ----------------------------- #
78              
79             sub property {
80 211     211 0 315 my ($self) = @_;
81 211         924 return $self->{property};
82             }
83              
84             sub set_property {
85 0     0 0 0 my ($self, $property_obj) = @_;
86 0         0 $self->{property} = $property_obj;
87 0         0 return;
88             }
89              
90             sub for_class {
91 3     3 0 5 my ($self) = @_;
92 3         8 return $self->{for_class};
93             }
94              
95             sub set_for_class {
96 0     0 0 0 my ($self, $class) = @_;
97 0         0 $self->{for_class} = $class;
98 0         0 return;
99             }
100              
101             # ------------------------------ METHODS ------------------------------- #
102             sub subclass_name {
103 139     139 0 226 my ($self, $parent_class, $subclass_name) = @_;
104 139         447 return join q{::}, $parent_class, $subclass_name;
105             }
106              
107             my $created_classes = { };
108             sub create_class {
109 143     143 0 1196 my ($self, $class_name, $methods_ref, $append_isa_ref, $version) = @_;
110 143 100       455 return if exists $created_classes->{$class_name};
111              
112 77 100       190 $version = defined $version ? $version
113             : 1.0;
114              
115 16     16   109 no strict 'refs'; ## no critic
  16         27  
  16         490  
116 16     16   85 no warnings 'redefine'; ## no critic
  16         46  
  16         5877  
117              
118 77 100       264 if (_ARRAYLIKE($append_isa_ref)) {
119 75         92 my $isa_ref = \@{ "${class_name}::ISA" };
  75         516  
120 75         110 push @{ $isa_ref }, @{ $append_isa_ref };
  75         111  
  75         617  
121             }
122              
123 77 100       444 if (_HASHLIKE($methods_ref)) {
124 76         103 while (my ($method_name, $method_code) = each %{ $methods_ref }) {
  154         531  
125 78         94 *{ "${class_name}::$method_name" } = $method_code;
  78         463  
126             }
127             }
128              
129 77         112 ${ "${class_name}::VERSION" } = $version;
  77         349  
130              
131 77         166 $created_classes->{$class_name} = 1;
132              
133 77         182 return;
134             }
135              
136             sub append_superclasses_for {
137 32     32 0 79 my ($self, $inheritor, @superclasses) = @_;
138              
139 32         48 my $options_ref;
140 32 50       221 if (_HASHLIKE($superclasses[-1])) {
141 0         0 $options_ref = pop @superclasses;
142             }
143              
144 32         137 $options_ref->{append} = 1;
145              
146 32         132 return $self->superclasses_for(
147             $inheritor, @superclasses, $options_ref
148             );
149             }
150              
151             sub superclasses_for {
152 39     39 0 1081 my ($self, $inheritor, @superclasses) = @_;
153 39         74 my @final_isa;
154              
155             # If the last element of @superclasses is a hashref
156             # it is considered options for this method.
157 39         63 my $options_ref = { };
158 39 100       190 if (_HASHLIKE($superclasses[-1])) {
159 32         65 $options_ref = pop @superclasses;
160             }
161              
162 16     16   127 no strict 'refs'; ## no critic
  16         37  
  16         4493  
163              
164             SUPERCLASS:
165 39         87 for my $base (@superclasses) {
166 40 100       124 if ($inheritor eq $base) {
167 1         6 carp "Class '$inheritor' tried to inherit from itself.";
168 1         6 next SUPERCLASS;
169             }
170              
171 39 100       578 next SUPERCLASS if $inheritor->isa($base);
172              
173 37 50       189 if (!require_class($base)) {
174 0         0 croak "Couldn't load base class '$base'\n";
175             }
176              
177 37         560 push @final_isa, $base;
178             }
179              
180             # Append to the existing ISA if the "append" option is set
181             # (this is used by the {append_superclasses_for()} method).
182 39 100       135 if($options_ref->{append}) {
183 32         52 push @final_isa, @{ "$inheritor\::ISA" };
  32         204  
184             }
185              
186             # Setting all base classes as one is an optimization
187             # over pushing them one for one, atleast in perl > 5.9.5.
188             # see `perldoc mro` for more information.
189 39         71 @{ "$inheritor\::ISA" } = @final_isa;
  39         738  
190              
191 39         196 return;
192             }
193              
194             sub _get_linear_isa_pureperl {
195 0     0   0 my ($self, $class) = @_;
196              
197 0         0 my @stream = $class;
198 0         0 my @final;
199             my %seen;
200              
201 16     16   92 no strict 'refs'; ## no critic
  16         30  
  16         11477  
202             STREAM:
203 0         0 while (defined (my $atom = shift @stream)) {
204 0         0 my @isa = @{ "$atom\::ISA" };
  0         0  
205 0         0 my @keep;
206              
207             ISA:
208 0         0 for my $isa_class (@isa) {
209 0 0       0 next ISA if exists $seen{$isa_class};
210 0         0 $seen{$isa_class} = 1;
211 0         0 push @final, $isa_class;
212 0         0 push @stream, $isa_class;
213             }
214             }
215              
216 0         0 unshift @final, $class;
217 0         0 return \@final;
218             }
219              
220             sub get_linear_isa {
221 114     114 0 172 my ($self, $class) = @_;
222 114 50       555 my $isa = defined $mro::VERSION ? mro::get_linear_isa($class)
223             : $self->_get_linear_isa_pureperl($class);
224              
225 114         272 return $isa;
226             }
227              
228             sub metaclass_for {
229 0     0 0 0 my ($self, $other) = @_;
230 0 0       0 confess 'Need the class name or an instance of the '
231             . 'class you want metaclass for'
232             if not defined $other;
233              
234 0 0       0 my $other_class = ref $other ? ref $other
235             : $other;
236              
237 0         0 return $REGISTRY->get_metaclass_for($other);
238             }
239              
240             sub composites {
241 3     3 0 6 my ($self, $attribute_name, $composite_class) = @_;
242 3         9 my $property = $self->property;
243 3         8 my $for_class = $self->for_class;
244              
245 3         13 return $property->composites_for(
246             ($for_class, $attribute_name) => $composite_class
247             );
248             }
249              
250             sub create_constructor {
251 29     29 0 67 my ($self, $caller_class) = @_;
252 29         111 my $options = $REGISTRY->get_options_for($caller_class);
253              
254             return subname "${caller_class}::new" => sub { ## no critic
255 43     43   10474 my $class = shift @_;
        43      
        35      
        14      
        14      
256              
257 43   66     261 my $has_options = (
258             defined $_[0]
259             && ref $_[0]
260             && ref $_[0] eq 'HASH'
261             );
262              
263 43 100       156 my $options_ref = $has_options ? shift @_
264             : { };
265              
266 43         66 my $self;
267 43 100       142 if ($options->{'-optimized'}) {
268 22         37 $self = bless {%{ $options_ref }}, $class;
  22         77  
269             }
270             else {
271 21         65 $self = bless { }, $class;
272 55         198 OPTION:
273 21         34 while (my ($opt_key, $opt_value) = each %{$options_ref}) {
274             #my $attr_meta = $self->__meta__($opt_key);
275             #next OPTION if not $attr_meta;
276             #my $set_attr = $attr_meta->setter_name;
277             #$set_attr ||= "set_$opt_key";
278            
279             #if ($self->can($set_attr)) {
280             # $self->$set_attr($opt_value);
281             #}
282 34         113 $self->__setattr__($opt_key, $opt_value);
283             }
284             }
285              
286 43 100       518 if ($self->can('BUILD')) {
287 15 100       80 my $ret = $self->BUILD(
288             $has_options ? $options_ref
289             : @_
290             );
291 15 100       139 if ($options->{'-rebuild'}) {
292 7 100       25 if (ref $ret) {
293 4         5 $self = $ret;
294             }
295             }
296             }
297              
298 43         169 return $self;
299             }
300 29         435 }
301              
302             sub create_destructor {
303 32     32 0 65 my ($self, $caller_class) = @_;
304              
305             return subname "${caller_class}::DESTROY" => sub {
306 42     42   9920 my ($self) = @_;
        34      
        24      
        56      
307              
308 42 100       351 if ($self->can('DEMOLISH')) {
309 3         14 $self->DEMOLISH();
310             }
311              
312 42         1498 return;
313             }
314 32         335 }
315              
316             1;
317              
318             __END__