Commit | Line | Data |
a7d0cd00 |
1 | |
2 | =pod |
3 | |
4 | =head1 NAME |
5 | |
496b74ab |
6 | Moose::Cookbook::Recipe10 - The Moose::Role example |
a7d0cd00 |
7 | |
8 | =head1 SYNOPSIS |
9e93dd19 |
9 | |
446e850f |
10 | package Eq; |
a7d0cd00 |
11 | use Moose::Role; |
12 | |
446e850f |
13 | requires 'equal_to'; |
a7d0cd00 |
14 | |
446e850f |
15 | sub not_equal_to { |
16 | my ($self, $other) = @_; |
9e93dd19 |
17 | not $self->equal_to($other); |
a7d0cd00 |
18 | } |
19 | |
9e93dd19 |
20 | package Comparable; |
a7d0cd00 |
21 | use Moose::Role; |
22 | |
446e850f |
23 | with 'Eq'; |
a7d0cd00 |
24 | |
446e850f |
25 | requires 'compare'; |
a7d0cd00 |
26 | |
446e850f |
27 | sub equal_to { |
28 | my ($self, $other) = @_; |
29 | $self->compare($other) == 0; |
30 | } |
a7d0cd00 |
31 | |
446e850f |
32 | sub greater_than { |
33 | my ($self, $other) = @_; |
34 | $self->compare($other) == 1; |
35 | } |
a7d0cd00 |
36 | |
446e850f |
37 | sub less_than { |
38 | my ($self, $other) = @_; |
39 | $self->compare($other) == -1; |
a7d0cd00 |
40 | } |
41 | |
446e850f |
42 | sub greater_than_or_equal_to { |
43 | my ($self, $other) = @_; |
44 | $self->greater_than($other) || $self->equal_to($other); |
45 | } |
a7d0cd00 |
46 | |
446e850f |
47 | sub less_than_or_equal_to { |
48 | my ($self, $other) = @_; |
49 | $self->less_than($other) || $self->equal_to($other); |
9e93dd19 |
50 | } |
51 | |
52 | package Printable; |
9e93dd19 |
53 | use Moose::Role; |
54 | |
55 | requires 'to_string'; |
a7d0cd00 |
56 | |
446e850f |
57 | package US::Currency; |
a7d0cd00 |
58 | use Moose; |
59 | |
9e93dd19 |
60 | with 'Comparable', 'Printable'; |
a7d0cd00 |
61 | |
9e93dd19 |
62 | has 'amount' => (is => 'rw', isa => 'Num', default => 0); |
446e850f |
63 | |
64 | sub compare { |
65 | my ($self, $other) = @_; |
66 | $self->amount <=> $other->amount; |
67 | } |
a7d0cd00 |
68 | |
9e93dd19 |
69 | sub to_string { |
70 | my $self = shift; |
71 | sprintf '$%0.2f USD' => $self->amount |
72 | } |
cb26ee7e |
73 | |
a7d0cd00 |
74 | =head1 DESCRIPTION |
75 | |
cb26ee7e |
76 | In this recipe we examine the role support provided in Moose. "Roles" may be |
77 | described in many ways, but there are two main ways in which they are used: as |
78 | interfaces, and as a means of code reuse. This recipe demonstrates the |
79 | construction and incorporation of roles that define comparison and display of |
80 | objects. |
81 | |
82 | Let's start by examining B<Eq>. You'll notice that instead of the familiar C<use |
83 | Moose> you might be expecting, here we use C<Moose::Role> to make it clear that |
84 | this is a role. We encounter a new keyword, C<requires>: |
85 | |
86 | requires 'equal_to'; |
87 | |
88 | What this does is to indicate that any class which "consumes" (that is to say, |
89 | "includes using C<with>", as we'll see a little later) the B<Eq> role I<must> |
90 | include an C<equal_to> method, whether this is provided by the class itself, one |
91 | of its superclasses, or another role consumed by the class (1). |
92 | |
93 | In addition to requiring an C<equal_to> method, B<Eq> defines a C<not_equal_to> |
94 | method, which simply inverts the result of C<equal_to>. Defining additional |
95 | methods in this way, by using only a few base methods that target classes must |
96 | define, is a useful pattern to provide maximum functionality with minimum |
97 | effort. |
98 | |
99 | After the minimal B<Eq>, we next move on to B<Comparable>. The first thing you |
100 | will notice is another new keyword, C<with>: |
101 | |
102 | with 'Eq'; |
103 | |
104 | C<with> is used to provide a list of roles which this class (or role) consumes. |
105 | Here, B<Comparable> only consumes one role (B<Eq>). In effect, it is as if we |
106 | defined a C<not_equal_to> method within Comparable, and also promised to fulfill |
107 | the requirement of an C<equal_to> method. |
108 | |
109 | B<Comparable> itself states that it requires C<compare>. Again, it means that |
110 | any classes consuming this role must implement a C<compare> method. |
111 | |
112 | requires 'compare'; |
113 | |
114 | B<Comparable> defines an C<equal_to> method which satisfies the B<Eq> role's |
115 | requirements. This, along with a number of other methods (C<greater_than>, |
116 | C<less_than>, C<greater_than_or_equal_to>, and C<less_than_or_equal_to>) is |
117 | simply defined in terms of C<compare>, once again demonstrating the pattern of |
118 | defining a number of utility methods in terms of only a single method that the |
119 | target class need implement. |
120 | |
121 | sub equal_to { |
122 | my ($self, $other) = @_; |
123 | $self->compare($other) == 0; |
124 | } |
125 | |
126 | sub greater_than { |
127 | my ($self, $other) = @_; |
128 | $self->compare($other) == 1; |
129 | } |
130 | |
131 | sub less_than { |
132 | my ($self, $other) = @_; |
133 | $self->compare($other) == -1; |
134 | } |
135 | |
136 | sub greater_than_or_equal_to { |
137 | my ($self, $other) = @_; |
138 | $self->greater_than($other) || $self->equal_to($other); |
139 | } |
140 | |
141 | sub less_than_or_equal_to { |
142 | my ($self, $other) = @_; |
143 | $self->less_than($other) || $self->equal_to($other); |
144 | } |
145 | |
146 | Next up is B<Printable>. This is a very simple role, akin to B<Eq>. It merely |
147 | requires a C<to_string> method. |
148 | |
149 | Finally, we come to B<US::Currency>, a class that allows us to reap the benefits |
150 | of our hard work. This is a regular Moose class, so we include the normal C<use |
151 | Moose>. It consumes both B<Comparable> and B<Printable>, as the following line |
152 | shows: |
153 | |
154 | with 'Comparable', 'Printable'; |
155 | |
156 | It also defines a regular Moose attribute, C<amount>, with a type constraint of |
157 | C<Num> and a default of C<0>: |
158 | |
159 | has 'amount' => (is => 'rw', isa => 'Num', default => 0); |
160 | |
161 | Now we come to the core of the class. First up, we define a C<compare> method: |
162 | |
163 | sub compare { |
164 | my ($self, $other) = @_; |
165 | $self->amount <=> $other->amount; |
166 | } |
167 | |
168 | As you can see, it simply compares the C<amount> attribute of this object with |
169 | the C<amount> attribute of the other object passed to it. With the single |
170 | definition of this method, we gain the following methods for free: C<equal_to>, |
171 | C<greater_than>, C<less_than>, C<greater_than_or_equal_to> and |
172 | C<less_than_or_equal_to>. |
173 | |
174 | We end the class with a definition of the C<to_string> method, which formats the |
175 | C<amount> attribute for display: |
176 | |
177 | sub to_string { |
178 | my $self = shift; |
179 | sprintf '$%0.2f USD' => $self->amount |
180 | } |
181 | |
182 | =head1 CONCLUSION |
183 | |
184 | This recipe has shown that roles can be very powerful and immensely useful, and |
185 | save a great deal of repetition. |
186 | |
187 | =head1 FOOTNOTES |
188 | |
189 | =over 4 |
190 | |
191 | =item (1) |
192 | |
193 | At present, method requirements from roles cannot be satisfied by attribute |
194 | accessors. This is a limitation of Moose, and will most likely be rectified in a |
195 | future release. |
196 | |
197 | =back |
a7d0cd00 |
198 | |
a7d0cd00 |
199 | =head1 AUTHOR |
200 | |
201 | Stevan Little E<lt>stevan@iinteractive.comE<gt> |
202 | |
203 | =head1 COPYRIGHT AND LICENSE |
204 | |
778db3ac |
205 | Copyright 2006-2008 by Infinity Interactive, Inc. |
a7d0cd00 |
206 | |
207 | L<http://www.iinteractive.com> |
208 | |
209 | This library is free software; you can redistribute it and/or modify |
210 | it under the same terms as Perl itself. |
211 | |
212 | =cut |