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   31306 use Mouse::Util qw/:meta/; # enables strict and warnings
  282         318  
  282         1218  
3              
4 282     282   1091 use Scalar::Util ();
  282         281  
  282         3400  
5              
6 282     282   790 use Mouse::Meta::Module;
  282         268  
  282         23879  
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   5304 my($class, %args) = @_;
20              
21 759         4902 $args{attributes} = {};
22 759         4722 $args{methods} = {};
23 759         4862 $args{roles} = [];
24              
25 759         4354 $args{superclasses} = do {
26 282     282   977 no strict 'refs';
  282         284  
  282         568526  
27 759         4258 \@{ $args{package} . '::ISA' };
  759         14863  
28             };
29              
30 759   66     6529 my $self = bless \%args, ref($class) || $class;
31 759 100       5421 if(ref($self) ne __PACKAGE__){
32 26         60 $self->meta->_initialize_object($self, \%args);
33             }
34 759         14608 return $self;
35             }
36              
37             sub create_anon_class{
38 103     103 0 40477 my $self = shift;
39 103         4289 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 10 my $self = shift;
48 10         7 my %seen;
49 12         37 return grep { !$seen{ $_->name }++ }
50 10         13 map { $_->calculate_all_roles } @{ $self->roles };
  10         27  
  10         20  
51             }
52              
53             sub superclasses {
54 1328     1328 1 5331 my $self = shift;
55              
56 1328 100       3798 if (@_) {
57 750         2558 foreach my $super(@_){
58 756         3385 Mouse::Util::load_class($super);
59 754         2904 my $meta = Mouse::Util::get_metaclass_by_name($super);
60 754 100       2952 next if $self->verify_superclass($super, $meta);
61 4         20 $self->_reconcile_with_superclass_meta($meta);
62             }
63 747         2328 return @{ $self->{superclasses} } = @_;
  747         10708  
64             }
65              
66 578         532 return @{ $self->{superclasses} };
  578         2386  
67             }
68              
69             sub verify_superclass {
70 754     754 0 2339 my($self, $super, $super_meta) = @_;
71              
72 754 100       2943 if(defined $super_meta) {
73 164 100       2503 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         3122 my $mm = $super->can('meta');
83 590 100 66     3491 if(!($mm && $mm == \&Mouse::Util::meta)) {
84 5 100 66     46 if($super->can('new') or $super->can('DESTROY')) {
85 2         6 $self->inherit_from_foreign_class($super);
86             }
87             }
88 590         3926 return 1; # always ok
89             }
90              
91 163         3430 return $self->isa(ref $super_meta); # checks metaclass compatibility
92             }
93              
94             sub inherit_from_foreign_class {
95 2     2 0 5 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         5 my %metaroles;
116 4         10 foreach my $metaclass_type(@MetaClassTypes){
117 16   66     158 my $accessor = $self->can($metaclass_type . '_metaclass')
118             || $self->can($metaclass_type . '_class');
119              
120 16         32 my $other_c = $other->$accessor();
121 16         20 my $self_c = $self->$accessor();
122              
123 16 100       110 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         25 $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         26 require Mouse::Util::MetaRole;
134 4         27 $_[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   7 my ($self, $other) = @_;
143              
144             # find common ancestor
145 5         27 my @self_lin_isa = $self->linearized_isa;
146 5         14 my @other_lin_isa = $other->linearized_isa;
147              
148 5         6 my(@self_anon_supers, @other_anon_supers);
149 5         16 push @self_anon_supers, shift @self_lin_isa while $self_lin_isa[0]->meta->is_anon_class;
150 5         16 push @other_anon_supers, shift @other_lin_isa while $other_lin_isa[0]->meta->is_anon_class;
151              
152 5   33     29 my $common_ancestor = $self_lin_isa[0] eq $other_lin_isa[0] && $self_lin_isa[0];
153              
154 5 50       11 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         7 my %seen;
160 12         40 return sort grep { !$seen{$_}++ } ## no critic
161 4         10 (map{ $_->name } map{ $_->meta->calculate_all_roles } @self_anon_supers),
  4         9  
162 5         10 (map{ $_->name } map{ $_->meta->calculate_all_roles } @other_anon_supers),
  8         15  
  6         15  
163             ;
164             }
165              
166              
167             sub find_method_by_name {
168 38     38 0 37 my($self, $method_name) = @_;
169 38 50       59 defined($method_name)
170             or $self->throw_error('You must define a method name to find');
171              
172 38         88 foreach my $class( $self->linearized_isa ){
173 75         114 my $method = $self->initialize($class)->get_method($method_name);
174 75 100       236 return $method if defined $method;
175             }
176 0         0 return undef;
177             }
178              
179             sub get_all_methods {
180 2     2 1 4 my($self) = @_;
181 2         7 return map{ $self->find_method_by_name($_) } $self->get_all_method_names;
  24         27  
182             }
183              
184             sub get_all_method_names {
185 10     10 0 18 my $self = shift;
186 10         13 my %uniq;
187 93         137 return grep { $uniq{$_}++ == 0 }
188 10         45 map { Mouse::Meta::Class->initialize($_)->get_method_list() }
  21         44  
189             $self->linearized_isa;
190             }
191              
192             sub find_attribute_by_name {
193 14     14 0 16 my($self, $name) = @_;
194 14 50       28 defined($name)
195             or $self->throw_error('You must define an attribute name to find');
196 14         60 foreach my $attr($self->get_all_attributes) {
197 22 100       90 return $attr if $attr->name eq $name;
198             }
199 1         3 return undef;
200             }
201              
202             sub add_attribute {
203 607     607 1 11641 my $self = shift;
204              
205 607         1320 my($attr, $name);
206              
207 607 100       3345 if(Scalar::Util::blessed($_[0])){
208 6         664 $attr = $_[0];
209              
210 6 50       687 $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         1355 $name = $attr->name;
214             }
215             else{
216             # _process_attribute
217 601         826 $name = shift;
218              
219 601 100       2176 my %args = (@_ == 1) ? %{$_[0]} : @_;
  84         276  
220              
221 601 50       1306 defined($name)
222             or $self->throw_error('You must provide a name for the attribute');
223              
224 601 100       1454 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         45 my $inherited_attr;
229 35         32 foreach my $i ( @{ $self->_calculate_all_attributes } ) {
  35         89  
230 81 100       191 if ( $i->name eq $name ) {
231 33         30 $inherited_attr = $i;
232 33         36 last;
233             }
234             }
235 35 100       78 $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         110 $attr = $inherited_attr->clone_and_inherit_options(%args);
239             }
240             else{
241 566         5352 my($attribute_class, @traits) = $self->attribute_metaclass->interpolate_class(\%args);
242 566 100       1100 $args{traits} = \@traits if @traits;
243              
244 566         2083 $attr = $attribute_class->new($name, %args);
245             }
246             }
247              
248 589         3188 Scalar::Util::weaken( $attr->{associated_class} = $self );
249              
250             # install accessors first
251 589         2280 $attr->install_accessors();
252              
253             # then register the attribute to the metaclass
254 585         1141 $attr->{insertion_order} = keys %{ $self->{attributes} };
  585         2778  
255 585         1532 $self->{attributes}{$name} = $attr;
256 585         3186 $self->_invalidate_metaclass_cache();
257              
258 585 100 100     2402 if(!$attr->{associated_methods} && ($attr->{is} || '') ne 'bare'){
      100        
259 2         205 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         4770 return $attr;
263             }
264              
265             sub _calculate_all_attributes {
266 534     534   102232 my($self) = @_;
267 534         1439 my %seen;
268             my @all_attrs;
269 534         2367 foreach my $class($self->linearized_isa) {
270 1257 100       4875 my $meta = Mouse::Util::get_metaclass_by_name($class) or next;
271 711         2317 my @attrs = grep { !$seen{$_->name}++ } values %{$meta->{attributes}};
  936         2608  
  711         4596  
272             @attrs = sort {
273 711         3043 $b->{insertion_order} <=> $a->{insertion_order}
274 908         945 } @attrs;
275 711         2764 push @all_attrs, @attrs;
276             }
277 534         12580 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 119 my ( $self, @args ) = @_;
287              
288             return (
289 83         413 inline_constructor => 1,
290             inline_destructor => 1,
291             constructor_name => 'new',
292             @args,
293             );
294             }
295              
296             sub make_immutable {
297 83     83 0 2066 my $self = shift;
298 83         256 my %args = $self->immutable_options(@_);
299              
300 83         181 $self->{is_immutable}++;
301              
302 83 50       356 if ($args{inline_constructor}) {
303             $self->add_method($args{constructor_name} =>
304 83         460 Mouse::Util::load_class($self->constructor_class)
305             ->_generate_constructor($self, \%args));
306             }
307              
308 83 50       239 if ($args{inline_destructor}) {
309 83         323 $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         296 return 1;
317             }
318              
319             sub make_mutable {
320 2     2 0 1425 my($self) = @_;
321 2         4 $self->{is_immutable} = 0;
322 2         5 return;
323             }
324              
325             sub is_immutable;
326 10     10 0 49 sub is_mutable { !$_[0]->is_immutable }
327              
328             sub _install_modifier {
329 79     79   106 my( $self, $type, $name, $code ) = @_;
330 79         187 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         173 my $modifier_table = $self->{modifiers}{$name};
336              
337 78 100       157 if(!$modifier_table){
338 56         52 my(@before, @after, @around);
339 56         59 my $cache = $original;
340             my $modified = sub {
341 79 100   79   28650 if(@before) {
        117      
        62      
        54      
        10      
        10      
        10      
342 24         36 for my $c (@before) { $c->(@_) }
  27         72  
343             }
344 79 100       283 unless(@after) {
345 51         157 return $cache->(@_);
346             }
347              
348 28 100       85 if(wantarray){ # list context
    100          
349 2         7 my @rval = $cache->(@_);
350              
351 2         9 for my $c(@after){ $c->(@_) }
  2         6  
352 2         17 return @rval;
353             }
354             elsif(defined wantarray){ # scalar context
355 3         9 my $rval = $cache->(@_);
356              
357 3         12 for my $c(@after){ $c->(@_) }
  3         8  
358 3         18 return $rval;
359             }
360             else{ # void context
361 23         63 $cache->(@_);
362              
363 23         66 for my $c(@after){ $c->(@_) }
  25         59  
364 23         187 return;
365             }
366 56         258 };
367              
368 56         259 $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         471 $self->add_method($name => $modified);
379             }
380              
381 78 100       249 if($type eq 'before'){
    100          
382 23         23 unshift @{$modifier_table->{before}}, $code;
  23         47  
383             }
384             elsif($type eq 'after'){
385 24         25 push @{$modifier_table->{after}}, $code;
  24         47  
386             }
387             else{ # around
388 31         32 push @{$modifier_table->{around}}, $code;
  31         55  
389              
390 31         34 my $next = ${ $modifier_table->{cache} };
  31         43  
391 31     42   87 ${ $modifier_table->{cache} } = sub{ $code->($next, @_) };
  31         55  
  42         113  
392             }
393              
394 78         235 return;
395             }
396              
397             sub add_before_method_modifier {
398 23     23 0 32 my ( $self, $name, $code ) = @_;
399 23         50 $self->_install_modifier( 'before', $name, $code );
400             }
401              
402             sub add_around_method_modifier {
403 32     32 0 277 my ( $self, $name, $code ) = @_;
404 32         80 $self->_install_modifier( 'around', $name, $code );
405             }
406              
407             sub add_after_method_modifier {
408 24     24 0 38 my ( $self, $name, $code ) = @_;
409 24         52 $self->_install_modifier( 'after', $name, $code );
410             }
411              
412             sub add_override_method_modifier {
413 24     24 0 40 my ($self, $name, $code) = @_;
414              
415 24 100       66 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         68 my $package = $self->name;
420              
421 23 100       183 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   12129 local $Mouse::SUPER_PACKAGE = $package;
426 26         34 local $Mouse::SUPER_BODY = $super_body;
427 26         170 local @Mouse::SUPER_ARGS = @_;
428 26         28 &{$code};
  26         53  
429 21         161 });
430 21         48 return;
431             }
432              
433             sub add_augment_method_modifier {
434 11     20 0 17 my ($self, $name, $code) = @_;
435 11 100       35 if($self->has_method($name)){
436 1         5 $self->throw_error("Cannot add an augment method if a local method is already present");
437             }
438              
439 10 50       28 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         20 my $super_body = $super->body;
444              
445             $self->add_method($name => sub {
446 14     23   4917 local $Mouse::INNER_BODY{$super_package} = $code;
        31      
        17      
447 14         27 local $Mouse::INNER_ARGS{$super_package} = [@_];
448 14         11 &{$super_body};
  14         36  
449 10         116 });
450 10         37 return;
451             }
452              
453             sub does_role {
454 510     516 0 5794 my ($self, $role_name) = @_;
455              
456 510 100       3739 (defined $role_name)
457             || $self->throw_error("You must supply a role name to look for");
458              
459 509 100       4172 $role_name = $role_name->name if ref $role_name;
460              
461 509         4360 for my $class ($self->linearized_isa) {
462 865 100       5798 my $meta = Mouse::Util::get_metaclass_by_name($class)
463             or next;
464              
465 624         4166 for my $role (@{ $meta->roles }) {
  624         9474  
466              
467 311 100       2187 return 1 if $role->does_role($role_name);
468             }
469             }
470              
471 268         3709 return 0;
472             }
473              
474             1;
475             __END__