File Coverage

blib/lib/Mouse/Meta/Class.pm
Criterion Covered Total %
statement 236 239 98.7
branch 75 84 89.2
condition 14 20 70.0
subroutine 41 41 100.0
pod 3 20 15.0
total 369 404 91.3


line stmt bran cond sub pod time code
1             package Mouse::Meta::Class;
2 282     285   38688 use Mouse::Util qw/:meta/; # enables strict and warnings
  282         696  
  282         1752  
3              
4 282     282   2131 use Scalar::Util ();
  282         664  
  282         4889  
5              
6 282     282   1662 use Mouse::Meta::Module;
  282         634  
  282         31731  
7             our @ISA = qw(Mouse::Meta::Module);
8              
9             our @CARP_NOT = qw(Mouse); # trust Mouse
10              
11             sub attribute_metaclass;
12             sub method_metaclass;
13              
14             sub constructor_class;
15             sub destructor_class;
16              
17              
18             sub _construct_meta {
19 759     759   6389 my($class, %args) = @_;
20              
21 759         5303 $args{attributes} = {};
22 759         5142 $args{methods} = {};
23 759         5174 $args{roles} = [];
24              
25 759         4582 $args{superclasses} = do {
26 282     282   1997 no strict 'refs';
  282         719  
  282         736096  
27 759         4495 \@{ $args{package} . '::ISA' };
  759         15058  
28             };
29              
30 759   66     7571 my $self = bless \%args, ref($class) || $class;
31 759 100       6056 if(ref($self) ne __PACKAGE__){
32 26         90 $self->meta->_initialize_object($self, \%args);
33             }
34 759         15172 return $self;
35             }
36              
37             sub create_anon_class{
38 103     103 0 45272 my $self = shift;
39 103         3783 return $self->create(undef, @_);
40             }
41              
42             sub is_anon_class;
43              
44             sub roles;
45              
46             sub calculate_all_roles {
47 10     10 0 23 my $self = shift;
48 10         16 my %seen;
49 12         59 return grep { !$seen{ $_->name }++ }
50 10         16 map { $_->calculate_all_roles } @{ $self->roles };
  10         39  
  10         35  
51             }
52              
53             sub superclasses {
54 1328     1328 1 6578 my $self = shift;
55              
56 1328 100       5280 if (@_) {
57 750         3355 foreach my $super(@_){
58 756         4585 Mouse::Util::load_class($super);
59 754         3816 my $meta = Mouse::Util::get_metaclass_by_name($super);
60 754 100       3870 next if $self->verify_superclass($super, $meta);
61 4         22 $self->_reconcile_with_superclass_meta($meta);
62             }
63 747         3201 return @{ $self->{superclasses} } = @_;
  747         13148  
64             }
65              
66 578         1182 return @{ $self->{superclasses} };
  578         3978  
67             }
68              
69             sub verify_superclass {
70 754     754 0 3629 my($self, $super, $super_meta) = @_;
71              
72 754 100       4009 if(defined $super_meta) {
73 164 100       2601 if(Mouse::Util::is_a_metarole($super_meta)){
74 1         8 $self->throw_error("You cannot inherit from a Mouse Role ($super)");
75             }
76             }
77             else {
78             # The metaclass of $super is not initialized.
79             # i.e. it might be Mouse::Object, a mixin package (e.g. Exporter),
80             # or a foreign class including Moose classes.
81             # See also Mouse::Foreign::Meta::Role::Class.
82 590         4711 my $mm = $super->can('meta');
83 590 100 66     5228 if(!($mm && $mm == \&Mouse::Util::meta)) {
84 5 100 66     57 if($super->can('new') or $super->can('DESTROY')) {
85 2         14 $self->inherit_from_foreign_class($super);
86             }
87             }
88 590         4735 return 1; # always ok
89             }
90              
91 163         3240 return $self->isa(ref $super_meta); # checks metaclass compatibility
92             }
93              
94             sub inherit_from_foreign_class {
95 2     2 0 8 my($class, $super) = @_;
96 2 50       76 if($ENV{PERL_MOUSE_STRICT}) {
97 0         0 Carp::carp("You inherit from non-Mouse class ($super),"
98             . " but it is unlikely to work correctly."
99             . " Please consider using MouseX::Foreign");
100             }
101 2         9 return;
102             }
103              
104             my @MetaClassTypes = (
105             'attribute', # Mouse::Meta::Attribute
106             'method', # Mouse::Meta::Method
107             'constructor', # Mouse::Meta::Method::Constructor
108             'destructor', # Mouse::Meta::Method::Destructor
109             );
110              
111             sub _reconcile_with_superclass_meta {
112 4     4   14 my($self, $other) = @_;
113              
114             # find incompatible traits
115 4         9 my %metaroles;
116 4         13 foreach my $metaclass_type(@MetaClassTypes){
117 16   66     137 my $accessor = $self->can($metaclass_type . '_metaclass')
118             || $self->can($metaclass_type . '_class');
119              
120 16         52 my $other_c = $other->$accessor();
121 16         36 my $self_c = $self->$accessor();
122              
123 16 100       130 if(!$self_c->isa($other_c)){
124 1         5 $metaroles{$metaclass_type}
125             = [ $self_c->meta->_collect_roles($other_c->meta) ];
126             }
127             }
128              
129 4         18 $metaroles{class} = [$self->meta->_collect_roles($other->meta)];
130              
131             #use Data::Dumper; print Data::Dumper->new([\%metaroles], ['*metaroles'])->Indent(1)->Dump;
132              
133 4         30 require Mouse::Util::MetaRole;
134 4         28 $_[0] = Mouse::Util::MetaRole::apply_metaroles(
135             for => $self,
136             class_metaroles => \%metaroles,
137             );
138 4         15 return;
139             }
140              
141             sub _collect_roles {
142 5     5   15 my ($self, $other) = @_;
143              
144             # find common ancestor
145 5         37 my @self_lin_isa = $self->linearized_isa;
146 5         23 my @other_lin_isa = $other->linearized_isa;
147              
148 5         12 my(@self_anon_supers, @other_anon_supers);
149 5         21 push @self_anon_supers, shift @self_lin_isa while $self_lin_isa[0]->meta->is_anon_class;
150 5         21 push @other_anon_supers, shift @other_lin_isa while $other_lin_isa[0]->meta->is_anon_class;
151              
152 5   33     32 my $common_ancestor = $self_lin_isa[0] eq $other_lin_isa[0] && $self_lin_isa[0];
153              
154 5 50       18 if(!$common_ancestor){
155 0         0 $self->throw_error(sprintf '%s cannot have %s as a super class because of their metaclass incompatibility',
156             $self->name, $other->name);
157             }
158              
159 5         10 my %seen;
160 12         55 return sort grep { !$seen{$_}++ } ## no critic
161 4         21 (map{ $_->name } map{ $_->meta->calculate_all_roles } @self_anon_supers),
  4         28  
162 5         13 (map{ $_->name } map{ $_->meta->calculate_all_roles } @other_anon_supers),
  8         22  
  6         21  
163             ;
164             }
165              
166              
167             sub find_method_by_name {
168 38     38 0 88 my($self, $method_name) = @_;
169 38 50       101 defined($method_name)
170             or $self->throw_error('You must define a method name to find');
171              
172 38         239 foreach my $class( $self->linearized_isa ){
173 75         298 my $method = $self->initialize($class)->get_method($method_name);
174 75 100       355 return $method if defined $method;
175             }
176 0         0 return undef;
177             }
178              
179             sub get_all_methods {
180 2     2 1 7 my($self) = @_;
181 2         11 return map{ $self->find_method_by_name($_) } $self->get_all_method_names;
  24         61  
182             }
183              
184             sub get_all_method_names {
185 10     10 0 33 my $self = shift;
186 10         18 my %uniq;
187 93         271 return grep { $uniq{$_}++ == 0 }
188 10         82 map { Mouse::Meta::Class->initialize($_)->get_method_list() }
  21         89  
189             $self->linearized_isa;
190             }
191              
192             sub find_attribute_by_name {
193 14     14 0 41 my($self, $name) = @_;
194 14 50       41 defined($name)
195             or $self->throw_error('You must define an attribute name to find');
196 14         155 foreach my $attr($self->get_all_attributes) {
197 22 100       131 return $attr if $attr->name eq $name;
198             }
199 1         3 return undef;
200             }
201              
202             sub add_attribute {
203 607     607 1 10961 my $self = shift;
204              
205 607         3405 my($attr, $name);
206              
207 607 100       3833 if(Scalar::Util::blessed($_[0])){
208 6         650 $attr = $_[0];
209              
210 6 50       679 $attr->isa('Mouse::Meta::Attribute')
211             || $self->throw_error("Your attribute must be an instance of Mouse::Meta::Attribute (or a subclass)");
212              
213 6         1300 $name = $attr->name;
214             }
215             else{
216             # _process_attribute
217 601         1846 $name = shift;
218              
219 601 100       2869 my %args = (@_ == 1) ? %{$_[0]} : @_;
  84         385  
220              
221 601 50       1831 defined($name)
222             or $self->throw_error('You must provide a name for the attribute');
223              
224 601 100       2020 if ($name =~ s/^\+//) { # inherited attributes
225             # Workaround for https://github.com/gfx/p5-Mouse/issues/64
226             # Do not use find_attribute_by_name to avoid problems with cached attributes list
227             # because we're about to change it anyway
228 35         72 my $inherited_attr;
229 35         63 foreach my $i ( @{ $self->_calculate_all_attributes } ) {
  35         104  
230 79 100       259 if ( $i->name eq $name ) {
231 33         64 $inherited_attr = $i;
232 33         62 last;
233             }
234             }
235 35 100       140 $self->throw_error("Could not find an attribute by the name of '$name' to inherit from in ".$self->name)
236             unless $inherited_attr;
237              
238 33         163 $attr = $inherited_attr->clone_and_inherit_options(%args);
239             }
240             else{
241 566         4150 my($attribute_class, @traits) = $self->attribute_metaclass->interpolate_class(\%args);
242 566 100       1826 $args{traits} = \@traits if @traits;
243              
244 566         3048 $attr = $attribute_class->new($name, %args);
245             }
246             }
247              
248 589         5149 Scalar::Util::weaken( $attr->{associated_class} = $self );
249              
250             # install accessors first
251 589         3224 $attr->install_accessors();
252              
253             # then register the attribute to the metaclass
254 585         1551 $attr->{insertion_order} = keys %{ $self->{attributes} };
  585         3738  
255 585         2292 $self->{attributes}{$name} = $attr;
256 585         4229 $self->_invalidate_metaclass_cache();
257              
258 585 100 100     3167 if(!$attr->{associated_methods} && ($attr->{is} || '') ne 'bare'){
      100        
259 2         384 Carp::carp(qq{Attribute ($name) of class }.$self->name
260             .qq{ has no associated methods (did you mean to provide an "is" argument?)});
261             }
262 585         5355 return $attr;
263             }
264              
265             sub _calculate_all_attributes {
266 534     534   131412 my($self) = @_;
267 534         2392 my %seen;
268             my @all_attrs;
269 534         3095 foreach my $class($self->linearized_isa) {
270 1257 100       5875 my $meta = Mouse::Util::get_metaclass_by_name($class) or next;
271 711         2846 my @attrs = grep { !$seen{$_->name}++ } values %{$meta->{attributes}};
  936         4193  
  711         4903  
272             @attrs = sort {
273 711         3587 $b->{insertion_order} <=> $a->{insertion_order}
274 845         1789 } @attrs;
275 711         3163 push @all_attrs, @attrs;
276             }
277 534         13743 return [reverse @all_attrs];
278             }
279              
280             sub linearized_isa;
281              
282             sub new_object;
283             sub clone_object;
284              
285             sub immutable_options {
286 83     83 0 223 my ( $self, @args ) = @_;
287              
288             return (
289 83         447 inline_constructor => 1,
290             inline_destructor => 1,
291             constructor_name => 'new',
292             @args,
293             );
294             }
295              
296             sub make_immutable {
297 83     83 0 2503 my $self = shift;
298 83         360 my %args = $self->immutable_options(@_);
299              
300 83         248 $self->{is_immutable}++;
301              
302 83 50       395 if ($args{inline_constructor}) {
303             $self->add_method($args{constructor_name} =>
304 83         538 Mouse::Util::load_class($self->constructor_class)
305             ->_generate_constructor($self, \%args));
306             }
307              
308 83 50       391 if ($args{inline_destructor}) {
309 83         383 $self->add_method(DESTROY =>
310             Mouse::Util::load_class($self->destructor_class)
311             ->_generate_destructor($self, \%args));
312             }
313              
314             # Moose's make_immutable returns true allowing calling code to skip
315             # setting an explicit true value at the end of a source file.
316 83         414 return 1;
317             }
318              
319             sub make_mutable {
320 2     2 0 2396 my($self) = @_;
321 2         7 $self->{is_immutable} = 0;
322 2         8 return;
323             }
324              
325             sub is_immutable;
326 10     10 0 62 sub is_mutable { !$_[0]->is_immutable }
327              
328             sub _install_modifier {
329 79     79   232 my( $self, $type, $name, $code ) = @_;
330 79         308 my $into = $self->name;
331              
332 79 100       779 my $original = $into->can($name)
333             or $self->throw_error("The method '$name' was not found in the inheritance hierarchy for $into");
334              
335 78         302 my $modifier_table = $self->{modifiers}{$name};
336              
337 78 100       257 if(!$modifier_table){
338 56         152 my(@before, @after, @around);
339 56         122 my $cache = $original;
340             my $modified = sub {
341 79 100   79   51778 if(@before) {
        117      
        62      
        54      
        10      
        10      
        10      
342 24         70 for my $c (@before) { $c->(@_) }
  27         110  
343             }
344 79 100       458 unless(@after) {
345 51         278 return $cache->(@_);
346             }
347              
348 28 100       134 if(wantarray){ # list context
    100          
349 2         11 my @rval = $cache->(@_);
350              
351 2         62 for my $c(@after){ $c->(@_) }
  2         11  
352 2         35 return @rval;
353             }
354             elsif(defined wantarray){ # scalar context
355 3         15 my $rval = $cache->(@_);
356              
357 3         28 for my $c(@after){ $c->(@_) }
  3         97  
358 3         39 return $rval;
359             }
360             else{ # void context
361 23         97 $cache->(@_);
362              
363 23         173 for my $c(@after){ $c->(@_) }
  25         89  
364 23         234 return;
365             }
366 56         388 };
367              
368 56         355 $self->{modifiers}{$name} = $modifier_table = {
369             original => $original,
370              
371             before => \@before,
372             after => \@after,
373             around => \@around,
374              
375             cache => \$cache, # cache for around modifiers
376             };
377              
378 56         572 $self->add_method($name => $modified);
379             }
380              
381 78 100       391 if($type eq 'before'){
    100          
382 23         45 unshift @{$modifier_table->{before}}, $code;
  23         77  
383             }
384             elsif($type eq 'after'){
385 24         62 push @{$modifier_table->{after}}, $code;
  24         77  
386             }
387             else{ # around
388 31         72 push @{$modifier_table->{around}}, $code;
  31         104  
389              
390 31         66 my $next = ${ $modifier_table->{cache} };
  31         83  
391 31     42   145 ${ $modifier_table->{cache} } = sub{ $code->($next, @_) };
  31         86  
  42         157  
392             }
393              
394 78         342 return;
395             }
396              
397             sub add_before_method_modifier {
398 23     23 0 72 my ( $self, $name, $code ) = @_;
399 23         85 $self->_install_modifier( 'before', $name, $code );
400             }
401              
402             sub add_around_method_modifier {
403 32     32 0 105 my ( $self, $name, $code ) = @_;
404 32         140 $self->_install_modifier( 'around', $name, $code );
405             }
406              
407             sub add_after_method_modifier {
408 24     24 0 74 my ( $self, $name, $code ) = @_;
409 24         89 $self->_install_modifier( 'after', $name, $code );
410             }
411              
412             sub add_override_method_modifier {
413 24     24 0 113 my ($self, $name, $code) = @_;
414              
415 24 100       169 if($self->has_method($name)){
416 1         10 $self->throw_error("Cannot add an override method if a local method is already present");
417             }
418              
419 23         106 my $package = $self->name;
420              
421 23 100       255 my $super_body = $package->can($name)
422             or $self->throw_error("You cannot override '$name' because it has no super method");
423              
424             $self->add_method($name => sub {
425 26     26   25965 local $Mouse::SUPER_PACKAGE = $package;
426 26         68 local $Mouse::SUPER_BODY = $super_body;
427 26         94 local @Mouse::SUPER_ARGS = @_;
428 26         62 &{$code};
  26         92  
429 21         285 });
430 21         92 return;
431             }
432              
433             sub add_augment_method_modifier {
434 11     20 0 35 my ($self, $name, $code) = @_;
435 11 100       114 if($self->has_method($name)){
436 1         6 $self->throw_error("Cannot add an augment method if a local method is already present");
437             }
438              
439 10 50       45 my $super = $self->find_method_by_name($name)
440             or $self->throw_error("You cannot augment '$name' because it has no super method");
441              
442 10         44 my $super_package = $super->package_name;
443 10         35 my $super_body = $super->body;
444              
445             $self->add_method($name => sub {
446 14     23   8884 local $Mouse::INNER_BODY{$super_package} = $code;
        31      
        17      
447 14         50 local $Mouse::INNER_ARGS{$super_package} = [@_];
448 14         32 &{$super_body};
  14         61  
449 10         159 });
450 10         69 return;
451             }
452              
453             sub does_role {
454 511     517 0 6536 my ($self, $role_name) = @_;
455              
456 511 100       4049 (defined $role_name)
457             || $self->throw_error("You must supply a role name to look for");
458              
459 510 100       4532 $role_name = $role_name->name if ref $role_name;
460              
461 510         4757 for my $class ($self->linearized_isa) {
462 866 100       6332 my $meta = Mouse::Util::get_metaclass_by_name($class)
463             or next;
464              
465 625         4435 for my $role (@{ $meta->roles }) {
  625         9106  
466              
467 312 100       2580 return 1 if $role->does_role($role_name);
468             }
469             }
470              
471 268         3856 return 0;
472             }
473              
474             1;
475             __END__