OOP::Perlish::Class - A Base class implementation providing the fundimental infrastructure for perl OOP
A Base class for creating Objects that conform to all common OOP practices, while still remaining very much perl.
{ 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; } }
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.*/ ); };
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.
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.
In addition the the accessor properties specified above, certain types of accessors have additional special properties.
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 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
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.
The construction of objects has been broken up into multiple parts:
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.
%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:
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
Used internally, typically should not be overloaded -- See source.
Walks @ISA of every parent, and infers their accessors, preserving hiarchial inheritance for overloaded accessors.
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.
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'
Call _preinit, see below.
Usually not overloaded. -- See source.
Initializes all non-required fields.
Call _init, see below.
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)
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.
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.
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.
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.
Test if a particular accessor has been set, even if set to undef;
Croak of an error with MSG
Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_ERROR
Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_WARNING
Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_INFO
Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_VERBOSE
Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_DEBUG0
Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_DEBUG1
Report an error; Emitted only if _emitlevel >= OOP_PERLISH_CLASS_EMITLEVEL_DEBUG2
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.
Overload this if you use a different class for Accessors than the default OOP::Perlish::Class::Accessor.
Overload this if you wish to change the behavior of 'error', 'warning', 'info', 'verbose', 'debug', 'debug1', and 'debug2' discussed above.
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.
Optain a list of all classes this class has in its ancestory. This list will be sorted in nearest => furthest.
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.
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.
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.
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'
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
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:
Produce a subroutine closure for get and set accessors
Recursively traverse isa of this class, and all parents, skipping loops
return a static hash-reference of name => accessor-object pairs for every accessor in this class.
return a static array-reference to the list of required-fields for this class.
Returns a list of all required fields for this class, and all ancestors.
Verify that default values are valid for the type and validator of every accessor
To install OOP::Perlish::Class, copy and paste the appropriate command in to your terminal.
cpanm
cpanm OOP::Perlish::Class
CPAN shell
perl -MCPAN -e shell install OOP::Perlish::Class
For more information on module installation, please visit the detailed CPAN module installation guide.