Checking in changes prior to tagging of version 0.60.
[gitmo/Mouse.git] / lib / Mouse / Meta / Class.pm
index 520cc9d..e0bfb7a 100644 (file)
@@ -6,18 +6,14 @@ use Scalar::Util qw/blessed weaken/;
 use Mouse::Meta::Module;
 our @ISA = qw(Mouse::Meta::Module);
 
+our @CARP_NOT = qw(Mouse); # trust Mouse
+
 sub attribute_metaclass;
 sub method_metaclass;
 
 sub constructor_class;
 sub destructor_class;
 
-my @MetaClassTypes = qw(
-    attribute_metaclass
-    method_metaclass
-    constructor_class
-    destructor_class
-);
 
 sub _construct_meta {
     my($class, %args) = @_;
@@ -77,41 +73,69 @@ sub superclasses {
 
     return @{ $self->{superclasses} };
 }
+my @MetaClassTypes = (
+    'attribute',   # Mouse::Meta::Attribute
+    'method',      # Mouse::Meta::Method
+    'constructor', # Mouse::Meta::Method::Constructor
+    'destructor',  # Mouse::Meta::Method::Destructor
+);
 
 sub _reconcile_with_superclass_meta {
-    my($self, $super_meta) = @_;
-
-    my @incompatibles;
+    my($self, $other) = @_;
 
+    # find incompatible traits
+    my %metaroles;
     foreach my $metaclass_type(@MetaClassTypes){
-        my $super_c = $super_meta->$metaclass_type();
-        my $self_c  = $self->$metaclass_type();
+        my $accessor = $self->can($metaclass_type . '_metaclass')
+            || $self->can($metaclass_type . '_class');
 
-        if(!$super_c->isa($self_c)){
-            push @incompatibles, ($metaclass_type => $super_c);
-        }
-    }
+        my $other_c = $other->$accessor();
+        my $self_c  = $self->$accessor();
 
-    my @roles;
-
-    foreach my $role($self->meta->calculate_all_roles){
-        if(!$super_meta->meta->does_role($role->name)){
-            push @roles, $role->name;
+        if(!$self_c->isa($other_c)){
+            $metaroles{$metaclass_type}
+                = [ $self_c->meta->_collect_roles($other_c->meta) ];
         }
     }
 
-    #print "reconcile($self vs. $super_meta; @roles; @incompatibles)\n";
+    $metaroles{class} = [$self->meta->_collect_roles($other->meta)];
+
+    #use Data::Dumper; print Data::Dumper->new([\%metaroles], ['*metaroles'])->Indent(1)->Dump;
 
     require Mouse::Util::MetaRole;
-    Mouse::Util::MetaRole::apply_metaclass_roles(
-        for_class       => $self,
-        metaclass       => ref $super_meta,
-        metaclass_roles => \@roles,
-        @incompatibles,
+    $_[0] = Mouse::Util::MetaRole::apply_metaroles(
+        for             => $self,
+        class_metaroles => \%metaroles,
     );
     return;
 }
 
+sub _collect_roles {
+    my ($self, $other) = @_;
+
+    # find common ancestor
+    my @self_lin_isa  = $self->linearized_isa;
+    my @other_lin_isa = $other->linearized_isa;
+
+    my(@self_anon_supers, @other_anon_supers);
+    push @self_anon_supers,  shift @self_lin_isa  while $self_lin_isa[0]->meta->is_anon_class;
+    push @other_anon_supers, shift @other_lin_isa while $other_lin_isa[0]->meta->is_anon_class;
+
+    my $common_ancestor = $self_lin_isa[0] eq $other_lin_isa[0] && $self_lin_isa[0];
+
+    if(!$common_ancestor){
+        $self->throw_error(sprintf '%s cannot have %s as a super class because of their metaclass incompatibility',
+            $self->name, $other->name);
+    }
+
+    my %seen;
+    return sort grep { !$seen{$_}++ } ## no critic
+        (map{ $_->name } map{ $_->meta->calculate_all_roles } @self_anon_supers),
+        (map{ $_->name } map{ $_->meta->calculate_all_roles } @other_anon_supers),
+    ;
+}
+
+
 sub find_method_by_name{
     my($self, $method_name) = @_;
     defined($method_name)
@@ -188,8 +212,9 @@ sub add_attribute {
     $self->{attributes}{$attr->name} = $attr;
     $attr->install_accessors();
 
-    if(Mouse::Util::_MOUSE_VERBOSE && !$attr->{associated_methods} && ($attr->{is} || '') ne 'bare'){
-        Carp::cluck(qq{Attribute (}.$attr->name.qq{) of class }.$self->name.qq{ has no associated methods (did you mean to provide an "is" argument?)});
+    if(!$attr->{associated_methods} && ($attr->{is} || '') ne 'bare'){
+        Carp::carp(qq{Attribute ($name) of class }.$self->name
+            .qq{ has no associated methods (did you mean to provide an "is" argument?)});
     }
     return $attr;
 }
@@ -203,20 +228,8 @@ sub compute_all_applicable_attributes { # DEPRECATED
 sub linearized_isa;
 
 sub new_object;
+sub clone_object;
 
-sub clone_object {
-    my $class  = shift;
-    my $object = shift;
-    my %params = (@_ == 1) ? %{$_[0]} : @_;
-
-    (blessed($object) && $object->isa($class->name))
-        || $class->throw_error("You must pass an instance of the metaclass (" . $class->name . "), not ($object)");
-
-    my $cloned = bless { %$object }, ref $object;
-    $class->_initialize_object($cloned, \%params);
-
-    return $cloned;
-}
 
 sub clone_instance { # DEPRECATED
     my ($class, $instance, %params) = @_;
@@ -245,18 +258,18 @@ sub make_immutable {
 
     $self->{is_immutable}++;
 
+    $self->{strict_constructor} = $args{strict_constructor};
+
     if ($args{inline_constructor}) {
-        my $c = $self->constructor_class;
-        Mouse::Util::load_class($c);
         $self->add_method($args{constructor_name} =>
-            $c->_generate_constructor($self, \%args));
+            Mouse::Util::load_class($self->constructor_class)
+                ->_generate_constructor($self, \%args));
     }
 
     if ($args{inline_destructor}) {
-        my $c = $self->destructor_class;
-        Mouse::Util::load_class($c);
         $self->add_method(DESTROY =>
-            $c->_generate_destructor($self, \%args));
+            Mouse::Util::load_class($self->destructor_class)
+                ->_generate_destructor($self, \%args));
     }
 
     # Moose's make_immutable returns true allowing calling code to skip setting an explicit true value
@@ -342,10 +355,10 @@ sub _install_modifier_pp{
 sub _install_modifier {
     my ( $self, $type, $name, $code ) = @_;
 
-    # load Class::Method::Modifiers first
+    # load Data::Util first
     my $no_cmm_fast = do{
         local $@;
-        eval q{ require Class::Method::Modifiers::Fast };
+        eval q{ use Data::Util 0.55 () };
         $@;
     };
 
@@ -354,16 +367,27 @@ sub _install_modifier {
         $impl = \&_install_modifier_pp;
     }
     else{
-        my $install_modifier = Class::Method::Modifiers::Fast->can('_install_modifier');
         $impl = sub {
             my ( $self, $type, $name, $code ) = @_;
             my $into = $self->name;
-            $install_modifier->($into, $type, $name, $code);
 
-            $self->add_method($name => do{
-                no strict 'refs';
-                \&{ $into . '::' . $name };
-            });
+            my $method = Mouse::Util::get_code_ref( $into, $name );
+
+            if ( !$method || !Data::Util::subroutine_modifier($method) ) {
+                unless ($method) {
+                    $method = $into->can($name)
+                        or Carp::confess("The method '$name' is not found in the inheritance hierarchy for class $into");
+                }
+                $method = Data::Util::modify_subroutine( $method,
+                    $type => [$code] );
+
+                $self->add_method($name => $method);
+            }
+            else {
+                Data::Util::subroutine_modifier( $method, $type => $code );
+                $self->add_method($name => Mouse::Util::get_code_ref($into, $name));
+            }
+
             return;
         };
     }
@@ -440,6 +464,8 @@ sub does_role {
     (defined $role_name)
         || $self->throw_error("You must supply a role name to look for");
 
+    $role_name = $role_name->name if ref $role_name;
+
     for my $class ($self->linearized_isa) {
         my $meta = Mouse::Util::get_metaclass_by_name($class)
             or next;
@@ -462,7 +488,7 @@ Mouse::Meta::Class - The Mouse class metaclass
 
 =head1 VERSION
 
-This document describes Mouse version 0.46
+This document describes Mouse version 0.60
 
 =head1 METHODS