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_list : SubHV             ..........
             execute_scalar : SubHV             ..........
               execute_void : SubHV             ..........
             execute_method : SubHV         Moose  Mouse  
        execute_method_list : SubHV             ..........
      execute_method_scalar : SubHV             ..........
        execute_method_void : SubHV             ..........
  
      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  
               delete_where : SubHV                       
                   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.

