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