NAME
    Sub::SymMethod - symbiotic methods; methods that act a little like BUILD
    and DEMOLISH

SYNOPSIS
      use strict;
      use warnings;
      use feature 'say';
  
      {
        package Local::Base;
        use Class::Tiny;
        use Sub::SymMethod;
    
        symmethod foo => sub { say __PACKAGE__ };
      }
  
      {
        package Local::Role;
        use Role::Tiny;
        use Sub::SymMethod;
    
        symmethod foo => sub { say __PACKAGE__ };
      }
  
      {
        package Local::Derived;
        use parent -norequire, 'Local::Base';
        use Role::Tiny::With; with 'Local::Role';
        use Sub::SymMethod;
    
        symmethod foo => sub { say __PACKAGE__ };
      }
  
      'Local::Derived'->foo();
      # Local::Base
      # Local::Role
      # Local::Derived

DESCRIPTION
    Sub::SymMethod creates hierarchies of methods so that when you call one,
    all the methods in the inheritance chain (including ones defined in roles)
    are invoked.

    They are invoked from the most basal class to the most derived class.
    Methods defined in roles are invoked before methods defined in the class
    they were composed into.

    This is similar to how the `BUILD` and `DEMOLISH` methods are invoked in
    Moo, Moose, and Mouse. (You should *not* use this module to define `BUILD`
    and `DEMOLISH` methods though, as Moo/Moose/Mouse already includes all the
    plumbing to ensure that they are called correctly. This module is instead
    intended to allow you to define your own methods which behave similarly.)

    You can think of "symmethod" as being short for "symbiotic method",
    "syncretic method", or "synarchy of methods".

    If you are familiar with multi methods, you can think of a symmethod as a
    multi method where instead of picking one "winning" candidate to dispatch
    to, the dispatcher dispatches to as many candidates as it can find!

  Use Cases
    Symmethods are useful for "hooks". For example, the following pseudocode:

      class Message {
        method send () {
          $self->on_send();
          $self->do_smtp_stuff();
        }
    
        symmethod on_send () {
          # do nothing
        }
      }
  
      role LoggedMessage {
        symmethod on_send () {
          print STDERR "Sending message\n";
        }
      }
  
      class ImportantMessage {
        extends Message;
        with LoggedMessage;
    
        symmethod on_send () {
          $self->add_to_archive( "Important" );
        }
      }

    When the `send` method gets called on an ImportantMessage object, the
    inherited `send` method from Message will get invoked. This will call
    `on_send`, which will call every `on_send` definition in the inheritance
    hierarchy for ImportantMessage, ensuring the sending of the important
    message gets logged to STDERR and the message gets archived.

  Functions
    Sub::SymMethod exports one function, but which may be called in two
    different ways.

    `symmethod $name => $coderef`
        Creates a symmethod.

    `symmethod $name => %spec`
        Creates a symmethod.

        The specification hash must contain a `code` key, which must be a
        coderef. It may also include an `order` key, which must be numeric.
        Any other keys are passed to `signature` from Type::Params to build a
        signature for the symmethod.

  Invoking Symmethods
    Given the following pseudocode:

      class Base {
        symmethod foo () {
          say wantarray ? "List context" : "Scalar context";
          return "BASE";
        }
      }
  
      class Derived {
        extends Base;
    
        symmethod foo () {
          say wantarray ? "List context" : "Scalar context";
          return "DERIVED";
        }
      }
  
      my @r = Derived->foo();
      my $r = Derived->foo();

    "Scalar context" will be said four times. Symmethods are always invoked in
    scalar context even when they have been called in list context!

    The @r array will be `( "BASE", "DERIVED" )`. When a symmethod is called
    in list context, a list of the returned values will be returned.

    The variable $r will be 2. It is the count of the returned values.

    If a symmethod throws an exception this will not be caught, so any further
    symmethods waiting to be invoked will not get invoked.

   Invocation Order
    It is possible to force a symmethod to run early by setting `order` to a
    negative number.

      symmethod foo => (
        order => -100,
        code  => sub { my $self = shift; ... },
      );

    It is possible to force a symmethod to run late by setting order to a
    positive number.

      symmethod foo => (
        order => 100,
        code  => sub { my $self = shift; ... },
      );

    The default `order` is 0 for all symmethods, and in most cases this will
    be fine.

    Where symmethods have the same order (the usual case!) symmethods are
    invoked from most basal class to most derived class -- i.e. from parent to
    child. Where a class consumes symmethods from roles, a symmethods defined
    in a role will be invoked before a symmethod defined in the class, but
    after any inherited from base/parent classes.

  Symmethods and Signatures
    When defining symmethods, you can define a signature using the same
    options supported by `signature` from Type::Params.

      use Types::Standard 'Num';
      use Sub::SymMethod;
  
      symmethod foo => (
        positional => [ Num ],
        code       => sub {
          my ( $self, $num ) = @_;
          print $num, "\n";
        },
      );
  
      symmethod foo => (
        named => [ mynum => Num ],
        code  => sub {
          my ( $self, $arg ) = @_;
          print $arg->mynum, "\n";
        },
      );

    When the symmethod is called, any symmethods where the arguments do not
    match the signature are simply skipped.

    The invocant ($self or $class or whatever) is *not* included in the
    signature.

    The coderef given in `code` receives the list of arguments *after* they've
    been passed through the signature, which may coerce them, etc.

    Using a signature requires Type::Params to be installed.

  API
    Sub::SymMethod has an object oriented API for metaprogramming.

    When describing it, we'll borrow the terms *dispatcher* and *candidate*
    from Sub::MultiMethod. The candidates are the coderefs you gave to
    Sub::SymMethod -- so there might be a candidate defined in your parent
    class and a candidate defined in your child class. The dispatcher is the
    method that Sub::SymMethod creates for you (probably just in the base
    class, but theoretically perhaps also in the child class) which is
    responsible for finding the candidates and calling them.

    The Sub::SymMethod API offers the following methods:

    `install_symmethod( $target, $name, %spec )`
        Installs a candidate method for a class or role.

        $target is the class or role the candidate is being defined for. $name
        is the name of the method. %spec must include a `code` key and
        optionally an `order` key. Any keys not directly supported by
        Sub::SymMethod will be passed through to Type::Params to provide a
        signature for the method.

        If $target is a class, this will also install a dispatcher into the
        class. Passing `no_dispatcher => 1` in the spec will avoid this.

        If $target is a role, this will also install hooks to the role to
        notify Sub::SymMethod whenever the role gets consumed by a class.
        Passing `no_hooks => 1` in the spec will avoid this.

        This will also perform any needed cache invalidation.

    `build_dispatcher( $target, $name )`
        Builds a coderef that could potentially be installed into
        `*{"$target\::$name"}` to be used as a dispatcher.

    `install_dispatcher( $target, $name )`
        Builds a coderef that could potentially be installed into
        `*{"$target\::$name"}` to be used as a dispatcher, and actually
        installs it.

        This complains if it notices it's overwriting an existing method which
        isn't a dispatcher. (It also remembers the coderef being installed is
        a dispatcher, which can later be checked using `is_dispatcher`.)

    `is_dispatcher( $coderef )`
        Checks to see if $coderef is a dispatcher.

        Can also be called as `is_dispatcher( $coderef, 0 )` or
        `is_dispatcher( $coderef, 1 )` to teach it about a coderef.

    `dispatch( $invocant, $name, @args )`
        Equivalent to calling `$invocant->$name(@args)` except doesn't use the
        dispatcher installed into the invocant's class, instead building a new
        dispatcher and using that.

    `install_hooks( $rolename )`
        Given a role, sets up the required hooks which ensure that when the
        role is composed with a class, dispatchers will be installed into the
        class to handle all of the role's symmethods, and Sub::SymMethod will
        know that the class consumed the role.

        Also performs cache invalidation.

    `get_roles_for_class ( $classname )`
        Returns an arrayref containing a list of roles the class is known to
        consume. We only care about roles that define symmethods.

        If you need to manually specify that a class consumes a role, you can
        push the role name onto the arrayref. This would usually only be
        necessary if you were using an unsupported role implementation.
        (Supported role implementations include Role::Tiny, Role::Basic,
        Moo::Role, Moose::Role, and Mouse::Role.)

    `clear_cache( $name )`
        Clears all caches associated with any symmethods with a given name.
        The target class is irrelevent because symmethods can be created in
        roles which may be consumed by multiple unrelated classes.

    `get_symmethod_names( $target )`
        For a given class or role, returns a list of the names of symmethods
        defined directly in that class or role, not considering inheritance
        and composition.

    `get_symmethods( $target, $name )`
        For a given class or role and a method name, returns an arrayref of
        spec hashrefs for that symmethod, not considering inheritance and
        composition.

        This arrayref can be pushed onto to define more candidates, though
        this bypasses setting up hooks, installing dispatches, and performing
        cache invalidation, so `install_symmethod` is generally preferred
        unless you're doing something unusual.

    `get_all_symmethods( $target, $name )`
        Like `get_symmethods`, but *does* consider inheritance and
        composition. Returns the arrayref of the spec hashrefs in the order
        they will be called when dispatching.

    `compile_signature( \%spec )`
        Does the job of finding keys within the spec to compile into a
        signature.

    `_generate_symmethod( $name, \%opts, \%globalopts )`
        This method is used by `import` to generate a coderef that will be
        installed into the called as `symmethod`.

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

SEE ALSO
    Sub::MultiMethod, Type::Params, NEXT.

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.

