break roles attic/dumb_roles
Yuval Kogman [Wed, 2 Aug 2006 14:36:44 +0000 (14:36 +0000)]
lib/Moose/Meta/Role.pm
t/034_attribute_does.t
t/046_role_requires.t [new file with mode: 0644]

index 3343dbd..722d76d 100644 (file)
@@ -1,4 +1,3 @@
-
 package Moose::Meta::Role;
 
 use strict;
@@ -171,39 +170,118 @@ sub get_method_list {
 
 # attributes
 
+my $id;
 sub add_attribute {
-    my $self = shift;
-    my $name = shift;
-    my $attr_desc;
-    if (scalar @_ == 1 && ref($_[0]) eq 'HASH') {
-        $attr_desc = $_[0];
-    }
-    else {
-        $attr_desc = { @_ };
-    }
-    $self->get_attribute_map->{$name} = $attr_desc;
+    my $self      = shift;
+    # either we have an attribute object already
+    # or we need to create one from the args provided
+    require Moose::Meta::Attribute;
+    my $attribute = blessed($_[0]) ? $_[0] : $self->_role_meta->attribute_metaclass->new(@_);
+    # make sure it is derived from the correct type though
+    ($attribute->isa('Class::MOP::Attribute'))
+        || confess "Your attribute must be an instance of Class::MOP::Attribute (or a subclass)";    
+    $attribute->attach_to_class($self->_role_meta);
+    # FIXME attribute vs. method shadowing # $attribute->install_accessors();
+    $self->get_attribute_map->{$attribute->name} = $attribute;
+
+    $attribute->{__id} ||= ++$id;
+
+    $self->remove_required_methods(
+        grep { defined }
+            map { $attribute->$_ }
+                qw/accessor reader writer/
+    );
+
+       # FIXME
+       # in theory we have to tell everyone the slot structure may have changed
 }
 
 sub has_attribute {
-    my ($self, $name) = @_;
-    exists $self->get_attribute_map->{$name} ? 1 : 0;
-}
+    my ($self, $attribute_name) = @_;
+    (defined $attribute_name && $attribute_name)
+        || confess "You must define an attribute name";
+    exists $self->get_attribute_map->{$attribute_name} ? 1 : 0;    
+} 
 
 sub get_attribute {
-    my ($self, $name) = @_;
-    $self->get_attribute_map->{$name}
+    my ($self, $attribute_name) = @_;
+    (defined $attribute_name && $attribute_name)
+        || confess "You must define an attribute name";
+    return $self->get_attribute_map->{$attribute_name} 
+        if $self->has_attribute($attribute_name);   
+    return; 
 }
 
 sub remove_attribute {
-    my ($self, $name) = @_;
-    delete $self->get_attribute_map->{$name}
-}
+    my ($self, $attribute_name) = @_;
+    (defined $attribute_name && $attribute_name)
+        || confess "You must define an attribute name";
+    my $removed_attribute = $self->get_attribute_map->{$attribute_name};    
+    return unless defined $removed_attribute;
+    delete $self->get_attribute_map->{$attribute_name};        
+    $removed_attribute->remove_accessors(); 
+    $removed_attribute->detach_from_class();
+    return $removed_attribute;
+} 
 
 sub get_attribute_list {
     my ($self) = @_;
     keys %{$self->get_attribute_map};
 }
 
+sub compute_all_applicable_attributes {
+    my $self = shift;
+    my @attrs;
+
+    my %attrs;
+
+    foreach my $role (@{ $self->get_roles() }) {
+        foreach my $attr ( $role->compute_all_applicable_attributes ) {
+            push @{ $attrs{$attr->name} ||= [] }, $attr;
+        }
+    }
+
+    # remove all conflicting attributes
+    foreach my $attr_name ( keys %attrs ) {
+        if ( @{ $attrs{$attr_name} } == 1 ) {
+            $attrs{$attr_name} = $attrs{$attr_name}[0];
+        } else {
+            delete $attrs{$attr_name};
+        }
+    }
+
+    # overlay our own attributes
+    my @local_attr_list = $self->get_attribute_list;
+    @attrs{@local_attr_list} = map { $self->get_attribute($_) } @local_attr_list;
+
+    return values %attrs;
+}
+
+sub find_attribute_by_name {
+    my ($self, $attr_name) = @_;
+    # keep a record of what we have seen
+    # here, this will handle all the 
+    # inheritence issues because we are 
+    # using the &class_precedence_list
+
+    if ( $self->has_attribute( $attr_name ) ) {
+        return $self->get_attribute( $attr_name );
+    } else {
+        my @found;
+        foreach my $role ( @{ $self->get_roles } ) {
+            if ( my $attr = $role->find_attribute_by_name ) {
+                push @found, $attr;
+            }
+        }
+
+        if ( @found == 1 ) {
+            # there's no conflict
+            return $found[0];
+        } else {
+            return;
+        }
+    }
+}
 
 ## applying a role to a class ...
 
@@ -233,6 +311,10 @@ sub _check_required_methods {
     # attribute accessors. However I am thinking 
     # that maybe those are somehow exempt from 
     # the require methods stuff.  
+
+    ### FIXME
+    # attributes' accessors are not being treated as first class methods as far as role composition is concerned.
+
     foreach my $required_method_name ($self->get_required_method_list) {
         
         unless ($other->find_method_by_name($required_method_name)) {
@@ -249,16 +331,18 @@ sub _check_required_methods {
 
 sub _apply_attributes {
     my ($self, $other) = @_;    
-    foreach my $attribute_name ($self->get_attribute_list) {
+    foreach my $attr ($self->compute_all_applicable_attributes) {
         # it if it has one already
-        if ($other->has_attribute($attribute_name) &&
-            # make sure we haven't seen this one already too
-            $other->get_attribute($attribute_name) != $self->get_attribute($attribute_name)) {
+        my $other_attr = $other->find_attribute_by_name($attr->name);
+
+        # __id is a hack to allow cloned attrs to compare as equal
+        if ( $other_attr && !(( exists ($other_attr->{__id}) && exists($other_attr->{__id}) && $other_attr->{__id} == $attr->{__id} ) || $other_attr == $attr ) ) { 
             # see if we are being composed  
             # into a role or not
             if ($other->isa('Moose::Meta::Role')) {                
                 # all attribute conflicts between roles 
                 # result in an immediate fatal error 
+                # FIXME - do they
                 confess "Role '" . $self->name . "' has encountered an attribute conflict " . 
                         "during composition. This is fatal error and cannot be disambiguated.";
             }
@@ -270,10 +354,8 @@ sub _apply_attributes {
             }
         }
         else {
-            $other->add_attribute(
-                $attribute_name,
-                $self->get_attribute($attribute_name)
-            );
+            my $clone = $attr->meta->clone_object( $attr );
+            $other->add_attribute( $clone );
         }
     }    
 }
@@ -282,9 +364,8 @@ sub _apply_methods {
     my ($self, $other) = @_;   
     foreach my $method_name ($self->get_method_list) {
         # it if it has one already
-        if ($other->has_method($method_name) &&
-            # and if they are not the same thing ...
-            $other->get_method($method_name) != $self->get_method($method_name)) {
+        my $other_method = $other->find_method_by_name($method_name);
+        if ($other_method && $other_method != $self->get_method($method_name)) {
             # see if we are composing into a role
             if ($other->isa('Moose::Meta::Role')) { 
                 # method conflicts between roles result 
index 06dd05f..c1749a6 100644 (file)
@@ -18,6 +18,11 @@ BEGIN {
     # we create a type constraint for 
     # it, just as we do for isa()
     has 'bar' => (is => 'rw', does => 'Bar::Role'); 
+    
+    package Foo::Class;
+    use Moose;
+    
+    with 'Foo::Role';
 
     package Bar::Role;
     use Moose::Role;
@@ -26,11 +31,6 @@ BEGIN {
     # if it does work... then the does() check is actually not needed 
     # since the isa() check will imply the does() check    
     has 'foo' => (is => 'rw', isa => 'Foo::Class', does => 'Foo::Role');    
-    
-    package Foo::Class;
-    use Moose;
-    
-    with 'Foo::Role';
 
     package Bar::Class;
     use Moose;
diff --git a/t/046_role_requires.t b/t/046_role_requires.t
new file mode 100644 (file)
index 0000000..8b7bde9
--- /dev/null
@@ -0,0 +1,485 @@
+#!/usr/bin/perl
+
+# FIXME
+## Add variants for everything
+## with delegation
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+use Test::Exception;
+
+# a role requires methods
+# this requirement can be satisfied by a matrix multiplication:
+# either via an attribute (an accessor or a delegation) 
+# or with an "actual" method
+# which is provided via either the class, a base class, or a role
+
+###########################
+# Keep the noise level down
+no warnings 'redefine';
+sub Test::Builder::diag { }
+###########################
+
+
+{
+    package Role::Requires;
+    use Moose::Role;
+
+    requires "foo";
+
+    package Role::Provides;
+    use Moose::Role;
+
+    sub foo { __PACKAGE__ . "::foo" }
+
+    package Class::Provides;
+    use Moose;
+
+    sub foo { __PACKAGE__ . "::foo" }
+}
+
+{
+    package Class::Provider::class::Type::method::WithRole::requires;
+    use Moose;
+    
+    sub foo { __PACKAGE__ . "::method::foo" }
+
+    ::lives_ok { with 'Role::Requires' } "composed role that requires foo";
+
+    ::ok( __PACKAGE__->does('Role::Requires'), "package does role" );
+
+    ::is( __PACKAGE__->new->foo, __PACKAGE__ . "::method::foo", "foo method is from this class" );
+}
+
+{
+    package Class::Provider::class::Type::method::WithRole::provides;
+    use Moose;
+    
+    sub foo { __PACKAGE__ . "::method::foo" }
+
+    ::lives_ok { with 'Role::Provides' } "composed role that provides foo";
+    
+    ::ok( __PACKAGE__->does('Role::Provides'), "package does role" );
+
+    ::is( __PACKAGE__->new->foo, __PACKAGE__ . "::method::foo", "foo method is from this class" );
+}
+
+{
+    package Class::Provider::class::Type::attr::WithRole::requires;
+    use Moose;
+    
+    ::dies_ok { with 'Role::Requires' } "can't compose the role that requires foo yet";
+    
+    has foo => (
+        isa => "Str",
+        is  => "rw",
+        default => __PACKAGE__ . "::attr::foo",
+    );
+
+    ::lives_ok { with 'Role::Requires' } "composed role that requires foo";
+
+    ::ok( __PACKAGE__->does('Role::Requires'), "package does role" );
+
+    ::is( __PACKAGE__->new->foo, __PACKAGE__ . "::attr::foo", "foo method is from an attr in this class" );
+}
+
+{
+    package Class::Provider::class::Type::attr::WithRole::provides;
+    use Moose;
+    
+    has foo => (
+        isa => "Str",
+        is  => "rw",
+        default => __PACKAGE__ . "::attr::foo",
+    );    
+    
+    ::lives_ok { with 'Role::Provides' } "composed role that provides foo";
+    
+    ::ok( __PACKAGE__->does('Role::Provides'), "package does role" );
+
+    ::is( __PACKAGE__->new->foo, __PACKAGE__ . "::attr::foo", "foo method is from an attr in this class" );
+}
+
+{
+    package Class::Provider::baseclass::Type::method::WithRole::requires;
+    use Moose;
+
+    ::dies_ok { with 'Role::Requires' } "can't compose the role that requires foo yet";
+
+    extends "Class::Provides";
+
+    ::lives_ok { with 'Role::Requires' } "composed role that requires foo, foo satisfied by base class";
+    ::ok( __PACKAGE__->does('Role::Requires'), "package does role" );
+
+    ::is( __PACKAGE__->new->foo, "Class::Provides::foo", "method came from base class" );
+}
+
+{
+    package Class::Provider::baseclass::Type::method::WithRole::provides;
+    use Moose;
+    
+    extends "Class::Provides";
+
+    ::lives_ok { with 'Role::Provides' } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provides'), "package does role" );
+
+    ::is( __PACKAGE__->new->foo, "Class::Provides::foo", "role didn't overwrite method from base class" );
+}
+
+
+{
+    package Role::Provider::role::Type::method::WithRole::requires;
+    use Moose::Role;
+
+    with "Role::Requires";
+
+    sub foo { __PACKAGE__ . "::method::foo" }
+}
+
+{
+    package Role::Provider::role::Type::attr::WithRole::requires;
+    use Moose::Role;
+
+    with "Role::Requires";
+
+    has foo => (
+        isa => "Str",
+        is  => "rw",
+        default => __PACKAGE__ . "::attr::foo",
+    );
+}
+
+{
+    package Role::Provider::role::Type::attr::WithRole::provides;
+    use Moose::Role;
+
+    with "Role::Provides";
+
+    has foo => (
+        isa => "Str",
+        is  => "rw",
+        default => __PACKAGE__ . "::attr::foo",
+    );
+}
+
+
+{
+    package Role::Provider::baserole::Type::method;
+    use Moose::Role;
+
+    with "Role::Provides";
+}
+
+
+{
+    package Role::Provider::baserole::Type::attr::WithRole::requires::base;
+    use Moose::Role;
+
+    with "Role::Requires";
+
+    has foo => (
+        isa => "Str",
+        is  => "rw",
+        default => __PACKAGE__ . "::attr::foo",
+    ); 
+
+    package Role::Provider::baserole::Type::attr::WithRole::requires;
+    use Moose::Role;
+
+    with "Role::Provider::baserole::Type::attr::WithRole::requires::base";
+
+    package Role::Provider::baserole::Type::attr::WithRole::requires::overridden;
+    use Moose::Role;
+
+    with "Role::Provider::baserole::Type::attr::WithRole::requires::base";
+
+    sub foo { __PACKAGE__ . "::method::foo" }
+}
+
+{
+    package Role::Provider::baserole::Type::method::WithRole::requires::base;
+    use Moose::Role;
+
+    with "Role::Requires";
+
+    sub foo { __PACKAGE__ . "::method::foo" }
+
+    package Role::Provider::baserole::Type::method::WithRole::requires::overridden;
+    use Moose::Role;
+
+    with "Role::Provider::baserole::Type::method::WithRole::requires::base";
+
+    has foo => (
+        isa => "Str",
+        is  => "rw",
+        default => __PACKAGE__ . "::attr::foo",
+    );
+}
+
+##############################
+
+
+{
+    package Role::Provider::role::Type::method::WithRole::requires::ClassWithNothing;
+    use Moose;
+    
+    ::lives_ok { with "Role::Provider::role::Type::method::WithRole::requires" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provider::role::Type::method::WithRole::requires'), "package does role" );
+    ::ok( __PACKAGE__->does('Role::Requires'), "package does base role" );
+
+    ::is(  __PACKAGE__->new->foo, "Role::Provider::role::Type::method::WithRole::requires::method::foo", "foo was defined in the role" );
+}
+
+{
+    package Role::Provider::role::Type::method::WithRole::requires::ClassWithMethod;
+    use Moose;
+
+    ::lives_ok { with "Role::Provider::role::Type::method::WithRole::requires" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Requires'), "package does role" );
+
+    sub foo { __PACKAGE__ . "::method::foo" }
+
+    ::is(  __PACKAGE__->new->foo, __PACKAGE__ . "::method::foo", "foo was defined in the class" );
+}
+
+{
+    package Role::Provider::role::Type::method::WithRole::requires::ClassWithBase;
+    use Moose;
+
+    extends "Class::Provides";
+
+    ::lives_ok { with "Role::Provider::role::Type::method::WithRole::requires" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Requires'), "package does role" );
+
+    ::is(  __PACKAGE__->new->foo, "Class::Provides::foo", "foo was defined in the base class" );
+}
+
+##
+
+{
+    package Role::Provider::role::Type::attr::WithRole::requires::ClassWithNothing;
+    use Moose;
+
+    ::lives_ok { with "Role::Provider::role::Type::attr::WithRole::requires" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Requires'), "package does role" );
+
+    ::is( eval {  __PACKAGE__->new->foo }, "Role::Provider::role::Type::attr::WithRole::requires::attr::foo", "foo was defined in the class" );
+}
+
+{
+    package Role::Provider::role::Type::attr::WithRole::requires::ClassWithMethod;
+    use Moose;
+
+    ::lives_ok { with "Role::Provider::role::Type::attr::WithRole::requires" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Requires'), "package does role" );
+
+    sub foo { __PACKAGE__ . "::method::foo" }
+
+    local our $TODO = "Attribute vs. method shadowing not yet clear";
+    ::is(  __PACKAGE__->new->foo, __PACKAGE__ . "::method::foo", "foo was defined in the class" );
+}
+
+{
+    package Role::Provider::role::Type::attr::WithRole::requires::ClassWithBase;
+    use Moose;
+    
+    extends "Class::Provides";
+
+    ::lives_ok { with "Role::Provider::role::Type::attr::WithRole::requires" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Requires'), "package does role" );
+
+    local our $TODO = "Attribute vs. method shadowing not yet clear";
+    ::is(  __PACKAGE__->new->foo, "Class::Provides::foo", "foo was defined in the base class" );
+}
+
+##
+
+{
+    package Role::Provider::role::Type::attr::WithRole::provides::ClassWithNothing;
+    use Moose;
+    
+    ::lives_ok { with "Role::Provider::role::Type::attr::WithRole::provides" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provides'), "package does role" );
+
+    ::is(  __PACKAGE__->new->foo, "Role::Provider::role::Type::attr::WithRole::provides::attr::foo", "foo was defined in the role" );
+}
+
+{
+    package Role::Provider::role::Type::attr::WithRole::provides::ClassWithMethod;
+    use Moose;
+
+    ::lives_ok { with "Role::Provider::role::Type::attr::WithRole::provides" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provides'), "package does role" );
+
+    sub foo { __PACKAGE__ . "::method::foo" }
+
+    local our $TODO = "Attribute vs. method shadowing not yet clear";
+    ::is(  __PACKAGE__->new->foo, __PACKAGE__ . "::method::foo", "foo was defined in the class" );
+}
+
+{
+    package Role::Provider::role::Type::attr::WithRole::provides::ClassWithBase;
+    use Moose;
+    
+    extends "Class::Provides";
+
+    ::lives_ok { with "Role::Provider::role::Type::attr::WithRole::provides" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provides'), "package does role" );
+
+    local our $TODO = "Attribute vs. method shadowing not yet clear";
+    ::is(  __PACKAGE__->new->foo, "Class::Provides::foo", "foo was defined in the base class" );
+}
+
+##
+
+{
+    package Role::Provider::baserole::Type::method::ClassWithNothing;
+    use Moose;
+    
+    ::lives_ok { with "Role::Provider::baserole::Type::method" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provides'), "package does role" );
+
+    ::is(  __PACKAGE__->new->foo, "Role::Provides::foo", "foo was defined in the base role" );
+}
+
+{
+    package Role::Provider::baserole::Type::method::ClassWithMethod;
+    use Moose;
+
+    ::lives_ok { with "Role::Provider::baserole::Type::method" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provides'), "package does role" );
+
+    sub foo { __PACKAGE__ . "::method::foo" }
+
+    ::is(  __PACKAGE__->new->foo, __PACKAGE__ . "::method::foo", "foo was defined in the role" );
+}
+
+{
+    package Role::Provider::baserole::Type::method::ClassWithBase;
+    use Moose;
+    
+    extends "Class::Provides";
+
+    ::lives_ok { with "Role::Provider::baserole::Type::method" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provides'), "package does role" );
+
+    ::is(  __PACKAGE__->new->foo, "Class::Provides::foo", "foo was defined in the base class" );
+}
+
+##
+
+{
+    package Role::Provider::baserole::Type::attr::WithRole::requires::ClassWithNothing;
+    use Moose;
+    ::lives_ok { with "Role::Provider::baserole::Type::attr::WithRole::requires" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provider::baserole::Type::attr::WithRole::requires'), "package does role" );
+
+    ::is(  eval { __PACKAGE__->new->foo }, "Role::Provider::baserole::Type::attr::WithRole::requires::base::attr::foo", "foo was defined in the base role" );
+}
+
+{
+    package Role::Provider::baserole::Type::attr::WithRole::requires::ClassWithMethod;
+    use Moose;
+
+    ::lives_ok { with "Role::Provider::baserole::Type::attr::WithRole::requires" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provider::baserole::Type::attr::WithRole::requires'), "package does role" );
+
+    sub foo { __PACKAGE__ . "::method::foo" }
+
+    local our $TODO = "Attribute vs. method shadowing not yet clear";
+    ::is(  __PACKAGE__->new->foo, __PACKAGE__ . "::method::foo", "foo was defined in the class" );
+}
+
+{
+    package Role::Provider::baserole::Type::attr::WithRole::requires::ClassWithBase;
+    use Moose;
+
+    extends "Class::Provides";
+
+    ::lives_ok { with "Role::Provider::baserole::Type::attr::WithRole::requires" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provider::baserole::Type::attr::WithRole::requires'), "package does role" );
+
+    local our $TODO = "Attribute vs. method shadowing not yet clear";
+    ::is(  __PACKAGE__->new->foo, "Class::Provides::foo", "foo was defined in the base class" );
+}
+
+
+##
+
+{
+    package Role::Provider::baserole::Type::attr::WithRole::requires::overridden::ClassWithNothing;
+    use Moose;
+
+    ::lives_ok { with "Role::Provider::baserole::Type::attr::WithRole::requires::overridden" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provider::baserole::Type::attr::WithRole::requires::overridden'), "package does role" );
+
+    local our $TODO = "Attribute vs. method shadowing not yet clear";
+    ::is(  eval {  __PACKAGE__->new->foo}, "Role::Provider::baserole::Type::attr::WithRole::requires::overridden::method::foo", "foo was defined in the overridding role" );
+}
+
+{
+    package Role::Provider::baserole::Type::attr::WithRole::requires::overridden::ClassWithMethod;
+    use Moose;
+
+    ::lives_ok { with "Role::Provider::baserole::Type::attr::WithRole::requires::overridden" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provider::baserole::Type::attr::WithRole::requires::overridden'), "package does role" );
+
+    sub foo { __PACKAGE__ . "::method::foo" }
+
+    local our $TODO = "Attribute vs. method shadowing not yet clear";
+    ::is(  __PACKAGE__->new->foo, __PACKAGE__ . "::method::foo", "foo was defined in the class" );
+}
+
+{
+    package Role::Provider::baserole::Type::attr::WithRole::requires::overridden::ClassWithBase;
+    use Moose;
+
+    extends "Class::Provides";
+
+    ::lives_ok { with "Role::Provider::baserole::Type::attr::WithRole::requires::overridden" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provider::baserole::Type::attr::WithRole::requires::overridden'), "package does role" );
+
+    local our $TODO = "Attribute vs. method shadowing not yet clear";
+    ::is(  __PACKAGE__->new->foo, "Class::Provides::foo", "foo was defined in the base class" );
+}
+
+##
+
+{
+    package Role::Provider::baserole::Type::method::WithRole::requires::overridden::ClassWithNothing;
+    use Moose;
+
+    ::lives_ok { with "Role::Provider::baserole::Type::method::WithRole::requires::overridden" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provider::baserole::Type::method::WithRole::requires::overridden'), "package does role" );
+
+    ::is(  __PACKAGE__->new->foo, "Role::Provider::baserole::Type::method::WithRole::requires::overridden::attr::foo", "foo was defined in the overriding role" );
+}
+
+{
+    package Role::Provider::baserole::Type::method::WithRole::requires::overridden::ClassWithMethod;
+    use Moose;
+
+    ::lives_ok { with "Role::Provider::baserole::Type::method::WithRole::requires::overridden" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provider::baserole::Type::method::WithRole::requires::overridden'), "package does role" );
+
+    sub foo { __PACKAGE__ . "::method::foo" }
+
+    local our $TODO = "Attribute vs. method shadowing not yet clear";
+    ::is(  __PACKAGE__->new->foo, __PACKAGE__ . "::method::foo", "foo was defined in the class" );
+}
+
+{
+    package Role::Provider::baserole::Type::method::WithRole::requires::overridden::ClassWithBase;
+    use Moose;
+
+    extends "Class::Provides";
+
+    ::lives_ok { with "Role::Provider::baserole::Type::method::WithRole::requires::overridden" } "composed role that provides foo";
+    ::ok( __PACKAGE__->does('Role::Provider::baserole::Type::method::WithRole::requires::overridden'), "package does role" );
+
+    local our $TODO = "Attribute vs. method shadowing not yet clear";
+    ::is(  __PACKAGE__->new->foo, "Class::Provides::foo", "foo was defined in the base class" );
+}