.\" -*- mode: troff; coding: utf-8 -*- .\" Automatically generated by Pod::Man 5.01 (Pod::Simple 3.43) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" \*(C` and \*(C' are quotes in nroff, nothing in troff, for use with C<>. .ie n \{\ . ds C` "" . ds C' "" 'br\} .el\{\ . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Sub::HandlesVia 3" .TH Sub::HandlesVia 3 2023-07-26 "perl v5.38.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH NAME Sub::HandlesVia \- alternative handles_via implementation .SH SYNOPSIS .IX Header "SYNOPSIS" .Vb 4 \& package Kitchen { \& use Moo; \& use Sub::HandlesVia; \& use Types::Standard qw( ArrayRef Str ); \& \& has food => ( \& is => \*(Aqro\*(Aq, \& isa => ArrayRef[Str], \& handles_via => \*(AqArray\*(Aq, \& default => sub { [] }, \& handles => { \& \*(Aqadd_food\*(Aq => \*(Aqpush\*(Aq, \& \*(Aqfind_food\*(Aq => \*(Aqgrep\*(Aq, \& }, \& ); \& } \& \& my $kitchen = Kitchen\->new; \& $kitchen\->add_food(\*(AqBacon\*(Aq); \& $kitchen\->add_food(\*(AqEggs\*(Aq); \& $kitchen\->add_food(\*(AqSausages\*(Aq); \& $kitchen\->add_food(\*(AqBeans\*(Aq); \& \& my @foods = $kitchen\->find_food(sub { /^B/i }); .Ve .SH DESCRIPTION .IX Header "DESCRIPTION" If you've used Moose's native attribute traits, or MooX::HandlesVia before, you should have a fairly good idea what this does. .PP Why re-invent the wheel? Well, this is an implementation that should work okay with Moo, Moose, Mouse, and any other OO toolkit you throw at it. One ring to rule them all, so to speak. .PP Also, unlike MooX::HandlesVia, it honours type constraints, plus it doesn't have the limitation that it can't mutate non-reference values. .SS "Using with Moo" .IX Subsection "Using with Moo" You should be able to use it as a drop-in replacement for MooX::HandlesVia. .PP .Vb 4 \& package Kitchen { \& use Moo; \& use Sub::HandlesVia; \& use Types::Standard qw( ArrayRef Str ); \& \& has food => ( \& is => \*(Aqro\*(Aq, \& isa => ArrayRef[Str], \& handles_via => \*(AqArray\*(Aq, \& default => sub { [] }, \& handles => { \& \*(Aqadd_food\*(Aq => \*(Aqpush\*(Aq, \& \*(Aqfind_food\*(Aq => \*(Aqgrep\*(Aq, \& }, \& ); \& } .Ve .SS "Using with Mouse" .IX Subsection "Using with Mouse" It works the same as Moo basically. .PP .Vb 4 \& package Kitchen { \& use Mouse; \& use Sub::HandlesVia; \& use Types::Standard qw( ArrayRef Str ); \& \& has food => ( \& is => \*(Aqro\*(Aq, \& isa => ArrayRef[Str], \& handles_via => \*(AqArray\*(Aq, \& default => sub { [] }, \& handles => { \& \*(Aqadd_food\*(Aq => \*(Aqpush\*(Aq, \& \*(Aqfind_food\*(Aq => \*(Aqgrep\*(Aq, \& }, \& ); \& } .Ve .PP You are not forced to use Types::Standard. Mouse native types should work fine. .PP .Vb 3 \& package Kitchen { \& use Mouse; \& use Sub::HandlesVia; \& \& has food => ( \& is => \*(Aqro\*(Aq, \& isa => \*(AqArrayRef[Str]\*(Aq, \& handles_via => \*(AqArray\*(Aq, \& default => sub { [] }, \& handles => { \& \*(Aqadd_food\*(Aq => \*(Aqpush\*(Aq, \& \*(Aqfind_food\*(Aq => \*(Aqgrep\*(Aq, \& }, \& ); \& } .Ve .PP Sub::HandlesVia will also recognize MooseX::NativeTraits\-style traits. It will jump in and handle them before MooseX::NativeTraits notices! .PP .Vb 3 \& package Kitchen { \& use Mouse; \& use Sub::HandlesVia; \& \& has food => ( \& is => \*(Aqro\*(Aq, \& isa => \*(AqArrayRef[Str]\*(Aq, \& traits => [\*(AqArray\*(Aq], \& default => sub { [] }, \& handles => { \& \*(Aqadd_food\*(Aq => \*(Aqpush\*(Aq, \& \*(Aqfind_food\*(Aq => \*(Aqgrep\*(Aq, \& }, \& ); \& } .Ve .PP (If you have a mouse in your kitchen though, that might not be very hygienic.) .SS "Using with Moose" .IX Subsection "Using with Moose" It works the same as Mouse basically. .PP .Vb 4 \& package Kitchen { \& use Moose; \& use Sub::HandlesVia; \& use Types::Standard qw( ArrayRef Str ); \& \& has food => ( \& is => \*(Aqro\*(Aq, \& isa => ArrayRef[Str], \& handles_via => \*(AqArray\*(Aq, \& default => sub { [] }, \& handles => { \& \*(Aqadd_food\*(Aq => \*(Aqpush\*(Aq, \& \*(Aqfind_food\*(Aq => \*(Aqgrep\*(Aq, \& }, \& ); \& } .Ve .PP You are not forced to use Types::Standard. Moose native types should work fine. .PP .Vb 3 \& package Kitchen { \& use Moose; \& use Sub::HandlesVia; \& \& has food => ( \& is => \*(Aqro\*(Aq, \& isa => \*(AqArrayRef[Str]\*(Aq, \& handles_via => \*(AqArray\*(Aq, \& default => sub { [] }, \& handles => { \& \*(Aqadd_food\*(Aq => \*(Aqpush\*(Aq, \& \*(Aqfind_food\*(Aq => \*(Aqgrep\*(Aq, \& }, \& ); \& } .Ve .PP Sub::HandlesVia will also recognize native-traits-style traits. It will jump in and handle them before Moose notices! .PP .Vb 3 \& package Kitchen { \& use Moose; \& use Sub::HandlesVia; \& \& has food => ( \& is => \*(Aqro\*(Aq, \& isa => \*(AqArrayRef[Str]\*(Aq, \& traits => [\*(AqArray\*(Aq], \& default => sub { [] }, \& handles => { \& \*(Aqadd_food\*(Aq => \*(Aqpush\*(Aq, \& \*(Aqfind_food\*(Aq => \*(Aqgrep\*(Aq, \& }, \& ); \& } .Ve .PP (If you have a moose in your kitchen, that might be even worse than the mouse.) .SS "Using with Anything" .IX Subsection "Using with Anything" For Moose and Mouse, Sub::HandlesVia can use their metaobject protocols to grab an attribute's definition and install the methods it needs to. For Moo, it can wrap \f(CW\*(C`has\*(C'\fR and do its stuff that way. For other classes, you need to be more explicit and tell it what methods to delegate to what attributes. .PP .Vb 4 \& package Kitchen { \& use Class::Tiny { \& food => sub { [] }, \& }; \& \& use Sub::HandlesVia qw( delegations ); \& \& delegations( \& attribute => \*(Aqfood\*(Aq \& handles_via => \*(AqArray\*(Aq, \& handles => { \& \*(Aqadd_food\*(Aq => \*(Aqpush\*(Aq, \& \*(Aqfind_food\*(Aq => \*(Aqgrep\*(Aq, \& }, \& ); \& } .Ve .PP Setting \f(CW\*(C`attribute\*(C'\fR to "food" means that when Sub::HandlesVia needs to get the food list, it will call \f(CW\*(C`$kitchen\->food\*(C'\fR and when it needs to set the food list, it will call \f(CW\*(C`$kitchen\->food($value)\*(C'\fR. If you have separate getter and setter methods, just do: .PP .Vb 1 \& attribute => [ \*(Aqget_food\*(Aq, \*(Aqset_food\*(Aq ], .Ve .PP Or if you don't have any accessors and want Sub::HandlesVia to directly access the underlying hashref: .PP .Vb 1 \& attribute => \*(Aq{food}\*(Aq, .Ve .PP Or maybe you have a setter, but want to use hashref access for the getter: .PP .Vb 1 \& attribute => [ \*(Aq{food}\*(Aq, \*(Aqset_food\*(Aq ], .Ve .PP Or maybe you still want direct access for the getter, but your object is a blessed arrayref instead of a blessed hashref: .PP .Vb 1 \& attribute => [ \*(Aq[7]\*(Aq, \*(Aqset_food\*(Aq ], .Ve .PP Or maybe your needs are crazy unique: .PP .Vb 1 \& attribute => [ \e&getter, \e&setter ], .Ve .PP The coderefs are passed the instance as their first argument, and the setter is also passed a value to set. .PP Really, I don't think there's any object system that this won't work for! .PP If you supply an arrayref with a getter and setter, it's also possible to supply a third argument which is a coderef or string which will be called as a method if needing to "reset" the value. This can be thought of like a default or builder. .PP (The \f(CW\*(C`delegations\*(C'\fR function can be imported into Moo/Mouse/Moose classes too, in which case the \f(CW\*(C`attribute\*(C'\fR needs to be the same attribute name you passed to \f(CW\*(C`has\*(C'\fR. You cannot use a arrayref, coderef, hash key, or array index.) .SS "What methods can be delegated to?" .IX Subsection "What methods can be delegated to?" The following table compares Sub::HandlesVia with Data::Perl, Moose native traits, and MouseX::NativeTraits. .PP .Vb 10 \& Array =========================================== \& accessor : SubHV DataP Moose Mouse \& all : SubHV DataP \& all_true : SubHV \& any : SubHV Mouse \& apply : SubHV Mouse \& clear : SubHV DataP Moose Mouse \& count : SubHV DataP Moose Mouse \& delete : SubHV DataP Moose Mouse \& elements : SubHV DataP Moose Mouse \& fetch : Mouse (alias: get) \& first : SubHV DataP Moose Mouse \& first_index : SubHV DataP Moose \& flatten : SubHV DataP \& flatten_deep : SubHV DataP \& for_each : SubHV Mouse \& for_each_pair : SubHV Mouse \& get : SubHV DataP Moose Mouse \& grep : SubHV DataP Moose Mouse \& head : SubHV DataP \& insert : SubHV DataP Moose Mouse \& is_empty : SubHV DataP Moose Mouse \& join : SubHV DataP Moose Mouse \& map : SubHV DataP Moose Mouse \& max : SubHV \& maxstr : SubHV \& min : SubHV \& minstr : SubHV \& natatime : SubHV DataP Moose \& not_all_true : SubHV \& pairfirst : SubHV \& pairgrep : SubHV \& pairkeys : SubHV \& pairmap : SubHV \& pairs : SubHV \& pairvalues : SubHV \& pick_random : SubHV \& pop : SubHV DataP Moose Mouse \& print : SubHV DataP \& product : SubHV \& push : SubHV DataP Moose Mouse \& reduce : SubHV DataP Moose Mouse \& reductions : SubHV \& remove : Mouse (alias: delete) \& reset : SubHV \& reverse : SubHV DataP \& sample : SubHV \& set : SubHV DataP Moose Mouse \& shallow_clone : SubHV DataP Moose \& shift : SubHV DataP Moose Mouse \& shuffle : SubHV DataP Moose Mouse \& shuffle_in_place : SubHV \& sort : SubHV DataP Moose Mouse \& sort_by : Mouse (sort) \& sort_in_place : SubHV DataP Moose Mouse \& sort_in_place_by : Mouse (sort_in_place) \& splice : SubHV DataP Moose Mouse \& store : Mouse (alias: set) \& sum : SubHV \& tail : SubHV DataP \& uniq : SubHV DataP Moose Mouse \& uniq_in_place : SubHV \& uniqnum : SubHV \& uniqnum_in_place : SubHV \& uniqstr : SubHV \& uniqstr_in_place : SubHV \& unshift : SubHV DataP Moose Mouse \& \& Bool ============================================ \& not : SubHV DataP Moose Mouse \& reset : SubHV \& set : SubHV DataP Moose Mouse \& toggle : SubHV DataP Moose Mouse \& unset : SubHV DataP Moose Mouse \& \& Code ============================================ \& execute : SubHV DataP Moose Mouse \& execute_method : SubHV Moose Mouse \& \& Counter ========================================= \& dec : SubHV DataP Moose Mouse \& inc : SubHV DataP Moose Mouse \& reset : SubHV DataP Moose Mouse \& set : SubHV Moose Mouse \& \& Hash ============================================ \& accessor : SubHV DataP Moose Mouse \& all : SubHV DataP \& clear : SubHV DataP Moose Mouse \& count : SubHV DataP Moose Mouse \& defined : SubHV DataP Moose Mouse \& delete : SubHV DataP Moose Mouse \& elements : SubHV DataP Moose Mouse \& exists : SubHV DataP Moose Mouse \& fetch : Mouse (alias: get) \& for_each_key : SubHV Mouse \& for_each_pair : SubHV Mouse \& for_each_value : SubHV Mouse \& get : SubHV DataP Moose Mouse \& is_empty : SubHV DataP Moose Mouse \& keys : SubHV DataP Moose Mouse \& kv : SubHV DataP Moose Mouse \& reset : SubHV \& set : SubHV DataP Moose Mouse \& shallow_clone : SubHV DataP Moose \& sorted_keys : SubHV Mouse \& store : Mouse (alias: set) \& values : SubHV DataP Moose Mouse \& \& Number ========================================== \& abs : SubHV DataP Moose Mouse \& add : SubHV DataP Moose Mouse \& div : SubHV DataP Moose Mouse \& get : SubHV \& mod : SubHV DataP Moose Mouse \& mul : SubHV DataP Moose Mouse \& set : SubHV Moose \& sub : SubHV DataP Moose Mouse \& \& String ========================================== \& append : SubHV DataP Moose Mouse \& chomp : SubHV DataP Moose Mouse \& chop : SubHV DataP Moose Mouse \& clear : SubHV DataP Moose Mouse \& get : SubHV \& inc : SubHV DataP Moose Mouse \& length : SubHV DataP Moose Mouse \& match : SubHV DataP Moose Mouse \& prepend : SubHV DataP Moose Mouse \& replace : SubHV DataP Moose Mouse \& replace_globally : SubHV Mouse \& reset : SubHV \& set : SubHV \& substr : SubHV DataP Moose Mouse .Ve .SS "Method Chaining" .IX Subsection "Method Chaining" Say you have the following .PP .Vb 6 \& handles_via => \*(AqArray\*(Aq, \& handles => { \& \*(Aqadd_food\*(Aq => \*(Aqpush\*(Aq, \& \*(Aqfind_food\*(Aq => \*(Aqgrep\*(Aq, \& \*(Aqremove_food\*(Aq => \*(Aqpop\*(Aq, \& }, .Ve .PP Now \f(CW\*(C`$kitchen\->remove_food\*(C'\fR will remove the last food on the list and return it. But what if we don't care about what food was removed? We just want to remove the food and discard it. You can do this: .PP .Vb 6 \& handles_via => \*(AqArray\*(Aq, \& handles => { \& \*(Aqadd_food\*(Aq => \*(Aqpush\*(Aq, \& \*(Aqfind_food\*(Aq => \*(Aqgrep\*(Aq, \& \*(Aqremove_food\*(Aq => \*(Aqpop...\*(Aq, \& }, .Ve .PP Now the \f(CW\*(C`remove_food\*(C'\fR method will return the kitchen object instead of returning the food. This makes it suitable for chaining method calls: .PP .Vb 2 \& # remove the three most recent foods \& $kitchen\->remove_food\->remove_food\->remove_food; .Ve .SS "Hand Waving" .IX Subsection "Hand Waving" Sub::HandlesVia tries to be strict by default, but you can tell it to be less rigourous checking method arguments, etc using the \f(CW\*(C`~\*(C'\fR prefix: .PP .Vb 4 \& handles_via => \*(AqArray\*(Aq, \& handles => { \& \*(Aqfind_food\*(Aq => \*(Aq~grep\*(Aq, \& }, .Ve .SS CodeRefs .IX Subsection "CodeRefs" You can delegate to coderefs: .PP .Vb 4 \& handles_via => \*(AqArray\*(Aq, \& handles => { \& \*(Aqfind_healthiest\*(Aq => sub { my $foods = shift; ... }, \& } .Ve .SS "Named Methods" .IX Subsection "Named Methods" Let's say "FoodList" is a class where instances are blessed arrayrefs of strings. .PP .Vb 6 \& isa => InstanceOf[\*(AqFoodList\*(Aq], \& handles_via => \*(AqArray\*(Aq, \& handles => { \& \*(Aqfind_food\*(Aq => \*(Aqgrep\*(Aq, \& \*(Aqfind_healthiest_food\*(Aq => \*(Aqfind_healthiest\*(Aq, \& }, .Ve .PP Now \f(CW\*(C`$kitchen\->find_food($coderef)\*(C'\fR does this (which breaks encapsulation of course): .PP .Vb 1 \& my @result = grep $coderef\->(), @{ $kitchen\->food }; .Ve .PP And \f(CW\*(C`$kitchen\->find_healthiest_food\*(C'\fR does this: .PP .Vb 1 \& $kitchen\->food\->find_healthiest .Ve .PP Basically, because \f(CW\*(C`find_healthiest\*(C'\fR isn't one of the methods offered by Sub::HandlesVia::HandlerList::Array, it assumes you want to call it on the arrayref like a proper method. .SS "Currying Favour" .IX Subsection "Currying Favour" All this talk of food is making me hungry, but as much as I'd like to eat a curry right now, that's not the kind of currying we're talking about. .PP .Vb 4 \& handles_via => \*(AqArray\*(Aq, \& handles => { \& \*(Aqget_food\*(Aq => \*(Aqget\*(Aq, \& }, .Ve .PP \&\f(CW\*(C`$kitchen\->get_food(0)\*(C'\fR will return the first item on the list. \&\f(CW\*(C`$kitchen\->get_food(1)\*(C'\fR will return the second item on the list. And so on. .PP .Vb 5 \& handles_via => \*(AqArray\*(Aq, \& handles => { \& \*(Aqfirst_food\*(Aq => [ \*(Aqget\*(Aq => 0 ], \& \*(Aqsecond_food\*(Aq => [ \*(Aqget\*(Aq => 1 ], \& }, .Ve .PP I think you already know what this does. Right? .PP And yes, currying works with coderefs. .PP .Vb 4 \& handles_via => \*(AqArray\*(Aq, \& handles => { \& \*(Aqblargy\*(Aq => [ sub { ... }, @curried ], \& }, .Ve .SS "Pick and Mix" .IX Subsection "Pick and Mix" .Vb 6 \& isa => ArrayRef|HashRef, \& handles_via => [ \*(AqArray\*(Aq, \*(AqHash\*(Aq ], \& handles => { \& the_keys => \*(Aqkeys\*(Aq, \& ship_shape => \*(Aqsort_in_place\*(Aq, \& } .Ve .PP Here you have an attribute which might be an arrayref or a hashref. When it's an arrayref, \f(CW\*(C`$object\->ship_shape\*(C'\fR will work nicely, but \f(CW\*(C`$object\->the_keys\*(C'\fR will fail badly. .PP Still, this sort of thing can kind of make sense if you have an object that overloads both \f(CW\*(C`@{}\*(C'\fR and \f(CW\*(C`%{}\*(C'\fR. .PP Sometime a method will be ambiguous. For example, there's a \f(CW\*(C`get\*(C'\fR method for both hashes and arrays. In this case, the array one will win because you listed it first in \f(CW\*(C`handles_via\*(C'\fR. .PP But you can be specific: .PP .Vb 6 \& isa => ArrayRef|HashRef, \& handles_via => [ \*(AqArray\*(Aq, \*(AqHash\*(Aq ], \& handles => { \& get_foo => \*(AqArray\->get\*(Aq, \& get_bar => \*(AqHash\->get\*(Aq, \& } .Ve .SH BUGS .IX Header "BUGS" Please report any bugs to . .PP (There are known bugs for Moose native types that do coercion.) .SH "SEE ALSO" .IX Header "SEE ALSO" Moose, MouseX::NativeTraits, Data::Perl, MooX::HandlesVia. .SH AUTHOR .IX Header "AUTHOR" Toby Inkster . .SH "COPYRIGHT AND LICENCE" .IX Header "COPYRIGHT AND LICENCE" This software is copyright (c) 2020 by Toby Inkster. .PP This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. .SH "DISCLAIMER OF WARRANTIES" .IX Header "DISCLAIMER OF WARRANTIES" THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.