default => sub { [] }
));
+__PACKAGE__->meta->add_attribute('method_aliases' => (
+ init_arg => 'alias',
+ reader => 'get_method_aliases',
+ default => sub { {} }
+));
+
sub new {
my ($class, %params) = @_;
return 0;
}
+sub is_method_aliased {
+ my ($self, $method_name) = @_;
+ exists $self->get_method_aliases->{$method_name} ? 1 : 0
+}
+
+sub is_aliased_method {
+ my ($self, $method_name) = @_;
+ my %aliased_names = reverse %{$self->get_method_aliases};
+ exists $aliased_names{$method_name} ? 1 : 0;
+}
+
sub apply {
my $self = shift;
=item B<is_method_excluded>
+=item B<get_method_aliases>
+
+=item B<is_aliased_method>
+
+=item B<is_method_aliased>
+
=item B<apply>
=item B<check_role_exclusions>
# the require methods stuff.
foreach my $required_method_name ($role->get_required_method_list) {
- unless ($class->find_method_by_name($required_method_name)) {
+ if (!$class->find_method_by_name($required_method_name)) {
+
+ next if $self->is_aliased_method($required_method_name);
+
confess "'" . $role->name . "' requires the method '$required_method_name' " .
"to be implemented by '" . $class->name . "'";
}
next if $self->is_method_excluded($method_name);
+ my $orig_method_name = $method_name;
+
+ if ($self->is_method_aliased($method_name)) {
+ $method_name = $self->get_method_aliases->{$method_name};
+ }
+
# it if it has one already
if ($class->has_method($method_name) &&
# and if they are not the same thing ...
# add it, although it could be overriden
$class->alias_method(
$method_name,
- $role->get_method($method_name)
+ $role->get_method($orig_method_name)
);
}
}
sub check_required_methods {
my ($self, $role1, $role2) = @_;
foreach my $required_method_name ($role1->get_required_method_list) {
+
+ next if $self->is_aliased_method($required_method_name);
+
$role2->add_required_methods($required_method_name)
unless $role2->find_method_by_name($required_method_name);
}
next if $self->is_method_excluded($method_name);
+ my $orig_method_name = $method_name;
+
+ if ($self->is_method_aliased($method_name)) {
+ $method_name = $self->get_method_aliases->{$method_name};
+ }
+
# it if it has one already
if ($role2->has_method($method_name) &&
# and if they are not the same thing ...
# add it, although it could be overriden
$role2->alias_method(
$method_name,
- $role1->get_method($method_name)
+ $role1->get_method($orig_method_name)
);
}
}
=head2 Role Composition
-=head3 Composing into a Role
+=head3 Composing into a Class
=over 4
=back
-=head3 Composing into a Class
+=head3 Composing into a Instance
+
+=head3 Composing into a Role
=over 4
=back
-=head3 Composing into a Instance
-
-=head2 Role Summation
+=head3 Role Summation
When multiple roles are added to another role (using the
C<with @roles> keyword) the roles are composed symmetrically.
=back
+=head3 Composition Edge Cases
+
+This is a just a set of complex edge cases which can easily get
+confused. This attempts to clarify those cases and provide an
+explination of what is going on in them.
+
+=over 4
+
+=item Role Method Overriding
+
+Many people want to "override" methods in roles they are consuming.
+This works fine for classes, since the local class method is favored
+over the role method. However in roles it is trickier, this is because
+conflicts result in neither method being chosen and the method being
+"required" instead.
+
+Here is an example of this (incorrect) type of overriding.
+
+ package Role::Foo;
+ use Moose::Role;
+
+ sub foo { ... }
+
+ package Role::FooBar;
+ use Moose::Role;
+
+ with 'Role::Foo';
+
+ sub foo { ... }
+ sub bar { ... }
+
+Here the C<foo> methods conflict and the Role::FooBar now requires a
+class or role consuming it to implement C<foo>. This is very often not
+what the user wants.
+
+Now here is an example of the (correct) type of overriding, only it is
+not overriding at all, as is explained in the text below.
+
+ package Role::Foo;
+ use Moose::Role;
+
+ sub foo { ... }
+
+ package Role::Bar;
+ use Moose::Role;
+
+ sub foo { ... }
+ sub bar { ... }
+
+ package Role::FooBar;
+ use Moose::Role;
+
+ with 'Role::Foo', 'Role::Bar';
+
+ sub foo { ... }
+
+This works because the combination of Role::Foo and Role::Bar produce
+a conflict with the C<foo> method. This conflict results in the
+composite role (that was created by the combination of Role::Foo
+and Role::Bar using the I<with> keyword) having a method requirement
+of C<foo>. The Role::FooBar then fufills this requirement.
+
+It is important to note that Role::FooBar is simply fufilling the
+required C<foo> method, and **NOT** overriding C<foo>. This is an
+important distinction to make.
+
+=back
+
=head1 SEE ALSO
=over 4
--- /dev/null
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More no_plan => 1;
+use Test::Exception;
+
+BEGIN {
+ use_ok('Moose');
+}
+
+{
+ package My::Role;
+ use Moose::Role;
+
+ sub foo { 'Foo::foo' }
+ sub bar { 'Foo::bar' }
+ sub baz { 'Foo::baz' }
+
+ requires 'role_bar';
+
+ package My::Class;
+ use Moose;
+
+ ::lives_ok {
+ with 'My::Role' => { alias => { bar => 'role_bar' } };
+ } '... this succeeds';
+}
+
+ok(My::Class->meta->has_method($_), "we have a $_ method") for qw(foo baz role_bar);
+ok(!My::Class->meta->has_method('bar'), '... but we dont get bar');
+
+{
+ package My::OtherRole;
+ use Moose::Role;
+
+ ::lives_ok {
+ with 'My::Role' => { alias => { bar => 'role_bar' } };
+ } '... this succeeds';
+
+ sub bar { 'My::OtherRole::bar' }
+}
+
+ok(My::OtherRole->meta->has_method($_), "we have a $_ method") for qw(foo bar 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');
+
+
+
+
+
use Moose::Role;
package Role::Baz;
- use Moose::Role;
+ use Moose::Role;
+
+ package Role::Gorch;
+ use Moose::Role;
}
{
lives_ok {
Moose::Meta::Role::Application::RoleSummation->new->apply($c);
- } '... this composed okay';
+ } '... this composed okay';
+
+ ##... now nest 'em
+ {
+ my $c2 = Moose::Meta::Role::Composite->new(
+ roles => [
+ $c,
+ Role::Gorch->meta,
+ ]
+ );
+ isa_ok($c2, 'Moose::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
+ );
+ }
}