Rename Roles::Recipe2 to Roles::Restartable_AdvancedComposition
[gitmo/Moose.git] / lib / Moose / Cookbook / Roles / Restartable_AdvancedComposition.pod
CommitLineData
706d2d37 1package Moose::Cookbook::Roles::Restartable_AdvancedComposition
2e3d0a0a 2
daa0fd7d 3# ABSTRACT: Advanced Role Composition - method exclusion and aliasing
4
5__END__
2e3d0a0a 6
2e3d0a0a 7
daa0fd7d 8=pod
2e3d0a0a 9
10=head1 SYNOPSIS
11
12 package Restartable;
13 use Moose::Role;
14
15 has 'is_paused' => (
16 is => 'rw',
9711d93b 17 isa => 'Bool',
2e3d0a0a 18 default => 0,
19 );
20
21 requires 'save_state', 'load_state';
22
c79239a2 23 sub stop { 1 }
2e3d0a0a 24
c79239a2 25 sub start { 1 }
2e3d0a0a 26
27 package Restartable::ButUnreliable;
28 use Moose::Role;
29
a39ea7dc 30 with 'Restartable' => {
c8b8d92f 31 -alias => {
a39ea7dc 32 stop => '_stop',
33 start => '_start'
0d412f7a 34 },
35 -excludes => [ 'stop', 'start' ],
a39ea7dc 36 };
2e3d0a0a 37
38 sub stop {
39 my $self = shift;
40
41 $self->explode() if rand(1) > .5;
42
43 $self->_stop();
44 }
45
46 sub start {
47 my $self = shift;
48
49 $self->explode() if rand(1) > .5;
50
51 $self->_start();
52 }
53
54 package Restartable::ButBroken;
55 use Moose::Role;
56
c8b8d92f 57 with 'Restartable' => { -excludes => [ 'stop', 'start' ] };
2e3d0a0a 58
59 sub stop {
60 my $self = shift;
61
62 $self->explode();
63 }
64
65 sub start {
66 my $self = shift;
67
68 $self->explode();
69 }
70
71=head1 DESCRIPTION
72
aa8d5e2d 73In this example, we demonstrate how to exercise fine-grained control
74over what methods we consume from a role. We have a C<Restartable>
75role which provides an C<is_paused> attribute, and two methods,
76C<stop> and C<start>.
2e3d0a0a 77
aa8d5e2d 78Then we have two more roles which implement the same interface, each
79putting their own spin on the C<stop> and C<start> methods.
2e3d0a0a 80
81In the C<Restartable::ButUnreliable> role, we want to provide a new
82implementation of C<stop> and C<start>, but still have access to the
83original implementation. To do this, we alias the methods from
84C<Restartable> to private methods, and provide wrappers around the
85originals (1).
86
0d412f7a 87Note that aliasing simply I<adds> a name, so we also need to exclude the
88methods with their original names.
89
aa8d5e2d 90 with 'Restartable' => {
c8b8d92f 91 -alias => {
aa8d5e2d 92 stop => '_stop',
93 start => '_start'
0d412f7a 94 },
95 -excludes => [ 'stop', 'start' ],
aa8d5e2d 96 };
97
2e3d0a0a 98In the C<Restartable::ButBroken> role, we want to provide an entirely
aa8d5e2d 99new behavior for C<stop> and C<start>. We exclude them entirely when
2e3d0a0a 100composing the C<Restartable> role into C<Restartable::ButBroken>.
101
c8b8d92f 102It's worth noting that the C<-excludes> parameter also accepts a single
2e3d0a0a 103string as an argument if you just want to exclude one method.
104
c8b8d92f 105 with 'Restartable' => { -excludes => [ 'stop', 'start' ] };
aa8d5e2d 106
2e3d0a0a 107=head1 CONCLUSION
108
aa8d5e2d 109Exclusion and renaming are a power tool that can be handy, especially
110when building roles out of other roles. In this example, all of our
111roles implement the C<Restartable> role. Each role provides same API,
112but each has a different implementation under the hood.
2e3d0a0a 113
114You can also use the method aliasing and excluding features when
115composing a role into a class.
116
117=head1 FOOTNOTES
118
119=over 4
120
121=item (1)
122
123The mention of wrapper should tell you that we could do the same thing
124using method modifiers, but for the sake of this example, we don't.
125
126=back
127
c79239a2 128=begin testing
129
130{
131 my $unreliable = Moose::Meta::Class->create_anon_class(
132 superclasses => [],
133 roles => [qw/Restartable::ButUnreliable/],
134 methods => {
135 explode => sub { }, # nop.
136 'save_state' => sub { },
137 'load_state' => sub { },
138 },
139 )->new_object();
140 ok( $unreliable, 'made anon class with Restartable::ButUnreliable role' );
141 can_ok( $unreliable, qw/start stop/ );
142}
143
144{
145 my $cnt = 0;
146 my $broken = Moose::Meta::Class->create_anon_class(
147 superclasses => [],
148 roles => [qw/Restartable::ButBroken/],
149 methods => {
150 explode => sub { $cnt++ },
151 'save_state' => sub { },
152 'load_state' => sub { },
153 },
154 )->new_object();
155
156 ok( $broken, 'made anon class with Restartable::ButBroken role' );
157
158 $broken->start();
159
160 is( $cnt, 1, '... start called explode' );
161
162 $broken->stop();
163
164 is( $cnt, 2, '... stop also called explode' );
165}
166
167=end testing
168
2e3d0a0a 169=cut