Provided by: libsub-handlesvia-perl_0.035-1_all bug

NAME

       Sub::HandlesVia - alternative handles_via implementation

SYNOPSIS

        package Kitchen {
          use Moo;
          use Sub::HandlesVia;
          use Types::Standard qw( ArrayRef Str );

          has food => (
            is          => 'ro',
            isa         => ArrayRef[Str],
            handles_via => 'Array',
            default     => sub { [] },
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
            },
          );
        }

        my $kitchen = Kitchen->new;
        $kitchen->add_food('Bacon');
        $kitchen->add_food('Eggs');
        $kitchen->add_food('Sausages');
        $kitchen->add_food('Beans');

        my @foods = $kitchen->find_food(sub { /^B/i });

DESCRIPTION

       If you've used Moose's native attribute traits, or MooX::HandlesVia before, you should
       have a fairly good idea what this does.

       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.

       Also, unlike MooX::HandlesVia, it honours type constraints, plus it doesn't have the
       limitation that it can't mutate non-reference values.

       Note: as Sub::HandlesVia needs to detect whether you're using Moo, Moose, or Mouse, and
       often needs to detect whether your package is a class or a role, it needs to be loaded
       after Moo/Moose/Mouse.

   Using with Moo
       You should be able to use it as a drop-in replacement for MooX::HandlesVia.

        package Kitchen {
          use Moo;
          use Sub::HandlesVia;
          use Types::Standard qw( ArrayRef Str );

          has food => (
            is          => 'ro',
            isa         => ArrayRef[Str],
            handles_via => 'Array',
            default     => sub { [] },
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
            },
          );
        }

   Using with Mouse
       It works the same as Moo basically.

        package Kitchen {
          use Mouse;
          use Sub::HandlesVia;
          use Types::Standard qw( ArrayRef Str );

          has food => (
            is          => 'ro',
            isa         => ArrayRef[Str],
            handles_via => 'Array',
            default     => sub { [] },
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
            },
          );
        }

       You are not forced to use Types::Standard. Mouse native types should work fine.

        package Kitchen {
          use Mouse;
          use Sub::HandlesVia;

          has food => (
            is          => 'ro',
            isa         => 'ArrayRef[Str]',
            handles_via => 'Array',
            default     => sub { [] },
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
            },
          );
        }

       Sub::HandlesVia will also recognize MooseX::NativeTraits-style traits. It will jump in and
       handle them before MooseX::NativeTraits notices!

        package Kitchen {
          use Mouse;
          use Sub::HandlesVia;

          has food => (
            is          => 'ro',
            isa         => 'ArrayRef[Str]',
            traits      => ['Array'],
            default     => sub { [] },
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
            },
          );
        }

       (If you have a mouse in your kitchen though, that might not be very hygienic.)

   Using with Moose
       It works the same as Mouse basically.

        package Kitchen {
          use Moose;
          use Sub::HandlesVia;
          use Types::Standard qw( ArrayRef Str );

          has food => (
            is          => 'ro',
            isa         => ArrayRef[Str],
            handles_via => 'Array',
            default     => sub { [] },
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
            },
          );
        }

       You are not forced to use Types::Standard. Moose native types should work fine.

        package Kitchen {
          use Moose;
          use Sub::HandlesVia;

          has food => (
            is          => 'ro',
            isa         => 'ArrayRef[Str]',
            handles_via => 'Array',
            default     => sub { [] },
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
            },
          );
        }

       Sub::HandlesVia will also recognize native-traits-style traits. It will jump in and handle
       them before Moose notices!

        package Kitchen {
          use Moose;
          use Sub::HandlesVia;

          has food => (
            is          => 'ro',
            isa         => 'ArrayRef[Str]',
            traits      => ['Array'],
            default     => sub { [] },
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
            },
          );
        }

       (If you have a moose in your kitchen, that might be even worse than the mouse.)

   Using with Mite
       You should be able to use Sub::HandlesVia with Mite 0.001011 or above.  Your project will
       still have a dependency on Sub::HandlesVia.

        package MyApp::Kitchen {
          use MyApp::Mite;
          use Sub::HandlesVia;

          has food => (
            is          => 'ro',
            isa         => 'ArrayRef[Str]',
            handles_via => 'Array',
            default     => sub { [] },
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
            },
          );
        }

        You should be able to use Sub::HandlesVia with L<Mite> 0.001011 or above.
        Your project will still have a dependency on Sub::HandlesVia.

         package MyApp::Kitchen {
           use MyApp::Mite;
           use Sub::HandlesVia;

           has food => (
             is          => 'ro',
             isa         => 'ArrayRef[Str]',
             handles_via => 'Array',
             default     => sub { [] },
             handles     => {
               'add_food'    => 'push',
               'find_food'   => 'grep',
             },
           );
         }

       If you have Mite 0.009000 or above, you can probably use its "handles_via" support, and
       avoid your project having a Sub::HandlesVia dependency!

        package MyApp::Kitchen {
          use MyApp::Mite;

          has food => (
            is          => 'ro',
            isa         => 'ArrayRef[Str]',
            handles_via => 'Array',
            default     => sub { [] },
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
            },
          );
        }

   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 "has"
       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.

        package Kitchen {
          use Class::Tiny {
            food => sub { [] },
          };

          use Sub::HandlesVia qw( delegations );

          delegations(
            attribute   => 'food'
            handles_via => 'Array',
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
            },
          );
        }

       Setting "attribute" to "food" means that when Sub::HandlesVia needs to get the food list,
       it will call "$kitchen->food" and when it needs to set the food list, it will call
       "$kitchen->food($value)".  If you have separate getter and setter methods, just do:

            attribute   => [ 'get_food', 'set_food' ],

       Or if you don't have any accessors and want Sub::HandlesVia to directly access the
       underlying hashref:

            attribute   => '{food}',

       Or maybe you have a setter, but want to use hashref access for the getter:

            attribute   => [ '{food}', 'set_food' ],

       Or maybe you still want direct access for the getter, but your object is a blessed
       arrayref instead of a blessed hashref:

            attribute   => [ '[7]', 'set_food' ],

       Or maybe your needs are crazy unique:

            attribute   => [ \&getter, \&setter ],

       The coderefs are passed the instance as their first argument, and the setter is also
       passed a value to set.

       Really, I don't think there's any object system that this won't work for!

       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.

       (The "delegations" function can be imported into Moo/Mouse/Moose classes too, in which
       case the "attribute" needs to be the same attribute name you passed to "has". You cannot
       use a arrayref, coderef, hash key, or array index.)

   What methods can be delegated to?
       The following table compares Sub::HandlesVia with Data::Perl, Moose native traits, and
       MouseX::NativeTraits.

         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
                        cmp : SubHV
                        div : SubHV  DataP  Moose  Mouse
                         eq : SubHV
                         ge : SubHV
                        get : SubHV
                         gt : SubHV
                         le : SubHV
                         lt : SubHV
                        mod : SubHV  DataP  Moose  Mouse
                        mul : SubHV  DataP  Moose  Mouse
                         ne : SubHV
                        set : SubHV         Moose
                        sub : SubHV  DataP  Moose  Mouse

         Scalar ==========================================
                make_getter : SubHV
                make_setter : SubHV
           scalar_reference : SubHV

         String ==========================================
                     append : SubHV  DataP  Moose  Mouse
                      chomp : SubHV  DataP  Moose  Mouse
                       chop : SubHV  DataP  Moose  Mouse
                      clear : SubHV  DataP  Moose  Mouse
                        cmp : SubHV
                       cmpi : SubHV
                   contains : SubHV
                 contains_i : SubHV
                  ends_with : SubHV
                ends_with_i : SubHV
                         eq : SubHV
                        eqi : SubHV
                         fc : SubHV
                         ge : SubHV
                        gei : SubHV
                        get : SubHV
                         gt : SubHV
                        gti : SubHV
                        inc : SubHV  DataP  Moose  Mouse
                         lc : SubHV
                         le : SubHV
                        lei : SubHV
                     length : SubHV  DataP  Moose  Mouse
                         lt : SubHV
                        lti : SubHV
                      match : SubHV  DataP  Moose  Mouse
                    match_i : SubHV
                         ne : SubHV
                        nei : SubHV
                    prepend : SubHV  DataP  Moose  Mouse
                    replace : SubHV  DataP  Moose  Mouse
           replace_globally : SubHV                Mouse
                      reset : SubHV
                        set : SubHV
                starts_with : SubHV
              starts_with_i : SubHV
                     substr : SubHV  DataP  Moose  Mouse
                         uc : SubHV

       For further details see: Array, Bool, Code, Counter, Hash, Number, Scalar, and String.

   Method Chaining
       Say you have the following

            handles_via => 'Array',
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
              'remove_food' => 'pop',
            },

       Now "$kitchen->remove_food" 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:

            handles_via => 'Array',
            handles     => {
              'add_food'    => 'push',
              'find_food'   => 'grep',
              'remove_food' => 'pop...',
            },

       Now the "remove_food" method will return the kitchen object instead of returning the food.
       This makes it suitable for chaining method calls:

         # remove the three most recent foods
         $kitchen->remove_food->remove_food->remove_food;

   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 "~" prefix:

            handles_via => 'Array',
            handles     => {
              'find_food'   => '~grep',
            },

   CodeRefs
       You can delegate to coderefs:

            handles_via => 'Array',
            handles    => {
              'find_healthiest' => sub { my $foods = shift; ... },
            }

   Named Methods
       Let's say "FoodList" is a class where instances are blessed arrayrefs of strings.

            isa         => InstanceOf['FoodList'],
            handles_via => 'Array',
            handles     => {
              'find_food'             => 'grep',
              'find_healthiest_food'  => 'find_healthiest',
            },

       Now "$kitchen->find_food($coderef)" does this (which breaks encapsulation of course):

         my @result = grep $coderef->(), @{ $kitchen->food };

       And "$kitchen->find_healthiest_food" does this:

         $kitchen->food->find_healthiest

       Basically, because "find_healthiest" 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.

   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.

            handles_via => 'Array',
            handles     => {
              'get_food'   => 'get',
            },

       "$kitchen->get_food(0)" will return the first item on the list.  "$kitchen->get_food(1)"
       will return the second item on the list.  And so on.

            handles_via => 'Array',
            handles     => {
              'first_food'   => [ 'get' => 0 ],
              'second_food'  => [ 'get' => 1 ],
            },

       I think you already know what this does. Right?

       And yes, currying works with coderefs.

            handles_via => 'Array',
            handles     => {
              'blargy'       => [ sub { ... }, @curried ],
            },

   Pick and Mix
           isa         => ArrayRef|HashRef,
           handles_via => [ 'Array', 'Hash' ],
           handles     => {
             the_keys     => 'keys',
             ship_shape   => 'sort_in_place',
           }

       Here you have an attribute which might be an arrayref or a hashref.  When it's an
       arrayref, "$object->ship_shape" will work nicely, but "$object->the_keys" will fail badly.

       Still, this sort of thing can kind of make sense if you have an object that overloads both
       "@{}" and "%{}".

       Sometime a method will be ambiguous. For example, there's a "get" method for both hashes
       and arrays. In this case, the array one will win because you listed it first in
       "handles_via".

       But you can be specific:

           isa         => ArrayRef|HashRef,
           handles_via => [ 'Array', 'Hash' ],
           handles     => {
             get_foo => 'Array->get',
             get_bar => 'Hash->get',
           }

BUGS

       Please report any bugs to <https://github.com/tobyink/p5-sub-handlesvia/issues>.

       (There are known bugs for Moose native types that do coercion.)

SEE ALSO

       Documentation for delegatable methods: Array, Bool, Code, Counter, Hash, Number, Scalar,
       and String.

       Other implementations of the same concept: Moose::Meta::Attribute::Native,
       MouseX::NativeTraits, and MooX::HandlesVia with Data::Perl.

AUTHOR

       Toby Inkster <tobyink@cpan.org>.

COPYRIGHT AND LICENCE

       This software is copyright (c) 2020, 2022 by Toby Inkster.

       This is free software; you can redistribute it and/or modify it under the same terms as
       the Perl 5 programming language system itself.

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.