Commit | Line | Data |
daa0fd7d |
1 | package Moose::Manual::MethodModifiers; |
2 | |
3 | # ABSTRACT: Moose's method modifiers |
92cd015f |
4 | |
daa0fd7d |
5 | __END__ |
92cd015f |
6 | |
daa0fd7d |
7 | =pod |
92cd015f |
8 | |
9 | =head1 WHAT IS A METHOD MODIFIER? |
10 | |
646e0fb0 |
11 | Moose provides a feature called "method modifiers". You can also think |
12 | of these as "hooks" or "advice". |
92cd015f |
13 | |
14 | It's probably easiest to understand this feature with a few examples: |
15 | |
16 | package Example; |
17 | |
18 | use Moose; |
19 | |
20 | sub foo { |
909103e1 |
21 | print " foo\n"; |
92cd015f |
22 | } |
23 | |
24 | before 'foo' => sub { print "about to call foo\n"; }; |
08f950aa |
25 | after 'foo' => sub { print "just called foo\n"; }; |
92cd015f |
26 | |
27 | around 'foo' => sub { |
28 | my $orig = shift; |
29 | my $self = shift; |
30 | |
909103e1 |
31 | print " I'm around foo\n"; |
92cd015f |
32 | |
33 | $self->$orig(@_); |
34 | |
909103e1 |
35 | print " I'm still around foo\n"; |
92cd015f |
36 | }; |
37 | |
38 | Now if I call C<< Example->new->foo >> I'll get the following output: |
39 | |
40 | about to call foo |
909103e1 |
41 | I'm around foo |
42 | foo |
43 | I'm still around foo |
92cd015f |
44 | just called foo |
45 | |
46 | You probably could have figured that out from the names "before", |
47 | "after", and "around". |
48 | |
49 | Also, as you can see, the before modifiers come before around |
50 | modifiers, and after modifiers come last. |
51 | |
52 | When there are multiple modifiers of the same type, the before and |
53 | around modifiers run from the last added to the first, and after |
54 | modifiers run from first added to last: |
55 | |
56 | before 2 |
57 | before 1 |
58 | around 2 |
59 | around 1 |
60 | primary |
61 | around 1 |
62 | around 2 |
63 | after 1 |
64 | after 2 |
65 | |
66 | =head1 WHY USE THEM? |
67 | |
909103e1 |
68 | Method modifiers have many uses. They are often used in roles to alter the |
69 | behavior of methods in the classes that consume the role. See |
70 | L<Moose::Manual::Roles> for more information about roles. |
92cd015f |
71 | |
dab94063 |
72 | Since modifiers are mostly useful in roles, some of the examples below |
73 | are a bit artificial. They're intended to give you an idea of how |
74 | modifiers work, but may not be the most natural usage. |
92cd015f |
75 | |
76 | =head1 BEFORE, AFTER, AND AROUND |
77 | |
ebcf4093 |
78 | Method modifiers can be used to add behavior to methods without modifying the definition of those methods. |
79 | |
80 | =head2 BEFORE and AFTER modifiers |
81 | |
dab94063 |
82 | Method modifiers can be used to add behavior to a method that Moose |
83 | generates for you, such as an attribute accessor: |
92cd015f |
84 | |
85 | has 'size' => ( is => 'rw' ); |
86 | |
87 | before 'size' => sub { |
88 | my $self = shift; |
89 | |
90 | if (@_) { |
91 | Carp::cluck('Someone is setting size'); |
92 | } |
93 | }; |
94 | |
95 | Another use for the before modifier would be to do some sort of |
6549b0d1 |
96 | prechecking on a method call. For example: |
92cd015f |
97 | |
98 | before 'size' => sub { |
99 | my $self = shift; |
100 | |
101 | die 'Cannot set size while the person is growing' |
102 | if @_ && $self->is_growing; |
103 | }; |
104 | |
646e0fb0 |
105 | This lets us implement logical checks that don't make sense as type |
106 | constraints. In particular, they're useful for defining logical rules |
107 | about an object's state changes. |
92cd015f |
108 | |
109 | Similarly, an after modifier could be used for logging an action that |
110 | was taken. |
111 | |
112 | Note that the return values of both before and after modifiers are |
113 | ignored. |
114 | |
ebcf4093 |
115 | =head2 AROUND modifiers |
116 | |
909103e1 |
117 | An around modifier is more powerful than either a before or |
646e0fb0 |
118 | after modifier. It can modify the arguments being passed to the |
119 | original method, and you can even decide to simply not call the |
dab94063 |
120 | original method at all. You can also modify the return value with an |
121 | around modifier. |
92cd015f |
122 | |
123 | An around modifier receives the original method as its first argument, |
124 | I<then> the object, and finally any arguments passed to the method. |
125 | |
126 | around 'size' => sub { |
127 | my $orig = shift; |
128 | my $self = shift; |
129 | |
130 | return $self->$orig() |
131 | unless @_; |
132 | |
133 | my $size = shift; |
134 | $size = $size / 2 |
135 | if $self->likes_small_things(); |
136 | |
137 | return $self->$orig($size); |
138 | }; |
139 | |
ebcf4093 |
140 | =head2 Wrapping multiple methods at once |
141 | |
78946cf8 |
142 | C<before>, C<after>, and C<around> can also modify multiple methods |
143 | at once. The simplest example of this is passing them as a list: |
144 | |
909103e1 |
145 | before [qw(foo bar baz)] => sub { |
78946cf8 |
146 | warn "something is being called!"; |
147 | }; |
148 | |
149 | This will add a C<before> modifier to each of the C<foo>, C<bar>, |
150 | and C<baz> methods in the current class, just as though a separate |
151 | call to C<before> was made for each of them. The list can be passed |
152 | either as a bare list, or as an arrayref. Note that the name of the |
153 | function being modified isn't passed in in any way; this syntax is |
154 | only intended for cases where the function being modified doesn't |
909103e1 |
155 | actually matter. If the function name does matter, use something like this: |
78946cf8 |
156 | |
157 | for my $func (qw(foo bar baz)) { |
158 | before $func => sub { |
159 | warn "$func was called!"; |
160 | }; |
161 | } |
162 | |
ebcf4093 |
163 | =head2 Using regular expressions to select methods to wrap |
164 | |
78946cf8 |
165 | In addition, you can specify a regular expression to indicate the |
166 | methods to wrap, like so: |
167 | |
168 | after qr/^command_/ => sub { |
169 | warn "got a command"; |
170 | }; |
171 | |
172 | This will match the regular expression against each method name |
173 | returned by L<Class::MOP::Class/get_method_list>, and add a modifier |
ebcf4093 |
174 | to each one that matches. The same caveats apply as above. |
175 | |
176 | Using regular expressions to determine methods to wrap is quite a bit more |
177 | powerful than the previous alternatives, but it's also quite a bit more |
178 | dangerous. Bear in mind that if your regular expression matches certain Perl |
179 | and Moose reserved method names with a special meaning to Moose or Perl, such |
180 | as C<meta>, C<new>, C<BUILD>, C<DESTROY>, C<AUTOLOAD>, etc, this could cause |
181 | unintended (and hard to debug) problems and is best avoided. |
182 | |
78946cf8 |
183 | |
92cd015f |
184 | =head1 INNER AND AUGMENT |
185 | |
186 | Augment and inner are two halves of the same feature. The augment |
187 | modifier provides a sort of inverted subclassing. You provide part of |
188 | the implementation in a superclass, and then document that subclasses |
189 | are expected to provide the rest. |
190 | |
191 | The superclass calls C<inner()>, which then calls the C<augment> |
192 | modifier in the subclass: |
193 | |
194 | package Document; |
195 | |
196 | use Moose; |
197 | |
198 | sub as_xml { |
199 | my $self = shift; |
200 | |
201 | my $xml = "<document>\n"; |
202 | $xml .= inner(); |
203 | $xml .= "</document>\n"; |
204 | |
205 | return $xml; |
206 | } |
207 | |
208 | Using C<inner()> in this method makes it possible for one or more |
209 | subclasses to then augment this method with their own specific |
210 | implementation: |
211 | |
212 | package Report; |
213 | |
214 | use Moose; |
215 | |
216 | extends 'Document'; |
217 | |
218 | augment 'as_xml' => sub { |
219 | my $self = shift; |
220 | |
909103e1 |
221 | my $xml = " <report>\n"; |
92cd015f |
222 | $xml .= inner(); |
909103e1 |
223 | $xml .= " </report>\n"; |
92cd015f |
224 | |
225 | return $xml; |
226 | }; |
227 | |
228 | When we call C<as_xml> on a Report object, we get something like this: |
229 | |
230 | <document> |
909103e1 |
231 | <report> |
232 | </report> |
92cd015f |
233 | </document> |
234 | |
235 | But we also called C<inner()> in C<Report>, so we can continue |
236 | subclassing and adding more content inside the document: |
237 | |
238 | package Report::IncomeAndExpenses; |
239 | |
240 | use Moose; |
241 | |
242 | extends 'Report'; |
243 | |
244 | augment 'as_xml' => sub { |
245 | my $self = shift; |
246 | |
909103e1 |
247 | my $xml = ' <income>' . $self->income . '</income>'; |
92cd015f |
248 | $xml .= "\n"; |
909103e1 |
249 | $xml .= ' <expenses>' . $self->expenses . '</expenses>'; |
92cd015f |
250 | $xml .= "\n"; |
251 | |
252 | $xml .= inner() || q{}; |
253 | |
254 | return $xml; |
255 | }; |
256 | |
257 | Now our report has some content: |
258 | |
259 | <document> |
909103e1 |
260 | <report> |
261 | <income>$10</income> |
262 | <expenses>$8</expenses> |
263 | </report> |
92cd015f |
264 | </document> |
265 | |
266 | What makes this combination of C<augment> and C<inner()> special is |
ce5e6e3c |
267 | that it allows us to have methods which are called from parent (least |
646e0fb0 |
268 | specific) to child (most specific). This inverts the normal |
269 | inheritance pattern. |
92cd015f |
270 | |
909103e1 |
271 | Note that in C<Report::IncomeAndExpenses> we call C<inner()> again. If the |
272 | object is an instance of C<Report::IncomeAndExpenses> then this call is a |
273 | no-op, and just returns false. It's a good idea to always call C<inner()> to |
274 | allow for future subclassing. |
92cd015f |
275 | |
276 | =head1 OVERRIDE AND SUPER |
277 | |
278 | Finally, Moose provides some simple sugar for Perl's built-in method |
279 | overriding scheme. If you want to override a method from a parent |
280 | class, you can do this with C<override>: |
281 | |
282 | package Employee; |
283 | |
284 | use Moose; |
285 | |
286 | extends 'Person'; |
287 | |
288 | has 'job_title' => ( is => 'rw' ); |
289 | |
290 | override 'display_name' => sub { |
291 | my $self = shift; |
292 | |
293 | return super() . q{, } . $self->title(); |
294 | }; |
295 | |
296 | The call to C<super()> is almost the same as calling C<< |
297 | $self->SUPER::display_name >>. The difference is that the arguments |
298 | passed to the superclass's method will always be the same as the ones |
299 | passed to the method modifier, and cannot be changed. |
300 | |
301 | All arguments passed to C<super()> are ignored, as are any changes |
302 | made to C<@_> before C<super()> is called. |
303 | |
304 | =head1 SEMI-COLONS |
305 | |
306 | Because all of these method modifiers are implemented as Perl |
307 | functions, you must always end the modifier declaration with a |
308 | semi-colon: |
309 | |
310 | after 'foo' => sub { }; |
311 | |
92cd015f |
312 | =cut |
49d85bb5 |
313 | |
314 | =head1 CAVEATS |
315 | |
316 | These method modification features do not work well with multiple inheritance, |
317 | due to how method resolution is performed in Perl. Experiment with a test |
abcafa00 |
318 | program to ensure your class hierarchy works as expected, or more preferably, |
49d85bb5 |
319 | don't use multiple inheritance (roles can help with this)! |
320 | |
321 | |