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
-
- 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