096abfd2a3d484f8bca57e4861b700c97c7aeedd
[gitmo/Moose-OldDocs.git] / lib / Moose / Cookbook / Roles / Recipe2.pod
1 package Moose::Cookbook::Roles::Recipe2;
2
3 # ABSTRACT: Advanced Role Composition - method exclusion and aliasing
4
5 __END__
6
7
8 =pod
9
10 =head1 SYNOPSIS
11
12   package Restartable;
13   use Moose::Role;
14
15   has 'is_paused' => (
16       is      => 'rw',
17       isa     => 'Bool',
18       default => 0,
19   );
20
21   requires 'save_state', 'load_state';
22
23   sub stop { 1 }
24
25   sub start { 1 }
26
27   package Restartable::ButUnreliable;
28   use Moose::Role;
29
30   with 'Restartable' => {
31       -alias => {
32           stop  => '_stop',
33           start => '_start'
34       },
35       -excludes => [ 'stop', 'start' ],
36   };
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
57   with 'Restartable' => { -excludes => [ 'stop', 'start' ] };
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
73 In this example, we demonstrate how to exercise fine-grained control
74 over what methods we consume from a role. We have a C<Restartable>
75 role which provides an C<is_paused> attribute, and two methods,
76 C<stop> and C<start>.
77
78 Then we have two more roles which implement the same interface, each
79 putting their own spin on the C<stop> and C<start> methods.
80
81 In the C<Restartable::ButUnreliable> role, we want to provide a new
82 implementation of C<stop> and C<start>, but still have access to the
83 original implementation. To do this, we alias the methods from
84 C<Restartable> to private methods, and provide wrappers around the
85 originals (1).
86
87 Note that aliasing simply I<adds> a name, so we also need to exclude the
88 methods with their original names.
89
90   with 'Restartable' => {
91       -alias => {
92           stop  => '_stop',
93           start => '_start'
94       },
95       -excludes => [ 'stop', 'start' ],
96   };
97
98 In the C<Restartable::ButBroken> role, we want to provide an entirely
99 new behavior for C<stop> and C<start>. We exclude them entirely when
100 composing the C<Restartable> role into C<Restartable::ButBroken>.
101
102 It's worth noting that the C<-excludes> parameter also accepts a single
103 string as an argument if you just want to exclude one method.
104
105   with 'Restartable' => { -excludes => [ 'stop', 'start' ] };
106
107 =head1 CONCLUSION
108
109 Exclusion and renaming are a power tool that can be handy, especially
110 when building roles out of other roles. In this example, all of our
111 roles implement the C<Restartable> role. Each role provides same API,
112 but each has a different implementation under the hood.
113
114 You can also use the method aliasing and excluding features when
115 composing a role into a class.
116
117 =head1 FOOTNOTES
118
119 =over 4
120
121 =item (1)
122
123 The mention of wrapper should tell you that we could do the same thing
124 using method modifiers, but for the sake of this example, we don't.
125
126 =back
127
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
169 =cut