Commit | Line | Data |
a7d0cd00 |
1 | |
2 | =pod |
3 | |
4 | =head1 NAME |
5 | |
021b8139 |
6 | Moose::Cookbook::Roles::Recipe1 - The Moose::Role example |
a7d0cd00 |
7 | |
8 | =head1 SYNOPSIS |
9e93dd19 |
9 | |
446e850f |
10 | package Eq; |
a7d0cd00 |
11 | use Moose::Role; |
a39ea7dc |
12 | |
446e850f |
13 | requires 'equal_to'; |
a39ea7dc |
14 | |
15 | sub not_equal_to { |
16 | my ( $self, $other ) = @_; |
9e93dd19 |
17 | not $self->equal_to($other); |
a7d0cd00 |
18 | } |
a39ea7dc |
19 | |
9e93dd19 |
20 | package Comparable; |
a7d0cd00 |
21 | use Moose::Role; |
a39ea7dc |
22 | |
446e850f |
23 | with 'Eq'; |
a39ea7dc |
24 | |
446e850f |
25 | requires 'compare'; |
a39ea7dc |
26 | |
446e850f |
27 | sub equal_to { |
a39ea7dc |
28 | my ( $self, $other ) = @_; |
446e850f |
29 | $self->compare($other) == 0; |
a39ea7dc |
30 | } |
31 | |
446e850f |
32 | sub greater_than { |
a39ea7dc |
33 | my ( $self, $other ) = @_; |
446e850f |
34 | $self->compare($other) == 1; |
a39ea7dc |
35 | } |
36 | |
446e850f |
37 | sub less_than { |
a39ea7dc |
38 | my ( $self, $other ) = @_; |
446e850f |
39 | $self->compare($other) == -1; |
a7d0cd00 |
40 | } |
a39ea7dc |
41 | |
446e850f |
42 | sub greater_than_or_equal_to { |
a39ea7dc |
43 | my ( $self, $other ) = @_; |
446e850f |
44 | $self->greater_than($other) || $self->equal_to($other); |
a39ea7dc |
45 | } |
46 | |
446e850f |
47 | sub less_than_or_equal_to { |
a39ea7dc |
48 | my ( $self, $other ) = @_; |
446e850f |
49 | $self->less_than($other) || $self->equal_to($other); |
a39ea7dc |
50 | } |
51 | |
9e93dd19 |
52 | package Printable; |
9e93dd19 |
53 | use Moose::Role; |
a39ea7dc |
54 | |
55 | requires 'to_string'; |
56 | |
446e850f |
57 | package US::Currency; |
a7d0cd00 |
58 | use Moose; |
a39ea7dc |
59 | |
9e93dd19 |
60 | with 'Comparable', 'Printable'; |
a39ea7dc |
61 | |
62 | has 'amount' => ( is => 'rw', isa => 'Num', default => 0 ); |
63 | |
446e850f |
64 | sub compare { |
a39ea7dc |
65 | my ( $self, $other ) = @_; |
446e850f |
66 | $self->amount <=> $other->amount; |
67 | } |
a39ea7dc |
68 | |
9e93dd19 |
69 | sub to_string { |
70 | my $self = shift; |
a39ea7dc |
71 | sprintf '$%0.2f USD' => $self->amount; |
9e93dd19 |
72 | } |
cb26ee7e |
73 | |
a7d0cd00 |
74 | =head1 DESCRIPTION |
75 | |
efaf28e9 |
76 | Roles have two primary purposes: as interfaces, and as a means of code |
77 | reuse. This recipe demonstrates the latter, with roles that define |
78 | comparison and display code for objects. |
cb26ee7e |
79 | |
19320607 |
80 | Let's start with C<Eq>. First, note that we've replaced C<use Moose> |
efaf28e9 |
81 | with C<use Moose::Role>. We also have a new sugar function, C<required>: |
cb26ee7e |
82 | |
83 | requires 'equal_to'; |
84 | |
efaf28e9 |
85 | This says that any class which consumes this role must provide an |
86 | C<equal_to> method. It can provide this method directly, or by |
87 | consuming some other role. |
cb26ee7e |
88 | |
efaf28e9 |
89 | The C<Eq> role defines its C<not_equal_to> method in terms of the |
90 | required C<equal_to> method. This lets us minimize the methods that |
91 | consuming classes must provide. |
cb26ee7e |
92 | |
efaf28e9 |
93 | The next role, C<Comparable>, builds on the C<Eq> role. We include |
94 | C<Eq> in C<Comparable> using C<with>, another new sugar function: |
cb26ee7e |
95 | |
96 | with 'Eq'; |
97 | |
efaf28e9 |
98 | The C<with> function takes a list of roles to consume. In our example, |
99 | the C<Comparable> role provides the C<equal_to> method required by |
100 | C<Eq>. However, it could opt not to, in which case a class that |
101 | consumed C<Comparable> would have to provide its own C<equal_to>. In |
102 | other words, a role can consume another role I<without> providing any |
103 | required methods. |
cb26ee7e |
104 | |
efaf28e9 |
105 | The C<Comparable> role requires a method, C<compare>: |
cb26ee7e |
106 | |
107 | requires 'compare'; |
108 | |
efaf28e9 |
109 | The C<Comparable> role also provides a number of other methods, all of |
110 | which ultimately rely on C<compare>. |
cb26ee7e |
111 | |
112 | sub equal_to { |
a39ea7dc |
113 | my ( $self, $other ) = @_; |
cb26ee7e |
114 | $self->compare($other) == 0; |
115 | } |
a39ea7dc |
116 | |
cb26ee7e |
117 | sub greater_than { |
a39ea7dc |
118 | my ( $self, $other ) = @_; |
cb26ee7e |
119 | $self->compare($other) == 1; |
a39ea7dc |
120 | } |
121 | |
cb26ee7e |
122 | sub less_than { |
a39ea7dc |
123 | my ( $self, $other ) = @_; |
cb26ee7e |
124 | $self->compare($other) == -1; |
125 | } |
a39ea7dc |
126 | |
cb26ee7e |
127 | sub greater_than_or_equal_to { |
a39ea7dc |
128 | my ( $self, $other ) = @_; |
cb26ee7e |
129 | $self->greater_than($other) || $self->equal_to($other); |
a39ea7dc |
130 | } |
131 | |
cb26ee7e |
132 | sub less_than_or_equal_to { |
a39ea7dc |
133 | my ( $self, $other ) = @_; |
cb26ee7e |
134 | $self->less_than($other) || $self->equal_to($other); |
135 | } |
136 | |
efaf28e9 |
137 | Finally, we the C<Printable> role. This role exists solely to provide |
138 | an interface. It has no methods, just a list of required methods. In |
139 | this case, it just requires a C<to_string> method. |
cb26ee7e |
140 | |
efaf28e9 |
141 | An interface role is useful because it defines both a method and a |
142 | I<name>. We know that any class which does this role has a |
143 | C<to_string> method, but we can also assume that this method has the |
144 | semantics we want. Presumably, in real code we would define those |
145 | semantics in the documentation for the C<Printable> role. (1) |
146 | |
147 | Finally, we have the C<US::Currency> class which consumes both the |
148 | C<Comparable> and C<Printable> roles. |
cb26ee7e |
149 | |
150 | with 'Comparable', 'Printable'; |
151 | |
efaf28e9 |
152 | It also defines a regular Moose attribute, C<amount>: |
cb26ee7e |
153 | |
a39ea7dc |
154 | has 'amount' => ( is => 'rw', isa => 'Num', default => 0 ); |
cb26ee7e |
155 | |
efaf28e9 |
156 | Finally we see the implementation of the methods required by our |
157 | roles. We have a C<compare> method: |
cb26ee7e |
158 | |
159 | sub compare { |
a39ea7dc |
160 | my ( $self, $other ) = @_; |
cb26ee7e |
161 | $self->amount <=> $other->amount; |
162 | } |
163 | |
efaf28e9 |
164 | By consuming the C<Comparable> role and defining this method, we gain |
165 | the following methods for free: C<equal_to>, C<greater_than>, |
166 | C<less_than>, C<greater_than_or_equal_to> and |
cb26ee7e |
167 | C<less_than_or_equal_to>. |
168 | |
efaf28e9 |
169 | Then we have our C<to_string> method: |
cb26ee7e |
170 | |
171 | sub to_string { |
172 | my $self = shift; |
a39ea7dc |
173 | sprintf '$%0.2f USD' => $self->amount; |
cb26ee7e |
174 | } |
175 | |
176 | =head1 CONCLUSION |
177 | |
efaf28e9 |
178 | Roles can very powerful. They are a great way of encapsulating |
179 | reusable behavior, as well as communicating (semantic and interface) |
180 | information about the methods our classes provide. |
cb26ee7e |
181 | |
182 | =head1 FOOTNOTES |
183 | |
184 | =over 4 |
185 | |
186 | =item (1) |
187 | |
efaf28e9 |
188 | Consider two classes, C<Runner> and C<Process>, both of which define a |
189 | C<run> method. If we just require that an object implements a C<run> |
190 | method, we still aren't saying anything about what that method |
191 | I<actually does>. If we require an object that implements the |
192 | C<Executable> role, we're saying something about semantics. |
cb26ee7e |
193 | |
194 | =back |
a7d0cd00 |
195 | |
efaf28e9 |
196 | =head1 AUTHORS |
a7d0cd00 |
197 | |
198 | Stevan Little E<lt>stevan@iinteractive.comE<gt> |
199 | |
efaf28e9 |
200 | Dave Rolsky E<lt>autarch@urth.orgE<gt> |
201 | |
a7d0cd00 |
202 | =head1 COPYRIGHT AND LICENSE |
203 | |
2840a3b2 |
204 | Copyright 2006-2009 by Infinity Interactive, Inc. |
a7d0cd00 |
205 | |
206 | L<http://www.iinteractive.com> |
207 | |
208 | This library is free software; you can redistribute it and/or modify |
209 | it under the same terms as Perl itself. |
210 | |
a39ea7dc |
211 | =cut |