Checking in changes prior to tagging of version 0.52. Changelog diff is:
[gitmo/Mouse.git] / lib / Mouse / Meta / Class.pm
index a37a963..80f7af7 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();
-
-        if(!$super_c->isa($self_c)){
-            push @incompatibles, ($metaclass_type => $super_c);
-        }
-    }
+        my $accessor = $self->can($metaclass_type . '_metaclass')
+            || $self->can($metaclass_type . '_class');
 
-    my @roles;
+        my $other_c = $other->$accessor();
+        my $self_c  = $self->$accessor();
 
-    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;
 }
@@ -207,13 +232,13 @@ sub new_object;
 sub clone_object {
     my $class  = shift;
     my $object = shift;
-    my %params = (@_ == 1) ? %{$_[0]} : @_;
+    my $args   = $object->Mouse::Object::BUILDARGS(@_);
 
     (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);
+    $class->_initialize_object($cloned, $args);
 
     return $cloned;
 }
@@ -245,18 +270,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
@@ -360,10 +385,7 @@ sub _install_modifier {
             my $into = $self->name;
             $install_modifier->($into, $type, $name, $code);
 
-            $self->add_method($name => do{
-                no strict 'refs';
-                \&{ $into . '::' . $name };
-            });
+            $self->add_method($name => Mouse::Util::get_code_ref($into, $name));
             return;
         };
     }
@@ -440,6 +462,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 +486,7 @@ Mouse::Meta::Class - The Mouse class metaclass
 
 =head1 VERSION
 
-This document describes Mouse version 0.47
+This document describes Mouse version 0.52
 
 =head1 METHODS