-=pod
+package Moose::Manual::Roles;
+
+# ABSTRACT: Roles, an alternative to deep hierarchies and base classes
-=head1 NAME
+__END__
-Moose::Manual::Roles - Roles, an alternative to deep hierarchies and base classes
+=pod
=head1 WHAT IS A ROLE?
-A role is something that classes do. Usually, a role encapsulates some
-piece of behavior or state that can be shared between classes. It is
-important to understand that I<roles are not classes>. You cannot
-inherit from a role, and a role cannot be instantiated. We sometimes
-say that roles are I<consumed>, either by classes or other roles.
+A role encapsulates some piece of behavior or state that can be shared between
+classes. It is something that classes I<do>. It is important to understand that
+I<roles are not classes>. You cannot inherit from a role, and a role cannot be
+instantiated. We sometimes say that roles are I<consumed>, either by classes
+or other roles.
Instead, a role is I<composed> into a class. In practical terms, this
-means that all of the methods and attributes defined in a role are
+means that all of the methods, method modifiers, and attributes defined in a role are
added directly to (we sometimes say "flattened into") the class that
consumes the role. These attributes and methods then appear as if they
were defined in the class itself. A subclass of the consuming class
methods, in which case the role would be very much like a Java
interface.
+Note that attribute accessors also count as methods for the
+purposes of satisfying the requirements of a role.
+
=head1 A SIMPLE ROLE
Creating a role looks a lot like creating a Moose class:
my $car = Car->new( engine => Engine->new );
- print $car->is_broken ? 'Still working' : 'Busted';
+ print $car->is_broken ? 'Busted' : 'Still working';
$car->break;
- print $car->is_broken ? 'Still working' : 'Busted';
+ print $car->is_broken ? 'Busted' : 'Still working';
$car->does('Breakable'); # true
isa => 'Marrow',
);
+See also L<Moose::Cookbook::Roles::Comparable_CodeReuse> for an example.
+
=head1 REQUIRED METHODS
As mentioned previously, a role can require that consuming classes
simply consume the interface role in each class which implements that
interface.
+=head2 Required Attributes
+
+As mentioned before, a role's required method may also be satisfied by an
+attribute accessor. However, the call to C<has> which defines an attribute
+happens at runtime. This means that you must define the attribute I<before>
+consuming the role, or else the role will not see the generated accessor.
+
+ package Breakable;
+
+ use Moose::Role;
+
+ requires 'stress';
+
+ package Car;
+
+ use Moose;
+
+ has 'stress' => (
+ is => 'rw',
+ isa => 'Int',
+ );
+
+ with 'Breakable';
+
=head1 USING METHOD MODIFIERS
Method modifiers and roles are a very powerful combination. Often, a
with 'Breakable', 'ExplodesOnBreakage';
-Assuming that the new C<ExplodesOnBreakage> method I<also> has an
+Assuming that the new C<ExplodesOnBreakage> role I<also> has an
C<after> modifier on C<break>, the C<after> modifiers will run one
after the other. The modifier from C<Breakable> will run first, then
the one from C<ExplodesOnBreakage>.
sub break { ... }
+A role can be a collection of other roles:
+
+ package Break::Bundle;
+
+ use Moose::Role;
+
+ with ('Breakable', 'Breakdancer');
+
=head1 METHOD EXCLUSION AND ALIASING
If we want our C<FragileDancer> class to be able to call the methods
use Moose;
- with 'Breakable' => { alias => { break => 'break_bone' } },
- 'Breakdancer' => { alias => { break => 'break_dance' } };
+ with 'Breakable' => { -alias => { break => 'break_bone' } },
+ 'Breakdancer' => { -alias => { break => 'break_dance' } };
However, aliasing a method simply makes a I<copy> of the method with
the new name. We also need to exclude the original name:
with 'Breakable' => {
- alias => { break => 'break_bone' },
- excludes => 'break',
+ -alias => { break => 'break_bone' },
+ -excludes => 'break',
},
'Breakdancer' => {
- alias => { break => 'break_dance' },
- excludes => 'break',
+ -alias => { break => 'break_dance' },
+ -excludes => 'break',
};
The excludes parameter prevents the C<break> method from being composed
In some use cases we might alias and exclude methods from roles, but
then provide a method of the same name in the class itself.
+Also see L<Moose::Cookbook::Roles::Restartable_AdvancedComposition> for an example.
+
=head1 ROLE EXCLUSION
A role can say that it cannot be combined with some other role. This
excludes 'BreakDancer';
-=head1 AUTHOR
+=head1 ADDING A ROLE TO AN OBJECT INSTANCE
+
+You may want to add a role to an object instance, rather than to a class. For
+example, you may want to add debug tracing to one instance of an object while
+debugging a particular bug. Another use case might be to dynamically change
+objects based on a user's configuration, as a plugin system.
+
+The best way to do this is to use the C<apply_all_roles()> function from
+L<Moose::Util>:
+
+ use Moose::Util qw( apply_all_roles );
+
+ my $car = Car->new;
+ apply_all_roles( $car, 'Breakable' );
+
+This function can apply more than one role at a time, and will do so using the
+normal Moose role combination system. We recommend using this function to
+apply roles to an object. This is what Moose uses internally when you call
+C<with>.
+
+=head2 Handling required attributes for roles.
+
+Application of some roles will require additional parameters being specified to
+satisfy them, for example:
+
+ {
+ package Car;
+ use Moose;
+ }
+ {
+ package Breakable;
+ use Moose::Role;
+
+ has 'breakable_parts' => ( is => 'ro', required => 1 );
+ }
+
+ my $car = Car->new;
+
+ # next line dies with: Attribute (breakable_parts) is required
+ apply_all_roles( $car, 'Breakable' );
-Dave Rolsky E<lt>autarch@urth.orgE<gt>
+This will require passing the additional parameters at application time as
+follows:
-=head1 COPYRIGHT AND LICENSE
+ apply_all_roles( $car, 'Breakable' => {
+ rebless_params => {
+ # Parameters to 'Breakable'
+ breakable_parts => [qw( tires wheels windscreen )],
+ }
+ });
-Copyright 2009 by Infinity Interactive, Inc.
+Obviously, this interface is better simplified as a method on C<Car>:
-L<http://www.iinteractive.com>
+ sub make_breakable {
+ my ( $self, %params ) = @_;
+ apply_all_roles($self, 'Breakable', { rebless_params => \%params });
+ }
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself.
+ my $car = Car->new();
+ $car->make_breakable( breakable_parts => [qw( tires wheels windscreen )] );
=cut