Implemented Mouse::Role->does; modified Mouse::Meta::Class->initialise
Nick Woolley [Fri, 27 Mar 2009 20:50:08 +0000 (20:50 +0000)]
to allow use as an instance method to make this work.

Implemented Mouse::Role::override and ::super. To do this, added
Mouse::Meta::Class->add_override_method_modifier,

Implemented throwing stubs for Mouse::Role::augment and ::inner, as in
Moose::Role.

Added 020_roles/ tests from latest respoitory version of Moose.
Modified some tests to pass; the rest have been moved to
020_roles/failing for later examination.

Implemented Mouse::Role->does_role, from Moose.  This does not yet
quite seem to pass all the tests it should, not sure why.

Fixed bug in Mouse::Meta::Role->apply and ->combine_apply, so that
030_roles/002_role.t tests pass.

Implemented ->version, ->authority and ->identifier in Mouse/Utils.pm,
imported for use as methods by Mouse::Meta::Role and
Mouse::Meta::Class.

Tweaked .gitignore.

"make test" passes all tests, including the new ones.

38 files changed:
.gitignore
lib/Mouse/Meta/Class.pm
lib/Mouse/Meta/Role.pm
lib/Mouse/Role.pm
lib/Mouse/Util.pm
t/030_roles/002_role.t [new file with mode: 0755]
t/030_roles/003_apply_role.t [new file with mode: 0755]
t/030_roles/019_build.t [new file with mode: 0644]
t/030_roles/031_roles_applied_in_create.t [new file with mode: 0644]
t/030_roles/failing/001_meta_role.t [new file with mode: 0755]
t/030_roles/failing/004_role_composition_errors.t [new file with mode: 0644]
t/030_roles/failing/005_role_conflict_detection.t [new file with mode: 0755]
t/030_roles/failing/006_role_exclusion.t [new file with mode: 0644]
t/030_roles/failing/007_roles_and_req_method_edge_cases.t [new file with mode: 0644]
t/030_roles/failing/008_role_conflict_edge_cases.t [new file with mode: 0644]
t/030_roles/failing/009_more_role_edge_cases.t [new file with mode: 0644]
t/030_roles/failing/010_run_time_role_composition.t [new file with mode: 0644]
t/030_roles/failing/011_overriding.t [new file with mode: 0644]
t/030_roles/failing/012_method_exclusion_in_composition.t [new file with mode: 0644]
t/030_roles/failing/013_method_aliasing_in_composition.t [new file with mode: 0644]
t/030_roles/failing/014_more_alias_and_exclude.t [new file with mode: 0644]
t/030_roles/failing/015_runtime_roles_and_attrs.t [new file with mode: 0644]
t/030_roles/failing/016_runtime_roles_and_nonmoose.t [new file with mode: 0644]
t/030_roles/failing/017_extending_role_attrs.t [new file with mode: 0644]
t/030_roles/failing/018_runtime_roles_w_params.t [new file with mode: 0644]
t/030_roles/failing/020_role_composite.t [new file with mode: 0644]
t/030_roles/failing/021_role_composite_exclusion.t [new file with mode: 0644]
t/030_roles/failing/022_role_composition_req_methods.t [new file with mode: 0644]
t/030_roles/failing/023_role_composition_attributes.t [new file with mode: 0644]
t/030_roles/failing/024_role_composition_methods.t [new file with mode: 0644]
t/030_roles/failing/025_role_composition_override.t [new file with mode: 0644]
t/030_roles/failing/026_role_composition_method_mods.t [new file with mode: 0644]
t/030_roles/failing/032_roles_and_method_cloning.t [new file with mode: 0644]
t/030_roles/failing/033_role_exclusion_and_alias_bug.t [new file with mode: 0644]
t/030_roles/failing/034_create_role.t [new file with mode: 0644]
t/030_roles/failing/035_anonymous_roles.t [new file with mode: 0644]
t/030_roles/failing/036_free_anonymous_roles.t [new file with mode: 0644]
t/030_roles/failing/037_create_role_subclass.t [new file with mode: 0644]

index c2fe79f..858eae4 100644 (file)
@@ -1,10 +1,11 @@
 META.yml
 Makefile
-blib/
-inc/
+blib/*
+inc/*
 *.sw[po]
 pm_to_blib
 MANIFEST
 MANIFEST.bak
 SIGNATURE
 lib/Mouse/Tiny.pm
+*~
\ No newline at end of file
index 8e89567..0eb22ec 100644 (file)
@@ -5,7 +5,7 @@ use warnings;
 use Mouse::Meta::Method::Constructor;
 use Mouse::Meta::Method::Destructor;
 use Scalar::Util qw/blessed/;
-use Mouse::Util qw/get_linear_isa/;
+use Mouse::Util qw/get_linear_isa version authority identifier/;
 use Carp 'confess';
 
 do {
@@ -20,8 +20,9 @@ do {
     }
 
     sub initialize {
-        my $class = shift;
-        my $name  = shift;
+        my $class = blessed($_[0]) || $_[0];
+        my $name  = $_[1];
+
         $METACLASS_CACHE{$name} = $class->new(name => $name)
             if !exists($METACLASS_CACHE{$name});
         return $METACLASS_CACHE{$name};
@@ -76,7 +77,7 @@ my $get_methods_for_class = sub {
     no strict 'refs';
     # Get all the CODE symbol table entries
     my @functions =
-      grep !/^(?:has|with|around|before|after|blessed|extends|confess|override|super)$/,
+      grep !/^(?:has|with|around|before|after|augment|inner|blessed|extends|confess|override|super)$/,
       grep { defined &{"${name}::$_"} }
       keys %{"${name}::"};
     push @functions, keys %{$self->{'methods'}->{$name}} if $self;
@@ -260,6 +261,22 @@ sub add_after_method_modifier {
     $self->_install_modifier( $self->name, 'after', $name, $code );
 }
 
+sub add_override_method_modifier {
+    my ($self, $name, $code) = @_;
+
+    my $pkg = $self->name;
+    my $method = "${pkg}::${name}";
+
+    # Class::Method::Modifiers won't do this for us, so do it ourselves
+
+    my $body = $pkg->can($name)
+        or confess "You cannot override '$method' because it has no super method";
+
+    no strict 'refs';
+    *$method = sub { $code->($pkg, $body, @_) };
+}
+
+
 sub roles { $_[0]->{roles} }
 
 sub does_role {
index e82690d..f7ebd21 100644 (file)
@@ -2,7 +2,7 @@ package Mouse::Meta::Role;
 use strict;
 use warnings;
 use Carp 'confess';
-use Mouse::Util;
+use Mouse::Util qw(version authority identifier);
 
 do {
     my %METACLASS_CACHE;
@@ -43,6 +43,8 @@ sub add_required_methods {
     push @{$self->{required_methods}}, @methods;
 }
 
+
+
 sub add_attribute {
     my $self = shift;
     my $name = shift;
@@ -88,16 +90,18 @@ sub apply {
         for my $name ($self->get_method_list) {
             next if $name eq 'meta';
 
-            if ($classname->can($name)) {
+            my $class_function = "${classname}::${name}";
+            my $role_function = "${selfname}::${name}";
+            if (defined &$class_function) {
                 # XXX what's Moose's behavior?
                 #next;
             } else {
-                *{"${classname}::${name}"} = *{"${selfname}::${name}"};
+                *$class_function = *$role_function;
             }
             if ($args{alias} && $args{alias}->{$name}) {
                 my $dstname = $args{alias}->{$name};
                 unless ($classname->can($dstname)) {
-                    *{"${classname}::${dstname}"} = *{"${selfname}::${name}"};
+                    *{"${classname}::${dstname}"} = *$role_function;
                 }
             }
         }
@@ -133,7 +137,7 @@ sub apply {
     }
 
     # XXX Room for speed improvement in role to role
-    for my $modifier_type (qw/before after around/) {
+    for my $modifier_type (qw/before after around override/) {
         my $add_method = "add_${modifier_type}_method_modifier";
         my $modified = $self->{"${modifier_type}_method_modifiers"};
 
@@ -177,16 +181,18 @@ sub combine_apply {
             for my $name ($self->get_method_list) {
                 next if $name eq 'meta';
 
-                if ($classname->can($name)) {
+                my $class_function = "${classname}::${name}";
+                my $role_function = "${selfname}::${name}";
+                if (defined &$class_function) {
                     # XXX what's Moose's behavior?
                     #next;
                 } else {
-                    *{"${classname}::${name}"} = *{"${selfname}::${name}"};
+                    *$class_function = *$role_function;
                 }
                 if ($args{alias} && $args{alias}->{$name}) {
                     my $dstname = $args{alias}->{$name};
                     unless ($classname->can($dstname)) {
-                        *{"${classname}::${dstname}"} = *{"${selfname}::${name}"};
+                        *{"${classname}::${dstname}"} = *$role_function;
                     }
                 }
             }
@@ -230,7 +236,7 @@ sub combine_apply {
     }
 
     # XXX Room for speed improvement in role to role
-    for my $modifier_type (qw/before after around/) {
+    for my $modifier_type (qw/before after around override/) {
         my $add_method = "add_${modifier_type}_method_modifier";
         for my $role_spec (@roles) {
             my $self = $role_spec->[0]->meta;
@@ -246,17 +252,17 @@ sub combine_apply {
 
     # append roles
     my %role_apply_cache;
-    my @apply_roles;
+    my $apply_roles = $class->roles;
     for my $role_spec (@roles) {
         my $self = $role_spec->[0]->meta;
-        push @apply_roles, $self unless $role_apply_cache{$self}++;
-        for my $role ($self->roles) {
-            push @apply_roles, $role unless $role_apply_cache{$role}++;
+        push @$apply_roles, $self unless $role_apply_cache{$self}++;
+        for my $role (@{ $self->roles }) {
+            push @$apply_roles, $role unless $role_apply_cache{$role}++;
         }
     }
 }
 
-for my $modifier_type (qw/before after around/) {
+for my $modifier_type (qw/before after around override/) {
     no strict 'refs';
     *{ __PACKAGE__ . '::' . "add_${modifier_type}_method_modifier" } = sub {
         my ($self, $method_name, $method) = @_;
@@ -273,5 +279,23 @@ for my $modifier_type (qw/before after around/) {
 
 sub roles { $_[0]->{roles} }
 
+
+# This is currently not passing all the Moose tests.
+sub does_role {
+    my ($self, $role_name) = @_;
+
+    (defined $role_name)
+        || confess "You must supply a role name to look for";
+
+    # if we are it,.. then return true
+    return 1 if $role_name eq $self->name;
+
+    for my $role (@{ $self->{roles} }) {
+        return 1 if $role->does_role($role_name);
+    }
+    return 0;
+}
+
+
 1;
 
index b57169d..efd7f95 100644 (file)
@@ -3,12 +3,12 @@ use strict;
 use warnings;
 use base 'Exporter';
 
-use Carp 'confess';
+use Carp 'confess', 'croak';
 use Scalar::Util 'blessed';
 
 use Mouse::Meta::Role;
 
-our @EXPORT = qw(before after around has extends with requires excludes confess blessed);
+our @EXPORT = qw(before after around super override inner augment has extends with requires excludes confess blessed);
 
 sub before {
     my $meta = Mouse::Meta::Role->initialize(caller);
@@ -37,6 +37,42 @@ sub around {
     }
 }
 
+
+sub super {
+    return unless $Mouse::SUPER_BODY; 
+    $Mouse::SUPER_BODY->(@Mouse::SUPER_ARGS);
+}
+
+sub override {
+    my $classname = caller;
+    my $meta = Mouse::Meta::Role->initialize($classname);
+
+    my $name = shift;
+    my $code = shift;
+    my $fullname = "${classname}::${name}";
+
+    defined &$fullname
+        && confess "Cannot add an override of method '$fullname' " .
+                   "because there is a local version of '$fullname'";
+
+    $meta->add_override_method_modifier($name => sub {
+        local $Mouse::SUPER_PACKAGE = shift;
+        local $Mouse::SUPER_BODY = shift;
+        local @Mouse::SUPER_ARGS = @_;
+
+        $code->(@_);
+    });
+}
+
+# We keep the same errors messages as Moose::Role emits, here.
+sub inner {
+    croak "Moose::Role cannot support 'inner'";
+}
+
+sub augment {
+    croak "Moose::Role cannot support 'augment'";
+}
+
 sub has {
     my $meta = Mouse::Meta::Role->initialize(caller);
 
@@ -127,6 +163,22 @@ L<Class::Method::Modifiers/after>.
 Sets up an "around" method modifier. See L<Moose/around> or
 L<Class::Method::Modifiers/around>.
 
+=item B<super>
+
+Sets up the "super" keyword. See L<Moose/super>.
+
+=item B<override ($name, &sub)>
+
+Sets up an "override" method modifier. See L<Moose/Role/override>.
+
+=item B<inner>
+
+This is not supported and emits an error. See L<Moose/Role>.
+
+=item B<augment ($name, &sub)>
+
+This is not supported and emits an error. See L<Moose/Role>.
+
 =head2 has (name|names) => parameters
 
 Sets up an attribute (or if passed an arrayref of names, multiple attributes) to
index 53af4cf..8452299 100644 (file)
@@ -7,6 +7,9 @@ use Carp;
 our @EXPORT_OK = qw(
     get_linear_isa
     apply_all_roles
+    version 
+    authority
+    identifier
 );
 our %EXPORT_TAGS = (
     all  => \@EXPORT_OK,
@@ -53,6 +56,20 @@ BEGIN {
     *{ __PACKAGE__ . '::get_linear_isa'} = $impl;
 }
 
+{ # adapted from Class::MOP::Module
+
+    sub version { no strict 'refs'; ${shift->name.'::VERSION'} }
+    sub authority { no strict 'refs'; ${shift->name.'::AUTHORITY'} }  
+    sub identifier {
+        my $self = shift;
+        join '-' => (
+            $self->name,
+            ($self->version   || ()),
+            ($self->authority || ()),
+        );
+    }
+}
+
 # taken from Class/MOP.pm
 {
     my %cache;
diff --git a/t/030_roles/002_role.t b/t/030_roles/002_role.t
new file mode 100755 (executable)
index 0000000..afbe34e
--- /dev/null
@@ -0,0 +1,154 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 36;
+use Test::Exception;
+
+=pod
+
+NOTE:
+
+Should we be testing here that the has & override
+are injecting their methods correctly? In other 
+words, should 'has_method' return true for them?
+
+=cut
+
+{
+    package FooRole;
+    use Mouse::Role;
+    
+    our $VERSION = '0.01';
+    
+    has 'bar' => (is => 'rw', isa => 'Foo');
+    has 'baz' => (is => 'ro');    
+    
+    sub foo { 'FooRole::foo' }
+    sub boo { 'FooRole::boo' }    
+    
+    before 'boo' => sub { "FooRole::boo:before" };
+    
+    after  'boo' => sub { "FooRole::boo:after1"  }; 
+    after  'boo' => sub { "FooRole::boo:after2"  };        
+    
+    around 'boo' => sub { "FooRole::boo:around" };  
+    
+    override 'bling' => sub { "FooRole::bling:override" };   
+    override 'fling' => sub { "FooRole::fling:override" };  
+    
+    ::dies_ok { extends() } '... extends() is not supported';
+    ::dies_ok { augment() } '... augment() is not supported';    
+    ::dies_ok { inner()   } '... inner() is not supported';        
+
+    no Mouse::Role;
+}
+
+my $foo_role = FooRole->meta;
+isa_ok($foo_role, 'Mouse::Meta::Role');
+SKIP: { skip "Mouse: doesn't use Class::MOP" => 1;
+isa_ok($foo_role, 'Class::MOP::Module');
+}
+
+is($foo_role->name, 'FooRole', '... got the right name of FooRole');
+is($foo_role->version, '0.01', '... got the right version of FooRole'); 
+
+# methods ...
+
+TODO: { todo_skip "Mouse: not yet implemented" => 6;
+ok($foo_role->has_method('foo'), '... FooRole has the foo method'); 
+is($foo_role->get_method('foo')->body, \&FooRole::foo, '... FooRole got the foo method'); 
+
+isa_ok($foo_role->get_method('foo'), 'Mouse::Meta::Role::Method');
+
+ok($foo_role->has_method('boo'), '... FooRole has the boo method');
+is($foo_role->get_method('boo')->body, \&FooRole::boo, '... FooRole got the boo method');
+
+isa_ok($foo_role->get_method('boo'), 'Mouse::Meta::Role::Method');
+}
+
+is_deeply(
+    [ sort $foo_role->get_method_list() ],
+    [ 'boo', 'foo' ],
+    '... got the right method list');
+
+ok(FooRole->can('foo'), "locally defined methods are still there");
+ok(!FooRole->can('has'), "sugar was unimported");
+
+# attributes ...
+
+is_deeply(
+    [ sort $foo_role->get_attribute_list() ],
+    [ 'bar', 'baz' ],
+    '... got the right attribute list');
+
+ok($foo_role->has_attribute('bar'), '... FooRole does have the bar attribute');
+
+is_deeply(
+    $foo_role->get_attribute('bar'),
+    { is => 'rw', isa => 'Foo' },
+    '... got the correct description of the bar attribute');
+
+ok($foo_role->has_attribute('baz'), '... FooRole does have the baz attribute');
+
+is_deeply(
+    $foo_role->get_attribute('baz'),
+    { is => 'ro' },
+    '... got the correct description of the baz attribute');
+
+# method modifiers
+TODO: { todo_skip  "Mouse: not yet implemented" => 15;
+
+ok($foo_role->has_before_method_modifiers('boo'), '... now we have a boo:before modifier');
+is(($foo_role->get_before_method_modifiers('boo'))[0]->(), 
+    "FooRole::boo:before", 
+    '... got the right method back');
+
+is_deeply(
+    [ $foo_role->get_method_modifier_list('before') ],
+    [ 'boo' ],
+    '... got the right list of before method modifiers');
+
+ok($foo_role->has_after_method_modifiers('boo'), '... now we have a boo:after modifier');
+is(($foo_role->get_after_method_modifiers('boo'))[0]->(), 
+    "FooRole::boo:after1", 
+    '... got the right method back');
+is(($foo_role->get_after_method_modifiers('boo'))[1]->(), 
+    "FooRole::boo:after2", 
+    '... got the right method back');    
+
+is_deeply(
+    [ $foo_role->get_method_modifier_list('after') ],
+    [ 'boo' ],
+    '... got the right list of after method modifiers');
+    
+ok($foo_role->has_around_method_modifiers('boo'), '... now we have a boo:around modifier');
+is(($foo_role->get_around_method_modifiers('boo'))[0]->(), 
+    "FooRole::boo:around", 
+    '... got the right method back');
+
+is_deeply(
+    [ $foo_role->get_method_modifier_list('around') ],
+    [ 'boo' ],
+    '... got the right list of around method modifiers');
+
+
+## overrides
+
+ok($foo_role->has_override_method_modifier('bling'), '... now we have a bling:override modifier');
+is($foo_role->get_override_method_modifier('bling')->(), 
+    "FooRole::bling:override", 
+    '... got the right method back');
+
+ok($foo_role->has_override_method_modifier('fling'), '... now we have a fling:override modifier');
+is($foo_role->get_override_method_modifier('fling')->(), 
+    "FooRole::fling:override", 
+    '... got the right method back');
+
+is_deeply(
+    [ sort $foo_role->get_method_modifier_list('override') ],
+    [ 'bling', 'fling' ],
+    '... got the right list of override method modifiers');
+
+}
diff --git a/t/030_roles/003_apply_role.t b/t/030_roles/003_apply_role.t
new file mode 100755 (executable)
index 0000000..1ab9f3f
--- /dev/null
@@ -0,0 +1,190 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 86;
+use Test::Exception;
+
+{
+    package FooRole;
+    use Mouse::Role;
+
+    has 'bar' => ( is => 'rw', isa => 'FooClass' );
+    has 'baz' => ( is => 'ro' );
+
+    sub goo {'FooRole::goo'}
+    sub foo {'FooRole::foo'}
+
+    override 'boo' => sub { 'FooRole::boo -> ' . super() };
+#    sub boo { 'FooRole::boo -> ' . shift->SUPER::boo() }
+
+    around 'blau' => sub {
+        my $c = shift;
+        'FooRole::blau -> ' . $c->();
+    };
+}
+
+{
+    package BarRole;
+    use Mouse::Role;
+    sub woot {'BarRole::woot'}
+}
+
+{
+    package BarClass;
+    use Mouse;
+
+    sub boo {'BarClass::boo'}
+    sub foo {'BarClass::foo'}    # << the role overrides this ...
+}
+
+{
+    package FooClass;
+    use Mouse;
+
+    extends 'BarClass';
+    with 'FooRole';
+
+    sub blau {'FooClass::blau'}    # << the role wraps this ...
+
+    sub goo {'FooClass::goo'}      # << overrides the one from the role ...
+}
+
+{
+    package FooBarClass;
+    use Mouse;
+
+    extends 'FooClass';
+    with 'FooRole', 'BarRole';
+}
+
+my $foo_class_meta = FooClass->meta;
+isa_ok( $foo_class_meta, 'Mouse::Meta::Class' );
+
+my $foobar_class_meta = FooBarClass->meta;
+isa_ok( $foobar_class_meta, 'Mouse::Meta::Class' );
+
+dies_ok {
+    $foo_class_meta->does_role();
+}
+'... does_role requires a role name';
+
+dies_ok {
+    $foo_class_meta->add_role();
+}
+'... apply_role requires a role';
+
+dies_ok {
+    $foo_class_meta->add_role( bless( {} => 'Fail' ) );
+}
+'... apply_role requires a role';
+
+ok( $foo_class_meta->does_role('FooRole'),
+    '... the FooClass->meta does_role FooRole' );
+ok( !$foo_class_meta->does_role('OtherRole'),
+    '... the FooClass->meta !does_role OtherRole' );
+
+ok( $foobar_class_meta->does_role('FooRole'),
+    '... the FooBarClass->meta does_role FooRole' );
+ok( $foobar_class_meta->does_role('BarRole'),
+    '... the FooBarClass->meta does_role BarRole' );
+ok( !$foobar_class_meta->does_role('OtherRole'),
+    '... the FooBarClass->meta !does_role OtherRole' );
+
+foreach my $method_name (qw(bar baz foo boo blau goo)) {
+#    ok( $foo_class_meta->has_method($method_name), ## Mouse: no ->has_method
+    ok( FooClass->can($method_name),
+        '... FooClass has the method ' . $method_name );
+#    ok( $foobar_class_meta->has_method($method_name), ## Mouse: no ->has_method
+    ok( FooClass->can($method_name),
+        '... FooBarClass has the method ' . $method_name );
+}
+
+#ok( !$foo_class_meta->has_method('woot'), ## Mouse: no ->has_method
+ok( !FooClass->can('woot'),
+    '... FooClass lacks the method woot' );
+#ok( $foobar_class_meta->has_method('woot'), ## Mouse: no ->has_method
+ok( FooBarClass->can('woot'),
+    '... FooBarClass has the method woot' );
+
+foreach my $attr_name (qw(bar baz)) {
+    ok( $foo_class_meta->has_attribute($attr_name),
+        '... FooClass has the attribute ' . $attr_name );
+    ok( $foobar_class_meta->has_attribute($attr_name),
+        '... FooBarClass has the attribute ' . $attr_name );
+}
+
+can_ok( 'FooClass', 'does' );
+ok( FooClass->does('FooRole'),    '... the FooClass does FooRole' );
+ok( !FooClass->does('BarRole'),   '... the FooClass does not do BarRole' );
+ok( !FooClass->does('OtherRole'), '... the FooClass does not do OtherRole' );
+
+can_ok( 'FooBarClass', 'does' );
+ok( FooBarClass->does('FooRole'), '... the FooClass does FooRole' );
+ok( FooBarClass->does('BarRole'), '... the FooBarClass does FooBarRole' );
+ok( !FooBarClass->does('OtherRole'),
+    '... the FooBarClass does not do OtherRole' );
+
+my $foo = FooClass->new();
+isa_ok( $foo, 'FooClass' );
+
+my $foobar = FooBarClass->new();
+isa_ok( $foobar, 'FooBarClass' );
+
+is( $foo->goo,    'FooClass::goo', '... got the right value of goo' );
+is( $foobar->goo, 'FooRole::goo',  '... got the right value of goo' );
+
+is( $foo->boo, 'FooRole::boo -> BarClass::boo',
+    '... got the right value from ->boo' );
+is( $foobar->boo, 'FooRole::boo -> FooRole::boo -> BarClass::boo',
+    '... got the right value from ->boo (double wrapped)' );
+
+is( $foo->blau, 'FooRole::blau -> FooClass::blau',
+    '... got the right value from ->blau' );
+is( $foobar->blau, 'FooRole::blau -> FooRole::blau -> FooClass::blau',
+    '... got the right value from ->blau' );
+
+foreach my $foo ( $foo, $foobar ) {
+    can_ok( $foo, 'does' );
+    ok( $foo->does('FooRole'), '... an instance of FooClass does FooRole' );
+    ok( !$foo->does('OtherRole'),
+        '... and instance of FooClass does not do OtherRole' );
+
+    can_ok( $foobar, 'does' );
+    ok( $foobar->does('FooRole'),
+        '... an instance of FooBarClass does FooRole' );
+    ok( $foobar->does('BarRole'),
+        '... an instance of FooBarClass does BarRole' );
+    ok( !$foobar->does('OtherRole'),
+        '... and instance of FooBarClass does not do OtherRole' );
+
+    for my $method (qw/bar baz foo boo goo blau/) {
+        can_ok( $foo, $method );
+    }
+
+    is( $foo->foo, 'FooRole::foo', '... got the right value of foo' );
+
+    ok( !defined( $foo->baz ), '... $foo->baz is undefined' );
+    ok( !defined( $foo->bar ), '... $foo->bar is undefined' );
+
+    dies_ok {
+        $foo->baz(1);
+    }
+    '... baz is a read-only accessor';
+
+    dies_ok {
+        $foo->bar(1);
+    }
+    '... bar is a read-write accessor with a type constraint';
+
+    my $foo2 = FooClass->new();
+    isa_ok( $foo2, 'FooClass' );
+
+    lives_ok {
+        $foo->bar($foo2);
+    }
+    '... bar is a read-write accessor with a type constraint';
+
+    is( $foo->bar, $foo2, '... got the right value for bar now' );
+}
diff --git a/t/030_roles/019_build.t b/t/030_roles/019_build.t
new file mode 100644 (file)
index 0000000..f76ea5a
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More tests => 6;
+
+# this test script ensures that my idiom of:
+# role: sub BUILD, after BUILD
+# continues to work to run code after object initialization, whether the class
+# has a BUILD method or not
+
+my @CALLS;
+
+do {
+    package TestRole;
+    use Mouse::Role;
+
+    sub BUILD           { push @CALLS, 'TestRole::BUILD' }
+    before BUILD => sub { push @CALLS, 'TestRole::BUILD:before' };
+    after  BUILD => sub { push @CALLS, 'TestRole::BUILD:after' };
+};
+
+do {
+    package ClassWithBUILD;
+    use Mouse;
+    with 'TestRole';
+
+    sub BUILD { push @CALLS, 'ClassWithBUILD::BUILD' }
+};
+
+do {
+    package ClassWithoutBUILD;
+    use Mouse;
+    with 'TestRole';
+};
+
+is_deeply([splice @CALLS], [], "no calls to BUILD yet");
+
+ClassWithBUILD->new;
+
+is_deeply([splice @CALLS], [
+    'TestRole::BUILD:before',
+    'ClassWithBUILD::BUILD',
+    'TestRole::BUILD:after',
+]);
+
+ClassWithoutBUILD->new;
+
+is_deeply([splice @CALLS], [
+    'TestRole::BUILD:before',
+    'TestRole::BUILD',
+    'TestRole::BUILD:after',
+]);
+
+ClassWithBUILD->meta->make_immutable;
+ClassWithoutBUILD->meta->make_immutable;
+
+is_deeply([splice @CALLS], [], "no calls to BUILD yet");
+
+ClassWithBUILD->new;
+
+is_deeply([splice @CALLS], [
+    'TestRole::BUILD:before',
+    'ClassWithBUILD::BUILD',
+    'TestRole::BUILD:after',
+]);
+
+ClassWithoutBUILD->new;
+
+is_deeply([splice @CALLS], [
+    'TestRole::BUILD:before',
+    'TestRole::BUILD',
+    'TestRole::BUILD:after',
+]);
+
diff --git a/t/030_roles/031_roles_applied_in_create.t b/t/030_roles/031_roles_applied_in_create.t
new file mode 100644 (file)
index 0000000..defad7d
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+use Test::Exception;
+use Mouse::Meta::Class;
+use Mouse::Util;
+
+use lib 't/lib', 'lib';
+
+
+# Note that this test passed (pre svn #5543) if we inlined the role
+# definitions in this file, as it was very timing sensitive.
+lives_ok(
+    sub {
+        my $builder_meta = Mouse::Meta::Class->create(
+            'YATTA' => (
+                superclass => 'Mouse::Meta::Class',
+                roles      => [qw( Role::Interface Role::Child )],
+            )
+        );
+    },
+    'Create a new class with several roles'
+);
+
diff --git a/t/030_roles/failing/001_meta_role.t b/t/030_roles/failing/001_meta_role.t
new file mode 100755 (executable)
index 0000000..8db3590
--- /dev/null
@@ -0,0 +1,106 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 27;
+use Test::Exception;
+
+use Mouse::Meta::Role;
+
+{
+    package FooRole;
+    
+    our $VERSION = '0.01';
+    
+    sub foo { 'FooRole::foo' }
+}
+
+my $foo_role = Mouse::Meta::Role->initialize('FooRole');
+isa_ok($foo_role, 'Mouse::Meta::Role');
+#isa_ok($foo_role, 'Class::MOP::Module'); ## Mouse: doesn't use Class::MOP
+
+is($foo_role->name, 'FooRole', '... got the right name of FooRole');
+#is($foo_role->version, '0.01', '... got the right version of FooRole'); ## Mouse: ->version is cfrom Class::MOP
+
+# methods ...
+
+ok($foo_role->has_method('foo'), '... FooRole has the foo method');
+is($foo_role->get_method('foo')->body, \&FooRole::foo, '... FooRole got the foo method');
+
+isa_ok($foo_role->get_method('foo'), 'Mouse::Meta::Role::Method');
+
+is_deeply(
+    [ $foo_role->get_method_list() ],
+    [ 'foo' ],
+    '... got the right method list');
+    
+# attributes ...
+
+is_deeply(
+    [ $foo_role->get_attribute_list() ],
+    [],
+    '... got the right attribute list');
+
+ok(!$foo_role->has_attribute('bar'), '... FooRole does not have the bar attribute');
+
+lives_ok {
+    $foo_role->add_attribute('bar' => (is => 'rw', isa => 'Foo'));
+} '... added the bar attribute okay';
+
+is_deeply(
+    [ $foo_role->get_attribute_list() ],
+    [ 'bar' ],
+    '... got the right attribute list');
+
+ok($foo_role->has_attribute('bar'), '... FooRole does have the bar attribute');
+
+is_deeply(
+    $foo_role->get_attribute('bar'),
+    { is => 'rw', isa => 'Foo' },
+    '... got the correct description of the bar attribute');
+
+lives_ok {
+    $foo_role->add_attribute('baz' => (is => 'ro'));
+} '... added the baz attribute okay';
+
+is_deeply(
+    [ sort $foo_role->get_attribute_list() ],
+    [ 'bar', 'baz' ],
+    '... got the right attribute list');
+
+ok($foo_role->has_attribute('baz'), '... FooRole does have the baz attribute');
+
+is_deeply(
+    $foo_role->get_attribute('baz'),
+    { is => 'ro' },
+    '... got the correct description of the baz attribute');
+
+lives_ok {
+    $foo_role->remove_attribute('bar');
+} '... removed the bar attribute okay';
+
+is_deeply(
+    [ $foo_role->get_attribute_list() ],
+    [ 'baz' ],
+    '... got the right attribute list');
+
+ok(!$foo_role->has_attribute('bar'), '... FooRole does not have the bar attribute');
+ok($foo_role->has_attribute('baz'), '... FooRole does still have the baz attribute');
+
+# method modifiers
+
+ok(!$foo_role->has_before_method_modifiers('boo'), '... no boo:before modifier');
+
+my $method = sub { "FooRole::boo:before" };
+lives_ok {
+    $foo_role->add_before_method_modifier('boo' => $method);
+} '... added a method modifier okay';
+
+ok($foo_role->has_before_method_modifiers('boo'), '... now we have a boo:before modifier');
+is(($foo_role->get_before_method_modifiers('boo'))[0], $method, '... got the right method back');
+
+is_deeply(
+    [ $foo_role->get_method_modifier_list('before') ],
+    [ 'boo' ],
+    '... got the right list of before method modifiers');
diff --git a/t/030_roles/failing/004_role_composition_errors.t b/t/030_roles/failing/004_role_composition_errors.t
new file mode 100644 (file)
index 0000000..837af9f
--- /dev/null
@@ -0,0 +1,157 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 14;
+use Test::Exception;
+
+
+
+{
+
+    package Foo::Role;
+    use Mouse::Role;
+
+    requires 'foo';
+}
+
+is_deeply(
+    [ sort Foo::Role->meta->get_required_method_list ],
+    ['foo'],
+    '... the Foo::Role has a required method (foo)'
+);
+
+# classes which does not implement required method
+{
+
+    package Foo::Class;
+    use Mouse;
+
+    ::dies_ok { with('Foo::Role') }
+        '... no foo method implemented by Foo::Class';
+}
+
+# class which does implement required method
+{
+
+    package Bar::Class;
+    use Mouse;
+
+    ::dies_ok { with('Foo::Class') }
+        '... cannot consume a class, it must be a role';
+    ::lives_ok { with('Foo::Role') }
+        '... has a foo method implemented by Bar::Class';
+
+    sub foo {'Bar::Class::foo'}
+}
+
+# role which does implement required method
+{
+
+    package Bar::Role;
+    use Mouse::Role;
+
+    ::lives_ok { with('Foo::Role') }
+        '... has a foo method implemented by Bar::Role';
+
+    sub foo {'Bar::Role::foo'}
+}
+
+is_deeply(
+    [ sort Bar::Role->meta->get_required_method_list ],
+    [],
+    '... the Bar::Role has not inherited the required method from Foo::Role'
+);
+
+# role which does not implement required method
+{
+
+    package Baz::Role;
+    use Mouse::Role;
+
+    ::lives_ok { with('Foo::Role') }
+        '... no foo method implemented by Baz::Role';
+}
+
+is_deeply(
+    [ sort Baz::Role->meta->get_required_method_list ],
+    ['foo'],
+    '... the Baz::Role has inherited the required method from Foo::Role'
+);
+
+# classes which does not implement required method
+{
+
+    package Baz::Class;
+    use Mouse;
+
+    ::dies_ok { with('Baz::Role') }
+        '... no foo method implemented by Baz::Class2';
+}
+
+# class which does implement required method
+{
+
+    package Baz::Class2;
+    use Mouse;
+
+    ::lives_ok { with('Baz::Role') }
+        '... has a foo method implemented by Baz::Class2';
+
+    sub foo {'Baz::Class2::foo'}
+}
+
+
+{
+    package Quux::Role;
+    use Mouse::Role;
+
+    requires qw( meth1 meth2 meth3 meth4 );
+}
+
+# RT #41119
+{
+
+    package Quux::Class;
+    use Mouse;
+
+    ::throws_ok { with('Quux::Role') }
+        qr/\Q'Quux::Role' requires the methods 'meth1', 'meth2', 'meth3', and 'meth4' to be implemented by 'Quux::Class'/,
+        'exception mentions all the missing required methods at once';
+}
+
+{
+    package Quux::Class2;
+    use Mouse;
+
+    sub meth1 { }
+
+    ::throws_ok { with('Quux::Role') }
+        qr/'Quux::Role' requires the methods 'meth2', 'meth3', and 'meth4' to be implemented by 'Quux::Class2'/,
+        'exception mentions all the missing required methods at once, but not the one that exists';
+}
+
+{
+    package Quux::Class3;
+    use Mouse;
+
+    has 'meth1' => ( is => 'ro' );
+    has 'meth2' => ( is => 'ro' );
+
+    ::throws_ok { with('Quux::Role') }
+        qr/'Quux::Role' requires the methods 'meth3' and 'meth4' to be implemented by 'Quux::Class3'/,
+        'exception mentions all the missing methods at once, but not the accessors';
+}
+
+{
+    package Quux::Class4;
+    use Mouse;
+
+    sub meth1 { }
+    has 'meth2' => ( is => 'ro' );
+
+    ::throws_ok { with('Quux::Role') }
+        qr/'Quux::Role' requires the methods 'meth3' and 'meth4' to be implemented by 'Quux::Class4'/,
+        'exception mentions all the require methods that are accessors at once, as well as missing methods, but not the one that exists';
+}
diff --git a/t/030_roles/failing/005_role_conflict_detection.t b/t/030_roles/failing/005_role_conflict_detection.t
new file mode 100755 (executable)
index 0000000..eea1dc3
--- /dev/null
@@ -0,0 +1,560 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 87; # it's really 124 with kolibrie's tests;
+use Test::Exception;
+
+=pod
+
+Mutually recursive roles.
+
+=cut
+
+{
+    package Role::Foo;
+    use Mouse::Role;
+
+    requires 'foo';
+    
+    sub bar { 'Role::Foo::bar' }
+    
+    package Role::Bar;
+    use Mouse::Role;
+    
+    requires 'bar';
+    
+    sub foo { 'Role::Bar::foo' }    
+}
+
+{
+    package My::Test1;
+    use Mouse;
+    
+    ::lives_ok {
+        with 'Role::Foo', 'Role::Bar';
+    } '... our mutually recursive roles combine okay';
+    
+    package My::Test2;
+    use Mouse;
+    
+    ::lives_ok {
+        with 'Role::Bar', 'Role::Foo';
+    } '... our mutually recursive roles combine okay (no matter what order)';    
+}
+
+my $test1 = My::Test1->new;
+isa_ok($test1, 'My::Test1');
+
+ok($test1->does('Role::Foo'), '... $test1 does Role::Foo');
+ok($test1->does('Role::Bar'), '... $test1 does Role::Bar');
+
+can_ok($test1, 'foo');
+can_ok($test1, 'bar');
+
+is($test1->foo, 'Role::Bar::foo', '... $test1->foo worked');
+is($test1->bar, 'Role::Foo::bar', '... $test1->bar worked');
+
+my $test2 = My::Test2->new;
+isa_ok($test2, 'My::Test2');
+
+ok($test2->does('Role::Foo'), '... $test2 does Role::Foo');
+ok($test2->does('Role::Bar'), '... $test2 does Role::Bar');
+
+can_ok($test2, 'foo');
+can_ok($test2, 'bar');
+
+is($test2->foo, 'Role::Bar::foo', '... $test2->foo worked');
+is($test2->bar, 'Role::Foo::bar', '... $test2->bar worked');
+
+# check some meta-stuff
+
+TODO: { todo_skip "Mouse: not yet implemented" => 4;
+ok(Role::Foo->meta->has_method('bar'), '... it still has the bar method');
+ok(Role::Foo->meta->requires_method('foo'), '... it still has the required foo method');
+
+ok(Role::Bar->meta->has_method('foo'), '... it still has the foo method');
+ok(Role::Bar->meta->requires_method('bar'), '... it still has the required bar method');
+}
+
+=pod
+
+Role method conflicts
+
+=cut
+
+{
+    package Role::Bling;
+    use Mouse::Role;
+    
+    sub bling { 'Role::Bling::bling' }
+    
+    package Role::Bling::Bling;
+    use Mouse::Role;
+    
+    sub bling { 'Role::Bling::Bling::bling' }    
+}
+
+{
+    package My::Test3;
+    use Mouse;
+    
+    ::throws_ok {
+        with 'Role::Bling', 'Role::Bling::Bling';
+    } qr/requires the method \'bling\' to be implemented/, '... role methods conflicted and method was required';
+    
+    package My::Test4;
+    use Mouse;
+    
+    ::lives_ok {
+        with 'Role::Bling';
+        with 'Role::Bling::Bling';
+    } '... role methods didnt conflict when manually combined';    
+    
+    package My::Test5;
+    use Mouse;
+    
+    ::lives_ok {
+        with 'Role::Bling::Bling';
+        with 'Role::Bling';
+    } '... role methods didnt conflict when manually combined (in opposite order)';    
+    
+    package My::Test6;
+    use Mouse;
+    
+    ::lives_ok {
+        with 'Role::Bling::Bling', 'Role::Bling';
+    } '... role methods didnt conflict when manually resolved';    
+    
+    sub bling { 'My::Test6::bling' }
+}
+
+TODO: { todo_skip "Mouse: not yet implemented" => 4;
+ok(!My::Test3->meta->has_method('bling'), '... we didnt get any methods in the conflict');
+ok(My::Test4->meta->has_method('bling'), '... we did get the method when manually dealt with');
+ok(My::Test5->meta->has_method('bling'), '... we did get the method when manually dealt with');
+ok(My::Test6->meta->has_method('bling'), '... we did get the method when manually dealt with');
+}
+
+ok(!My::Test3->does('Role::Bling'), '... our class does() the correct roles');
+ok(!My::Test3->does('Role::Bling::Bling'), '... our class does() the correct roles');
+ok(My::Test4->does('Role::Bling'), '... our class does() the correct roles');
+ok(My::Test4->does('Role::Bling::Bling'), '... our class does() the correct roles');
+ok(My::Test5->does('Role::Bling'), '... our class does() the correct roles');
+ok(My::Test5->does('Role::Bling::Bling'), '... our class does() the correct roles');
+ok(My::Test6->does('Role::Bling'), '... our class does() the correct roles');
+ok(My::Test6->does('Role::Bling::Bling'), '... our class does() the correct roles');
+
+is(My::Test4->bling, 'Role::Bling::bling', '... and we got the first method that was added');
+is(My::Test5->bling, 'Role::Bling::Bling::bling', '... and we got the first method that was added');
+is(My::Test6->bling, 'My::Test6::bling', '... and we got the local method');
+
+# check how this affects role compostion
+
+{
+    package Role::Bling::Bling::Bling;
+    use Mouse::Role;
+    
+    with 'Role::Bling::Bling';
+    
+    sub bling { 'Role::Bling::Bling::Bling::bling' }    
+}
+
+TODO: { todo_skip "Mouse: not yet implemented" => 1;
+ok(Role::Bling::Bling->meta->has_method('bling'), '... still got the bling method in Role::Bling::Bling');
+    }
+ok(Role::Bling::Bling->meta->does_role('Role::Bling::Bling'), '... our role correctly does() the other role');
+TODO: { todo_skip "Mouse: not yet implemented" => 2;
+ok(Role::Bling::Bling::Bling->meta->has_method('bling'), '... dont have the bling method in Role::Bling::Bling::Bling');
+is(Role::Bling::Bling::Bling->meta->get_method('bling')->(), 
+    'Role::Bling::Bling::Bling::bling',
+    '... still got the bling method in Role::Bling::Bling::Bling');
+}
+
+=pod
+
+Role attribute conflicts
+
+=cut
+
+{
+    package Role::Boo;
+    use Mouse::Role;
+    
+    has 'ghost' => (is => 'ro', default => 'Role::Boo::ghost');
+    
+    package Role::Boo::Hoo;
+    use Mouse::Role;
+    
+    has 'ghost' => (is => 'ro', default => 'Role::Boo::Hoo::ghost');
+}
+
+{
+    package My::Test7;
+    use Mouse;
+    
+    ::throws_ok {
+        with 'Role::Boo', 'Role::Boo::Hoo';
+    } qr/We have encountered an attribute conflict/, 
+      '... role attrs conflicted and method was required';
+
+    package My::Test8;
+    use Mouse;
+
+    ::lives_ok {
+        with 'Role::Boo';
+        with 'Role::Boo::Hoo';
+    } '... role attrs didnt conflict when manually combined';
+    
+    package My::Test9;
+    use Mouse;
+
+    ::lives_ok {
+        with 'Role::Boo::Hoo';
+        with 'Role::Boo';
+    } '... role attrs didnt conflict when manually combined';    
+
+    package My::Test10;
+    use Mouse;
+    
+    has 'ghost' => (is => 'ro', default => 'My::Test10::ghost');    
+    
+    ::throws_ok {
+        with 'Role::Boo', 'Role::Boo::Hoo';
+    } qr/We have encountered an attribute conflict/, 
+      '... role attrs conflicted and cannot be manually disambiguted';  
+
+}
+
+ok(!My::Test7->meta->has_attribute('ghost'), '... we didnt get any attributes in the conflict');
+ok(My::Test8->meta->has_attribute('ghost'), '... we did get an attributes when manually composed');
+ok(My::Test9->meta->has_attribute('ghost'), '... we did get an attributes when manually composed');
+ok(My::Test10->meta->has_attribute('ghost'), '... we did still have an attribute ghost (conflict does not mess with class)');
+
+ok(!My::Test7->does('Role::Boo'), '... our class does() the correct roles');
+ok(!My::Test7->does('Role::Boo::Hoo'), '... our class does() the correct roles');
+ok(My::Test8->does('Role::Boo'), '... our class does() the correct roles');
+ok(My::Test8->does('Role::Boo::Hoo'), '... our class does() the correct roles');
+ok(My::Test9->does('Role::Boo'), '... our class does() the correct roles');
+ok(My::Test9->does('Role::Boo::Hoo'), '... our class does() the correct roles');
+ok(!My::Test10->does('Role::Boo'), '... our class does() the correct roles');
+ok(!My::Test10->does('Role::Boo::Hoo'), '... our class does() the correct roles');
+
+can_ok('My::Test8', 'ghost');
+can_ok('My::Test9', 'ghost');
+can_ok('My::Test10', 'ghost');
+
+is(My::Test8->new->ghost, 'Role::Boo::ghost', '... got the expected default attr value');
+is(My::Test9->new->ghost, 'Role::Boo::Hoo::ghost', '... got the expected default attr value');
+is(My::Test10->new->ghost, 'My::Test10::ghost', '... got the expected default attr value');
+
+=pod
+
+Role override method conflicts
+
+=cut
+
+{
+    package Role::Plot;
+    use Mouse::Role;
+    
+    override 'twist' => sub {
+        super() . ' -> Role::Plot::twist';
+    };
+    
+    package Role::Truth;
+    use Mouse::Role;
+    
+    override 'twist' => sub {
+        super() . ' -> Role::Truth::twist';
+    };
+}
+
+{
+    package My::Test::Base;
+    use Mouse;
+    
+    sub twist { 'My::Test::Base::twist' }
+        
+    package My::Test11;
+    use Mouse;
+    
+    extends 'My::Test::Base';
+
+    ::lives_ok {
+        with 'Role::Truth';
+    } '... composed the role with override okay';
+       
+    package My::Test12;
+    use Mouse;
+
+    extends 'My::Test::Base';
+
+    ::lives_ok {    
+       with 'Role::Plot';
+    } '... composed the role with override okay';
+              
+    package My::Test13;
+    use Mouse;
+
+    ::dies_ok {
+        with 'Role::Plot';       
+    } '... cannot compose it because we have no superclass';
+    
+    package My::Test14;
+    use Mouse;
+
+    extends 'My::Test::Base';
+
+    ::throws_ok {
+        with 'Role::Plot', 'Role::Truth';       
+    } qr/Two \'override\' methods of the same name encountered/, 
+      '... cannot compose it because we have no superclass';       
+}
+
+ok(My::Test11->meta->has_method('twist'), '... the twist method has been added');
+ok(My::Test12->meta->has_method('twist'), '... the twist method has been added');
+ok(!My::Test13->meta->has_method('twist'), '... the twist method has not been added');
+ok(!My::Test14->meta->has_method('twist'), '... the twist method has not been added');
+
+ok(!My::Test11->does('Role::Plot'), '... our class does() the correct roles');
+ok(My::Test11->does('Role::Truth'), '... our class does() the correct roles');
+ok(!My::Test12->does('Role::Truth'), '... our class does() the correct roles');
+ok(My::Test12->does('Role::Plot'), '... our class does() the correct roles');
+ok(!My::Test13->does('Role::Plot'), '... our class does() the correct roles');
+ok(!My::Test14->does('Role::Truth'), '... our class does() the correct roles');
+ok(!My::Test14->does('Role::Plot'), '... our class does() the correct roles');
+
+is(My::Test11->twist(), 'My::Test::Base::twist -> Role::Truth::twist', '... got the right method return');
+is(My::Test12->twist(), 'My::Test::Base::twist -> Role::Plot::twist', '... got the right method return');
+ok(!My::Test13->can('twist'), '... no twist method here at all');
+is(My::Test14->twist(), 'My::Test::Base::twist', '... got the right method return (from superclass)');
+
+{
+    package Role::Reality;
+    use Mouse::Role;
+
+    ::throws_ok {    
+        with 'Role::Plot';
+    } qr/A local method of the same name as been found/, 
+    '... could not compose roles here, it dies';
+
+    sub twist {
+        'Role::Reality::twist';
+    }
+}    
+
+ok(Role::Reality->meta->has_method('twist'), '... the twist method has not been added');
+#ok(!Role::Reality->meta->does_role('Role::Plot'), '... our role does() the correct roles');
+is(Role::Reality->meta->get_method('twist')->(), 
+    'Role::Reality::twist', 
+    '... the twist method returns the right value');
+
+=pod
+
+Role conflicts between attributes and methods
+
+[15:23]  <kolibrie> when class defines method and role defines method, class wins
+[15:24]  <kolibrie> when class 'has'   method and role defines method, class wins
+[15:24]  <kolibrie> when class defines method and role 'has'   method, role wins
+[15:24]  <kolibrie> when class 'has'   method and role 'has'   method, role wins
+[15:24]  <kolibrie> which means when class 'has' method and two roles 'has' method, no tiebreak is detected
+[15:24]  <perigrin> this is with role and has declaration in the exact same order in every case?
+[15:25]  <kolibrie> yes
+[15:25]  <perigrin> interesting
+[15:25]  <kolibrie> that's what I thought
+[15:26]  <kolibrie> does that sound like something I should write a test for?
+[15:27]  <perigrin> stevan, ping?
+[15:27]  <perigrin> I'm not sure what the right answer for composition is.
+[15:27]  <perigrin> who should win
+[15:27]  <perigrin> if I were to guess I'd say the class should always win.
+[15:27]  <kolibrie> that would be my guess, but I thought I would ask to make sure
+[15:29]  <stevan> kolibrie: please write a test
+[15:29]  <stevan> I am not exactly sure who should win either,.. but I suspect it is not working correctly right now
+[15:29]  <stevan> I know exactly why it is doing what it is doing though
+
+Now I have to decide actually what happens, and how to fix it.
+- SL
+
+{
+    package Role::Method;
+    use Mouse::Role;
+    
+    sub ghost { 'Role::Method::ghost' }
+
+    package Role::Method2;
+    use Mouse::Role;
+    
+    sub ghost { 'Role::Method2::ghost' }
+
+    package Role::Attribute;
+    use Mouse::Role;
+    
+    has 'ghost' => (is => 'ro', default => 'Role::Attribute::ghost');
+
+    package Role::Attribute2;
+    use Mouse::Role;
+    
+    has 'ghost' => (is => 'ro', default => 'Role::Attribute2::ghost');
+}
+
+{
+    package My::Test15;
+    use Mouse;
+
+    ::lives_ok {    
+       with 'Role::Method';
+    } '... composed the method role into the method class';
+
+    sub ghost { 'My::Test15::ghost' }
+
+    package My::Test16;
+    use Mouse;
+
+    ::lives_ok {
+       with 'Role::Method';
+    } '... composed the method role into the attribute class';
+
+    has 'ghost' => (is => 'ro', default => 'My::Test16::ghost');
+
+    package My::Test17;
+    use Mouse;
+
+    ::lives_ok {
+       with 'Role::Attribute';
+    } '... composed the attribute role into the method class';
+
+    sub ghost { 'My::Test17::ghost' }
+
+    package My::Test18;
+    use Mouse;
+
+    ::lives_ok {
+       with 'Role::Attribute';
+    } '... composed the attribute role into the attribute class';
+
+    has 'ghost' => (is => 'ro', default => 'My::Test18::ghost');
+
+    package My::Test19;
+    use Mouse;
+
+    ::lives_ok {
+       with 'Role::Method', 'Role::Method2';
+    } '... composed method roles into class with method tiebreaker';
+
+    sub ghost { 'My::Test19::ghost' }
+
+    package My::Test20;
+    use Mouse;
+
+    ::lives_ok {
+       with 'Role::Method', 'Role::Method2';
+    } '... composed method roles into class with attribute tiebreaker';
+
+    has 'ghost' => (is => 'ro', default => 'My::Test20::ghost');
+
+    package My::Test21;
+    use Mouse;
+
+    ::lives_ok {
+       with 'Role::Attribute', 'Role::Attribute2';
+    } '... composed attribute roles into class with method tiebreaker';
+
+    sub ghost { 'My::Test21::ghost' }
+
+    package My::Test22;
+    use Mouse;
+
+    ::lives_ok {
+       with 'Role::Attribute', 'Role::Attribute2';
+    } '... composed attribute roles into class with attribute tiebreaker';
+
+    has 'ghost' => (is => 'ro', default => 'My::Test22::ghost');
+
+    package My::Test23;
+    use Mouse;
+
+    ::lives_ok {
+        with 'Role::Method', 'Role::Attribute';
+    } '... composed method and attribute role into class with method tiebreaker';
+
+    sub ghost { 'My::Test23::ghost' }
+
+    package My::Test24;
+    use Mouse;
+
+    ::lives_ok {
+        with 'Role::Method', 'Role::Attribute';
+    } '... composed method and attribute role into class with attribute tiebreaker';
+
+    has 'ghost' => (is => 'ro', default => 'My::Test24::ghost');
+
+    package My::Test25;
+    use Mouse;
+
+    ::lives_ok {
+        with 'Role::Attribute', 'Role::Method';
+    } '... composed attribute and method role into class with method tiebreaker';
+
+    sub ghost { 'My::Test25::ghost' }
+
+    package My::Test26;
+    use Mouse;
+
+    ::lives_ok {
+        with 'Role::Attribute', 'Role::Method';
+    } '... composed attribute and method role into class with attribute tiebreaker';
+
+    has 'ghost' => (is => 'ro', default => 'My::Test26::ghost');
+}
+
+my $test15 = My::Test15->new;
+isa_ok($test15, 'My::Test15');
+is($test15->ghost, 'My::Test15::ghost', '... we access the method from the class and ignore the role method');
+
+my $test16 = My::Test16->new;
+isa_ok($test16, 'My::Test16');
+is($test16->ghost, 'My::Test16::ghost', '... we access the attribute from the class and ignore the role method');
+
+my $test17 = My::Test17->new;
+isa_ok($test17, 'My::Test17');
+is($test17->ghost, 'My::Test17::ghost', '... we access the method from the class and ignore the role attribute');
+
+my $test18 = My::Test18->new;
+isa_ok($test18, 'My::Test18');
+is($test18->ghost, 'My::Test18::ghost', '... we access the attribute from the class and ignore the role attribute');
+
+my $test19 = My::Test19->new;
+isa_ok($test19, 'My::Test19');
+is($test19->ghost, 'My::Test19::ghost', '... we access the method from the class and ignore the role methods');
+
+my $test20 = My::Test20->new;
+isa_ok($test20, 'My::Test20');
+is($test20->ghost, 'My::Test20::ghost', '... we access the attribute from the class and ignore the role methods');
+
+my $test21 = My::Test21->new;
+isa_ok($test21, 'My::Test21');
+is($test21->ghost, 'My::Test21::ghost', '... we access the method from the class and ignore the role attributes');
+
+my $test22 = My::Test22->new;
+isa_ok($test22, 'My::Test22');
+is($test22->ghost, 'My::Test22::ghost', '... we access the attribute from the class and ignore the role attributes');
+
+my $test23 = My::Test23->new;
+isa_ok($test23, 'My::Test23');
+is($test23->ghost, 'My::Test23::ghost', '... we access the method from the class and ignore the role method and attribute');
+
+my $test24 = My::Test24->new;
+isa_ok($test24, 'My::Test24');
+is($test24->ghost, 'My::Test24::ghost', '... we access the attribute from the class and ignore the role method and attribute');
+
+my $test25 = My::Test25->new;
+isa_ok($test25, 'My::Test25');
+is($test25->ghost, 'My::Test25::ghost', '... we access the method from the class and ignore the role attribute and method');
+
+my $test26 = My::Test26->new;
+isa_ok($test26, 'My::Test26');
+is($test26->ghost, 'My::Test26::ghost', '... we access the attribute from the class and ignore the role attribute and method');
+
+=cut
diff --git a/t/030_roles/failing/006_role_exclusion.t b/t/030_roles/failing/006_role_exclusion.t
new file mode 100644 (file)
index 0000000..5b69ee2
--- /dev/null
@@ -0,0 +1,123 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 22;
+use Test::Exception;
+
+=pod
+
+The idea and examples for this feature are taken
+from the Fortress spec.
+
+http://research.sun.com/projects/plrg/fortress0903.pdf
+
+trait OrganicMolecule extends Molecule 
+    excludes { InorganicMolecule } 
+end 
+trait InorganicMolecule extends Molecule end 
+
+=cut
+
+{
+    package Molecule;
+    use Mouse::Role;
+
+    package Molecule::Organic;
+    use Mouse::Role;
+    
+    with 'Molecule';
+    excludes 'Molecule::Inorganic';
+    
+    package Molecule::Inorganic;
+    use Mouse::Role;     
+    
+    with 'Molecule';       
+}
+
+ok(Molecule::Organic->meta->excludes_role('Molecule::Inorganic'), '... Molecule::Organic exludes Molecule::Inorganic');
+is_deeply(
+   [ Molecule::Organic->meta->get_excluded_roles_list() ], 
+   [ 'Molecule::Inorganic' ],
+   '... Molecule::Organic exludes Molecule::Inorganic');
+
+=pod
+
+Check some basic conflicts when combining  
+the roles into the same class
+
+=cut
+
+{
+    package My::Test1;
+    use Mouse;
+    
+    ::lives_ok {
+        with 'Molecule::Organic';
+    } '... adding the role (w/ excluded roles) okay';
+
+    package My::Test2;
+    use Mouse;
+    
+    ::throws_ok {
+        with 'Molecule::Organic', 'Molecule::Inorganic';
+    } qr/Conflict detected: Role Molecule::Organic excludes role 'Molecule::Inorganic'/, 
+    '... adding the role w/ excluded role conflict dies okay';    
+    
+    package My::Test3;
+    use Mouse;
+    
+    ::lives_ok {
+        with 'Molecule::Organic';
+    } '... adding the role (w/ excluded roles) okay';   
+    
+    ::throws_ok {
+        with 'Molecule::Inorganic';
+    } qr/Conflict detected: My::Test3 excludes role 'Molecule::Inorganic'/, 
+    '... adding the role w/ excluded role conflict dies okay'; 
+}
+
+ok(My::Test1->does('Molecule::Organic'), '... My::Test1 does Molecule::Organic');
+ok(My::Test1->does('Molecule'), '... My::Test1 does Molecule');
+ok(My::Test1->meta->excludes_role('Molecule::Inorganic'), '... My::Test1 excludes Molecule::Organic');
+
+ok(!My::Test2->does('Molecule::Organic'), '... ! My::Test2 does Molecule::Organic');
+ok(!My::Test2->does('Molecule::Inorganic'), '... ! My::Test2 does Molecule::Inorganic');
+
+ok(My::Test3->does('Molecule::Organic'), '... My::Test3 does Molecule::Organic');
+ok(My::Test3->does('Molecule'), '... My::Test1 does Molecule');
+ok(My::Test3->meta->excludes_role('Molecule::Inorganic'), '... My::Test3 excludes Molecule::Organic');
+ok(!My::Test3->does('Molecule::Inorganic'), '... ! My::Test3 does Molecule::Inorganic');
+
+=pod
+
+Check some basic conflicts when combining  
+the roles into the a superclass
+
+=cut
+
+{
+    package Methane;
+    use Mouse;
+    
+    with 'Molecule::Organic';
+    
+    package My::Test4;
+    use Mouse;
+    
+    extends 'Methane';    
+    
+    ::throws_ok {
+        with 'Molecule::Inorganic';    
+    } qr/Conflict detected: My::Test4 excludes role \'Molecule::Inorganic\'/,
+    '... cannot add exculded role into class which extends Methane';
+}
+
+ok(Methane->does('Molecule::Organic'), '... Methane does Molecule::Organic');
+ok(My::Test4->isa('Methane'), '... My::Test4 isa Methane');
+ok(My::Test4->does('Molecule::Organic'), '... My::Test4 does Molecule::Organic');
+ok(My::Test4->meta->does_role('Molecule::Organic'), '... My::Test4 meat does_role Molecule::Organic');
+ok(My::Test4->meta->excludes_role('Molecule::Inorganic'), '... My::Test4 meta excludes Molecule::Organic');
+ok(!My::Test4->does('Molecule::Inorganic'), '... My::Test4 does Molecule::Inorganic');
+
diff --git a/t/030_roles/failing/007_roles_and_req_method_edge_cases.t b/t/030_roles/failing/007_roles_and_req_method_edge_cases.t
new file mode 100644 (file)
index 0000000..f6efa6e
--- /dev/null
@@ -0,0 +1,277 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 15;
+use Test::Exception;
+
+=pod
+
+NOTE:
+A fair amount of these tests will likely be irrelevant 
+once we have more fine grained control over the class
+building process. A lot of the edge cases tested here
+are actually related to class construction order and 
+not any real functionality.
+- SL
+
+Role which requires a method implemented 
+in another role as an override (it does 
+not remove the requirement)
+
+=cut
+
+{
+    package Role::RequireFoo;
+    use strict;
+    use warnings;
+    use Mouse::Role;
+    
+    requires 'foo';
+    
+    package Role::ProvideFoo;
+    use strict;
+    use warnings;
+    use Mouse::Role;
+    
+    ::lives_ok {
+        with 'Role::RequireFoo';
+    } '... the required "foo" method will not exist yet (but we will live)';
+    
+    override 'foo' => sub { 'Role::ProvideFoo::foo' };    
+}
+
+is_deeply(
+    [ Role::ProvideFoo->meta->get_required_method_list ], 
+    [ 'foo' ], 
+    '... foo method is still required for Role::ProvideFoo');
+
+=pod
+
+Role which requires a method implemented 
+in the consuming class as an override. 
+It will fail since method modifiers are 
+second class citizens.
+
+=cut
+
+{
+    package Class::ProvideFoo::Base;
+    use Mouse;
+
+    sub foo { 'Class::ProvideFoo::Base::foo' }
+        
+    package Class::ProvideFoo::Override1;
+    use Mouse;
+    
+    extends 'Class::ProvideFoo::Base';
+    
+    ::lives_ok {
+        with 'Role::RequireFoo';
+    } '... the required "foo" method will be found in the superclass';
+    
+    override 'foo' => sub { 'Class::ProvideFoo::foo' };    
+    
+    package Class::ProvideFoo::Override2;
+    use Mouse;
+    
+    extends 'Class::ProvideFoo::Base';
+    
+    override 'foo' => sub { 'Class::ProvideFoo::foo' };     
+    
+    ::lives_ok {
+        with 'Role::RequireFoo';
+    } '... the required "foo" method exists, although it is overriden locally';
+
+}
+
+=pod
+
+Now same thing, but with a before 
+method modifier.
+
+=cut
+
+{
+    package Class::ProvideFoo::Before1;
+    use Mouse;
+    
+    extends 'Class::ProvideFoo::Base';
+    
+    ::lives_ok {
+        with 'Role::RequireFoo';
+    } '... the required "foo" method will be found in the superclass';
+    
+    before 'foo' => sub { 'Class::ProvideFoo::foo:before' };    
+    
+    package Class::ProvideFoo::Before2;
+    use Mouse;
+    
+    extends 'Class::ProvideFoo::Base';
+    
+    before 'foo' => sub { 'Class::ProvideFoo::foo:before' };     
+    
+    ::lives_ok {
+        with 'Role::RequireFoo';
+    } '... the required "foo" method exists, although it is a before modifier locally';    
+    
+    package Class::ProvideFoo::Before3;
+    use Mouse;
+    
+    extends 'Class::ProvideFoo::Base';
+    
+    sub foo { 'Class::ProvideFoo::foo' }
+    before 'foo' => sub { 'Class::ProvideFoo::foo:before' };    
+    
+    ::lives_ok {
+        with 'Role::RequireFoo';
+    } '... the required "foo" method exists locally, and it is modified locally';    
+    
+    package Class::ProvideFoo::Before4;
+    use Mouse;
+    
+    extends 'Class::ProvideFoo::Base';
+    
+    sub foo { 'Class::ProvideFoo::foo' }    
+    before 'foo' => sub { 'Class::ProvideFoo::foo:before' };     
+
+    ::isa_ok(__PACKAGE__->meta->get_method('foo'), 'Class::MOP::Method::Wrapped');
+    ::is(__PACKAGE__->meta->get_method('foo')->get_original_method->package_name, __PACKAGE__, 
+    '... but the original method is from our package');
+    
+    ::lives_ok {
+        with 'Role::RequireFoo';
+    } '... the required "foo" method exists in the symbol table (and we will live)'; 
+           
+}    
+
+=pod
+
+Now same thing, but with a method from an attribute
+method modifier.
+
+=cut
+
+{
+    
+    package Class::ProvideFoo::Attr1;
+    use Mouse;
+    
+    extends 'Class::ProvideFoo::Base';
+    
+    ::lives_ok {
+        with 'Role::RequireFoo';
+    } '... the required "foo" method will be found in the superclass (but then overriden)';
+    
+    has 'foo' => (is => 'ro');
+    
+    package Class::ProvideFoo::Attr2;
+    use Mouse;
+    
+    extends 'Class::ProvideFoo::Base';
+    
+    has 'foo' => (is => 'ro');     
+    
+    ::lives_ok {
+        with 'Role::RequireFoo';
+    } '... the required "foo" method exists, and is an accessor';
+}    
+
+# ...
+# a method required in a role, but then 
+# implemented in the superclass (as an 
+# attribute accessor too)
+    
+{
+    package Foo::Class::Base;
+    use Mouse;
+    
+    has 'bar' =>  ( 
+        isa     => 'Int', 
+        is      => 'rw', 
+        default => sub { 1 }
+    );
+}
+{
+    package Foo::Role;
+    use Mouse::Role;
+    
+    requires 'bar';
+    
+    has 'foo' => ( 
+        isa     => 'Int', 
+        is      => 'rw', 
+        lazy    => 1, 
+        default => sub { (shift)->bar + 1 } 
+    );
+}
+{
+    package Foo::Class::Child;
+    use Mouse;
+    extends 'Foo::Class::Base';
+    
+    ::lives_ok {
+        with 'Foo::Role';
+    } '... our role combined successfully';
+}
+
+# a method required in a role and implemented in a superclass, with a method
+# modifier in the subclass.  this should live, but dies in 0.26 -- hdp,
+# 2007-10-11
+
+{
+    package Bar::Class::Base;
+    use Mouse;
+
+    sub bar { "hello!" }
+}
+{
+    package Bar::Role;
+    use Mouse::Role;
+    requires 'bar';
+}
+{
+    package Bar::Class::Child;
+    use Mouse;
+    extends 'Bar::Class::Base';
+    after bar => sub { "o noes" };
+    # technically we could run lives_ok here, too, but putting it into a
+    # grandchild class makes it more obvious why this matters.
+}
+{
+    package Bar::Class::Grandchild;
+    use Mouse;
+    extends 'Bar::Class::Child';
+    ::lives_ok {
+        with 'Bar::Role';
+    } 'required method exists in superclass as non-modifier, so we live';
+}
+
+{
+    package Bar2::Class::Base;
+    use Mouse;
+
+    sub bar { "hello!" }
+}
+{
+    package Bar2::Role;
+    use Mouse::Role;
+    requires 'bar';
+}
+{
+    package Bar2::Class::Child;
+    use Mouse;
+    extends 'Bar2::Class::Base';
+    override bar => sub { "o noes" };
+    # technically we could run lives_ok here, too, but putting it into a
+    # grandchild class makes it more obvious why this matters.
+}
+{
+    package Bar2::Class::Grandchild;
+    use Mouse;
+    extends 'Bar2::Class::Child';
+    ::lives_ok {
+        with 'Bar2::Role';
+    } 'required method exists in superclass as non-modifier, so we live';
+}
diff --git a/t/030_roles/failing/008_role_conflict_edge_cases.t b/t/030_roles/failing/008_role_conflict_edge_cases.t
new file mode 100644 (file)
index 0000000..57824f4
--- /dev/null
@@ -0,0 +1,188 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 32;
+use Test::Exception;
+
+=pod
+
+Check for repeated inheritance causing 
+a method conflict (which is not really 
+a conflict)
+
+=cut
+
+{
+    package Role::Base;
+    use Mouse::Role;
+    
+    sub foo { 'Role::Base::foo' }
+    
+    package Role::Derived1;
+    use Mouse::Role;  
+    
+    with 'Role::Base';
+    
+    package Role::Derived2;
+    use Mouse::Role; 
+
+    with 'Role::Base';
+    
+    package My::Test::Class1;
+    use Mouse;      
+    
+    ::lives_ok {
+        with 'Role::Derived1', 'Role::Derived2';   
+    } '... roles composed okay (no conflicts)';
+}
+
+ok(Role::Base->meta->has_method('foo'), '... have the method foo as expected');
+ok(Role::Derived1->meta->has_method('foo'), '... have the method foo as expected');
+ok(Role::Derived2->meta->has_method('foo'), '... have the method foo as expected');
+ok(My::Test::Class1->meta->has_method('foo'), '... have the method foo as expected');
+
+is(My::Test::Class1->foo, 'Role::Base::foo', '... got the right value from method');
+
+=pod
+
+Check for repeated inheritance causing 
+a method conflict with method modifiers 
+(which is not really a conflict)
+
+=cut
+
+{
+    package Role::Base2;
+    use Mouse::Role;
+    
+    override 'foo' => sub { super() . ' -> Role::Base::foo' };
+    
+    package Role::Derived3;
+    use Mouse::Role;  
+    
+    with 'Role::Base2';
+    
+    package Role::Derived4;
+    use Mouse::Role; 
+
+    with 'Role::Base2';
+
+    package My::Test::Class2::Base;
+    use Mouse;
+    
+    sub foo { 'My::Test::Class2::Base' }
+    
+    package My::Test::Class2;
+    use Mouse;  
+    
+    extends 'My::Test::Class2::Base';    
+    
+    ::lives_ok {
+        with 'Role::Derived3', 'Role::Derived4';   
+    } '... roles composed okay (no conflicts)';
+}
+
+ok(Role::Base2->meta->has_override_method_modifier('foo'), '... have the method foo as expected');
+ok(Role::Derived3->meta->has_override_method_modifier('foo'), '... have the method foo as expected');
+ok(Role::Derived4->meta->has_override_method_modifier('foo'), '... have the method foo as expected');
+ok(My::Test::Class2->meta->has_method('foo'), '... have the method foo as expected');
+isa_ok(My::Test::Class2->meta->get_method('foo'), 'Mouse::Meta::Method::Overridden');
+ok(My::Test::Class2::Base->meta->has_method('foo'), '... have the method foo as expected');
+isa_ok(My::Test::Class2::Base->meta->get_method('foo'), 'Class::MOP::Method');
+
+is(My::Test::Class2::Base->foo, 'My::Test::Class2::Base', '... got the right value from method');
+is(My::Test::Class2->foo, 'My::Test::Class2::Base -> Role::Base::foo', '... got the right value from method');
+
+=pod
+
+Check for repeated inheritance of the 
+same code. There are no conflicts with 
+before/around/after method modifiers.
+
+This tests around, but should work the 
+same for before/afters as well
+
+=cut
+
+{
+    package Role::Base3;
+    use Mouse::Role;
+    
+    around 'foo' => sub { 'Role::Base::foo(' . (shift)->() . ')' };
+    
+    package Role::Derived5;
+    use Mouse::Role;  
+    
+    with 'Role::Base3';
+    
+    package Role::Derived6;
+    use Mouse::Role; 
+
+    with 'Role::Base3';
+
+    package My::Test::Class3::Base;
+    use Mouse;
+    
+    sub foo { 'My::Test::Class3::Base' }
+    
+    package My::Test::Class3;
+    use Mouse;  
+    
+    extends 'My::Test::Class3::Base';    
+    
+    ::lives_ok {
+        with 'Role::Derived5', 'Role::Derived6';   
+    } '... roles composed okay (no conflicts)';
+}
+
+ok(Role::Base3->meta->has_around_method_modifiers('foo'), '... have the method foo as expected');
+ok(Role::Derived5->meta->has_around_method_modifiers('foo'), '... have the method foo as expected');
+ok(Role::Derived6->meta->has_around_method_modifiers('foo'), '... have the method foo as expected');
+ok(My::Test::Class3->meta->has_method('foo'), '... have the method foo as expected');
+isa_ok(My::Test::Class3->meta->get_method('foo'), 'Class::MOP::Method::Wrapped');
+ok(My::Test::Class3::Base->meta->has_method('foo'), '... have the method foo as expected');
+isa_ok(My::Test::Class3::Base->meta->get_method('foo'), 'Class::MOP::Method');
+
+is(My::Test::Class3::Base->foo, 'My::Test::Class3::Base', '... got the right value from method');
+is(My::Test::Class3->foo, 'Role::Base::foo(My::Test::Class3::Base)', '... got the right value from method');
+
+=pod
+
+Check for repeated inheritance causing 
+a attr conflict (which is not really 
+a conflict)
+
+=cut
+
+{
+    package Role::Base4;
+    use Mouse::Role;
+    
+    has 'foo' => (is => 'ro', default => 'Role::Base::foo');
+    
+    package Role::Derived7;
+    use Mouse::Role;  
+    
+    with 'Role::Base4';
+    
+    package Role::Derived8;
+    use Mouse::Role; 
+
+    with 'Role::Base4';
+    
+    package My::Test::Class4;
+    use Mouse;      
+    
+    ::lives_ok {
+        with 'Role::Derived7', 'Role::Derived8';   
+    } '... roles composed okay (no conflicts)';
+}
+
+ok(Role::Base4->meta->has_attribute('foo'), '... have the attribute foo as expected');
+ok(Role::Derived7->meta->has_attribute('foo'), '... have the attribute foo as expected');
+ok(Role::Derived8->meta->has_attribute('foo'), '... have the attribute foo as expected');
+ok(My::Test::Class4->meta->has_attribute('foo'), '... have the attribute foo as expected');
+
+is(My::Test::Class4->new->foo, 'Role::Base::foo', '... got the right value from method');
diff --git a/t/030_roles/failing/009_more_role_edge_cases.t b/t/030_roles/failing/009_more_role_edge_cases.t
new file mode 100644 (file)
index 0000000..79abf14
--- /dev/null
@@ -0,0 +1,256 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 74;
+use Test::Exception;
+
+
+
+{
+    # NOTE:
+    # this tests that repeated role 
+    # composition will not cause 
+    # a conflict between two methods
+    # which are actually the same anyway
+    
+    {
+        package RootA;
+        use Mouse::Role;
+
+        sub foo { "RootA::foo" }
+
+        package SubAA;
+        use Mouse::Role;
+
+        with "RootA";
+
+        sub bar { "SubAA::bar" }
+
+        package SubAB;
+        use Mouse;
+
+        ::lives_ok { 
+            with "SubAA", "RootA"; 
+        } '... role was composed as expected';
+    }
+
+    ok( SubAB->does("SubAA"), "does SubAA");
+    ok( SubAB->does("RootA"), "does RootA");
+
+    isa_ok( my $i = SubAB->new, "SubAB" );
+
+    can_ok( $i, "bar" );
+    is( $i->bar, "SubAA::bar", "... got thr right bar rv" );
+
+    can_ok( $i, "foo" );
+    my $foo_rv;
+    lives_ok { 
+        $foo_rv = $i->foo; 
+    } '... called foo successfully';
+    is($foo_rv, "RootA::foo", "... got the right foo rv");
+}
+
+{
+    # NOTE:
+    # this edge cases shows the application of 
+    # an after modifier over a method which 
+    # was added during role composotion.
+    # The way this will work is as follows:
+    #    role SubBA will consume RootB and 
+    #    get a local copy of RootB::foo, it 
+    #    will also store a deferred after modifier
+    #    to be applied to whatever class SubBA is 
+    #    composed into.
+    #    When class SubBB comsumed role SubBA, the
+    #    RootB::foo method is added to SubBB, then 
+    #    the deferred after modifier from SubBA is 
+    #    applied to it.
+    # It is important to note that the application 
+    # of the after modifier does not happen until 
+    # role SubBA is composed into SubAA.
+    
+    {
+        package RootB;
+        use Mouse::Role;
+
+        sub foo { "RootB::foo" }
+
+        package SubBA;
+        use Mouse::Role;
+
+        with "RootB";
+
+        has counter => (
+            isa => "Num",
+            is  => "rw",
+            default => 0,
+        );
+
+        after foo => sub {
+            $_[0]->counter( $_[0]->counter + 1 );
+        };
+
+        package SubBB;
+        use Mouse;
+
+        ::lives_ok { 
+            with "SubBA";
+        } '... composed the role successfully';
+    }
+
+    ok( SubBB->does("SubBA"), "BB does SubBA" );
+    ok( SubBB->does("RootB"), "BB does RootB" );
+
+    isa_ok( my $i = SubBB->new, "SubBB" );
+
+    can_ok( $i, "foo" );
+    
+    my $foo_rv;
+    lives_ok { 
+        $foo_rv = $i->foo 
+    } '... called foo successfully';
+    is( $foo_rv, "RootB::foo", "foo rv" );
+    is( $i->counter, 1, "after hook called" );
+    
+    lives_ok { $i->foo } '... called foo successfully (again)';
+    is( $i->counter, 2, "after hook called (again)" );
+    
+    ok(SubBA->meta->has_method('foo'), '... this has the foo method');
+    #my $subba_foo_rv;
+    #lives_ok { 
+    #    $subba_foo_rv = SubBA::foo(); 
+    #} '... called the sub as a function correctly';
+    #is($subba_foo_rv, 'RootB::foo', '... the SubBA->foo is still the RootB version');
+}
+
+{
+    # NOTE:
+    # this checks that an override method
+    # does not try to trample over a locally
+    # composed in method. In this case the 
+    # RootC::foo, which is composed into 
+    # SubCA cannot be trampled with an 
+    # override of 'foo'
+    {
+        package RootC;
+        use Mouse::Role;
+
+        sub foo { "RootC::foo" }
+
+        package SubCA;
+        use Mouse::Role;
+
+        with "RootC";
+
+        ::dies_ok { 
+            override foo => sub { "overridden" };
+        } '... cannot compose an override over a local method';
+    }
+}
+
+# NOTE:
+# need to talk to Yuval about the motivation behind 
+# this test, I am not sure we are testing anything 
+# useful here (although more tests cant hurt)
+
+{
+    use List::Util qw/shuffle/;
+
+    {
+        package Abstract;
+        use Mouse::Role;
+
+        requires "method";
+        requires "other";
+
+        sub another { "abstract" }
+
+        package ConcreteA;
+        use Mouse::Role;
+        with "Abstract";
+
+        sub other { "concrete a" }
+
+        package ConcreteB;
+        use Mouse::Role;
+        with "Abstract";
+
+        sub method { "concrete b" }
+
+        package ConcreteC;
+        use Mouse::Role;
+        with "ConcreteA";
+
+        # NOTE:
+        # this was originally override, but 
+        # that wont work (see above set of tests)
+        # so I switched it to around.
+        # However, this may not be testing the 
+        # same thing that was originally intended
+        around other => sub {
+            return ( (shift)->() . " + c" );
+        };
+
+        package SimpleClassWithSome;
+        use Mouse;
+
+        eval { with ::shuffle qw/ConcreteA ConcreteB/ };
+        ::ok( !$@, "simple composition without abstract" ) || ::diag $@;
+
+        package SimpleClassWithAll;
+        use Mouse;
+
+        eval { with ::shuffle qw/ConcreteA ConcreteB Abstract/ };
+        ::ok( !$@, "simple composition with abstract" ) || ::diag $@;
+    }
+
+    foreach my $class (qw/SimpleClassWithSome SimpleClassWithAll/) {
+        foreach my $role (qw/Abstract ConcreteA ConcreteB/) {
+            ok( $class->does($role), "$class does $role");
+        }
+
+        foreach my $method (qw/method other another/) {
+            can_ok( $class, $method );
+        }
+
+        is( eval { $class->another }, "abstract", "provided by abstract" );
+        is( eval { $class->other }, "concrete a", "provided by concrete a" );
+        is( eval { $class->method }, "concrete b", "provided by concrete b" );
+    }        
+
+    {
+        package ClassWithSome;
+        use Mouse;
+        
+        eval { with ::shuffle qw/ConcreteC ConcreteB/ };
+        ::ok( !$@, "composition without abstract" ) || ::diag $@;
+
+        package ClassWithAll;
+        use Mouse;
+
+        eval { with ::shuffle qw/ConcreteC Abstract ConcreteB/ };
+        ::ok( !$@, "composition with abstract" ) || ::diag $@;
+
+        package ClassWithEverything;
+        use Mouse;
+
+        eval { with ::shuffle qw/ConcreteC Abstract ConcreteA ConcreteB/ }; # this should clash
+        ::ok( !$@, "can compose ConcreteA and ConcreteC together" );
+    }
+
+    foreach my $class (qw/ClassWithSome ClassWithAll ClassWithEverything/) {
+        foreach my $role (qw/Abstract ConcreteA ConcreteB ConcreteC/) {
+            ok( $class->does($role), "$class does $role");
+        }
+
+        foreach my $method (qw/method other another/) {
+            can_ok( $class, $method );
+        }
+
+        is( eval { $class->another }, "abstract", "provided by abstract" );
+        is( eval { $class->other }, "concrete a + c", "provided by concrete c + a" );
+        is( eval { $class->method }, "concrete b", "provided by concrete b" );
+    }
+}
diff --git a/t/030_roles/failing/010_run_time_role_composition.t b/t/030_roles/failing/010_run_time_role_composition.t
new file mode 100644 (file)
index 0000000..df873d3
--- /dev/null
@@ -0,0 +1,106 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 27;
+
+use Scalar::Util qw(blessed);
+
+
+
+=pod
+
+This test can be used as a basis for the runtime role composition.
+Apparently it is not as simple as just making an anon class. One of 
+the problems is the way that anon classes are DESTROY-ed, which is
+not very compatible with how instances are dealt with.
+
+=cut
+
+{
+    package Bark;
+    use Mouse::Role;
+
+    sub talk { 'woof' }
+
+    package Sleeper;
+    use Mouse::Role;
+
+    sub sleep { 'snore' }
+    sub talk { 'zzz' }
+
+    package My::Class;
+    use Mouse;
+
+    sub sleep { 'nite-nite' }
+}
+
+my $obj = My::Class->new;
+isa_ok($obj, 'My::Class');    
+    
+my $obj2 = My::Class->new;
+isa_ok($obj2, 'My::Class');    
+
+{
+    ok(!$obj->can( 'talk' ), "... the role is not composed yet");
+    
+    ok(!$obj->does('Bark'), '... we do not do any roles yet');
+    
+    Bark->meta->apply($obj);
+
+    ok($obj->does('Bark'), '... we now do the Bark role');
+    ok(!My::Class->does('Bark'), '... the class does not do the Bark role');    
+
+    isa_ok($obj, 'My::Class');
+    isnt(blessed($obj), 'My::Class', '... but it is no longer blessed into My::Class');
+
+    ok(!My::Class->can('talk'), "... the role is not composed at the class level");
+    ok($obj->can('talk'), "... the role is now composed at the object level");
+    
+    is($obj->talk, 'woof', '... got the right return value for the newly composed method');
+}
+
+{
+    ok(!$obj2->does('Bark'), '... we do not do any roles yet');
+    
+    Bark->meta->apply($obj2);
+    
+    ok($obj2->does('Bark'), '... we now do the Bark role');
+    is(blessed($obj), blessed($obj2), '... they share the same anon-class/role thing');
+}
+
+{
+    is($obj->sleep, 'nite-nite', '... the original method responds as expected');
+
+    ok(!$obj->does('Sleeper'), '... we do not do the Sleeper role');
+
+    Sleeper->meta->apply($obj);
+
+    ok($obj->does('Bark'), '... we still do the Bark role');
+    ok($obj->does('Sleeper'), '... we now do the Sleeper role too');   
+    
+    ok(!My::Class->does('Sleeper'), '... the class does not do the Sleeper role');     
+    
+    isnt(blessed($obj), blessed($obj2), '... they no longer share the same anon-class/role thing');        
+    
+    isa_ok($obj, 'My::Class');
+
+    is(My::Class->sleep, 'nite-nite', '... the original method still responds as expected');
+
+    is($obj->sleep, 'snore', '... got the right return value for the newly composed method');
+    is($obj->talk, 'zzz', '... got the right return value for the newly composed method');    
+}
+
+{
+    ok(!$obj2->does('Sleeper'), '... we do not do any roles yet');
+    
+    Sleeper->meta->apply($obj2);
+    
+    ok($obj2->does('Sleeper'), '... we now do the Bark role');
+    is(blessed($obj), blessed($obj2), '... they share the same anon-class/role thing again');
+}
+
+
+
+
diff --git a/t/030_roles/failing/011_overriding.t b/t/030_roles/failing/011_overriding.t
new file mode 100644 (file)
index 0000000..01df0c3
--- /dev/null
@@ -0,0 +1,229 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 39;
+use Test::Exception;
+
+
+
+{ 
+    # test no conflicts here
+    package Role::A;
+    use Mouse::Role;
+
+    sub bar { 'Role::A::bar' }
+
+    package Role::B;
+    use Mouse::Role;
+
+    sub xxy { 'Role::B::xxy' }
+
+    package Role::C;
+    use Mouse::Role;
+    
+    ::lives_ok {
+        with qw(Role::A Role::B); # no conflict here
+    } "define role C";
+
+    sub foo { 'Role::C::foo' }
+    sub zot { 'Role::C::zot' }
+
+    package Class::A;
+    use Mouse;
+
+    ::lives_ok {
+        with qw(Role::C);
+    } "define class A";
+    
+    sub zot { 'Class::A::zot' }
+}
+
+can_ok( Class::A->new, qw(foo bar xxy zot) );
+
+is( Class::A->new->foo, "Role::C::foo",  "... got the right foo method" );
+is( Class::A->new->zot, "Class::A::zot", "... got the right zot method" );
+is( Class::A->new->bar, "Role::A::bar",  "... got the right bar method" );
+is( Class::A->new->xxy, "Role::B::xxy",  "... got the right xxy method" );
+
+{
+    # check that when a role is added to another role
+    # and they conflict and the method they conflicted
+    # with is then required. 
+    
+    package Role::A::Conflict;
+    use Mouse::Role;
+    
+    with 'Role::A';
+    
+    sub bar { 'Role::A::Conflict::bar' }
+    
+    package Class::A::Conflict;
+    use Mouse;
+    
+    ::throws_ok {
+        with 'Role::A::Conflict';
+    }  qr/requires.*'bar'/, '... did not fufill the requirement of &bar method';
+    
+    package Class::A::Resolved;
+    use Mouse;
+    
+    ::lives_ok {
+        with 'Role::A::Conflict';
+    } '... did fufill the requirement of &bar method';    
+    
+    sub bar { 'Class::A::Resolved::bar' }
+}
+
+ok(Role::A::Conflict->meta->requires_method('bar'), '... Role::A::Conflict created the bar requirement');
+
+can_ok( Class::A::Resolved->new, qw(bar) );
+
+is( Class::A::Resolved->new->bar, 'Class::A::Resolved::bar', "... got the right bar method" );
+
+{
+    # check that when two roles are composed, they conflict
+    # but the composing role can resolve that conflict
+    
+    package Role::D;
+    use Mouse::Role;
+
+    sub foo { 'Role::D::foo' }
+    sub bar { 'Role::D::bar' }    
+
+    package Role::E;
+    use Mouse::Role;
+
+    sub foo { 'Role::E::foo' }
+    sub xxy { 'Role::E::xxy' }
+
+    package Role::F;
+    use Mouse::Role;
+
+    ::lives_ok {
+        with qw(Role::D Role::E); # conflict between 'foo's here
+    } "define role Role::F";
+    
+    sub foo { 'Role::F::foo' }
+    sub zot { 'Role::F::zot' }    
+    
+    package Class::B;
+    use Mouse;
+    
+    ::lives_ok {
+        with qw(Role::F);
+    } "define class Class::B";
+    
+    sub zot { 'Class::B::zot' }
+}
+
+can_ok( Class::B->new, qw(foo bar xxy zot) );
+
+is( Class::B->new->foo, "Role::F::foo",  "... got the &foo method okay" );
+is( Class::B->new->zot, "Class::B::zot", "... got the &zot method okay" );
+is( Class::B->new->bar, "Role::D::bar",  "... got the &bar method okay" );
+is( Class::B->new->xxy, "Role::E::xxy",  "... got the &xxy method okay" );
+
+ok(!Role::F->meta->requires_method('foo'), '... Role::F fufilled the &foo requirement');
+
+{
+    # check that a conflict can be resolved
+    # by a role, but also new ones can be 
+    # created just as easily ...
+    
+    package Role::D::And::E::Conflict;
+    use Mouse::Role;
+
+    ::lives_ok {
+        with qw(Role::D Role::E); # conflict between 'foo's here
+    } "... define role Role::D::And::E::Conflict";
+    
+    sub foo { 'Role::D::And::E::Conflict::foo' }  # this overrides ...
+      
+    # but these conflict      
+    sub xxy { 'Role::D::And::E::Conflict::xxy' }  
+    sub bar { 'Role::D::And::E::Conflict::bar' }        
+
+}
+
+ok(!Role::D::And::E::Conflict->meta->requires_method('foo'), '... Role::D::And::E::Conflict fufilled the &foo requirement');
+ok(Role::D::And::E::Conflict->meta->requires_method('xxy'), '... Role::D::And::E::Conflict adds the &xxy requirement');
+ok(Role::D::And::E::Conflict->meta->requires_method('bar'), '... Role::D::And::E::Conflict adds the &bar requirement');
+
+{
+    # conflict propagation
+    
+    package Role::H;
+    use Mouse::Role;
+
+    sub foo { 'Role::H::foo' }
+    sub bar { 'Role::H::bar' }    
+
+    package Role::J;
+    use Mouse::Role;
+
+    sub foo { 'Role::J::foo' }
+    sub xxy { 'Role::J::xxy' }
+
+    package Role::I;
+    use Mouse::Role;
+
+    ::lives_ok {
+        with qw(Role::J Role::H); # conflict between 'foo's here
+    } "define role Role::I";
+    
+    sub zot { 'Role::I::zot' }
+    sub zzy { 'Role::I::zzy' }
+
+    package Class::C;
+    use Mouse;
+    
+    ::throws_ok {
+        with qw(Role::I);
+    } qr/requires.*'foo'/, "defining class Class::C fails";
+
+    sub zot { 'Class::C::zot' }
+
+    package Class::E;
+    use Mouse;
+
+    ::lives_ok {
+        with qw(Role::I);
+    } "resolved with method";        
+
+    sub foo { 'Class::E::foo' }
+    sub zot { 'Class::E::zot' }    
+}
+
+can_ok( Class::E->new, qw(foo bar xxy zot) );
+
+is( Class::E->new->foo, "Class::E::foo", "... got the right &foo method" );
+is( Class::E->new->zot, "Class::E::zot", "... got the right &zot method" );
+is( Class::E->new->bar, "Role::H::bar",  "... got the right &bar method" );
+is( Class::E->new->xxy, "Role::J::xxy",  "... got the right &xxy method" );
+
+ok(Role::I->meta->requires_method('foo'), '... Role::I still have the &foo requirement');
+
+{
+    lives_ok {
+        package Class::D;
+        use Mouse;
+
+        has foo => ( default => __PACKAGE__ . "::foo", is => "rw" );
+
+        sub zot { 'Class::D::zot' }
+
+        with qw(Role::I);
+
+    } "resolved with attr";
+
+    can_ok( Class::D->new, qw(foo bar xxy zot) );
+    is( eval { Class::D->new->bar }, "Role::H::bar", "bar" );
+    is( eval { Class::D->new->zzy }, "Role::I::zzy", "zzy" );
+
+    is( eval { Class::D->new->foo }, "Class::D::foo", "foo" );
+    is( eval { Class::D->new->zot }, "Class::D::zot", "zot" );
+
+}
+
diff --git a/t/030_roles/failing/012_method_exclusion_in_composition.t b/t/030_roles/failing/012_method_exclusion_in_composition.t
new file mode 100644 (file)
index 0000000..56d8516
--- /dev/null
@@ -0,0 +1,115 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 19;
+use Test::Exception;
+
+
+
+{
+    package My::Role;
+    use Mouse::Role;
+
+    sub foo { 'Foo::foo' }
+    sub bar { 'Foo::bar' }
+    sub baz { 'Foo::baz' }
+
+    package My::Class;
+    use Mouse;
+
+    with 'My::Role' => { excludes => 'bar' };
+}
+
+ok(My::Class->meta->has_method($_), "we have a $_ method") for qw(foo baz);
+ok(!My::Class->meta->has_method('bar'), '... but we excluded bar');
+
+{
+    package My::OtherRole;
+    use Mouse::Role;
+
+    with 'My::Role' => { excludes => 'foo' };
+
+    sub foo { 'My::OtherRole::foo' }
+    sub bar { 'My::OtherRole::bar' }
+}
+
+ok(My::OtherRole->meta->has_method($_), "we have a $_ method") for qw(foo bar baz);
+
+ok(!My::OtherRole->meta->requires_method('foo'), '... and the &foo method is not required');
+ok(My::OtherRole->meta->requires_method('bar'), '... and the &bar method is required');
+
+{
+    package Foo::Role;
+    use Mouse::Role;
+    
+    sub foo { 'Foo::Role::foo' }
+    
+    package Bar::Role;
+    use Mouse::Role;
+    
+    sub foo { 'Bar::Role::foo' }    
+
+    package Baz::Role;
+    use Mouse::Role;
+    
+    sub foo { 'Baz::Role::foo' }   
+    
+    package My::Foo::Class;
+    use Mouse;
+    
+    ::lives_ok {
+        with 'Foo::Role' => { excludes => 'foo' },
+             'Bar::Role' => { excludes => 'foo' }, 
+             'Baz::Role';
+    } '... composed our roles correctly';
+    
+    package My::Foo::Class::Broken;
+    use Mouse;
+    
+    ::throws_ok {
+        with 'Foo::Role',
+             'Bar::Role' => { excludes => 'foo' }, 
+             'Baz::Role';
+    } qr/\'Foo::Role\|Bar::Role\|Baz::Role\' requires the method \'foo\' to be implemented by \'My::Foo::Class::Broken\'/, 
+      '... composed our roles correctly';    
+}
+
+{
+    my $foo = My::Foo::Class->new;
+    isa_ok($foo, 'My::Foo::Class');
+    can_ok($foo, 'foo');
+    is($foo->foo, 'Baz::Role::foo', '... got the right method');
+}
+
+{
+    package My::Foo::Role;
+    use Mouse::Role;
+
+    ::lives_ok {
+        with 'Foo::Role' => { excludes => 'foo' },
+             'Bar::Role' => { excludes => 'foo' }, 
+             'Baz::Role';
+    } '... composed our roles correctly';
+}
+
+ok(My::Foo::Role->meta->has_method('foo'), "we have a foo method");
+ok(!My::Foo::Role->meta->requires_method('foo'), '... and the &foo method is not required');
+
+{
+    package My::Foo::Role::Other;
+    use Mouse::Role;
+
+    ::lives_ok {
+        with 'Foo::Role',
+             'Bar::Role' => { excludes => 'foo' }, 
+             'Baz::Role';
+    } '... composed our roles correctly';
+}
+
+ok(!My::Foo::Role::Other->meta->has_method('foo'), "we dont have a foo method");
+ok(My::Foo::Role::Other->meta->requires_method('foo'), '... and the &foo method is required');
+
+
+
diff --git a/t/030_roles/failing/013_method_aliasing_in_composition.t b/t/030_roles/failing/013_method_aliasing_in_composition.t
new file mode 100644 (file)
index 0000000..2fbdbe8
--- /dev/null
@@ -0,0 +1,149 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 35;
+use Test::Exception;
+
+
+
+{
+    package My::Role;
+    use Mouse::Role;
+
+    sub foo { 'Foo::foo' }
+    sub bar { 'Foo::bar' }
+    sub baz { 'Foo::baz' }
+    
+    requires 'role_bar';
+
+    package My::Class;
+    use Mouse;
+
+    ::lives_ok {
+        with 'My::Role' => { alias => { bar => 'role_bar' } };
+    } '... this succeeds';
+    
+    package My::Class::Failure;
+    use Mouse;
+
+    ::throws_ok {
+        with 'My::Role' => { alias => { bar => 'role_bar' } };
+    } qr/Cannot create a method alias if a local method of the same name exists/, '... this succeeds';    
+    
+    sub role_bar { 'FAIL' }
+}
+
+ok(My::Class->meta->has_method($_), "we have a $_ method") for qw(foo baz bar role_bar);
+
+{
+    package My::OtherRole;
+    use Mouse::Role;
+
+    ::lives_ok {
+        with 'My::Role' => { alias => { bar => 'role_bar' } };
+    } '... this succeeds';
+
+    sub bar { 'My::OtherRole::bar' }
+    
+    package My::OtherRole::Failure;
+    use Mouse::Role;
+
+    ::throws_ok {
+        with 'My::Role' => { alias => { bar => 'role_bar' } };
+    } qr/Cannot create a method alias if a local method of the same name exists/, '... this succeeds';    
+    
+    sub role_bar { 'FAIL' }    
+}
+
+ok(My::OtherRole->meta->has_method($_), "we have a $_ method") for qw(foo baz role_bar);
+ok(!My::OtherRole->meta->requires_method('bar'), '... and the &bar method is not required');
+ok(!My::OtherRole->meta->requires_method('role_bar'), '... and the &role_bar method is not required');
+
+{
+    package My::AliasingRole;
+    use Mouse::Role;
+
+    ::lives_ok {
+        with 'My::Role' => { alias => { bar => 'role_bar' } };
+    } '... this succeeds';
+}
+
+ok(My::AliasingRole->meta->has_method($_), "we have a $_ method") for qw(foo baz role_bar);
+ok(My::AliasingRole->meta->requires_method('bar'), '... and the &bar method is required');
+
+{
+    package Foo::Role;
+    use Mouse::Role;
+    
+    sub foo { 'Foo::Role::foo' }
+    
+    package Bar::Role;
+    use Mouse::Role;
+    
+    sub foo { 'Bar::Role::foo' }    
+
+    package Baz::Role;
+    use Mouse::Role;
+    
+    sub foo { 'Baz::Role::foo' }   
+    
+    package My::Foo::Class;
+    use Mouse;
+    
+    ::lives_ok {
+        with 'Foo::Role' => { alias => { 'foo' => 'foo_foo' }, excludes => 'foo' },
+             'Bar::Role' => { alias => { 'foo' => 'bar_foo' }, excludes => 'foo' }, 
+             'Baz::Role';
+    } '... composed our roles correctly';   
+    
+    package My::Foo::Class::Broken;
+    use Mouse;
+    
+    ::throws_ok {
+        with 'Foo::Role' => { alias => { 'foo' => 'foo_foo' }, excludes => 'foo' },
+             'Bar::Role' => { alias => { 'foo' => 'foo_foo' }, excludes => 'foo' }, 
+             'Baz::Role';
+    } qr/\'Foo::Role\|Bar::Role\|Baz::Role\' requires the method \'foo_foo\' to be implemented by \'My::Foo::Class::Broken\'/, 
+      '... composed our roles correctly';    
+}
+
+{
+    my $foo = My::Foo::Class->new;
+    isa_ok($foo, 'My::Foo::Class');
+    can_ok($foo, $_) for qw/foo foo_foo bar_foo/;
+    is($foo->foo, 'Baz::Role::foo', '... got the right method');
+    is($foo->foo_foo, 'Foo::Role::foo', '... got the right method');    
+    is($foo->bar_foo, 'Bar::Role::foo', '... got the right method');        
+}
+
+{
+    package My::Foo::Role;
+    use Mouse::Role;
+
+    ::lives_ok {
+        with 'Foo::Role' => { alias => { 'foo' => 'foo_foo' }, excludes => 'foo' },
+             'Bar::Role' => { alias => { 'foo' => 'bar_foo' }, excludes => 'foo' }, 
+             'Baz::Role';
+    } '... composed our roles correctly';
+}
+
+ok(My::Foo::Role->meta->has_method($_), "we have a $_ method") for qw/foo foo_foo bar_foo/;;
+ok(!My::Foo::Role->meta->requires_method('foo'), '... and the &foo method is not required');
+
+
+{
+    package My::Foo::Role::Other;
+    use Mouse::Role;
+
+    ::lives_ok {
+        with 'Foo::Role' => { alias => { 'foo' => 'foo_foo' }, excludes => 'foo' },
+             'Bar::Role' => { alias => { 'foo' => 'foo_foo' }, excludes => 'foo' }, 
+             'Baz::Role';
+    } '... composed our roles correctly';
+}
+
+ok(!My::Foo::Role::Other->meta->has_method('foo_foo'), "we dont have a foo_foo method");
+ok(My::Foo::Role::Other->meta->requires_method('foo_foo'), '... and the &foo method is required');
+
diff --git a/t/030_roles/failing/014_more_alias_and_exclude.t b/t/030_roles/failing/014_more_alias_and_exclude.t
new file mode 100644 (file)
index 0000000..b9c9189
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 9;
+use Test::Exception;
+
+
+
+{
+    package Foo;
+    use Mouse::Role;
+    
+    sub foo   { 'Foo::foo'   }
+    sub bar   { 'Foo::bar'   }
+    sub baz   { 'Foo::baz'   }
+    sub gorch { 'Foo::gorch' }            
+    
+    package Bar;
+    use Mouse::Role;
+
+    sub foo   { 'Bar::foo'   }
+    sub bar   { 'Bar::bar'   }
+    sub baz   { 'Bar::baz'   }
+    sub gorch { 'Bar::gorch' }    
+
+    package Baz;
+    use Mouse::Role;
+    
+    sub foo   { 'Baz::foo'   }
+    sub bar   { 'Baz::bar'   }
+    sub baz   { 'Baz::baz'   }
+    sub gorch { 'Baz::gorch' }            
+    
+    package Gorch;
+    use Mouse::Role;
+    
+    sub foo   { 'Gorch::foo'   }
+    sub bar   { 'Gorch::bar'   }
+    sub baz   { 'Gorch::baz'   }
+    sub gorch { 'Gorch::gorch' }        
+}
+
+{
+    package My::Class;
+    use Mouse;
+    
+    ::lives_ok {
+        with 'Foo'   => { excludes => [qw/bar baz gorch/], alias => { gorch => 'foo_gorch' } },
+             'Bar'   => { excludes => [qw/foo baz gorch/] },
+             'Baz'   => { excludes => [qw/foo bar gorch/], alias => { foo => 'baz_foo', bar => 'baz_bar' } },
+             'Gorch' => { excludes => [qw/foo bar baz/] };
+    } '... everything works out all right';
+}
+
+my $c = My::Class->new;
+isa_ok($c, 'My::Class');
+
+is($c->foo, 'Foo::foo', '... got the right method');
+is($c->bar, 'Bar::bar', '... got the right method');
+is($c->baz, 'Baz::baz', '... got the right method');
+is($c->gorch, 'Gorch::gorch', '... got the right method');
+
+is($c->foo_gorch, 'Foo::gorch', '... got the right method');
+is($c->baz_foo, 'Baz::foo', '... got the right method');
+is($c->baz_bar, 'Baz::bar', '... got the right method');
+
+
+
+
+
diff --git a/t/030_roles/failing/015_runtime_roles_and_attrs.t b/t/030_roles/failing/015_runtime_roles_and_attrs.t
new file mode 100644 (file)
index 0000000..8d6bfc2
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 11;
+use Test::Exception;
+use Scalar::Util 'blessed';
+
+
+
+
+{
+    package Dog;
+    use Mouse::Role;
+
+    sub talk { 'woof' }
+
+    has fur => (
+        isa => "Str",
+        is  => "rw",
+        default => "dirty",
+    );
+
+    package Foo;
+    use Mouse;
+
+    has 'dog' => (
+        is   => 'rw',
+        does => 'Dog',
+    );
+}
+
+my $obj = Foo->new;
+isa_ok($obj, 'Foo');    
+
+ok(!$obj->can( 'talk' ), "... the role is not composed yet");
+ok(!$obj->can( 'fur' ), 'ditto');
+ok(!$obj->does('Dog'), '... we do not do any roles yet');
+
+dies_ok {
+    $obj->dog($obj)
+} '... and setting the accessor fails (not a Dog yet)';
+
+Dog->meta->apply($obj);
+
+ok($obj->does('Dog'), '... we now do the Bark role');
+ok($obj->can('talk'), "... the role is now composed at the object level");
+ok($obj->can('fur'), "it has fur");
+
+is($obj->talk, 'woof', '... got the right return value for the newly composed method');
+
+lives_ok {
+    $obj->dog($obj)
+} '... and setting the accessor is okay';
+
+is($obj->fur, "dirty", "role attr initialized");
diff --git a/t/030_roles/failing/016_runtime_roles_and_nonmoose.t b/t/030_roles/failing/016_runtime_roles_and_nonmoose.t
new file mode 100644 (file)
index 0000000..6a39f77
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 7;
+use Test::Exception;
+use Scalar::Util 'blessed';
+
+
+
+
+{
+    package Dog;
+    use Mouse::Role;
+
+    sub talk { 'woof' }
+
+    package Foo;
+    use Mouse;
+
+    has 'dog' => (
+        is   => 'rw',
+        does => 'Dog',
+    );
+
+    no Mouse;
+
+    package Bar;
+
+    sub new {
+      return bless {}, shift;
+    }
+}
+
+my $bar = Bar->new;
+isa_ok($bar, 'Bar');    
+
+my $foo = Foo->new;
+isa_ok($foo, 'Foo');  
+
+ok(!$bar->can( 'talk' ), "... the role is not composed yet");
+
+dies_ok {
+    $foo->dog($bar)
+} '... and setting the accessor fails (not a Dog yet)';
+
+Dog->meta->apply($bar);
+
+ok($bar->can('talk'), "... the role is now composed at the object level");
+
+is($bar->talk, 'woof', '... got the right return value for the newly composed method');
+
+lives_ok {
+    $foo->dog($bar)
+} '... and setting the accessor is okay';
+
diff --git a/t/030_roles/failing/017_extending_role_attrs.t b/t/030_roles/failing/017_extending_role_attrs.t
new file mode 100644 (file)
index 0000000..de47ece
--- /dev/null
@@ -0,0 +1,174 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 27;
+use Test::Exception;
+
+
+
+=pod
+
+This basically just makes sure that using +name 
+on role attributes works right.
+
+=cut
+
+{
+    package Foo::Role;
+    use Mouse::Role;
+    
+    has 'bar' => (
+        is      => 'rw',
+        isa     => 'Int',   
+        default => sub { 10 },
+    );
+    
+    package Foo;
+    use Mouse;
+    
+    with 'Foo::Role';
+    
+    ::lives_ok {
+        has '+bar' => (default => sub { 100 });
+    } '... extended the attribute successfully';  
+}
+
+my $foo = Foo->new;
+isa_ok($foo, 'Foo');
+
+is($foo->bar, 100, '... got the extended attribute');
+
+
+{
+    package Bar::Role;
+    use Mouse::Role;
+
+    has 'foo' => (
+        is      => 'rw',
+        isa     => 'Str | Int',
+    );
+
+    package Bar;
+    use Mouse;
+
+    with 'Bar::Role';
+
+    ::lives_ok {
+        has '+foo' => (
+            isa => 'Int',
+        )
+    } "... narrowed the role's type constraint successfully";
+}
+
+my $bar = Bar->new(foo => 42);
+isa_ok($bar, 'Bar');
+is($bar->foo, 42, '... got the extended attribute');
+$bar->foo(100);
+is($bar->foo, 100, "... can change the attribute's value to an Int");
+
+throws_ok { $bar->foo("baz") } qr/^Attribute \(foo\) does not pass the type constraint because: Validation failed for 'Int' failed with value baz at /;
+is($bar->foo, 100, "... still has the old Int value");
+
+
+{
+    package Baz::Role;
+    use Mouse::Role;
+
+    has 'baz' => (
+        is      => 'rw',
+        isa     => 'Value',
+    );
+
+    package Baz;
+    use Mouse;
+
+    with 'Baz::Role';
+
+    ::lives_ok {
+        has '+baz' => (
+            isa => 'Int | ClassName',
+        )
+    } "... narrowed the role's type constraint successfully";
+}
+
+my $baz = Baz->new(baz => 99);
+isa_ok($baz, 'Baz');
+is($baz->baz, 99, '... got the extended attribute');
+$baz->baz('Foo');
+is($baz->baz, 'Foo', "... can change the attribute's value to a ClassName");
+
+throws_ok { $baz->baz("zonk") } qr/^Attribute \(baz\) does not pass the type constraint because: Validation failed for 'ClassName\|Int' failed with value zonk at /;
+is_deeply($baz->baz, 'Foo', "... still has the old ClassName value");
+
+
+{
+    package Quux::Role;
+    use Mouse::Role;
+
+    has 'quux' => (
+        is      => 'rw',
+        isa     => 'Str | Int | Ref',
+    );
+
+    package Quux;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    with 'Quux::Role';
+
+    subtype 'Positive'
+        => as 'Int'
+        => where { $_ > 0 };
+
+    ::lives_ok {
+        has '+quux' => (
+            isa => 'Positive | ArrayRef',
+        )
+    } "... narrowed the role's type constraint successfully";
+}
+
+my $quux = Quux->new(quux => 99);
+isa_ok($quux, 'Quux');
+is($quux->quux, 99, '... got the extended attribute');
+$quux->quux(100);
+is($quux->quux, 100, "... can change the attribute's value to an Int");
+$quux->quux(["hi"]);
+is_deeply($quux->quux, ["hi"], "... can change the attribute's value to an ArrayRef");
+
+throws_ok { $quux->quux("quux") } qr/^Attribute \(quux\) does not pass the type constraint because: Validation failed for 'ArrayRef\|Positive' failed with value quux at /;
+is_deeply($quux->quux, ["hi"], "... still has the old ArrayRef value");
+
+throws_ok { $quux->quux({a => 1}) } qr/^Attribute \(quux\) does not pass the type constraint because: Validation failed for 'ArrayRef\|Positive' failed with value HASH\(\w+\) at /;
+is_deeply($quux->quux, ["hi"], "... still has the old ArrayRef value");
+
+
+{
+    package Err::Role;
+    use Mouse::Role;
+
+    for (1..3) {
+        has "err$_" => (
+            isa => 'Str | Int',
+        );
+    }
+
+    package Err;
+    use Mouse;
+
+    with 'Err::Role';
+
+    ::lives_ok {
+        has '+err1' => (isa => 'Defined');
+    } "can get less specific in the subclass";
+
+    ::lives_ok {
+        has '+err2' => (isa => 'Bool');
+    } "or change the type completely";
+
+    ::lives_ok {
+        has '+err3' => (isa => 'Str | ArrayRef');
+    } "or add new types to the union";
+}
+
diff --git a/t/030_roles/failing/018_runtime_roles_w_params.t b/t/030_roles/failing/018_runtime_roles_w_params.t
new file mode 100644 (file)
index 0000000..16d97f7
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 21;
+use Test::Exception;
+
+
+
+{
+    package Foo;
+    use Mouse;
+    has 'bar' => (is => 'ro');
+    
+    package Bar;
+    use Mouse::Role;
+    
+    has 'baz' => (is => 'ro', default => 'BAZ');    
+}
+
+# normal ...
+{
+    my $foo = Foo->new(bar => 'BAR');
+    isa_ok($foo, 'Foo');
+
+    is($foo->bar, 'BAR', '... got the expect value');
+    ok(!$foo->can('baz'), '... no baz method though');
+
+    lives_ok {
+        Bar->meta->apply($foo)
+    } '... this works';
+
+    is($foo->bar, 'BAR', '... got the expect value');
+    ok($foo->can('baz'), '... we have baz method now');
+    is($foo->baz, 'BAZ', '... got the expect value');
+}
+
+# with extra params ...
+{
+    my $foo = Foo->new(bar => 'BAR');
+    isa_ok($foo, 'Foo');
+
+    is($foo->bar, 'BAR', '... got the expect value');
+    ok(!$foo->can('baz'), '... no baz method though');
+
+    lives_ok {
+        Bar->meta->apply($foo, (rebless_params => { baz => 'FOO-BAZ' }))
+    } '... this works';
+
+    is($foo->bar, 'BAR', '... got the expect value');
+    ok($foo->can('baz'), '... we have baz method now');
+    is($foo->baz, 'FOO-BAZ', '... got the expect value');
+}
+
+# with extra params ...
+{
+    my $foo = Foo->new(bar => 'BAR');
+    isa_ok($foo, 'Foo');
+
+    is($foo->bar, 'BAR', '... got the expect value');
+    ok(!$foo->can('baz'), '... no baz method though');
+
+    lives_ok {
+        Bar->meta->apply($foo, (rebless_params => { bar => 'FOO-BAR', baz => 'FOO-BAZ' }))
+    } '... this works';
+
+    is($foo->bar, 'FOO-BAR', '... got the expect value');
+    ok($foo->can('baz'), '... we have baz method now');
+    is($foo->baz, 'FOO-BAZ', '... got the expect value');
+}
+
+
diff --git a/t/030_roles/failing/020_role_composite.t b/t/030_roles/failing/020_role_composite.t
new file mode 100644 (file)
index 0000000..788b352
--- /dev/null
@@ -0,0 +1,80 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 14;
+use Test::Exception;
+
+use Mouse::Meta::Role::Application::RoleSummation;
+use Mouse::Meta::Role::Composite;
+
+{
+    package Role::Foo;
+    use Mouse::Role;
+    
+    package Role::Bar;
+    use Mouse::Role;
+
+    package Role::Baz;
+    use Mouse::Role;      
+    
+    package Role::Gorch;
+    use Mouse::Role;       
+}
+
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::Bar->meta,
+            Role::Baz->meta,            
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::Bar|Role::Baz', '... got the composite role name');
+
+    is_deeply($c->get_roles, [
+        Role::Foo->meta,
+        Role::Bar->meta,
+        Role::Baz->meta,        
+    ], '... got the right roles');
+    
+    ok($c->does_role($_), '... our composite does the role ' . $_)
+        for qw(
+            Role::Foo
+            Role::Bar
+            Role::Baz            
+        );
+    
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this composed okay';   
+    
+    ##... now nest 'em
+    { 
+        my $c2 = Mouse::Meta::Role::Composite->new(
+            roles => [
+                $c,
+                Role::Gorch->meta,
+            ]
+        );
+        isa_ok($c2, 'Mouse::Meta::Role::Composite');
+
+        is($c2->name, 'Role::Foo|Role::Bar|Role::Baz|Role::Gorch', '... got the composite role name');
+
+        is_deeply($c2->get_roles, [
+            $c,
+            Role::Gorch->meta,  
+        ], '... got the right roles');
+
+        ok($c2->does_role($_), '... our composite does the role ' . $_)
+            for qw(
+                Role::Foo
+                Role::Bar
+                Role::Baz     
+                Role::Gorch                        
+            );     
+    }
+}
diff --git a/t/030_roles/failing/021_role_composite_exclusion.t b/t/030_roles/failing/021_role_composite_exclusion.t
new file mode 100644 (file)
index 0000000..4d0a8d3
--- /dev/null
@@ -0,0 +1,109 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 12;
+use Test::Exception;
+
+use Mouse::Meta::Role::Application::RoleSummation;
+use Mouse::Meta::Role::Composite;
+
+{
+    package Role::Foo;
+    use Mouse::Role;
+    
+    package Role::Bar;
+    use Mouse::Role;
+    
+    package Role::ExcludesFoo;
+    use Mouse::Role;
+    excludes 'Role::Foo';
+    
+    package Role::DoesExcludesFoo;
+    use Mouse::Role;
+    with 'Role::ExcludesFoo';  
+    
+    package Role::DoesFoo;
+    use Mouse::Role;
+    with 'Role::Foo';    
+}
+
+ok(Role::ExcludesFoo->meta->excludes_role('Role::Foo'), '... got the right exclusions');
+ok(Role::DoesExcludesFoo->meta->excludes_role('Role::Foo'), '... got the right exclusions');
+
+# test simple exclusion
+dies_ok {
+    Mouse::Meta::Role::Application::RoleSummation->new->apply(
+        Mouse::Meta::Role::Composite->new(
+            roles => [
+                Role::Foo->meta,
+                Role::ExcludesFoo->meta,
+            ]
+        )
+    );
+} '... this fails as expected';
+
+# test no conflicts
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::Bar->meta,
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::Bar', '... got the composite role name');
+    
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this lives as expected';    
+}
+
+# test no conflicts w/exclusion
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Bar->meta,
+            Role::ExcludesFoo->meta,            
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Bar|Role::ExcludesFoo', '... got the composite role name');
+    
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this lives as expected';    
+    
+    is_deeply([$c->get_excluded_roles_list], ['Role::Foo'], '... has excluded roles');    
+}
+
+
+# test conflict with an "inherited" exclusion
+dies_ok {
+    Mouse::Meta::Role::Application::RoleSummation->new->apply(
+        Mouse::Meta::Role::Composite->new(
+            roles => [
+                Role::Foo->meta,
+                Role::DoesExcludesFoo->meta,
+            ]
+        )
+    );
+    
+} '... this fails as expected';
+
+# test conflict with an "inherited" exclusion of an "inherited" role
+dies_ok {
+    Mouse::Meta::Role::Application::RoleSummation->new->apply(
+        Mouse::Meta::Role::Composite->new(        
+            roles => [
+                Role::DoesFoo->meta,            
+                Role::DoesExcludesFoo->meta,
+            ]
+        )
+    );
+} '... this fails as expected';
+
+
diff --git a/t/030_roles/failing/022_role_composition_req_methods.t b/t/030_roles/failing/022_role_composition_req_methods.t
new file mode 100644 (file)
index 0000000..c0ff4f9
--- /dev/null
@@ -0,0 +1,125 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 16;
+use Test::Exception;
+
+use Mouse::Meta::Role::Application::RoleSummation;
+use Mouse::Meta::Role::Composite;
+
+{
+    package Role::Foo;
+    use Mouse::Role;    
+    requires 'foo';
+    
+    package Role::Bar;
+    use Mouse::Role;
+    requires 'bar';
+    
+    package Role::ProvidesFoo;
+    use Mouse::Role;    
+    sub foo { 'Role::ProvidesFoo::foo' }
+    
+    package Role::ProvidesBar;
+    use Mouse::Role;    
+    sub bar { 'Role::ProvidesBar::bar' }     
+}
+
+# test simple requirement
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::Bar->meta,
+        ]        
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::Bar', '... got the composite role name');    
+    
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this succeeds as expected';    
+    
+    is_deeply(
+        [ sort $c->get_required_method_list ],
+        [ 'bar', 'foo' ],
+        '... got the right list of required methods'
+    );
+}
+
+# test requirement satisfied
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::ProvidesFoo->meta,
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::ProvidesFoo', '... got the composite role name');    
+    
+    lives_ok { 
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this succeeds as expected';    
+    
+    is_deeply(
+        [ sort $c->get_required_method_list ],
+        [],
+        '... got the right list of required methods'
+    );
+}
+
+# test requirement satisfied
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::ProvidesFoo->meta,
+            Role::Bar->meta,            
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::ProvidesFoo|Role::Bar', '... got the composite role name');    
+    
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this succeeds as expected';    
+    
+    is_deeply(
+        [ sort $c->get_required_method_list ],
+        [ 'bar' ],
+        '... got the right list of required methods'
+    );
+}
+
+# test requirement satisfied
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::ProvidesFoo->meta,
+            Role::ProvidesBar->meta,            
+            Role::Bar->meta,            
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::ProvidesFoo|Role::ProvidesBar|Role::Bar', '... got the composite role name');    
+    
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this succeeds as expected';    
+    
+    is_deeply(
+        [ sort $c->get_required_method_list ],
+        [ ],
+        '... got the right list of required methods'
+    );
+}
+
+
diff --git a/t/030_roles/failing/023_role_composition_attributes.t b/t/030_roles/failing/023_role_composition_attributes.t
new file mode 100644 (file)
index 0000000..69852dc
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 7;
+use Test::Exception;
+
+use Mouse::Meta::Role::Application::RoleSummation;
+use Mouse::Meta::Role::Composite;
+
+{
+    package Role::Foo;
+    use Mouse::Role;    
+    has 'foo' => (is => 'rw');
+    
+    package Role::Bar;
+    use Mouse::Role;
+    has 'bar' => (is => 'rw');
+    
+    package Role::FooConflict;
+    use Mouse::Role;    
+    has 'foo' => (is => 'rw');
+    
+    package Role::BarConflict;
+    use Mouse::Role;
+    has 'bar' => (is => 'rw');
+    
+    package Role::AnotherFooConflict;
+    use Mouse::Role;    
+    with 'Role::FooConflict';
+}
+
+# test simple attributes
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::Bar->meta,
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::Bar', '... got the composite role name');    
+    
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this succeeds as expected';    
+    
+    is_deeply(
+        [ sort $c->get_attribute_list ],
+        [ 'bar', 'foo' ],
+        '... got the right list of attributes'
+    );
+}
+
+# test simple conflict
+dies_ok {
+    Mouse::Meta::Role::Application::RoleSummation->new->apply(
+        Mouse::Meta::Role::Composite->new(
+            roles => [
+                Role::Foo->meta,
+                Role::FooConflict->meta,
+            ]
+        )
+    );
+} '... this fails as expected';
+
+# test complex conflict
+dies_ok {
+    Mouse::Meta::Role::Application::RoleSummation->new->apply(
+        Mouse::Meta::Role::Composite->new(
+            roles => [
+                Role::Foo->meta,
+                Role::Bar->meta,            
+                Role::FooConflict->meta,
+                Role::BarConflict->meta,            
+            ]
+        )
+    );
+} '... this fails as expected';
+
+# test simple conflict
+dies_ok {
+    Mouse::Meta::Role::Application::RoleSummation->new->apply(
+        Mouse::Meta::Role::Composite->new(
+            roles => [
+                Role::Foo->meta,
+                Role::AnotherFooConflict->meta,
+            ]
+        )
+    );
+} '... this fails as expected';
+
diff --git a/t/030_roles/failing/024_role_composition_methods.t b/t/030_roles/failing/024_role_composition_methods.t
new file mode 100644 (file)
index 0000000..355e56b
--- /dev/null
@@ -0,0 +1,151 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 19;
+use Test::Exception;
+
+use Mouse::Meta::Role::Application::RoleSummation;
+use Mouse::Meta::Role::Composite;
+
+{
+    package Role::Foo;
+    use Mouse::Role;
+    
+    sub foo { 'Role::Foo::foo' }    
+    
+    package Role::Bar;
+    use Mouse::Role;
+
+    sub bar { 'Role::Bar::bar' }
+    
+    package Role::FooConflict;
+    use Mouse::Role;    
+    
+    sub foo { 'Role::FooConflict::foo' }    
+    
+    package Role::BarConflict;
+    use Mouse::Role;
+    
+    sub bar { 'Role::BarConflict::bar' }
+    
+    package Role::AnotherFooConflict;
+    use Mouse::Role;    
+    with 'Role::FooConflict';
+
+    sub baz { 'Role::AnotherFooConflict::baz' }
+}
+
+# test simple attributes
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::Bar->meta,
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::Bar', '... got the composite role name');    
+    
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this succeeds as expected';    
+    
+    is_deeply(
+        [ sort $c->get_method_list ],
+        [ 'bar', 'foo' ],
+        '... got the right list of methods'
+    );
+}
+
+# test simple conflict
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::FooConflict->meta,
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::FooConflict', '... got the composite role name');    
+    
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this succeeds as expected';    
+    
+    is_deeply(
+        [ sort $c->get_method_list ],
+        [],
+        '... got the right list of methods'
+    );    
+    
+    is_deeply(
+        [ sort $c->get_required_method_list ],
+        [ 'foo' ],
+        '... got the right list of required methods'
+    );    
+}
+
+# test complex conflict
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::Bar->meta,            
+            Role::FooConflict->meta,
+            Role::BarConflict->meta,            
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::Bar|Role::FooConflict|Role::BarConflict', '... got the composite role name');    
+
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this succeeds as expected';
+
+    is_deeply(
+        [ sort $c->get_method_list ],
+        [],
+        '... got the right list of methods'
+    );    
+    
+    is_deeply(
+        [ sort $c->get_required_method_list ],
+        [ 'bar', 'foo' ],
+        '... got the right list of required methods'
+    );    
+}
+
+# test simple conflict
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::AnotherFooConflict->meta,
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::AnotherFooConflict', '... got the composite role name');    
+    
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this succeeds as expected';    
+    
+    is_deeply(
+        [ sort $c->get_method_list ],
+        [ 'baz' ],
+        '... got the right list of methods'
+    );    
+    
+    is_deeply(
+        [ sort $c->get_required_method_list ],
+        [ 'foo' ],
+        '... got the right list of required methods'
+    );    
+}
+
diff --git a/t/030_roles/failing/025_role_composition_override.t b/t/030_roles/failing/025_role_composition_override.t
new file mode 100644 (file)
index 0000000..31f4caf
--- /dev/null
@@ -0,0 +1,112 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 8;
+use Test::Exception;
+
+use Mouse::Meta::Role::Application::RoleSummation;
+use Mouse::Meta::Role::Composite;
+
+{
+    package Role::Foo;
+    use Mouse::Role;
+    
+    override foo => sub { 'Role::Foo::foo' };
+    
+    package Role::Bar;
+    use Mouse::Role;
+
+    override bar => sub { 'Role::Bar::bar' };
+    
+    package Role::FooConflict;
+    use Mouse::Role;    
+    
+    override foo => sub { 'Role::FooConflict::foo' };
+    
+    package Role::FooMethodConflict;
+    use Mouse::Role;    
+    
+    sub foo { 'Role::FooConflict::foo' }    
+    
+    package Role::BarMethodConflict;
+    use Mouse::Role;
+    
+    sub bar { 'Role::BarConflict::bar' }
+}
+
+# test simple overrides
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::Bar->meta,
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::Bar', '... got the composite role name');    
+    
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this lives ok';    
+    
+    is_deeply(
+        [ sort $c->get_method_modifier_list('override') ],
+        [ 'bar', 'foo' ],
+        '... got the right list of methods'
+    );
+}
+
+# test simple overrides w/ conflicts
+dies_ok {
+    Mouse::Meta::Role::Application::RoleSummation->new->apply(
+        Mouse::Meta::Role::Composite->new(
+            roles => [
+                Role::Foo->meta,
+                Role::FooConflict->meta,
+            ]
+        )
+    );
+} '... this fails as expected';
+
+# test simple overrides w/ conflicts
+dies_ok {
+    Mouse::Meta::Role::Application::RoleSummation->new->apply(
+        Mouse::Meta::Role::Composite->new(        
+            roles => [
+                Role::Foo->meta,
+                Role::FooMethodConflict->meta,
+            ]
+        )
+    );
+} '... this fails as expected';
+
+
+# test simple overrides w/ conflicts
+dies_ok {
+    Mouse::Meta::Role::Application::RoleSummation->new->apply(
+        Mouse::Meta::Role::Composite->new(
+            roles => [
+                Role::Foo->meta,
+                Role::Bar->meta,            
+                Role::FooConflict->meta,         
+            ]
+        )
+    );
+} '... this fails as expected';
+
+
+# test simple overrides w/ conflicts
+dies_ok {
+    Mouse::Meta::Role::Application::RoleSummation->new->apply(
+        Mouse::Meta::Role::Composite->new(        
+            roles => [
+                Role::Foo->meta,
+                Role::Bar->meta,            
+                Role::FooMethodConflict->meta,          
+            ]
+        )
+    );
+} '... this fails as expected';
diff --git a/t/030_roles/failing/026_role_composition_method_mods.t b/t/030_roles/failing/026_role_composition_method_mods.t
new file mode 100644 (file)
index 0000000..86816f3
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 7;
+use Test::Exception;
+
+use Mouse::Meta::Role::Application::RoleSummation;
+use Mouse::Meta::Role::Composite;
+
+{
+    package Role::Foo;
+    use Mouse::Role;
+
+    before foo => sub { 'Role::Foo::foo' };
+    around foo => sub { 'Role::Foo::foo' };    
+    after  foo => sub { 'Role::Foo::foo' };        
+    around baz => sub { [ 'Role::Foo', @{shift->(@_)} ] };
+
+    package Role::Bar;
+    use Mouse::Role;
+
+    before bar => sub { 'Role::Bar::bar' };
+    around bar => sub { 'Role::Bar::bar' };    
+    after  bar => sub { 'Role::Bar::bar' };    
+
+    package Role::Baz;
+    use Mouse::Role;
+
+    with 'Role::Foo';
+    around baz => sub { [ 'Role::Baz', @{shift->(@_)} ] };
+
+}
+
+{
+  package Class::FooBar;
+  use Mouse;
+
+  with 'Role::Baz';
+  sub foo { 'placeholder' }
+  sub baz { ['Class::FooBar'] }
+}
+
+#test modifier call order
+{
+    is_deeply(
+        Class::FooBar->baz,
+        ['Role::Baz','Role::Foo','Class::FooBar']
+    );
+}
+
+# test simple overrides
+{
+    my $c = Mouse::Meta::Role::Composite->new(
+        roles => [
+            Role::Foo->meta,
+            Role::Bar->meta,
+        ]
+    );
+    isa_ok($c, 'Mouse::Meta::Role::Composite');
+
+    is($c->name, 'Role::Foo|Role::Bar', '... got the composite role name');    
+
+    lives_ok {
+        Mouse::Meta::Role::Application::RoleSummation->new->apply($c);
+    } '... this succeeds as expected';    
+
+    is_deeply(
+        [ sort $c->get_method_modifier_list('before') ],
+        [ 'bar', 'foo' ],
+        '... got the right list of methods'
+    );
+
+    is_deeply(
+        [ sort $c->get_method_modifier_list('after') ],
+        [ 'bar', 'foo' ],
+        '... got the right list of methods'
+    );    
+
+    is_deeply(
+        [ sort $c->get_method_modifier_list('around') ],
+        [ 'bar', 'baz', 'foo' ],
+        '... got the right list of methods'
+    );    
+}
diff --git a/t/030_roles/failing/032_roles_and_method_cloning.t b/t/030_roles/failing/032_roles_and_method_cloning.t
new file mode 100644 (file)
index 0000000..bc5950a
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 14;
+
+
+{
+    package Role::Foo;
+    use Mouse::Role;
+
+    sub foo { }
+}
+
+{
+    package ClassA;
+    use Mouse;
+
+    with 'Role::Foo';
+}
+
+{
+    my $meth = ClassA->meta->get_method('foo');
+    ok( $meth, 'ClassA has a foo method' );
+    isa_ok( $meth, 'Mouse::Meta::Method' );
+    is( $meth->original_method, Role::Foo->meta->get_method('foo'),
+        'ClassA->foo was cloned from Role::Foo->foo' );
+    is( $meth->fully_qualified_name, 'ClassA::foo',
+        'fq name is ClassA::foo' );
+    is( $meth->original_fully_qualified_name, 'Role::Foo::foo',
+        'original fq name is Role::Foo::foo' );
+}
+
+{
+    package Role::Bar;
+    use Mouse::Role;
+    with 'Role::Foo';
+
+    sub bar { }
+}
+
+{
+    my $meth = Role::Bar->meta->get_method('foo');
+    ok( $meth, 'Role::Bar has a foo method' );
+    is( $meth->original_method, Role::Foo->meta->get_method('foo'),
+        'Role::Bar->foo was cloned from Role::Foo->foo' );
+    is( $meth->fully_qualified_name, 'Role::Bar::foo',
+        'fq name is Role::Bar::foo' );
+    is( $meth->original_fully_qualified_name, 'Role::Foo::foo',
+        'original fq name is Role::Foo::foo' );
+}
+
+{
+    package ClassB;
+    use Mouse;
+
+    with 'Role::Bar';
+}
+
+{
+    my $meth = ClassB->meta->get_method('foo');
+    ok( $meth, 'ClassB has a foo method' );
+    is( $meth->original_method, Role::Bar->meta->get_method('foo'),
+        'ClassA->foo was cloned from Role::Bar->foo' );
+    is( $meth->original_method->original_method, Role::Foo->meta->get_method('foo'),
+        '... which in turn was cloned from Role::Foo->foo' );
+    is( $meth->fully_qualified_name, 'ClassB::foo',
+        'fq name is ClassA::foo' );
+    is( $meth->original_fully_qualified_name, 'Role::Foo::foo',
+        'original fq name is Role::Foo::foo' );
+}
diff --git a/t/030_roles/failing/033_role_exclusion_and_alias_bug.t b/t/030_roles/failing/033_role_exclusion_and_alias_bug.t
new file mode 100644 (file)
index 0000000..837dc50
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 17;
+use Test::Mouse;
+
+{
+    package My::Role;
+    use Mouse::Role;
+    
+    sub foo { "FOO" }
+    sub bar { "BAR" }    
+}
+
+{
+    package My::Class;
+    use Mouse;
+    
+    with 'My::Role' => {
+        alias    => { foo => 'baz', bar => 'gorch' },
+        excludes => ['foo', 'bar'],        
+    };
+}
+
+{
+    my $x = My::Class->new;
+    isa_ok($x, 'My::Class');
+    does_ok($x, 'My::Role');
+
+    can_ok($x, $_) for qw[baz gorch];
+
+    ok(!$x->can($_), '... cant call method ' . $_) for qw[foo bar];
+
+    is($x->baz, 'FOO', '... got the right value');
+    is($x->gorch, 'BAR', '... got the right value');
+}
+
+{
+    package My::Role::Again;
+    use Mouse::Role;
+    
+    with 'My::Role' => {
+        alias    => { foo => 'baz', bar => 'gorch' },
+        excludes => ['foo', 'bar'],        
+    };
+    
+    package My::Class::Again;
+    use Mouse;
+    
+    with 'My::Role::Again';
+}
+
+{
+    my $x = My::Class::Again->new;
+    isa_ok($x, 'My::Class::Again');
+    does_ok($x, 'My::Role::Again');
+    does_ok($x, 'My::Role');
+
+    can_ok($x, $_) for qw[baz gorch];
+
+    ok(!$x->can($_), '... cant call method ' . $_) for qw[foo bar];
+
+    is($x->baz, 'FOO', '... got the right value');
+    is($x->gorch, 'BAR', '... got the right value');
+}
+
+
diff --git a/t/030_roles/failing/034_create_role.t b/t/030_roles/failing/034_create_role.t
new file mode 100644 (file)
index 0000000..e454dc3
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More tests => 4;
+use Mouse ();
+
+my $role = Mouse::Meta::Role->create(
+    'MyItem::Role::Equipment',
+    attributes => {
+        is_worn => {
+            is => 'rw',
+            isa => 'Bool',
+        },
+    },
+    methods => {
+        remove => sub { shift->is_worn(0) },
+    },
+);
+
+my $class = Mouse::Meta::Class->create('MyItem::Armor::Helmet' =>
+    roles => ['MyItem::Role::Equipment'],
+);
+
+my $visored = $class->construct_instance(is_worn => 0);
+ok(!$visored->is_worn, "attribute, accessor was consumed");
+$visored->is_worn(1);
+ok($visored->is_worn, "accessor was consumed");
+$visored->remove;
+ok(!$visored->is_worn, "method was consumed");
+
+ok(!$role->is_anon_role, "the role is not anonymous");
+
diff --git a/t/030_roles/failing/035_anonymous_roles.t b/t/030_roles/failing/035_anonymous_roles.t
new file mode 100644 (file)
index 0000000..c4fad90
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More tests => 7;
+use Mouse ();
+
+my $role = Mouse::Meta::Role->create_anon_role(
+    attributes => {
+        is_worn => {
+            is => 'rw',
+            isa => 'Bool',
+        },
+    },
+    methods => {
+        remove => sub { shift->is_worn(0) },
+    },
+);
+
+my $class = Mouse::Meta::Class->create('MyItem::Armor::Helmet');
+$role->apply($class);
+# XXX: Mouse::Util::apply_all_roles doesn't cope with references yet
+
+my $visored = $class->construct_instance(is_worn => 0);
+ok(!$visored->is_worn, "attribute, accessor was consumed");
+$visored->is_worn(1);
+ok($visored->is_worn, "accessor was consumed");
+$visored->remove;
+ok(!$visored->is_worn, "method was consumed");
+
+like($role->name, qr/^Mouse::Meta::Role::__ANON__::SERIAL::\d+$/, "");
+ok($role->is_anon_role, "the role knows it's anonymous");
+
+ok(Class::MOP::is_class_loaded(Mouse::Meta::Role->create_anon_role->name), "creating an anonymous role satisifes is_class_loaded");
+ok(Class::MOP::load_class(Mouse::Meta::Role->create_anon_role->name), "creating an anonymous role satisifes load_class");
+
diff --git a/t/030_roles/failing/036_free_anonymous_roles.t b/t/030_roles/failing/036_free_anonymous_roles.t
new file mode 100644 (file)
index 0000000..7429765
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More tests => 4;
+use Mouse ();
+use Scalar::Util 'weaken';
+
+my $weak;
+my $name;
+do {
+    my $anon_class;
+
+    do {
+        my $role = Mouse::Meta::Role->create_anon_role(
+            methods => {
+                improperly_freed => sub { 1 },
+            },
+        );
+        weaken($weak = $role);
+
+        $name = $role->name;
+
+        $anon_class = Mouse::Meta::Class->create_anon_class(
+            roles => [ $role->name ],
+        );
+    };
+
+    ok($weak, "we still have the role metaclass because the anonymous class that consumed it is still alive");
+    ok($name->can('improperly_freed'), "we have not blown away the role's symbol table");
+};
+
+ok(!$weak, "the role metaclass is freed after its last reference (from a consuming anonymous class) is freed");
+
+ok(!$name->can('improperly_freed'), "we blew away the role's symbol table entries");
diff --git a/t/030_roles/failing/037_create_role_subclass.t b/t/030_roles/failing/037_create_role_subclass.t
new file mode 100644 (file)
index 0000000..11e9105
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More tests => 2;
+use Mouse ();
+
+do {
+    package My::Meta::Role;
+    use Mouse;
+    extends 'Mouse::Meta::Role';
+
+    has test_serial => (
+        is      => 'ro',
+        isa     => 'Int',
+        default => 1,
+    );
+
+    no Mouse;
+};
+
+my $role = My::Meta::Role->create_anon_role;
+is($role->test_serial, 1, "default value for the serial attribute");
+
+my $nine_role = My::Meta::Role->create_anon_role(test_serial => 9);
+is($nine_role->test_serial, 9, "parameter value for the serial attribute");
+