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