use strict;
use warnings;
-use Test::More tests => 59;
+use lib 't/lib', 'lib';
+
+use Test::More;
+use Test::Exception;
use Moose::Util::MetaRole;
}
{
- package My::Meta::Attribute;
- use Moose;
- extends 'Moose::Meta::Attribute';
-}
-
-{
- package My::Meta::Method;
- use Moose;
- extends 'Moose::Meta::Method';
-}
-
-{
- package My::Meta::Instance;
- use Moose;
- extends 'Moose::Meta::Instance';
-}
-
-{
- package My::Meta::MethodConstructor;
- use Moose;
- extends 'Moose::Meta::Method::Constructor';
-}
-
-{
- package My::Meta::MethodDestructor;
- use Moose;
- extends 'Moose::Meta::Method::Destructor';
-}
-
-{
package Role::Foo;
use Moose::Role;
has 'foo' => ( is => 'ro', default => 10 );
}
{
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class',
- metaclass_roles => ['Role::Foo'],
+ package My::Role;
+ use Moose::Role;
+}
+
+{
+ Moose::Util::MetaRole::apply_metaroles(
+ for => My::Class->meta,
+ class_metaroles => { class => ['Role::Foo'] },
);
ok( My::Class->meta()->meta()->does_role('Role::Foo'),
}
{
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class',
- attribute_metaclass_roles => ['Role::Foo'],
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class',
+ class_metaroles => { attribute => ['Role::Foo'] },
);
ok( My::Class->meta()->attribute_metaclass()->meta()->does_role('Role::Foo'),
}
{
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class',
- method_metaclass_roles => ['Role::Foo'],
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class',
+ class_metaroles => { method => ['Role::Foo'] },
);
ok( My::Class->meta()->method_metaclass()->meta()->does_role('Role::Foo'),
}
{
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class',
- instance_metaclass_roles => ['Role::Foo'],
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class',
+ class_metaroles => { wrapped_method => ['Role::Foo'] },
+ );
+
+ ok( My::Class->meta()->wrapped_method_metaclass()->meta()->does_role('Role::Foo'),
+ q{apply Role::Foo to My::Class->meta()'s wrapped method metaclass} );
+ ok( My::Class->meta()->method_metaclass()->meta()->does_role('Role::Foo'),
+ '... My::Class->meta() still does Role::Foo' );
+ ok( My::Class->meta()->meta()->does_role('Role::Foo'),
+ '... My::Class->meta() still does Role::Foo' );
+ ok( My::Class->meta()->attribute_metaclass()->meta()->does_role('Role::Foo'),
+ q{... My::Class->meta()'s attribute metaclass still does Role::Foo} );
+
+ My::Class->meta()->add_after_method_modifier( 'bar' => sub { 'bar' } );
+ is( My::Class->meta()->get_method('bar')->foo(), 10,
+ '... call foo() on a wrapped method metaclass object' );
+}
+
+{
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class',
+ class_metaroles => { instance => ['Role::Foo'] },
);
ok( My::Class->meta()->instance_metaclass()->meta()->does_role('Role::Foo'),
}
{
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class',
- constructor_class_roles => ['Role::Foo'],
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class',
+ class_metaroles => { constructor => ['Role::Foo'] },
);
ok( My::Class->meta()->constructor_class()->meta()->does_role('Role::Foo'),
}
{
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class',
- destructor_class_roles => ['Role::Foo'],
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class',
+ class_metaroles => { destructor => ['Role::Foo'] },
);
ok( My::Class->meta()->destructor_class()->meta()->does_role('Role::Foo'),
}
{
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Role',
+ role_metaroles => { application_to_class => ['Role::Foo'] },
+ );
+
+ ok( My::Role->meta->application_to_class_class->meta->does_role('Role::Foo'),
+ q{apply Role::Foo to My::Role->meta's application_to_class class} );
+
+ is( My::Role->meta->application_to_class_class->new->foo, 10,
+ q{... call foo() on an application_to_class instance} );
+}
+
+{
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Role',
+ role_metaroles => { application_to_role => ['Role::Foo'] },
+ );
+
+ ok( My::Role->meta->application_to_role_class->meta->does_role('Role::Foo'),
+ q{apply Role::Foo to My::Role->meta's application_to_role class} );
+ ok( My::Role->meta->application_to_class_class->meta->does_role('Role::Foo'),
+ q{... My::Role->meta's application_to_class class still does Role::Foo} );
+
+ is( My::Role->meta->application_to_role_class->new->foo, 10,
+ q{... call foo() on an application_to_role instance} );
+}
+
+{
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Role',
+ role_metaroles => { application_to_instance => ['Role::Foo'] },
+ );
+
+ ok( My::Role->meta->application_to_instance_class->meta->does_role('Role::Foo'),
+ q{apply Role::Foo to My::Role->meta's application_to_instance class} );
+ ok( My::Role->meta->application_to_role_class->meta->does_role('Role::Foo'),
+ q{... My::Role->meta's application_to_role class still does Role::Foo} );
+ ok( My::Role->meta->application_to_class_class->meta->does_role('Role::Foo'),
+ q{... My::Role->meta's application_to_class class still does Role::Foo} );
+
+ is( My::Role->meta->application_to_instance_class->new->foo, 10,
+ q{... call foo() on an application_to_instance instance} );
+}
+
+{
Moose::Util::MetaRole::apply_base_class_roles(
- for_class => 'My::Class',
- roles => ['Role::Foo'],
+ for => 'My::Class',
+ roles => ['Role::Foo'],
);
ok( My::Class->meta()->does_role('Role::Foo'),
}
{
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class2',
- metaclass_roles => ['Role::Foo'],
- attribute_metaclass_roles => ['Role::Foo'],
- method_metaclass_roles => ['Role::Foo'],
- instance_metaclass_roles => ['Role::Foo'],
- constructor_class_roles => ['Role::Foo'],
- destructor_class_roles => ['Role::Foo'],
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class2',
+ class_metaroles => {
+ class => ['Role::Foo'],
+ attribute => ['Role::Foo'],
+ method => ['Role::Foo'],
+ instance => ['Role::Foo'],
+ constructor => ['Role::Foo'],
+ destructor => ['Role::Foo'],
+ },
);
ok( My::Class2->meta()->meta()->does_role('Role::Foo'),
{
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class3',
- metaclass_roles => ['Role::Foo'],
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class3',
+ class_metaroles => { class => ['Role::Foo'] },
);
ok( My::Class3->meta()->meta()->does_role('Role::Foo'),
is( My::Class3->meta()->foo(), 10,
'... and call foo() on that meta object' );
ok( ( grep { $_ eq 'My::Meta::Class' } My::Class3->meta()->meta()->superclasses() ),
- 'apply_metaclass_roles() does not interfere with metaclass set via Moose->init_meta()' );
+ 'apply_metaroles() does not interfere with metaclass set via Moose->init_meta()' );
}
{
}
{
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class4',
- metaclass_roles => ['Role::Foo'],
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class4',
+ class_metaroles => { class => ['Role::Foo'] },
);
ok( My::Class4->meta()->meta()->does_role('Role::Foo'),
'apply Role::Foo to My::Class4->meta()' );
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class4',
- metaclass_roles => ['Role::Bar'],
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class4',
+ class_metaroles => { class => ['Role::Bar'] },
);
ok( My::Class4->meta()->meta()->does_role('Role::Bar'),
{
ok( My::Class5->meta()->meta()->does_role('Role::Foo'),
- q{My::Class55->meta()'s does Role::Foo because it extends My::Class} );
+ q{My::Class5->meta()'s does Role::Foo because it extends My::Class} );
ok( My::Class5->meta()->attribute_metaclass()->meta()->does_role('Role::Foo'),
q{My::Class5->meta()'s attribute metaclass also does Role::Foo} );
ok( My::Class5->meta()->method_metaclass()->meta()->does_role('Role::Foo'),
}
{
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class5',
- metaclass_roles => ['Role::Bar'],
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class5',
+ class_metaroles => { class => ['Role::Bar'] },
);
ok( My::Class5->meta()->meta()->does_role('Role::Bar'),
package My::Class6;
use Moose;
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class6',
- metaclass_roles => ['Role::Bar'],
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class6',
+ class_metaroles => { class => ['Role::Bar'] },
);
extends 'My::Class';
ok( My::Class6->meta()->meta()->does_role('Role::Bar'),
q{apply Role::Bar My::Class6->meta() before extends} );
ok( My::Class6->meta()->meta()->does_role('Role::Foo'),
- q{... and My::Class6->meta() does Role::Foo because it extends My::Class} );
+ q{... and My::Class6->meta() does Role::Foo because My::Class6 extends My::Class} );
}
-# This is the hack needed to work around the
-# _fix_metaclass_incompatibility problem. You must call extends()
-# (which in turn calls _fix_metaclass_imcompatibility) _before_ you
-# apply more extensions in the subclass.
+# This is the hack that used to be needed to work around the
+# _fix_metaclass_incompatibility problem. You called extends() (which
+# in turn calls _fix_metaclass_imcompatibility) _before_ you apply
+# more extensions in the subclass. We wabt to make sure this continues
+# to work in the future.
{
package My::Class7;
use Moose;
# In real usage this would go in a BEGIN block so it happened
- # before apply_metaclass_roles was called by an extension.
+ # before apply_metaroles was called by an extension.
extends 'My::Class';
- Moose::Util::MetaRole::apply_metaclass_roles(
- for_class => 'My::Class7',
- metaclass_roles => ['Role::Bar'],
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class7',
+ class_metaroles => { class => ['Role::Bar'] },
);
}
ok( My::Class7->meta()->meta()->does_role('Role::Bar'),
q{apply Role::Bar My::Class7->meta() before extends} );
ok( My::Class7->meta()->meta()->does_role('Role::Foo'),
- q{... and My::Class7->meta() does Role::Foo because it extends My::Class} );
+ q{... and My::Class7->meta() does Role::Foo because My::Class7 extends My::Class} );
+}
+
+{
+ package My::Class8;
+ use Moose;
+
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class8',
+ class_metaroles => {
+ class => ['Role::Bar'],
+ attribute => ['Role::Bar'],
+ },
+ );
+
+ extends 'My::Class';
+}
+
+{
+ ok( My::Class8->meta()->meta()->does_role('Role::Bar'),
+ q{apply Role::Bar My::Class8->meta() before extends} );
+ ok( My::Class8->meta()->meta()->does_role('Role::Foo'),
+ q{... and My::Class8->meta() does Role::Foo because My::Class8 extends My::Class} );
+ ok( My::Class8->meta()->attribute_metaclass->meta()->does_role('Role::Bar'),
+ q{apply Role::Bar to My::Class8->meta()->attribute_metaclass before extends} );
+ ok( My::Class8->meta()->attribute_metaclass->meta()->does_role('Role::Foo'),
+ q{... and My::Class8->meta()->attribute_metaclass does Role::Foo because My::Class8 extends My::Class} );
+}
+
+
+{
+ package My::Class9;
+ use Moose;
+
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class9',
+ class_metaroles => { attribute => ['Role::Bar'] },
+ );
+
+ extends 'My::Class';
+}
+
+{
+ ok( My::Class9->meta()->meta()->does_role('Role::Foo'),
+ q{... and My::Class9->meta() does Role::Foo because My::Class9 extends My::Class} );
+ ok( My::Class9->meta()->attribute_metaclass->meta()->does_role('Role::Bar'),
+ q{apply Role::Bar to My::Class9->meta()->attribute_metaclass before extends} );
+ ok( My::Class9->meta()->attribute_metaclass->meta()->does_role('Role::Foo'),
+ q{... and My::Class9->meta()->attribute_metaclass does Role::Foo because My::Class9 extends My::Class} );
+}
+
+# This tests applying meta roles to a metaclass's metaclass. This is
+# completely insane, but is exactly what happens with
+# Fey::Meta::Class::Table. It's a subclass of Moose::Meta::Class
+# itself, and then it _uses_ MooseX::ClassAttribute, so the metaclass
+# for Fey::Meta::Class::Table does a role.
+#
+# At one point this caused a metaclass incompatibility error down
+# below, when we applied roles to the metaclass of My::Class10. It's
+# all madness but as long as the tests pass we're happy.
+{
+ package My::Meta::Class2;
+ use Moose;
+ extends 'Moose::Meta::Class';
+
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Meta::Class2',
+ class_metaroles => { class => ['Role::Foo'] },
+ );
+}
+
+{
+ package My::Object;
+ use Moose;
+ extends 'Moose::Object';
+}
+
+{
+ package My::Meta2;
+
+ use Moose::Exporter;
+ Moose::Exporter->setup_import_methods( also => 'Moose' );
+
+ sub init_meta {
+ shift;
+ my %p = @_;
+
+ Moose->init_meta(
+ %p,
+ metaclass => 'My::Meta::Class2',
+ base_class => 'My::Object',
+ );
+ }
+}
+
+{
+ package My::Class10;
+ My::Meta2->import;
+
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class10',
+ class_metaroles => { class => ['Role::Bar'] },
+ );
+}
+
+{
+ ok( My::Class10->meta()->meta()->meta()->does_role('Role::Foo'),
+ q{My::Class10->meta()->meta() does Role::Foo } );
+ ok( My::Class10->meta()->meta()->does_role('Role::Bar'),
+ q{My::Class10->meta()->meta() does Role::Bar } );
+ ok( My::Class10->meta()->isa('My::Meta::Class2'),
+ q{... and My::Class10->meta still isa(My::Meta::Class2)} );
+ ok( My::Class10->isa('My::Object'),
+ q{... and My::Class10 still isa(My::Object)} );
+}
+
+{
+ package My::Constructor;
+
+ use base 'Moose::Meta::Method::Constructor';
+}
+
+{
+ package My::Class11;
+
+ use Moose;
+
+ __PACKAGE__->meta->constructor_class('My::Constructor');
+
+ Moose::Util::MetaRole::apply_metaroles(
+ for => 'My::Class11',
+ class_metaroles => { class => ['Role::Foo'] },
+ );
+}
+
+{
+ ok( My::Class11->meta()->meta()->does_role('Role::Foo'),
+ q{My::Class11->meta()->meta() does Role::Foo } );
+ is( My::Class11->meta()->constructor_class, 'My::Constructor',
+ q{... and explicitly set constructor_class value is unchanged)} );
+}
+
+{
+ package ExportsMoose;
+
+ Moose::Exporter->setup_import_methods(
+ also => 'Moose',
+ );
+
+ sub init_meta {
+ shift;
+ my %p = @_;
+ Moose->init_meta(%p);
+ return Moose::Util::MetaRole::apply_metaroles(
+ for => $p{for_class},
+ # Causes us to recurse through init_meta, as we have to
+ # load MyMetaclassRole from disk.
+ class_metaroles => { class => [qw/MyMetaclassRole/] },
+ );
+ }
+}
+
+lives_ok {
+ package UsesExportedMoose;
+ ExportsMoose->import;
+} 'import module which loads a role from disk during init_meta';
+
+{
+ package Foo::Meta::Role;
+
+ use Moose::Role;
}
+
+{
+ package Foo::Role;
+
+ Moose::Exporter->setup_import_methods(
+ also => 'Moose::Role',
+ );
+
+ sub init_meta {
+ shift;
+ my %p = @_;
+
+ Moose::Role->init_meta(%p);
+
+ return Moose::Util::MetaRole::apply_metaroles(
+ for => $p{for_class},
+ role_metaroles => { method => ['Foo::Meta::Role'] },
+ );
+ }
+}
+
+{
+ package Role::Baz;
+
+ Foo::Role->import;
+
+ sub bla {}
+}
+
+{
+ package My::Class12;
+
+ use Moose;
+
+ with( 'Role::Baz' );
+}
+
+{
+ ok(
+ My::Class12->meta->does_role( 'Role::Baz' ),
+ 'role applied'
+ );
+
+ my $method = My::Class12->meta->get_method( 'bla' );
+ ok(
+ $method->meta->does_role( 'Foo::Meta::Role' ),
+ 'method_metaclass_role applied'
+ );
+}
+
+{
+ package Parent;
+ use Moose;
+
+ Moose::Util::MetaRole::apply_metaroles(
+ for => __PACKAGE__,
+ class_metaroles => { constructor => ['Role::Foo'] },
+ );
+}
+
+{
+ package Child;
+
+ use Moose;
+ extends 'Parent';
+}
+
+{
+ ok(
+ Parent->meta->constructor_class->meta->can('does_role')
+ && Parent->meta->constructor_class->meta->does_role('Role::Foo'),
+ 'Parent constructor class has metarole from Parent'
+ );
+
+ ok(
+ Child->meta->constructor_class->meta->can('does_role')
+ && Child->meta->constructor_class->meta->does_role(
+ 'Role::Foo'),
+ 'Child constructor class has metarole from Parent'
+ );
+}
+
+done_testing;