The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

mro::EVERY - EVERY & EVERY::LAST pseudo-packages using mro.

SYNOPSIS

    # EVERY & EVERY::LAST redispatch the named method into
    # all classes in the object/class hierarchy which 
    # implement the method or have a suitable can() and 
    # AUTOLOAD to handdle the method.

    package Giant;
    use mro;    
    use parent qw( fee fie foe fum );
    use mro::EVERY;

    # construct an object then dispatch the 'initialize'
    # method into each class from least-to-most derived 
    # that declares its own 'initialize' method.

    sub new
    {
        my $object  = &construct;

        $object->EVERY::LAST::initialize( @_ );
        $object
    }

    # tear down an object from the top down, calling
    # 'cleanup' for most-to-least derived classes.

    package Thingy;
    use mro qw( c3 );
    use parent qw( this that other );
    use mro::EVERY;

    DESTROY
    {
        my $object  = shift;

        $object->EVERY::cleanup;
    }

    # the "autoload" switch turns on scanning for 
    # $proto->can( $name ) and checking for AUTOLOAD
    # subs (vs. simply checking for a defined coderef
    # in the package). 
    # 
    # using this approach requires properly overloading
    # can() in the package. 
    # 
    # note that AUTOLOAD's can have all sorts of side
    # effects, this should be used with care and where
    # the handling classes really do have overloaded
    # "can" methods and really do handle the named
    # operation properly.
    #
    # lacking an overloaded can() and appropriate 
    # AUTOLOAD, this is a waste. nu, don't say I
    # didn't warn you.

    package Derived;
    use mro;
    use parent qw( Base1 );

    use mro::EVERY  qw( autoload );

    sub randomize
    {
        my $obj = shift;

        $obj->EVERY::methodical;
    }

    sub methodical
    {
        # first call from EVERY::methodical ends up 
        # here.

        ...
    }

    package Base1;

    # can has to return something for every method
    # the class can handle -- including UNIVERSAL
    # and any other base classes. this is a trivial
    # example that works because there are no other
    # bases classes than UNIVERSAL here.

    my %can = 
    (
        methodical  => \&AUTOLOAD
      , AUTOLOAD    => \&AUTOLOAD
      , bottle      => \&bottle
    );

    sub can
    {
        %can{ $_[1] }
        or
        UNIVERSAL->can( $_[1] )
    }

    sub bottle
    {
        # autoloaded or not, this has to be 
        # handled by can(), above.

        ...
    }

    our $AUTOLOAD   = '';
    AUTOLOD
    {
        # call ends up here becuase mro::EVERY can 
        # find that $pkg->can( 'methodical' ) and
        # also that there is an AUTOLOAD in the package
        # (not just inherited). 

        ...
    }

DESCRIPTION

The main use of both pseudo-classes is dispatching an arbitrary method up or down the inheritence stack without each of them having to do their own re-dispatch to another. One common use of this is in initializers, which can use EVERY::LAST to walk up the tree from least-to-most derived classes calling the method where it is declared in the class (vs. simply inherited).

Initializers will usually go "up" the inheritence stack using EVERY::LAST so that more derived classes can depend on their base class being set up. Destructors will use EVERY and tear down the object from most-derived to least derived.

With autoloads it gets a bit tricky becuase $package->can( 'X' ) needs to return true for X, there has to be an autoload, and the package has to be examined in isolation (i.e., with an empty @ISA so that we don't get an inherited value from can()). If the package is using an AUTOLOAD to do something it'll need to overload can() to hand back true for any method names dealt with via AUTOLOAD.

Without autoload this is quite simple: Walk down mro::get_linear_isa looking for packages that define their own code for the method name. Get a list back and call it in order for EVERY or reverse order for EVERY::LAST. This is pretty much the same guts as NEXT, just using mro for the package names rather than iteratingon @ISA.

SEE ALSO

mro

This describes the use of "dfs" & "c3" methologies for resolving class inheritence order.

NEXT

Further description EVERY & EVERY::LAST.

Note: This module is not compaitible with NEXT as they both attempt to define the same pseudo-classes EVERY & EVERY::LAST. If you are not working with mro, use NEXT; if you are use this.