Commit | Line | Data |
92cd015f |
1 | =pod |
2 | |
3 | =head1 NAME |
4 | |
5 | Moose::Manual::Attribute - Moose's Method Modifiers |
6 | |
7 | =head1 WHAT IS A METHOD MODIFIER? |
8 | |
9 | Moose provides a feature called "method modifiers". Another word for |
10 | this feature might be "hooks" or "advice". |
11 | |
12 | It's probably easiest to understand this feature with a few examples: |
13 | |
14 | package Example; |
15 | |
16 | use Moose; |
17 | |
18 | sub foo { |
08f950aa |
19 | print "foo\n"; |
92cd015f |
20 | } |
21 | |
22 | before 'foo' => sub { print "about to call foo\n"; }; |
08f950aa |
23 | after 'foo' => sub { print "just called foo\n"; }; |
92cd015f |
24 | |
25 | around 'foo' => sub { |
26 | my $orig = shift; |
27 | my $self = shift; |
28 | |
29 | print "I'm around foo\n"; |
30 | |
31 | $self->$orig(@_); |
32 | |
33 | print "I'm still around foo\n"; |
34 | }; |
35 | |
36 | Now if I call C<< Example->new->foo >> I'll get the following output: |
37 | |
38 | about to call foo |
39 | I'm around foo |
40 | foo |
41 | I'm still around foo |
42 | just called foo |
43 | |
44 | You probably could have figured that out from the names "before", |
45 | "after", and "around". |
46 | |
47 | Also, as you can see, the before modifiers come before around |
48 | modifiers, and after modifiers come last. |
49 | |
50 | When there are multiple modifiers of the same type, the before and |
51 | around modifiers run from the last added to the first, and after |
52 | modifiers run from first added to last: |
53 | |
54 | before 2 |
55 | before 1 |
56 | around 2 |
57 | around 1 |
58 | primary |
59 | around 1 |
60 | around 2 |
61 | after 1 |
62 | after 2 |
63 | |
64 | =head1 WHY USE THEM? |
65 | |
66 | Method modifiers have many uses. One very common use is in roles. This |
67 | lets roles alter the behavior of methods in the classes that use |
68 | them. See L<Moose::Manual::Roles> for more about roles. |
69 | |
70 | Modifiers really are at their most useful in roles, so some of the |
71 | examples below are a bit artificial. They're intended to give you an |
72 | idea of how modifiers work, but may not be the most natural usages. |
73 | |
74 | =head1 BEFORE, AFTER, AND AROUND |
75 | |
76 | Method modifiers can also be used to add behavior to a method that |
77 | Moose generates for you, such as an attribute accessor: |
78 | |
79 | has 'size' => ( is => 'rw' ); |
80 | |
81 | before 'size' => sub { |
82 | my $self = shift; |
83 | |
84 | if (@_) { |
85 | Carp::cluck('Someone is setting size'); |
86 | } |
87 | }; |
88 | |
89 | Another use for the before modifier would be to do some sort of |
90 | pre-checking on a method call. For example: |
91 | |
92 | before 'size' => sub { |
93 | my $self = shift; |
94 | |
95 | die 'Cannot set size while the person is growing' |
96 | if @_ && $self->is_growing; |
97 | }; |
98 | |
99 | This lets us implement logical checks that don't fit well into |
100 | constraints. |
101 | |
102 | Similarly, an after modifier could be used for logging an action that |
103 | was taken. |
104 | |
105 | Note that the return values of both before and after modifiers are |
106 | ignored. |
107 | |
108 | An around modifier is a bit more powerful than either a before or |
109 | after modifier. First, it is easy to modify the arguments being passed |
110 | onto the original method in an around modifier. Second, you can decide |
111 | to simply not call the original method at all, unlike with other |
112 | modifiers. Finally, you can modify the return value with an around |
113 | modifier. |
114 | |
115 | An around modifier receives the original method as its first argument, |
116 | I<then> the object, and finally any arguments passed to the method. |
117 | |
118 | around 'size' => sub { |
119 | my $orig = shift; |
120 | my $self = shift; |
121 | |
122 | return $self->$orig() |
123 | unless @_; |
124 | |
125 | my $size = shift; |
126 | $size = $size / 2 |
127 | if $self->likes_small_things(); |
128 | |
129 | return $self->$orig($size); |
130 | }; |
131 | |
132 | =head1 INNER AND AUGMENT |
133 | |
134 | Augment and inner are two halves of the same feature. The augment |
135 | modifier provides a sort of inverted subclassing. You provide part of |
136 | the implementation in a superclass, and then document that subclasses |
137 | are expected to provide the rest. |
138 | |
139 | The superclass calls C<inner()>, which then calls the C<augment> |
140 | modifier in the subclass: |
141 | |
142 | package Document; |
143 | |
144 | use Moose; |
145 | |
146 | sub as_xml { |
147 | my $self = shift; |
148 | |
149 | my $xml = "<document>\n"; |
150 | $xml .= inner(); |
151 | $xml .= "</document>\n"; |
152 | |
153 | return $xml; |
154 | } |
155 | |
156 | Using C<inner()> in this method makes it possible for one or more |
157 | subclasses to then augment this method with their own specific |
158 | implementation: |
159 | |
160 | package Report; |
161 | |
162 | use Moose; |
163 | |
164 | extends 'Document'; |
165 | |
166 | augment 'as_xml' => sub { |
167 | my $self = shift; |
168 | |
169 | my $xml = "<report>\n"; |
170 | $xml .= inner(); |
171 | $xml .= "</report>\n"; |
172 | |
173 | return $xml; |
174 | }; |
175 | |
176 | When we call C<as_xml> on a Report object, we get something like this: |
177 | |
178 | <document> |
179 | <report> |
180 | </report> |
181 | </document> |
182 | |
183 | But we also called C<inner()> in C<Report>, so we can continue |
184 | subclassing and adding more content inside the document: |
185 | |
186 | package Report::IncomeAndExpenses; |
187 | |
188 | use Moose; |
189 | |
190 | extends 'Report'; |
191 | |
192 | augment 'as_xml' => sub { |
193 | my $self = shift; |
194 | |
195 | my $xml = '<income>' . $self->income . '</income>'; |
196 | $xml .= "\n"; |
197 | my $xml = '<expenses>' . $self->expenses . '</expenses>'; |
198 | $xml .= "\n"; |
199 | |
200 | $xml .= inner() || q{}; |
201 | |
202 | return $xml; |
203 | }; |
204 | |
205 | Now our report has some content: |
206 | |
207 | <document> |
208 | <report> |
209 | <income>$10</income> |
210 | <expenses>$8</expenses> |
211 | </report> |
212 | </document> |
213 | |
214 | What makes this combination of C<augment> and C<inner()> special is |
215 | that it allows us to have methods which are called from I<parent |
216 | (least specific) to child (most specific). This inverts the normal |
217 | order, where the child's method is called first, and it in turn will |
218 | call C<< $self->SUPER::method >> to call the parent. |
219 | |
220 | Note that in C<Report::IncomeAndExpenses> we call C<inner()> again. If |
221 | the object is an instance of C<Report::IncomeAndExpenses> then this |
222 | call is a no-op, and just returns false. |
223 | |
224 | =head1 OVERRIDE AND SUPER |
225 | |
226 | Finally, Moose provides some simple sugar for Perl's built-in method |
227 | overriding scheme. If you want to override a method from a parent |
228 | class, you can do this with C<override>: |
229 | |
230 | package Employee; |
231 | |
232 | use Moose; |
233 | |
234 | extends 'Person'; |
235 | |
236 | has 'job_title' => ( is => 'rw' ); |
237 | |
238 | override 'display_name' => sub { |
239 | my $self = shift; |
240 | |
241 | return super() . q{, } . $self->title(); |
242 | }; |
243 | |
244 | The call to C<super()> is almost the same as calling C<< |
245 | $self->SUPER::display_name >>. The difference is that the arguments |
246 | passed to the superclass's method will always be the same as the ones |
247 | passed to the method modifier, and cannot be changed. |
248 | |
249 | All arguments passed to C<super()> are ignored, as are any changes |
250 | made to C<@_> before C<super()> is called. |
251 | |
252 | =head1 SEMI-COLONS |
253 | |
254 | Because all of these method modifiers are implemented as Perl |
255 | functions, you must always end the modifier declaration with a |
256 | semi-colon: |
257 | |
258 | after 'foo' => sub { }; |
259 | |
260 | =head1 AUTHOR |
261 | |
262 | Dave Rolsky E<lt>autarch@urth.orgE<gt> |
263 | |
264 | =head1 COPYRIGHT AND LICENSE |
265 | |
266 | Copyright 2008 by Infinity Interactive, Inc. |
267 | |
268 | L<http://www.iinteractive.com> |
269 | |
270 | This library is free software; you can redistribute it and/or modify |
271 | it under the same terms as Perl itself. |
272 | |
273 | =cut |