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