let reinitialization fix metaobjs via role reconciliation
Jesse Luehrs [Sun, 26 Sep 2010 19:37:48 +0000 (14:37 -0500)]
lib/Moose/Meta/Class.pm
t/050_metaclasses/060_reinitialize.t [new file with mode: 0644]

index b37b8c9..28bdbb6 100644 (file)
@@ -606,6 +606,22 @@ sub _replace_self {
     Class::MOP::weaken_metaclass( $self->name ) if $self->is_anon_class;
 }
 
+sub _get_compatible_single_metaclass_by_role_reconciliation {
+    my $self = shift;
+    my ($single_meta_name) = @_;
+
+    my $current_single_meta_name = $self->_get_associated_single_metaclass($single_meta_name);
+
+    return $self->_reconcile_roles_for_metaclass($single_meta_name, $current_single_meta_name)->name;
+}
+
+sub _get_compatible_single_metaclass {
+    my $self = shift;
+
+    return $self->SUPER::_get_compatible_single_metaclass(@_)
+        || $self->_get_compatible_single_metaclass_by_role_reconciliation(@_);
+}
+
 sub _process_attribute {
     my ( $self, $name, @args ) = @_;
 
diff --git a/t/050_metaclasses/060_reinitialize.t b/t/050_metaclasses/060_reinitialize.t
new file mode 100644 (file)
index 0000000..33de304
--- /dev/null
@@ -0,0 +1,193 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+use Test::Moose;
+use Test::Exception;
+
+sub check_meta_sanity {
+    my ($meta, $class) = @_;
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+    isa_ok($meta, 'Moose::Meta::Class');
+    is($meta->name, $class);
+    ok($meta->has_method('foo'));
+    isa_ok($meta->get_method('foo'), 'Moose::Meta::Method');
+    ok($meta->has_attribute('bar'));
+    isa_ok($meta->get_attribute('bar'), 'Moose::Meta::Attribute');
+}
+
+{
+    package Foo;
+    use Moose;
+    sub foo {}
+    has bar => (is => 'ro');
+}
+
+check_meta_sanity(Foo->meta, 'Foo');
+
+Moose::Meta::Class->reinitialize('Foo');
+check_meta_sanity(Foo->meta, 'Foo');
+
+{
+    package Foo::Role::Method;
+    use Moose::Role;
+
+    has foo => (is => 'rw');
+}
+
+{
+    package Foo::Role::Attribute;
+    use Moose::Role;
+    has oof => (is => 'rw');
+}
+
+Moose::Util::MetaRole::apply_metaroles(
+    for => 'Foo',
+    class_metaroles => {
+        method    => ['Foo::Role::Method'],
+        attribute => ['Foo::Role::Attribute'],
+    },
+);
+check_meta_sanity(Foo->meta, 'Foo');
+does_ok(Foo->meta->get_method('foo'), 'Foo::Role::Method');
+does_ok(Foo->meta->get_attribute('bar'), 'Foo::Role::Attribute');
+
+Moose::Meta::Class->reinitialize('Foo');
+check_meta_sanity(Foo->meta, 'Foo');
+does_ok(Foo->meta->get_method('foo'), 'Foo::Role::Method');
+does_ok(Foo->meta->get_attribute('bar'), 'Foo::Role::Attribute');
+
+Foo->meta->get_method('foo')->foo('TEST');
+Foo->meta->get_attribute('bar')->oof('TSET');
+is(Foo->meta->get_method('foo')->foo, 'TEST');
+is(Foo->meta->get_attribute('bar')->oof, 'TSET');
+Moose::Meta::Class->reinitialize('Foo');
+check_meta_sanity(Foo->meta, 'Foo');
+is(Foo->meta->get_method('foo')->foo, 'TEST');
+is(Foo->meta->get_attribute('bar')->oof, 'TSET');
+
+{
+    package Bar::Role::Method;
+    use Moose::Role;
+}
+
+{
+    package Bar::Role::Attribute;
+    use Moose::Role;
+}
+
+{
+    package Bar;
+    use Moose;
+    Moose::Util::MetaRole::apply_metaroles(
+        for => 'Bar',
+        class_metaroles => {
+            method    => ['Bar::Role::Method'],
+            attribute => ['Bar::Role::Attribute'],
+        },
+    );
+    sub foo {}
+    has bar => (is => 'ro');
+}
+
+check_meta_sanity(Bar->meta, 'Bar');
+does_ok(Bar->meta->get_method('foo'), 'Bar::Role::Method');
+does_ok(Bar->meta->get_attribute('bar'), 'Bar::Role::Attribute');
+
+Moose::Meta::Class->reinitialize('Bar');
+check_meta_sanity(Bar->meta, 'Bar');
+does_ok(Bar->meta->get_method('foo'), 'Bar::Role::Method');
+does_ok(Bar->meta->get_attribute('bar'), 'Bar::Role::Attribute');
+ok(!Moose::Util::does_role(Bar->meta->get_method('foo'), 'Foo::Role::Method'));
+ok(!Moose::Util::does_role(Bar->meta->get_attribute('bar'), 'Foo::Role::Attribute'));
+
+Moose::Util::MetaRole::apply_metaroles(
+    for => 'Bar',
+    class_metaroles => {
+        method    => ['Foo::Role::Method'],
+        attribute => ['Foo::Role::Attribute'],
+    },
+);
+check_meta_sanity(Bar->meta, 'Bar');
+does_ok(Bar->meta->get_method('foo'), 'Bar::Role::Method');
+does_ok(Bar->meta->get_attribute('bar'), 'Bar::Role::Attribute');
+does_ok(Bar->meta->get_method('foo'), 'Foo::Role::Method');
+does_ok(Bar->meta->get_attribute('bar'), 'Foo::Role::Attribute');
+
+{
+    package Bar::Meta::Method;
+    use Moose;
+    BEGIN { extends 'Moose::Meta::Method' };
+}
+
+{
+    package Bar::Meta::Attribute;
+    use Moose;
+    BEGIN { extends 'Moose::Meta::Attribute' };
+}
+
+Moose::Meta::Class->reinitialize(
+    'Bar',
+    method_metaclass    => 'Bar::Meta::Method',
+    attribute_metaclass => 'Bar::Meta::Attribute',
+);
+check_meta_sanity(Bar->meta, 'Bar');
+isa_ok(Bar->meta->get_method('foo'), 'Bar::Meta::Method');
+does_ok(Bar->meta->get_method('foo'), 'Bar::Role::Method');
+does_ok(Bar->meta->get_method('foo'), 'Foo::Role::Method');
+isa_ok(Bar->meta->get_attribute('bar'), 'Bar::Meta::Attribute');
+does_ok(Bar->meta->get_attribute('bar'), 'Bar::Role::Attribute');
+does_ok(Bar->meta->get_attribute('bar'), 'Foo::Role::Attribute');
+
+{
+    package Baz::Meta::Class;
+    use Moose;
+    BEGIN { extends 'Moose::Meta::Class' };
+
+    sub initialize {
+        my $self = shift;
+        return $self->SUPER::initialize(
+            @_,
+            method_metaclass    => 'Bar::Meta::Method',
+            attribute_metaclass => 'Bar::Meta::Attribute'
+        );
+    }
+}
+
+{
+    package Baz;
+    use Moose -metaclass => 'Baz::Meta::Class';
+    sub foo {}
+    has bar => (is => 'ro');
+}
+
+check_meta_sanity(Baz->meta, 'Baz');
+isa_ok(Baz->meta->get_method('foo'), 'Bar::Meta::Method');
+isa_ok(Baz->meta->get_attribute('bar'), 'Bar::Meta::Attribute');
+Moose::Meta::Class->reinitialize('Baz');
+check_meta_sanity(Baz->meta, 'Baz');
+isa_ok(Baz->meta->get_method('foo'), 'Bar::Meta::Method');
+isa_ok(Baz->meta->get_attribute('bar'), 'Bar::Meta::Attribute');
+
+Moose::Util::MetaRole::apply_metaroles(
+    for => 'Baz',
+    class_metaroles => {
+        method    => ['Foo::Role::Method'],
+        attribute => ['Foo::Role::Attribute'],
+    },
+);
+check_meta_sanity(Baz->meta, 'Baz');
+isa_ok(Baz->meta->get_method('foo'), 'Bar::Meta::Method');
+isa_ok(Baz->meta->get_attribute('bar'), 'Bar::Meta::Attribute');
+does_ok(Baz->meta->get_method('foo'), 'Foo::Role::Method');
+does_ok(Baz->meta->get_attribute('bar'), 'Foo::Role::Attribute');
+
+throws_ok {
+    Moose::Meta::Class->reinitialize(
+        'Baz',
+        method_metaclass    => 'Foo::Meta::Method',
+        attribute_metaclass => 'Foo::Meta::Attribute',
+    );
+} qr/compatible/;
+
+done_testing;