default => sub { [] }
));
+## excluded roles
+
+__PACKAGE__->meta->add_attribute('excluded_roles_map' => (
+ reader => 'get_excluded_roles_map',
+ default => sub { {} }
+));
+
## attributes
__PACKAGE__->meta->add_attribute('attribute_map' => (
return 0;
}
+## excluded roles
+
+sub add_excluded_roles {
+ my ($self, @excluded_role_names) = @_;
+ $self->get_excluded_roles_map->{$_} = undef foreach @excluded_role_names;
+}
+
+sub get_excluded_roles_list {
+ my ($self) = @_;
+ keys %{$self->get_excluded_roles_map};
+}
+
+sub excludes_role {
+ my ($self, $role_name) = @_;
+ exists $self->get_excluded_roles_map->{$role_name} ? 1 : 0;
+}
+
## required methods
sub add_required_methods {
sub apply {
my ($self, $other) = @_;
+ if ($other->excludes_role($self->name)) {
+ confess "Conflict detected: " . $other->name . " excludes role '" . $self->name . "'";
+ }
+
+# warn "... Checking " . $self->name . " for excluded methods";
+ foreach my $excluded_role_name ($self->get_excluded_roles_list) {
+# warn "... Checking if '$excluded_role_name' is done by " . $other->name . " for " . $self->name;
+ if ($other->does_role($excluded_role_name)) { # || $self->does_role($excluded_role_name)
+ confess "The class " . $other->name . " does the excluded role '$excluded_role_name'";
+ }
+ else {
+ if ($other->isa('Moose::Meta::Role')) {
+# warn ">>> The role " . $other->name . " does not do the excluded role '$excluded_role_name', so we are adding it in";
+ $other->add_excluded_roles($excluded_role_name);
+ }
+ else {
+# warn ">>> The class " . $other->name . " does not do the excluded role '$excluded_role_name', so we can just go about our business";
+ }
+ }
+ }
+
+
# NOTE:
# we might need to move this down below the
# the attributes so that we can require any
"to be implemented by '" . $other->name . "'";
}
}
- }
+ }
foreach my $attribute_name ($self->get_attribute_list) {
# it if it has one already
=over 4
+=item B<add_excluded_roles>
+
+=item B<excludes_role>
+
+=item B<get_excluded_roles_list>
+
+=item B<get_excluded_roles_map>
+
+=back
+
+=over 4
+
=item B<get_method>
=item B<has_method>
use strict;
use warnings;
-use Test::More tests => 62;
+use Test::More tests => 90;
use Test::Exception;
BEGIN {
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');
}
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');
ok(Role::Bling::Bling::Bling->meta->has_method('bling'), '... still got the bling method in Role::Bling::Bling::Bling');
is(Role::Bling::Bling::Bling->meta->get_method('bling')->(),
'Role::Bling::Bling::Bling::bling',
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');
=cut
{
- package Role::Spliff;
+ package Role::Plot;
use strict;
use warnings;
use Moose::Role;
override 'twist' => sub {
- super() . ' -> Role::Spliff::twist';
+ super() . ' -> Role::Plot::twist';
};
- package Role::Blunt;
+ package Role::Truth;
use strict;
use warnings;
use Moose::Role;
override 'twist' => sub {
- super() . ' -> Role::Blunt::twist';
+ super() . ' -> Role::Truth::twist';
};
}
extends 'My::Test::Base';
::lives_ok {
- with 'Role::Blunt';
+ with 'Role::Truth';
} '... composed the role with override okay';
package My::Test12;
extends 'My::Test::Base';
::lives_ok {
- with 'Role::Spliff';
+ with 'Role::Plot';
} '... composed the role with override okay';
package My::Test13;
use Moose;
::dies_ok {
- with 'Role::Spliff';
+ with 'Role::Plot';
} '... cannot compose it because we have no superclass';
package My::Test14;
extends 'My::Test::Base';
::throws_ok {
- with 'Role::Spliff', 'Role::Blunt';
+ with 'Role::Plot', 'Role::Truth';
} qr/Two \'override\' methods of the same name encountered/,
'... cannot compose it because we have no superclass';
}
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');
-is(My::Test11->twist(), 'My::Test::Base::twist -> Role::Blunt::twist', '... got the right method return');
-is(My::Test12->twist(), 'My::Test::Base::twist -> Role::Spliff::twist', '... got the right method return');
+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 strict;
+ use warnings;
+ use Moose::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');
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 15;
+use Test::Exception;
+
+BEGIN {
+ use_ok('Moose');
+ use_ok('Moose::Role');
+}
+
+=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 strict;
+ use warnings;
+ use Moose::Role;
+
+ package Molecule::Organic;
+ use strict;
+ use warnings;
+ use Moose::Role;
+
+ with 'Molecule';
+ excludes 'Molecule::Inorganic';
+
+ package Molecule::Inorganic;
+ use strict;
+ use warnings;
+ use Moose::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');
+
+{
+ package My::Test1;
+ use strict;
+ use warnings;
+ use Moose;
+
+ ::lives_ok {
+ with 'Molecule::Organic';
+ } '... adding the role (w/ excluded roles) okay';
+
+ package My::Test2;
+ use strict;
+ use warnings;
+ use Moose;
+
+ ::throws_ok {
+ with 'Molecule::Organic', 'Molecule::Inorganic';
+ } qr/Conflict detected: Class::MOP::Class::__ANON__::SERIAL::1 excludes role \'Molecule::Inorganic\'/,
+ '... adding the role w/ excluded role conflict dies okay';
+
+ package My::Test3;
+ use strict;
+ use warnings;
+ use Moose;
+
+ ::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->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->meta->excludes_role('Molecule::Inorganic'), '... My::Test3 excludes Molecule::Organic');
+ok(!My::Test3->does('Molecule::Inorganic'), '... ! My::Test3 does Molecule::Inorganic');
+
+
+
+
+
+
+
+
+