1Sub::HandlesVia(3) User Contributed Perl Documentation Sub::HandlesVia(3)
2
3
4
6 Sub::HandlesVia - alternative handles_via implementation
7
9 package Kitchen {
10 use Moo;
11 use Sub::HandlesVia;
12 use Types::Standard qw( ArrayRef Str );
13
14 has food => (
15 is => 'ro',
16 isa => ArrayRef[Str],
17 handles_via => 'Array',
18 default => sub { [] },
19 handles => {
20 'add_food' => 'push',
21 'find_food' => 'grep',
22 },
23 );
24 }
25
26 my $kitchen = Kitchen->new;
27 $kitchen->add_food('Bacon');
28 $kitchen->add_food('Eggs');
29 $kitchen->add_food('Sausages');
30 $kitchen->add_food('Beans');
31
32 my @foods = $kitchen->find_food(sub { /^B/i });
33
35 If you've used Moose's native attribute traits, or MooX::HandlesVia
36 before, you should have a fairly good idea what this does.
37
38 Why re-invent the wheel? Well, this is an implementation that should
39 work okay with Moo, Moose, Mouse, and any other OO toolkit you throw at
40 it. One ring to rule them all, so to speak.
41
42 Also, unlike MooX::HandlesVia, it honours type constraints, plus it
43 doesn't have the limitation that it can't mutate non-reference values.
44
45 Using with Moo
46 You should be able to use it as a drop-in replacement for
47 MooX::HandlesVia.
48
49 package Kitchen {
50 use Moo;
51 use Sub::HandlesVia;
52 use Types::Standard qw( ArrayRef Str );
53
54 has food => (
55 is => 'ro',
56 isa => ArrayRef[Str],
57 handles_via => 'Array',
58 default => sub { [] },
59 handles => {
60 'add_food' => 'push',
61 'find_food' => 'grep',
62 },
63 );
64 }
65
66 Using with Mouse
67 It works the same as Moo basically.
68
69 package Kitchen {
70 use Mouse;
71 use Sub::HandlesVia;
72 use Types::Standard qw( ArrayRef Str );
73
74 has food => (
75 is => 'ro',
76 isa => ArrayRef[Str],
77 handles_via => 'Array',
78 default => sub { [] },
79 handles => {
80 'add_food' => 'push',
81 'find_food' => 'grep',
82 },
83 );
84 }
85
86 You are not forced to use Types::Standard. Mouse native types should
87 work fine.
88
89 package Kitchen {
90 use Mouse;
91 use Sub::HandlesVia;
92
93 has food => (
94 is => 'ro',
95 isa => 'ArrayRef[Str]',
96 handles_via => 'Array',
97 default => sub { [] },
98 handles => {
99 'add_food' => 'push',
100 'find_food' => 'grep',
101 },
102 );
103 }
104
105 Sub::HandlesVia will also recognize MooseX::NativeTraits-style traits.
106 It will jump in and handle them before MooseX::NativeTraits notices!
107
108 package Kitchen {
109 use Mouse;
110 use Sub::HandlesVia;
111
112 has food => (
113 is => 'ro',
114 isa => 'ArrayRef[Str]',
115 traits => ['Array'],
116 default => sub { [] },
117 handles => {
118 'add_food' => 'push',
119 'find_food' => 'grep',
120 },
121 );
122 }
123
124 (If you have a mouse in your kitchen though, that might not be very
125 hygienic.)
126
127 Using with Moose
128 It works the same as Mouse basically.
129
130 package Kitchen {
131 use Moose;
132 use Sub::HandlesVia;
133 use Types::Standard qw( ArrayRef Str );
134
135 has food => (
136 is => 'ro',
137 isa => ArrayRef[Str],
138 handles_via => 'Array',
139 default => sub { [] },
140 handles => {
141 'add_food' => 'push',
142 'find_food' => 'grep',
143 },
144 );
145 }
146
147 You are not forced to use Types::Standard. Moose native types should
148 work fine.
149
150 package Kitchen {
151 use Moose;
152 use Sub::HandlesVia;
153
154 has food => (
155 is => 'ro',
156 isa => 'ArrayRef[Str]',
157 handles_via => 'Array',
158 default => sub { [] },
159 handles => {
160 'add_food' => 'push',
161 'find_food' => 'grep',
162 },
163 );
164 }
165
166 Sub::HandlesVia will also recognize native-traits-style traits. It will
167 jump in and handle them before Moose notices!
168
169 package Kitchen {
170 use Moose;
171 use Sub::HandlesVia;
172
173 has food => (
174 is => 'ro',
175 isa => 'ArrayRef[Str]',
176 traits => ['Array'],
177 default => sub { [] },
178 handles => {
179 'add_food' => 'push',
180 'find_food' => 'grep',
181 },
182 );
183 }
184
185 (If you have a moose in your kitchen, that might be even worse than the
186 mouse.)
187
188 Using with Anything
189 For Moose and Mouse, Sub::HandlesVia can use their metaobject protocols
190 to grab an attribute's definition and install the methods it needs to.
191 For Moo, it can wrap "has" and do its stuff that way. For other
192 classes, you need to be more explicit and tell it what methods to
193 delegate to what attributes.
194
195 package Kitchen {
196 use Class::Tiny {
197 food => sub { [] },
198 };
199
200 use Sub::HandlesVia qw( delegations );
201
202 delegations(
203 attribute => 'food'
204 handles_via => 'Array',
205 handles => {
206 'add_food' => 'push',
207 'find_food' => 'grep',
208 },
209 );
210 }
211
212 Setting "attribute" to "food" means that when Sub::HandlesVia needs to
213 get the food list, it will call "$kitchen->food" and when it needs to
214 set the food list, it will call "$kitchen->food($value)". If you have
215 separate getter and setter methods, just do:
216
217 attribute => [ 'get_food', 'set_food' ],
218
219 Or if you don't have any accessors and want Sub::HandlesVia to directly
220 access the underlying hashref:
221
222 attribute => '{food}',
223
224 Or maybe you have a setter, but want to use hashref access for the
225 getter:
226
227 attribute => [ '{food}', 'set_food' ],
228
229 Or maybe you still want direct access for the getter, but your object
230 is a blessed arrayref instead of a blessed hashref:
231
232 attribute => [ '[7]', 'set_food' ],
233
234 Or maybe your needs are crazy unique:
235
236 attribute => [ \&getter, \&setter ],
237
238 The coderefs are passed the instance as their first argument, and the
239 setter is also passed a value to set.
240
241 Really, I don't think there's any object system that this won't work
242 for!
243
244 If you supply an arrayref with a getter and setter, it's also possible
245 to supply a third argument which is a coderef or string which will be
246 called as a method if needing to "reset" the value. This can be
247 thought of like a default or builder.
248
249 (The "delegations" function can be imported into Moo/Mouse/Moose
250 classes too, in which case the "attribute" needs to be the same
251 attribute name you passed to "has". You cannot use a arrayref, coderef,
252 hash key, or array index.)
253
254 What methods can be delegated to?
255 The following table compares Sub::HandlesVia with Data::Perl, Moose
256 native traits, and MouseX::NativeTraits.
257
258 Array ===========================================
259 accessor : SubHV DataP Moose Mouse
260 all : SubHV DataP
261 all_true : SubHV
262 any : SubHV Mouse
263 apply : SubHV Mouse
264 clear : SubHV DataP Moose Mouse
265 count : SubHV DataP Moose Mouse
266 delete : SubHV DataP Moose Mouse
267 elements : SubHV DataP Moose Mouse
268 fetch : Mouse (alias: get)
269 first : SubHV DataP Moose Mouse
270 first_index : SubHV DataP Moose
271 flatten : SubHV DataP
272 flatten_deep : SubHV DataP
273 for_each : SubHV Mouse
274 for_each_pair : SubHV Mouse
275 get : SubHV DataP Moose Mouse
276 grep : SubHV DataP Moose Mouse
277 head : SubHV DataP
278 insert : SubHV DataP Moose Mouse
279 is_empty : SubHV DataP Moose Mouse
280 join : SubHV DataP Moose Mouse
281 map : SubHV DataP Moose Mouse
282 max : SubHV
283 maxstr : SubHV
284 min : SubHV
285 minstr : SubHV
286 natatime : SubHV DataP Moose
287 not_all_true : SubHV
288 pairfirst : SubHV
289 pairgrep : SubHV
290 pairkeys : SubHV
291 pairmap : SubHV
292 pairs : SubHV
293 pairvalues : SubHV
294 pick_random : SubHV
295 pop : SubHV DataP Moose Mouse
296 print : SubHV DataP
297 product : SubHV
298 push : SubHV DataP Moose Mouse
299 reduce : SubHV DataP Moose Mouse
300 reductions : SubHV
301 remove : Mouse (alias: delete)
302 reset : SubHV
303 reverse : SubHV DataP
304 sample : SubHV
305 set : SubHV DataP Moose Mouse
306 shallow_clone : SubHV DataP Moose
307 shift : SubHV DataP Moose Mouse
308 shuffle : SubHV DataP Moose Mouse
309 shuffle_in_place : SubHV
310 sort : SubHV DataP Moose Mouse
311 sort_by : Mouse (sort)
312 sort_in_place : SubHV DataP Moose Mouse
313 sort_in_place_by : Mouse (sort_in_place)
314 splice : SubHV DataP Moose Mouse
315 store : Mouse (alias: set)
316 sum : SubHV
317 tail : SubHV DataP
318 uniq : SubHV DataP Moose Mouse
319 uniq_in_place : SubHV
320 uniqnum : SubHV
321 uniqnum_in_place : SubHV
322 uniqstr : SubHV
323 uniqstr_in_place : SubHV
324 unshift : SubHV DataP Moose Mouse
325
326 Bool ============================================
327 not : SubHV DataP Moose Mouse
328 reset : SubHV
329 set : SubHV DataP Moose Mouse
330 toggle : SubHV DataP Moose Mouse
331 unset : SubHV DataP Moose Mouse
332
333 Code ============================================
334 execute : SubHV DataP Moose Mouse
335 execute_method : SubHV Moose Mouse
336
337 Counter =========================================
338 dec : SubHV DataP Moose Mouse
339 inc : SubHV DataP Moose Mouse
340 reset : SubHV DataP Moose Mouse
341 set : SubHV Moose Mouse
342
343 Hash ============================================
344 accessor : SubHV DataP Moose Mouse
345 all : SubHV DataP
346 clear : SubHV DataP Moose Mouse
347 count : SubHV DataP Moose Mouse
348 defined : SubHV DataP Moose Mouse
349 delete : SubHV DataP Moose Mouse
350 elements : SubHV DataP Moose Mouse
351 exists : SubHV DataP Moose Mouse
352 fetch : Mouse (alias: get)
353 for_each_key : SubHV Mouse
354 for_each_pair : SubHV Mouse
355 for_each_value : SubHV Mouse
356 get : SubHV DataP Moose Mouse
357 is_empty : SubHV DataP Moose Mouse
358 keys : SubHV DataP Moose Mouse
359 kv : SubHV DataP Moose Mouse
360 reset : SubHV
361 set : SubHV DataP Moose Mouse
362 shallow_clone : SubHV DataP Moose
363 sorted_keys : SubHV Mouse
364 store : Mouse (alias: set)
365 values : SubHV DataP Moose Mouse
366
367 Number ==========================================
368 abs : SubHV DataP Moose Mouse
369 add : SubHV DataP Moose Mouse
370 div : SubHV DataP Moose Mouse
371 get : SubHV
372 mod : SubHV DataP Moose Mouse
373 mul : SubHV DataP Moose Mouse
374 set : SubHV Moose
375 sub : SubHV DataP Moose Mouse
376
377 String ==========================================
378 append : SubHV DataP Moose Mouse
379 chomp : SubHV DataP Moose Mouse
380 chop : SubHV DataP Moose Mouse
381 clear : SubHV DataP Moose Mouse
382 get : SubHV
383 inc : SubHV DataP Moose Mouse
384 length : SubHV DataP Moose Mouse
385 match : SubHV DataP Moose Mouse
386 prepend : SubHV DataP Moose Mouse
387 replace : SubHV DataP Moose Mouse
388 replace_globally : SubHV Mouse
389 reset : SubHV
390 set : SubHV
391 substr : SubHV DataP Moose Mouse
392
393 Method Chaining
394 Say you have the following
395
396 handles_via => 'Array',
397 handles => {
398 'add_food' => 'push',
399 'find_food' => 'grep',
400 'remove_food' => 'pop',
401 },
402
403 Now "$kitchen->remove_food" will remove the last food on the list and
404 return it. But what if we don't care about what food was removed? We
405 just want to remove the food and discard it. You can do this:
406
407 handles_via => 'Array',
408 handles => {
409 'add_food' => 'push',
410 'find_food' => 'grep',
411 'remove_food' => 'pop...',
412 },
413
414 Now the "remove_food" method will return the kitchen object instead of
415 returning the food. This makes it suitable for chaining method calls:
416
417 # remove the three most recent foods
418 $kitchen->remove_food->remove_food->remove_food;
419
420 Hand Waving
421 Sub::HandlesVia tries to be strict by default, but you can tell it to
422 be less rigourous checking method arguments, etc using the "~" prefix:
423
424 handles_via => 'Array',
425 handles => {
426 'find_food' => '~grep',
427 },
428
429 CodeRefs
430 You can delegate to coderefs:
431
432 handles_via => 'Array',
433 handles => {
434 'find_healthiest' => sub { my $foods = shift; ... },
435 }
436
437 Named Methods
438 Let's say "FoodList" is a class where instances are blessed arrayrefs
439 of strings.
440
441 isa => InstanceOf['FoodList'],
442 handles_via => 'Array',
443 handles => {
444 'find_food' => 'grep',
445 'find_healthiest_food' => 'find_healthiest',
446 },
447
448 Now "$kitchen->find_food($coderef)" does this (which breaks
449 encapsulation of course):
450
451 my @result = grep $coderef->(), @{ $kitchen->food };
452
453 And "$kitchen->find_healthiest_food" does this:
454
455 $kitchen->food->find_healthiest
456
457 Basically, because "find_healthiest" isn't one of the methods offered
458 by Sub::HandlesVia::HandlerList::Array, it assumes you want to call it
459 on the arrayref like a proper method.
460
461 Currying Favour
462 All this talk of food is making me hungry, but as much as I'd like to
463 eat a curry right now, that's not the kind of currying we're talking
464 about.
465
466 handles_via => 'Array',
467 handles => {
468 'get_food' => 'get',
469 },
470
471 "$kitchen->get_food(0)" will return the first item on the list.
472 "$kitchen->get_food(1)" will return the second item on the list. And
473 so on.
474
475 handles_via => 'Array',
476 handles => {
477 'first_food' => [ 'get' => 0 ],
478 'second_food' => [ 'get' => 1 ],
479 },
480
481 I think you already know what this does. Right?
482
483 And yes, currying works with coderefs.
484
485 handles_via => 'Array',
486 handles => {
487 'blargy' => [ sub { ... }, @curried ],
488 },
489
490 Pick and Mix
491 isa => ArrayRef|HashRef,
492 handles_via => [ 'Array', 'Hash' ],
493 handles => {
494 the_keys => 'keys',
495 ship_shape => 'sort_in_place',
496 }
497
498 Here you have an attribute which might be an arrayref or a hashref.
499 When it's an arrayref, "$object->ship_shape" will work nicely, but
500 "$object->the_keys" will fail badly.
501
502 Still, this sort of thing can kind of make sense if you have an object
503 that overloads both "@{}" and "%{}".
504
505 Sometime a method will be ambiguous. For example, there's a "get"
506 method for both hashes and arrays. In this case, the array one will win
507 because you listed it first in "handles_via".
508
509 But you can be specific:
510
511 isa => ArrayRef|HashRef,
512 handles_via => [ 'Array', 'Hash' ],
513 handles => {
514 get_foo => 'Array->get',
515 get_bar => 'Hash->get',
516 }
517
519 Please report any bugs to
520 <http://rt.cpan.org/Dist/Display.html?Queue=Sub-HandlesVia>.
521
522 (There are known bugs for Moose native types that do coercion.)
523
525 Moose, MouseX::NativeTraits, Data::Perl, MooX::HandlesVia.
526
528 Toby Inkster <tobyink@cpan.org>.
529
531 This software is copyright (c) 2020 by Toby Inkster.
532
533 This is free software; you can redistribute it and/or modify it under
534 the same terms as the Perl 5 programming language system itself.
535
537 THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
538 WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
539 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
540
541
542
543perl v5.34.0 2021-10-22 Sub::HandlesVia(3)