Add metaclass compatibility stuff
[gitmo/Mouse.git] / lib / Mouse / Meta / Class.pm
CommitLineData
306290e8 1package Mouse::Meta::Class;
bc69ee88 2use Mouse::Util qw/:meta get_linear_isa not_supported/; # enables strict and warnings
c3398f5b 3
cecfb973 4use Scalar::Util qw/blessed weaken/;
6d28c5cf 5
6d28c5cf 6use Mouse::Meta::Module;
f3bb863f 7our @ISA = qw(Mouse::Meta::Module);
3a63a2e7 8
e058b279 9sub method_metaclass;
10sub attribute_metaclass;
3a63a2e7 11
e058b279 12sub constructor_class;
13sub destructor_class;
380e1cd7 14
8e64d0fa 15sub _construct_meta {
88ed7189 16 my($class, %args) = @_;
c3398f5b 17
5132ec42 18 $args{attributes} = {};
19 $args{methods} = {};
20 $args{roles} = [];
8536d351 21
c3398f5b 22 $args{superclasses} = do {
23 no strict 'refs';
88ed7189 24 \@{ $args{package} . '::ISA' };
c3398f5b 25 };
26
8d40c3b8 27 my $self = bless \%args, ref($class) || $class;
7eb3a8d5 28 if(ref($self) ne __PACKAGE__){
9009aca1 29 $self->meta->_initialize_object($self, \%args);
8d40c3b8 30 }
31 return $self;
7a50b450 32}
33
34sub create_anon_class{
35 my $self = shift;
36 return $self->create(undef, @_);
37}
38
43165725 39sub is_anon_class;
c3398f5b 40
43165725 41sub roles;
c3398f5b 42
e7264861 43sub calculate_all_roles {
44 my $self = shift;
45 my %seen;
46 return grep { !$seen{ $_->name }++ }
47 map { $_->calculate_all_roles } @{ $self->roles };
48}
49
c3398f5b 50sub superclasses {
51 my $self = shift;
52
53 if (@_) {
9d0686b2 54 foreach my $super(@_){
55 Mouse::Util::load_class($super);
56 my $meta = Mouse::Util::get_metaclass_by_name($super);
b6369395 57
58 next if not defined $meta;
59
f48920c1 60 if(Mouse::Util::is_a_metarole($meta)){
9d0686b2 61 $self->throw_error("You cannot inherit from a Mouse Role ($super)");
62 }
b6369395 63
64 next if $self->isa(ref $meta); # _superclass_meta_is_compatible
65
66 # XXX: should we check 'is_pristine' ?
67
68 $self->_reconcile_with_superclass_meta($meta);
9d0686b2 69 }
c3398f5b 70 @{ $self->{superclasses} } = @_;
71 }
72
8e64d0fa 73 return @{ $self->{superclasses} };
74}
75
b6369395 76my @MetaClassTypes = qw(
77 attribute_metaclass
78 method_metaclass
79 constructor_class
80 destructor_class
81);
82
83sub _reconcile_with_superclass_meta {
84 my($self, $super_meta) = @_;
85
86 my @incompatibles;
87 foreach my $metaclass_type(@MetaClassTypes){
88 my $super_c = $super_meta->$metaclass_type();
89 my $self_c = $self->$metaclass_type();
90
91 if(!$super_c->isa($self_c)){
92 push @incompatibles, $metaclass_type => $super_c;
93 }
94 }
95
96 if(@incompatibles){
97 $super_meta->reinitialize($self->name, @incompatibles);
98 }
99 return;
100}
101
102
8e64d0fa 103sub find_method_by_name{
104 my($self, $method_name) = @_;
105 defined($method_name)
106 or $self->throw_error('You must define a method name to find');
0126c27c 107
8e64d0fa 108 foreach my $class( $self->linearized_isa ){
109 my $method = $self->initialize($class)->get_method($method_name);
110 return $method if defined $method;
111 }
112 return undef;
113}
114
115sub get_all_methods {
116 my($self) = @_;
612d3e1a 117 return map{ $self->find_method_by_name($_) } $self->get_all_method_names;
c3398f5b 118}
119
60cfc6ad 120sub get_all_method_names {
121 my $self = shift;
122 my %uniq;
123 return grep { $uniq{$_}++ == 0 }
3a63a2e7 124 map { Mouse::Meta::Class->initialize($_)->get_method_list() }
60cfc6ad 125 $self->linearized_isa;
126}
127
2b908b79 128sub find_attribute_by_name{
129 my($self, $name) = @_;
130 my $attr;
131 foreach my $class($self->linearized_isa){
132 my $meta = Mouse::Util::get_metaclass_by_name($class) or next;
133 $attr = $meta->get_attribute($name) and last;
134 }
135 return $attr;
136}
137
87ca293b 138sub add_attribute {
c3398f5b 139 my $self = shift;
60f6eba9 140
87ca293b 141 my($attr, $name);
1b9e472d 142
87ca293b 143 if(blessed $_[0]){
144 $attr = $_[0];
1b9e472d 145
87ca293b 146 $attr->isa('Mouse::Meta::Attribute')
147 || $self->throw_error("Your attribute must be an instance of Mouse::Meta::Attribute (or a subclass)");
1b9e472d 148
87ca293b 149 $name = $attr->name;
1b9e472d 150 }
151 else{
87ca293b 152 # _process_attribute
153 $name = shift;
1b9e472d 154
87ca293b 155 my %args = (@_ == 1) ? %{$_[0]} : @_;
1b9e472d 156
87ca293b 157 defined($name)
158 or $self->throw_error('You must provide a name for the attribute');
1b9e472d 159
87ca293b 160 if ($name =~ s/^\+//) { # inherited attributes
2b908b79 161 my $inherited_attr = $self->find_attribute_by_name($name)
87ca293b 162 or $self->throw_error("Could not find an attribute by the name of '$name' to inherit from in ".$self->name);
163
8cf51b82 164 $attr = $inherited_attr->clone_and_inherit_options(%args);
87ca293b 165 }
166 else{
8cf51b82 167 my($attribute_class, @traits) = $self->attribute_metaclass->interpolate_class(\%args);
87ca293b 168 $args{traits} = \@traits if @traits;
169
2a464664 170 $attr = $attribute_class->new($name, %args);
87ca293b 171 }
172 }
1b9e472d 173
174 weaken( $attr->{associated_class} = $self );
175
176 $self->{attributes}{$attr->name} = $attr;
177 $attr->install_accessors();
178
8aba926d 179 if(Mouse::Util::_MOUSE_VERBOSE && !$attr->{associated_methods} && ($attr->{is} || '') ne 'bare'){
1b9e472d 180 Carp::cluck(qq{Attribute (}.$attr->name.qq{) of class }.$self->name.qq{ has no associated methods (did you mean to provide an "is" argument?)});
60f6eba9 181 }
1b9e472d 182 return $attr;
c3398f5b 183}
184
3a683350 185sub compute_all_applicable_attributes {
0126c27c 186 Carp::cluck('compute_all_applicable_attributes() has been deprecated')
8aba926d 187 if Mouse::Util::_MOUSE_VERBOSE;
3a683350 188 return shift->get_all_attributes(@_)
189}
190
cccb83de 191sub linearized_isa;
8536d351 192
ba153b33 193sub new_object;
1b9e472d 194
7a59f4e8 195sub clone_object {
926290ac 196 my $class = shift;
197 my $object = shift;
198 my %params = (@_ == 1) ? %{$_[0]} : @_;
7a59f4e8 199
926290ac 200 (blessed($object) && $object->isa($class->name))
201 || $class->throw_error("You must pass an instance of the metaclass (" . $class->name . "), not ($object)");
7a59f4e8 202
926290ac 203 my $cloned = bless { %$object }, ref $object;
204 $class->_initialize_object($cloned, \%params);
7a59f4e8 205
926290ac 206 return $cloned;
2cea7a5f 207}
7a59f4e8 208
2cea7a5f 209sub clone_instance {
210 my ($class, $instance, %params) = @_;
211
04493075 212 Carp::cluck('clone_instance has been deprecated. Use clone_object instead')
8aba926d 213 if Mouse::Util::_MOUSE_VERBOSE;
2cea7a5f 214 return $class->clone_object($instance, %params);
7a59f4e8 215}
216
fc1d8369 217sub make_immutable {
218 my $self = shift;
6a1d1835 219 my %args = (
7efbc77d 220 inline_constructor => 1,
e578d610 221 inline_destructor => 1,
2a464664 222 constructor_name => 'new',
6a1d1835 223 @_,
224 );
225
fc1d8369 226 $self->{is_immutable}++;
c7a6403f 227
6a1d1835 228 if ($args{inline_constructor}) {
a5c683f6 229 my $c = $self->constructor_class;
230 Mouse::Util::load_class($c);
380e1cd7 231 $self->add_method($args{constructor_name} =>
a5c683f6 232 $c->_generate_constructor($self, \%args));
c7a6403f 233 }
234
8632b6fe 235 if ($args{inline_destructor}) {
a5c683f6 236 my $c = $self->destructor_class;
237 Mouse::Util::load_class($c);
380e1cd7 238 $self->add_method(DESTROY =>
a5c683f6 239 $c->_generate_destructor($self, \%args));
8632b6fe 240 }
2276cb14 241
242 # Moose's make_immutable returns true allowing calling code to skip setting an explicit true value
243 # at the end of a source file.
244 return 1;
fc1d8369 245}
ad958001 246
fce211ae 247sub make_mutable { not_supported }
ad958001 248
6cfa1e5e 249sub is_immutable { $_[0]->{is_immutable} }
250sub is_mutable { !$_[0]->{is_immutable} }
84ef660f 251
3fbade18 252sub _install_modifier_pp{
8d59c723 253 my( $self, $type, $name, $code ) = @_;
254 my $into = $self->name;
3fbade18 255
256 my $original = $into->can($name)
257 or $self->throw_error("The method '$name' is not found in the inheritance hierarchy for class $into");
258
259 my $modifier_table = $self->{modifiers}{$name};
260
261 if(!$modifier_table){
262 my(@before, @after, @around, $cache, $modified);
263
264 $cache = $original;
265
266 $modified = sub {
267 for my $c (@before) { $c->(@_) }
268
269 if(wantarray){ # list context
270 my @rval = $cache->(@_);
271
272 for my $c(@after){ $c->(@_) }
273 return @rval;
274 }
275 elsif(defined wantarray){ # scalar context
276 my $rval = $cache->(@_);
277
278 for my $c(@after){ $c->(@_) }
279 return $rval;
280 }
281 else{ # void context
282 $cache->(@_);
283
284 for my $c(@after){ $c->(@_) }
285 return;
286 }
287 };
288
289 $self->{modifiers}{$name} = $modifier_table = {
290 original => $original,
291
292 before => \@before,
293 after => \@after,
294 around => \@around,
295
296 cache => \$cache, # cache for around modifiers
297 };
298
299 $self->add_method($name => $modified);
300 }
301
302 if($type eq 'before'){
303 unshift @{$modifier_table->{before}}, $code;
304 }
305 elsif($type eq 'after'){
306 push @{$modifier_table->{after}}, $code;
307 }
308 else{ # around
309 push @{$modifier_table->{around}}, $code;
310
311 my $next = ${ $modifier_table->{cache} };
312 ${ $modifier_table->{cache} } = sub{ $code->($next, @_) };
313 }
314
315 return;
316}
317
4859d490 318sub _install_modifier {
8d59c723 319 my ( $self, $type, $name, $code ) = @_;
4f5b44a0 320
3fbade18 321 # load Class::Method::Modifiers first
7ca5c5fb 322 my $no_cmm_fast = do{
3fbade18 323 local $@;
324 eval q{ require Class::Method::Modifiers::Fast };
325 $@;
4f5b44a0 326 };
4f5b44a0 327
3fbade18 328 my $impl;
329 if($no_cmm_fast){
330 $impl = \&_install_modifier_pp;
331 }
332 else{
333 my $install_modifier = Class::Method::Modifiers::Fast->can('_install_modifier');
334 $impl = sub {
8d59c723 335 my ( $self, $type, $name, $code ) = @_;
336 my $into = $self->name;
95ecd6f1 337 $install_modifier->($into, $type, $name, $code);
338
8d59c723 339 $self->add_method($name => do{
340 no strict 'refs';
341 \&{ $into . '::' . $name };
342 });
6cfa1e5e 343 return;
4f5b44a0 344 };
1b79a118 345 }
4f5b44a0 346
3fbade18 347 # replace this method itself :)
348 {
349 no warnings 'redefine';
350 *_install_modifier = $impl;
351 }
352
8d59c723 353 $self->$impl( $type, $name, $code );
4859d490 354}
355
50dc6ee5 356sub add_before_method_modifier {
4859d490 357 my ( $self, $name, $code ) = @_;
8d59c723 358 $self->_install_modifier( 'before', $name, $code );
50dc6ee5 359}
360
361sub add_around_method_modifier {
4859d490 362 my ( $self, $name, $code ) = @_;
8d59c723 363 $self->_install_modifier( 'around', $name, $code );
50dc6ee5 364}
365
366sub add_after_method_modifier {
4859d490 367 my ( $self, $name, $code ) = @_;
8d59c723 368 $self->_install_modifier( 'after', $name, $code );
50dc6ee5 369}
370
67199842 371sub add_override_method_modifier {
372 my ($self, $name, $code) = @_;
373
768804c0 374 if($self->has_method($name)){
375 $self->throw_error("Cannot add an override method if a local method is already present");
85bd3f44 376 }
377
6cfa1e5e 378 my $package = $self->name;
67199842 379
85bd3f44 380 my $super_body = $package->can($name)
6cfa1e5e 381 or $self->throw_error("You cannot override '$name' because it has no super method");
67199842 382
85bd3f44 383 $self->add_method($name => sub {
384 local $Mouse::SUPER_PACKAGE = $package;
385 local $Mouse::SUPER_BODY = $super_body;
386 local @Mouse::SUPER_ARGS = @_;
387
388 $code->(@_);
389 });
390 return;
67199842 391}
392
768804c0 393sub add_augment_method_modifier {
394 my ($self, $name, $code) = @_;
395 if($self->has_method($name)){
396 $self->throw_error("Cannot add an augment method if a local method is already present");
397 }
398
399 my $super = $self->find_method_by_name($name)
400 or $self->throw_error("You cannot augment '$name' because it has no super method");
401
402 my $super_package = $super->package_name;
403 my $super_body = $super->body;
404
405 $self->add_method($name => sub{
406 local $Mouse::INNER_BODY{$super_package} = $code;
407 local $Mouse::INNER_ARGS{$super_package} = [@_];
408 $super_body->(@_);
409 });
410 return;
411}
412
47f36c05 413sub does_role {
414 my ($self, $role_name) = @_;
ad958001 415
47f36c05 416 (defined $role_name)
fce211ae 417 || $self->throw_error("You must supply a role name to look for");
ad958001 418
f7fec86c 419 for my $class ($self->linearized_isa) {
95ecd6f1 420 my $meta = Mouse::Util::get_metaclass_by_name($class)
421 or next;
3a63a2e7 422
423 for my $role (@{ $meta->roles }) {
ff687069 424
3a63a2e7 425 return 1 if $role->does_role($role_name);
f7fec86c 426 }
47f36c05 427 }
ad958001 428
47f36c05 429 return 0;
430}
431
c3398f5b 4321;
c3398f5b 433__END__
434
435=head1 NAME
436
1820fffe 437Mouse::Meta::Class - The Mouse class metaclass
c3398f5b 438
a25ca8d6 439=head1 VERSION
440
8aba926d 441This document describes Mouse version 0.40_07
a25ca8d6 442
c3398f5b 443=head1 METHODS
444
612d3e1a 445=head2 C<< initialize(ClassName) -> Mouse::Meta::Class >>
c3398f5b 446
31c5194b 447Finds or creates a C<Mouse::Meta::Class> instance for the given ClassName. Only
306290e8 448one instance should exist for a given class.
c3398f5b 449
612d3e1a 450=head2 C<< name -> ClassName >>
c3398f5b 451
452Returns the name of the owner class.
453
612d3e1a 454=head2 C<< superclasses -> ClassNames >> C<< superclass(ClassNames) >>
c3398f5b 455
456Gets (or sets) the list of superclasses of the owner class.
457
31c5194b 458=head2 C<< add_method(name => CodeRef) >>
c3398f5b 459
31c5194b 460Adds a method to the owner class.
c3398f5b 461
31c5194b 462=head2 C<< has_method(name) -> Bool >>
72b88a88 463
31c5194b 464Returns whether we have a method with the given name.
72b88a88 465
31c5194b 466=head2 C<< get_method(name) -> Mouse::Meta::Method | undef >>
c68b4110 467
31c5194b 468Returns a L<Mouse::Meta::Method> with the given name.
469
470Note that you can also use C<< $metaclass->name->can($name) >> for a method body.
471
472=head2 C<< get_method_list -> Names >>
473
474Returns a list of method names which are defined in the local class.
475If you want a list of all applicable methods for a class, use the
476C<get_all_methods> method.
477
478=head2 C<< get_all_methods -> (Mouse::Meta::Method) >>
479
480Return the list of all L<Mouse::Meta::Method> instances associated with
481the class and its superclasses.
482
483=head2 C<< add_attribute(name => spec | Mouse::Meta::Attribute) >>
484
485Begins keeping track of the existing L<Mouse::Meta::Attribute> for the owner
486class.
c68b4110 487
612d3e1a 488=head2 C<< has_attribute(Name) -> Bool >>
66eea168 489
490Returns whether we have a L<Mouse::Meta::Attribute> with the given name.
491
31c5194b 492=head2 C<< get_attribute Name -> Mouse::Meta::Attribute | undef >>
c3398f5b 493
306290e8 494Returns the L<Mouse::Meta::Attribute> with the given name.
c3398f5b 495
31c5194b 496=head2 C<< get_attribute_list -> Names >>
497
498Returns a list of attribute names which are defined in the local
499class. If you want a list of all applicable attributes for a class,
500use the C<get_all_attributes> method.
501
502=head2 C<< get_all_attributes -> (Mouse::Meta::Attribute) >>
503
504Returns the list of all L<Mouse::Meta::Attribute> instances associated with
505this class and its superclasses.
506
612d3e1a 507=head2 C<< linearized_isa -> [ClassNames] >>
c3398f5b 508
509Returns the list of classes in method dispatch order, with duplicates removed.
510
612d3e1a 511=head2 C<< new_object(Parameters) -> Instance >>
2cea7a5f 512
612d3e1a 513Creates a new instance.
2cea7a5f 514
612d3e1a 515=head2 C<< clone_object(Instance, Parameters) -> Instance >>
f7b11a21 516
31c5194b 517Clones the given instance which must be an instance governed by this
f7b11a21 518metaclass.
519
31c5194b 520=head2 C<< throw_error(Message, Parameters) >>
521
522Throws an error with the given message.
523
1820fffe 524=head1 SEE ALSO
f7b11a21 525
1820fffe 526L<Moose::Meta::Class>
f7b11a21 527
31c5194b 528L<Class::MOP::Class>
529
c3398f5b 530=cut
531