The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

PTools::Extender - Abstract class facilitates "extendable" methods in modules

VERSION

This document describes version 0.03, released Nov 12, 2002.

DEPENDENCIES

This class depends upon the Loader class to dynamically use the "extended" Perl module at run time.

SYNOPSIS

First, include PTools::Extender as a "base classe" for YOUR module.

  package MyModule;

  use vars qw( $VERSION @ISA );
  $VERSION = '0.01';
  @ISA     = qw( PTools::Extender );

  use PTools::Extender;

Then, to create an "extendable" method, code the subroutine using the following methods contained in this class. See Example section, below.

  $ref = $self->extended( 'myMethod' );

  if ( $self->extended( 'myMethod' ) { . . . }

  $stat or ($stat,$err) = $self->expand( 'myMethod', @params);

  $self->unextend( 'myMethod' );

DESCRIPTION

This "utility" module provides a simple mechanism for developers of Perl modules to provide "extendable" methods in their classes. Using this module as a base class for a module, and using the methods as shown below, allows the USER of the derived class to specify which module that THEY choose to use, and even allows the user to create their own modules and "hook" them in.

Clear as mud? Some examples will help bring this into focus.

Say, for example, you are building a module that reads data files into memory (let's call the module "NewFileModule.pm"). As part of the design, you decide that it would be nice to provide a few methods that help manage the data while it resides in memory. For this example, methods will include "sort", "lock" and "unlock."

Further, since there are run-time issues that may make your default solution less than optimal, you decide that anyone who uses your module can choose which module will actually accomplish these tasks.

For the examples below these will be termed "extendable" methods, and will include the following variations.

  Sort Modules:   Sort::Bubble,   Sort::Shell, Sort::Quick
  Lock Modules:   Lock::Advisory, Lock::Hard,  Lock::Time

Constructor

None needed.

Methods

extend ( Methods, Class [, ParamRef ] [, UseRef ] )

The mechanism defined within a subclass that implements an extended method.

Methods

The name or names of the methods that will be invoked as via the method extension mechanism.

Class

The name of the module or Class that contains the extended method definitions.

ParamRef

Optional parameters passed to the constructor method of the Class.

UseRef

Optional parameters passed to the use statement of the Class.

Example:

 $userObj->extend( "method", "className",
                 [ "paramRef1" ], [ "qw( paramRef2 )" ] );

This method will be invoked in some user module or script that uses the class containing the "extendable" methods.

The $userObj object will be an instantiation of the module that is created in the examples under discussion here.

unextend ( Method )

If the specified Method is currently extended, this will unextend that method.

 $userObj->unextend( $method );

This method will be invoked in some user module or script that uses the class containing the "extendable" methods.

expand ( Method [, Params ] )

The mechanism used inside an "extendable" method definition that invokes a previously extended method. Method is the name of the extended method and Params, if any, are simply forwarded on to the actual method that performs the operation.

Module designers must note that the current $self object reference is prepended to the Params list. This is how the "extended" object gains access to the current object.

 ($stat,$err) = $self->expand( $method, @params);
extended ( Method )

Test to see if the specified Method is currently extended.

 if ($self->extended( $method ) { . . . }

This method should be available to both the designer of the module under discussion here and the consumer of the module.

EXAMPLE

First, include "PTools::Extender" as a "base classe" for YOUR module.

    package NewFileModule;

    use vars qw( $VERSION @ISA );
    $VERSION = '0.01';
    @ISA     = qw( PTools::Extender );

    use PTools::Extender;

Then, to create an "extendable" method, code the subroutine as follows. Note that, using this syntax, the actual Lock module is not pulled into the script until the first time the "lock" method is called. This may not be what you want. To load the Lock module when the script starts, simply use the "extend" method, as shown below, and specify the default Lock module.

    sub lock {
        my($self,@params) = @_;

        my($ref,$stat,$err) = (undef,0,"");
        #
        # If not already extended, use default extension class
        #
        $ref = $self->extended("lock");
        $ref or ($ref,$stat,$err) =
                  $self->extend( ["lock","unlock"], "Lock::Advisory" );
        #
        # Invoke the extended method
        #
        $stat or ($stat,$err) = $self->expand('lock',@params);
    
        $self->setErr( $stat,$err );
        return($stat,$err) if wantarray;
        return $stat;
    }

    sub unlock {
       my($self,@params) = @_;
       #
       # Invoke the extended method. This implies
       # that 'lock' must have been called (or
       # 'unlock' extended) first.
       #
       my($stat,$err) = $self->expand('unlock',@params);

       $self->setErr( $stat,$err );
       return($stat,$err) if wantarray;
       return $stat;
    }

This way, when a Perl programmer uses the NewFileModule, s/he decides which lock module will actually be used. By default it's your choice:

    use NewFileModule;

    $fileObj = new NewFileModule( $fileName );   # open file (e.g.)

    $stat = $fileObj->lock;                      # use default lock

    $stat = $fileObj->unlock;                    # use default unlock

Or they can choose another module at any time from their script. The syntax for calling the actual "extended" method(s) need not change (unless, of course, the "extended" module expects different params!)

    use NewFileModule;

    $fileObj = new NewFileModule( $fileName );   # open file (e.g.)

    $fileObj->extend( ["lock","unlock], "Lock::Hard" );

    $stat = $fileObj->lock;                      # "Hard" lock instead

    $stat = $fileObj->unlock;                    # "Hard" unlock instead

It is also possible to pass both "import" and "instantiation" parameters to the "extended" object as follows. Note that the brackets ("[]") below are literal and not to denote optional params. They are used to create array references that are passed to the object under creation.

    $fileObj->extend( "method", "className",
                    [ "paramRef1" ], [ "qw( paramRef2 )" ] );

   Where the "paramRef1" (etc.) is passed to the "new" method, 
     and the "paramRef2" (etc.) is passed to the "use" function

This assumes that the lock modules are well behaved and work in this environment. Note well that the "expand" method prepends "$self" to the argument list. Without this, the utility module "lock", "sort" or whatever, would not have access to the data to lock/sort. However, the extended module must be designed for this.

SEE ALSO

See SDF::File and SDF::Lock::Advisory for a complete example of implementing "extendable" methods via "utility" classes.

Also see SDF::SDF, SDF::Sort::Bubble, SDF::Sort::Bubble and SDF::Sort::Bubble for additional examples.

INHERITANCE

A module designer's subclass will inherit from this base class.

AUTHOR

Chris Cobb [no dot spam at ccobb dot net]

COPYRIGHT

Copyright (c) 2004-2007 by Chris Cobb. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.