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

NAME

OOP::Perlish::Class - A Base class implementation providing the fundimental infrastructure for perl OOP

DESCRIPTION

A Base class for creating Objects that conform to all common OOP practices, while still remaining very much perl.

Currently supported:

Multiple-Inheritance
Mix-in
Meta-programming (class introspection; quite useful with mix-ins)
Generational Inheritance (complex hiarchies of inheritance)
method overriding/overloading
operator overriding/overloading
Accessor validation
accessor cascading via validator subroutine
singletons
multitons (aka: multi-singletons, keyed singletons, named singletons, singleton-maps)
polymorphism (aka duck-typing for ruby folks)
abstract-classes (aka interfaces, protocols, traits, flavors, roles, class-prototypes, etc)

SYNOPSIS

Simple Example:

 {
     package Foo;
     use base qw(OOP::Perlish::Class);

     BEGIN {
        __PACKAGE__->_accessors(
            bar  => { type => 'SCALAR', },                           # accessor which accepts a scalar, and returns a scalar
            baz  => { type => 'HASH', },                             # accessor which returns a hash (may be set via hash or hashref)
            qux  => { type => 'HASHREF', },                          # accessor which returns a hashref (immutable), (may be set via hash or hashref)
            baz  => { type => 'ARRAY', },                            # accessor which returns a array (may be set via array or arrayref)
            bam  => { type => 'ARRAYREF', },                         # accessor which returns a arrayref (immutable), (may be set via array or arrayref)
            quux => { type => 'CODE', },                             # accessor which accepts and returns a code-reference
            fred => { type => 'OBJECT', },                           # accessor which accepts and returns a blessed reference
            thud => { type => 'REGEXP', },                           # accessor which accepts and returns a reference to a pre-compiled regular expression
            psdf => { type => 'GLOBREF', },                             # accessor which accepts and returns a reference to a glob (ref only), (synonym: GLOBREF)
        );
    };

    sub _preinit(@)
    {
        my ($self) = @_;

        # ... do some initialization stuff that needs to happen before you assign values to any accessors in the call to the constructor.

        return 1;
    }

    sub _init(@)
    {
        my ($self) = @_;

        # ... do some initialization stuff if necessary; DO THE ABSOLUTE MINIMUM HERE

        return 1;
    }
 }

Accessor Validation via regular expression:

Additionally, accessors may be more complicated than noted above. For instance, you might care what data is passed to an accessor. Here are some examples of validating input:

 BEGIN {
     __PACKAGE__->_accessors(
            bar  => { type => 'SCALAR',   validator => qr/.*bar.*/ },      # a scalar that must match the regular expression /.*bar.*/
            baz  => { type => 'HASH',     validator => qr/.*bar.*/ },      # a hash who's values must match the regular expression /.*bar.*/
            qux  => { type => 'HASHREF',  validator => qr/.*bar.*/ },      # a hashref who's values must match the regular expression /.*bar.*/
            baz  => { type => 'ARRAY',    validator => qr/.*bar.*/ },      # an array who's values must match the regular expression /.*bar.*/
            bam  => { type => 'ARRAYREF', validator => qr/.*bar.*/ },      # an arrayref who's values must match the regular expression /.*bar.*/
            fred => { type => 'OBJECT',   validator => qr/.*bar.*/ },      # an object who's string representation must match the regular expression /.*bar.*/
     );
 };

Accessor Validation via subroutine:

However, a simple regular expression might not provide you with the means to validate an attribute, so instead of using a regexp, you can use a sub routine

 BEGIN {
     __PACKAGE__->_accessors(
            bar  => { type => 'SCALAR',    validator => sub { my ($self, $value) = @_; <...>; return $value },
            baz  => { type => 'HASH',      validator => sub { my ($self, %values) = @_; <...>; return %values },
            qux  => { type => 'HASHREF',   validator => sub { my ($self, %values) = @_; <...>; return %values },
            baz  => { type => 'ARRAY',     validator => sub { my ($self, @values) = @_; <...>; return @values },
            bam  => { type => 'ARRAYREF',  validator => sub { my ($self, @values) = @_; <...>; return @values },
            quux => { type => 'CODE', },   validator => sub { my ($self, $coderef) = @_; <...>; return $coderef },
            fred => { type => 'OBJECT', }, validator => sub { my ($self, $blessed_ref ) = @_; <...>; return $blessed_ref },
            thud => { type => 'REGEXP', }, validator => sub { my ($self, $regexp ) = @_; <...>; return $regexp },
            psdf => { type => 'GLOBREF', },   validator => sub { my ($self, $globref ) = @_; <...>; return $globref },
     );
 };

Note that the "$self" value passed to your validation routine will be the $self of the class, and so you can invoke methods upon it, mutate it (within reason), etc as necessary.

Note also that the returned value(s) from your validation routine are the values that will actually be stored, allowing you to verify and/or mutate values on setting.

Note finally, that while the regular expression form of validation automatically de-taints values, when you use a subroutine validator, you must explicitely untaint the values you return.

Additional properties of all accessors:

Accessors can also have special properties associated with them, below is an example of all that are applicable to every type:

 BEGIN {
     __PACKAGE__->_accessors(
            foo  => { 
                      type => 'SCALAR',             # type, mandatory for any accessor
                      validator => qr/\d+/,         # validator, optional, may be compiled-regexp, or subroutine reference returning validated values.
                      required => 1,                # boolean, is this accessor required to be passed valid data to ->new()
                      default => '12345',           # default value, must be valid input for the type and validator.
                      readonly => 1,                # boolean, do not allow any further changes to the value of this accessor once object-initialization is complete
                    },
    );

Note that you should take care to ensure that any accessor used in any other accessor's 'validator => sub {}' mechanism is marked as 'required' so that it will have a valid value whenever it is used by other accessors.

Note also that 'readonly' marks an accessor as immutable after initialization; this means that you can modify it in its validator => sub {}, in _preinit, or in _init, but you will not be able to modify it after _init has completed.

Additional properties of special accessors:

In addition the the accessor properties specified above, certain types of accessors have additional special properties.

Special properties for OBJECTs

 BEGIN {
     __PACKAGE__->_accessors(
            foo  => { 
                      type => 'OBJECT',                         # type, mandatory for any accessor
                      object_can => [ qw(foo bar baz) ],        # specify a reference to an array containing method-names that any object passed must have.
                      object_isa => [ qw(A::Class, B::Class) ], # specify a reference to an array containing class-names which the object must have in @ISA.
                    },

Note that in 99% of cases, you are more interested in whether an object exposes a particular interface than you are interested in what its ancestory might be; hence, you should use object_can 99% of the time.

EXPORTS

tags
emitlevels

Exports the constants: OOP_PERLISH_CLASS_EMITLEVEL_FATAL, OOP_PERLISH_CLASS_EMITLEVEL_ERROR, OOP_PERLISH_CLASS_EMITLEVEL_WARNING, OOP_PERLISH_CLASS_EMITLEVEL_INFO, OOP_PERLISH_CLASS_EMITLEVEL_VERBOSE, OOP_PERLISH_CLASS_EMITLEVEL_DEBUG0, OOP_PERLISH_CLASS_EMITLEVEL_DEBUG1, OOP_PERLISH_CLASS_EMITLEVEL_DEBUG2

available exports:

All of the following are error-levels which can be set per-instance, per class, or for all OOP::Perlish::Class derived classes. See the section DIAGNOSTICS for more information.

OOP_PERLISH_CLASS_EMITLEVEL_FATAL
OOP_PERLISH_CLASS_EMITLEVEL_ERROR
OOP_PERLISH_CLASS_EMITLEVEL_WARNING
OOP_PERLISH_CLASS_EMITLEVEL_INFO
OOP_PERLISH_CLASS_EMITLEVEL_VERBOSE
OOP_PERLISH_CLASS_EMITLEVEL_DEBUG0
OOP_PERLISH_CLASS_EMITLEVEL_DEBUG1
OOP_PERLISH_CLASS_EMITLEVEL_DEBUG2

CONSTRUCTOR

The construction of objects has been broken up into multiple parts:

->new(ARGS)

The ->new() method simply gets a hash or hashref, puts it into a hash, blesses itself into all parent classes, and then invokes ____initialize_object(). Args may be a hash, or a hash-reference of name-value pairs referring to accessors-methods in the class, and the arguments to pass to them.

The ->new() method inherited fromt his module should seldem if ever be overridden.

->____initialize_object(%ARGS)

%ARGS is a hash of name-value pairs of accessor-methods in the class, and arguments to pass to them.

->____initialize_object() does all the heavy-lifting of object instantiation. In most cases, you wild not want or need to overload this method.

Things it calls, in order, are:

$self->____process_magic_arguments(@)

This method will automagically run any method in your class' namespace that begins with _magic_constructor_arg_handler. Simply defining a method that begins with this string will result in it being invoked as a 'magic' constructor-argument-handler

$self->____inherit_accessors();

Used internally, typically should not be overloaded -- See source.

Walks @ISA of every parent, and infers their accessors, preserving hiarchial inheritance for overloaded accessors.

$self->____pre_validate_opts()

Usually not overloaded, but occasionally you might want to do additional pre-validation prior to populating required fields.

Takes no arguments (other than $self), returns nothing; expected to croak if there is an error with the options passed to the constructor.

$self->____initialize_required_fields()

Usually not overloaded, -- See source.

This will be run if the 'magic' constructor-argument '_____oop_perlish_class__defer__required__fields__validation' was not specified. Said argument sets the %magic hash key 'defer_required_fields'

return unless( $self->_preinit() );

Call _preinit, see below.

$self->____initialize_non_required_fields();

Usually not overloaded. -- See source.

Initializes all non-required fields.

return unless( $self->_init() );

Call _init, see below.

->_preinit()

This method is specifically intended to be overloaded in your class to do any initialization required before any non-required fields have been initialized. Because required-fields are guaranteed to have been passed to the constructor, they are initialized prior to this method being called, however you may update their values if necessary here (even if they are marked as read-only)

->_init()

This method is specifically intended to be overloaded in your class to do any initialization required after all non-required fields have been initialized. You may still modify readonly accessors here, and as soon as this method completes, they will be locked-out from any further changes.

ACCESSORS

_emitlevel(LEVEL)

This accessor is defined for every derived class, and allows you to specify an emitlevel used for the 'freebie' diagnostic methods provided (see below)

LEVEL is a number from 0-7, which you can obtain constants for by using the export-tag ':emitlevels' when using any object.

The default value for this accessor will be inferred dynamically at runtime. if the class variable _emitlevel has been defined, for instance via 'perl -MClass::Name=_emitlevel:debug', or if there is a variable $main::_OOP_PERLISH_CLASS_EMITLEVEL defined with a number from 0-7, its value will be used globally for every derived class.

METHODS

->get(NAME)

This is used internally, but can often be handy to use yourself. rather than getting the value of an accessor via its method, you can use ->get(NAME) where NAME is the name of the accessor.

->set(NAME, VALUES)

This is used internally, but can often be handy to use yourself. rather than getting the value of an accessor via its method, you can use ->set(NAME, VALUES) where NAME is the name of the accessor, and VALUES are value(s) are values which are valid for the accessor.

->is_set(NAME)

Test if a particular accessor has been set, even if set to undef;

->fatal(MSG)

Croak of an error with MSG

->error(MSG)

Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_ERROR

->warning(MSG)

Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_WARNING

->info(MSG)

Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_INFO

->verbose(MSG)

Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_VERBOSE

->debug(MSG)

Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_DEBUG0

->debug1(MSG)

Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_DEBUG1

->debug2(MSG)

Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_DEBUG2

ADDITIONAL METHODS

The following methods are prefixed with a '_' indicating they are primarly for usage inside derived classes, but not typically used by users of derived classes.

_accessor_class_name

Overload this if you use a different class for Accessors than the default OOP::Perlish::Class::Accessor.

_emit

Overload this if you wish to change the behavior of 'error', 'warning', 'info', 'verbose', 'debug', 'debug1', and 'debug2' discussed above.

_all_methods

Obtain a list of all methods this class has. This list will be sorted in the order of methods-defined-in-furthest-ancestor => methods-defined-in-self.

_all_isa

Optain a list of all classes this class has in its ancestory. This list will be sorted in nearest => furthest.

_get_mutable_reference

ONLY USE IF YOU KNOW EXACTLY WHAT YOU ARE DOING

This will return a naked, completely unprotected reference to the underlying storage of an accessor. No validators will be checked, no type-checking will be performed, you are on your honor not to break things.

DIAGNOSTICS

Below are the three possible ways to enable diagnostics reporting for OOP::Perlish::Class and derived classes at various levels of verbosity. They are listed in order of precedence, with the first taking priority over the last.

Set via accessor

Always takes highest precedence

    use Class::Name qw(:emitlevels);
    my $c = Class::Name->new(_emitlevel => OOP_PERLISH_CLASS_EMITLEVEL_INFO); # create a new 'Class::Name' instance that will display info level messages.
Set via package-scoped global

Will be used any time an instance hasn't been set via accessor

    perl -MClass::Name=_emitlevel:verbose /path/to/foo  # enable verbose messages for the OOP::Perlish::Class derived class 'Class::Name'
Set via $main::_OOP_PERLISH_CLASS_EMITLEVEL

Will be used if there is no package-scoped global, and the instance has no value set via the accessor

    package main;
    use OOP::Perlish::Class qw(:emitlevels);

    our $_OOP_PERLISH_CLASS_EMITLEVEL = OOP_PERLISH_CLASS_EMITLEVEL_DEBUG2; # enable debug2 messages for every OOP::Perlish::Class derived class

See also: ->_emitlevel, ->fatal, ->error, ->warn, ->info, ->verbose, ->debug, ->debug1, ->debug2

INTERNAL GUTS

See source for complete details. The following methods are used internally, and you might find some need to overload them at some point, so they are mentioned here:

sub ____oop_perlish_class_accessor_factory(@)

Produce a subroutine closure for get and set accessors

sub ____recurse_isa(@)

Recursively traverse isa of this class, and all parents, skipping loops

sub ____OOP_PERLISH_CLASS_ACCESSORS(@)

return a static hash-reference of name => accessor-object pairs for every accessor in this class.

sub ____OOP_PERLISH_CLASS_REQUIRED_FIELDS(@)

return a static array-reference to the list of required-fields for this class.

sub ____identify_required_fields(@)

Returns a list of all required fields for this class, and all ancestors.

sub ____validate_defaults(@)

Verify that default values are valid for the type and validator of every accessor