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   33187 use Mouse::Util qw/:meta/; # enables strict and warnings
  282         311  
  282         1260  
3              
4 282     282   1141 use Scalar::Util ();
  282         296  
  282         3498  
5              
6 282     282   787 use Mouse::Meta::Module;
  282         283  
  282         24382  
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   5473 my($class, %args) = @_;
20              
21 759         5093 $args{attributes} = {};
22 759         4758 $args{methods} = {};
23 759         4591 $args{roles} = [];
24              
25 759         4282 $args{superclasses} = do {
26 282     282   1008 no strict 'refs';
  282         313  
  282         583601  
27 759         4155 \@{ $args{package} . '::ISA' };
  759         14809  
28             };
29              
30 759   66     6415 my $self = bless \%args, ref($class) || $class;
31 759 100       5362 if(ref($self) ne __PACKAGE__){
32 26         63 $self->meta->_initialize_object($self, \%args);
33             }
34 759         14514 return $self;
35             }
36              
37             sub create_anon_class{
38 103     103 0 45873 my $self = shift;
39 103         4368 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 11 my $self = shift;
48 10         8 my %seen;
49 12         40 return grep { !$seen{ $_->name }++ }
50 10         11 map { $_->calculate_all_roles } @{ $self->roles };
  10         31  
  10         23  
51             }
52              
53             sub superclasses {
54 1328     1328 1 5287 my $self = shift;
55              
56 1328 100       3920 if (@_) {
57 750         2566 foreach my $super(@_){
58 756         3364 Mouse::Util::load_class($super);
59 754         3030 my $meta = Mouse::Util::get_metaclass_by_name($super);
60 754 100       3058 next if $self->verify_superclass($super, $meta);
61 4         20 $self->_reconcile_with_superclass_meta($meta);
62             }
63 747         2376 return @{ $self->{superclasses} } = @_;
  747         11098  
64             }
65              
66 578         543 return @{ $self->{superclasses} };
  578         2482  
67             }
68              
69             sub verify_superclass {
70 754     754 0 2423 my($self, $super, $super_meta) = @_;
71              
72 754 100       3022 if(defined $super_meta) {
73 164 100       2499 if(Mouse::Util::is_a_metarole($super_meta)){
74 1         7 $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         3209 my $mm = $super->can('meta');
83 590 100 66     3644 if(!($mm && $mm == \&Mouse::Util::meta)) {
84 5 100 66     45 if($super->can('new') or $super->can('DESTROY')) {
85 2         8 $self->inherit_from_foreign_class($super);
86             }
87             }
88 590         4066 return 1; # always ok
89             }
90              
91 163         3368 return $self->isa(ref $super_meta); # checks metaclass compatibility
92             }
93              
94             sub inherit_from_foreign_class {
95 2     2 0 3 my($class, $super) = @_;
96 2 50       8 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         4 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   7 my($self, $other) = @_;
113              
114             # find incompatible traits
115 4         8 my %metaroles;
116 4         10 foreach my $metaclass_type(@MetaClassTypes){
117 16   66     119 my $accessor = $self->can($metaclass_type . '_metaclass')
118             || $self->can($metaclass_type . '_class');
119              
120 16         30 my $other_c = $other->$accessor();
121 16         21 my $self_c = $self->$accessor();
122              
123 16 100       128 if(!$self_c->isa($other_c)){
124 1         6 $metaroles{$metaclass_type}
125             = [ $self_c->meta->_collect_roles($other_c->meta) ];
126             }
127             }
128              
129 4         17 $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         33 require Mouse::Util::MetaRole;
134 4         33 $_[0] = Mouse::Util::MetaRole::apply_metaroles(
135             for => $self,
136             class_metaroles => \%metaroles,
137             );
138 4         12 return;
139             }
140              
141             sub _collect_roles {
142 5     5   8 my ($self, $other) = @_;
143              
144             # find common ancestor
145 5         27 my @self_lin_isa = $self->linearized_isa;
146 5         16 my @other_lin_isa = $other->linearized_isa;
147              
148 5         6 my(@self_anon_supers, @other_anon_supers);
149 5         15 push @self_anon_supers, shift @self_lin_isa while $self_lin_isa[0]->meta->is_anon_class;
150 5         14 push @other_anon_supers, shift @other_lin_isa while $other_lin_isa[0]->meta->is_anon_class;
151              
152 5   33     24 my $common_ancestor = $self_lin_isa[0] eq $other_lin_isa[0] && $self_lin_isa[0];
153              
154 5 50       13 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         8 my %seen;
160 12         45 return sort grep { !$seen{$_}++ } ## no critic
161 4         22 (map{ $_->name } map{ $_->meta->calculate_all_roles } @self_anon_supers),
  4         8  
162 5         10 (map{ $_->name } map{ $_->meta->calculate_all_roles } @other_anon_supers),
  8         17  
  6         17  
163             ;
164             }
165              
166              
167             sub find_method_by_name {
168 38     38 0 37 my($self, $method_name) = @_;
169 38 50       60 defined($method_name)
170             or $self->throw_error('You must define a method name to find');
171              
172 38         92 foreach my $class( $self->linearized_isa ){
173 75         106 my $method = $self->initialize($class)->get_method($method_name);
174 75 100       256 return $method if defined $method;
175             }
176 0         0 return undef;
177             }
178              
179             sub get_all_methods {
180 2     2 1 5 my($self) = @_;
181 2         6 return map{ $self->find_method_by_name($_) } $self->get_all_method_names;
  24         28  
182             }
183              
184             sub get_all_method_names {
185 10     10 0 17 my $self = shift;
186 10         9 my %uniq;
187 93         125 return grep { $uniq{$_}++ == 0 }
188 10         48 map { Mouse::Meta::Class->initialize($_)->get_method_list() }
  21         45  
189             $self->linearized_isa;
190             }
191              
192             sub find_attribute_by_name {
193 14     14 0 16 my($self, $name) = @_;
194 14 50       32 defined($name)
195             or $self->throw_error('You must define an attribute name to find');
196 14         66 foreach my $attr($self->get_all_attributes) {
197 22 100       87 return $attr if $attr->name eq $name;
198             }
199 1         3 return undef;
200             }
201              
202             sub add_attribute {
203 607     607 1 12474 my $self = shift;
204              
205 607         1366 my($attr, $name);
206              
207 607 100       3357 if(Scalar::Util::blessed($_[0])){
208 6         680 $attr = $_[0];
209              
210 6 50       731 $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         1404 $name = $attr->name;
214             }
215             else{
216             # _process_attribute
217 601         822 $name = shift;
218              
219 601 100       2243 my %args = (@_ == 1) ? %{$_[0]} : @_;
  84         250  
220              
221 601 50       1342 defined($name)
222             or $self->throw_error('You must provide a name for the attribute');
223              
224 601 100       1494 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         32 my $inherited_attr;
229 35         29 foreach my $i ( @{ $self->_calculate_all_attributes } ) {
  35         105  
230 82 100       183 if ( $i->name eq $name ) {
231 33         32 $inherited_attr = $i;
232 33         36 last;
233             }
234             }
235 35 100       90 $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         104 $attr = $inherited_attr->clone_and_inherit_options(%args);
239             }
240             else{
241 566         5269 my($attribute_class, @traits) = $self->attribute_metaclass->interpolate_class(\%args);
242 566 100       1151 $args{traits} = \@traits if @traits;
243              
244 566         2089 $attr = $attribute_class->new($name, %args);
245             }
246             }
247              
248 589         3277 Scalar::Util::weaken( $attr->{associated_class} = $self );
249              
250             # install accessors first
251 589         2307 $attr->install_accessors();
252              
253             # then register the attribute to the metaclass
254 585         1203 $attr->{insertion_order} = keys %{ $self->{attributes} };
  585         2866  
255 585         1544 $self->{attributes}{$name} = $attr;
256 585         3225 $self->_invalidate_metaclass_cache();
257              
258 585 100 100     2469 if(!$attr->{associated_methods} && ($attr->{is} || '') ne 'bare'){
      100        
259 2         195 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         5111 return $attr;
263             }
264              
265             sub _calculate_all_attributes {
266 534     534   103263 my($self) = @_;
267 534         1381 my %seen;
268             my @all_attrs;
269 534         2344 foreach my $class($self->linearized_isa) {
270 1257 100       4521 my $meta = Mouse::Util::get_metaclass_by_name($class) or next;
271 711         2176 my @attrs = grep { !$seen{$_->name}++ } values %{$meta->{attributes}};
  936         2649  
  711         4461  
272             @attrs = sort {
273 711         2905 $b->{insertion_order} <=> $a->{insertion_order}
274 874         973 } @attrs;
275 711         2527 push @all_attrs, @attrs;
276             }
277 534         12197 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 121 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 1980 my $self = shift;
298 83         273 my %args = $self->immutable_options(@_);
299              
300 83         180 $self->{is_immutable}++;
301              
302 83 50       389 if ($args{inline_constructor}) {
303             $self->add_method($args{constructor_name} =>
304 83         459 Mouse::Util::load_class($self->constructor_class)
305             ->_generate_constructor($self, \%args));
306             }
307              
308 83 50       247 if ($args{inline_destructor}) {
309 83         317 $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         298 return 1;
317             }
318              
319             sub make_mutable {
320 2     2 0 1397 my($self) = @_;
321 2         4 $self->{is_immutable} = 0;
322 2         4 return;
323             }
324              
325             sub is_immutable;
326 10     10 0 55 sub is_mutable { !$_[0]->is_immutable }
327              
328             sub _install_modifier {
329 79     79   105 my( $self, $type, $name, $code ) = @_;
330 79         219 my $into = $self->name;
331              
332 79 100       511 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         122 my $modifier_table = $self->{modifiers}{$name};
336              
337 78 100       138 if(!$modifier_table){
338 56         59 my(@before, @after, @around);
339 56         53 my $cache = $original;
340             my $modified = sub {
341 79 100   79   30457 if(@before) {
        117      
        62      
        54      
        10      
        10      
        10      
342 24         41 for my $c (@before) { $c->(@_) }
  27         75  
343             }
344 79 100       294 unless(@after) {
345 51         135 return $cache->(@_);
346             }
347              
348 28 100       89 if(wantarray){ # list context
    100          
349 2         8 my @rval = $cache->(@_);
350              
351 2         12 for my $c(@after){ $c->(@_) }
  2         6  
352 2         17 return @rval;
353             }
354             elsif(defined wantarray){ # scalar context
355 3         10 my $rval = $cache->(@_);
356              
357 3         12 for my $c(@after){ $c->(@_) }
  3         9  
358 3         17 return $rval;
359             }
360             else{ # void context
361 23         60 $cache->(@_);
362              
363 23         74 for my $c(@after){ $c->(@_) }
  25         58  
364 23         160 return;
365             }
366 56         249 };
367              
368 56         267 $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         437 $self->add_method($name => $modified);
379             }
380              
381 78 100       263 if($type eq 'before'){
    100          
382 23         30 unshift @{$modifier_table->{before}}, $code;
  23         52  
383             }
384             elsif($type eq 'after'){
385 24         25 push @{$modifier_table->{after}}, $code;
  24         45  
386             }
387             else{ # around
388 31         28 push @{$modifier_table->{around}}, $code;
  31         66  
389              
390 31         35 my $next = ${ $modifier_table->{cache} };
  31         44  
391 31     42   88 ${ $modifier_table->{cache} } = sub{ $code->($next, @_) };
  31         52  
  42         114  
392             }
393              
394 78         217 return;
395             }
396              
397             sub add_before_method_modifier {
398 23     23 0 30 my ( $self, $name, $code ) = @_;
399 23         56 $self->_install_modifier( 'before', $name, $code );
400             }
401              
402             sub add_around_method_modifier {
403 32     32 0 291 my ( $self, $name, $code ) = @_;
404 32         81 $self->_install_modifier( 'around', $name, $code );
405             }
406              
407             sub add_after_method_modifier {
408 24     24 0 29 my ( $self, $name, $code ) = @_;
409 24         61 $self->_install_modifier( 'after', $name, $code );
410             }
411              
412             sub add_override_method_modifier {
413 24     24 0 37 my ($self, $name, $code) = @_;
414              
415 24 100       58 if($self->has_method($name)){
416 1         5 $self->throw_error("Cannot add an override method if a local method is already present");
417             }
418              
419 23         67 my $package = $self->name;
420              
421 23 100       174 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   12257 local $Mouse::SUPER_PACKAGE = $package;
426 26         35 local $Mouse::SUPER_BODY = $super_body;
427 26         136 local @Mouse::SUPER_ARGS = @_;
428 26         28 &{$code};
  26         50  
429 21         153 });
430 21         47 return;
431             }
432              
433             sub add_augment_method_modifier {
434 11     20 0 14 my ($self, $name, $code) = @_;
435 11 100       39 if($self->has_method($name)){
436 1         3 $self->throw_error("Cannot add an augment method if a local method is already present");
437             }
438              
439 10 50       24 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         24 my $super_package = $super->package_name;
443 10         17 my $super_body = $super->body;
444              
445             $self->add_method($name => sub {
446 14     23   5562 local $Mouse::INNER_BODY{$super_package} = $code;
        31      
        17      
447 14         29 local $Mouse::INNER_ARGS{$super_package} = [@_];
448 14         16 &{$super_body};
  14         38  
449 10         111 });
450 10         40 return;
451             }
452              
453             sub does_role {
454 511     517 0 5322 my ($self, $role_name) = @_;
455              
456 511 100       3694 (defined $role_name)
457             || $self->throw_error("You must supply a role name to look for");
458              
459 510 100       4221 $role_name = $role_name->name if ref $role_name;
460              
461 510         4157 for my $class ($self->linearized_isa) {
462 866 100       5569 my $meta = Mouse::Util::get_metaclass_by_name($class)
463             or next;
464              
465 625         4170 for my $role (@{ $meta->roles }) {
  625         9148  
466              
467 312 100       2064 return 1 if $role->does_role($role_name);
468             }
469             }
470              
471 268         3761 return 0;
472             }
473              
474             1;
475             __END__