Class:...erarchy(3pm)

πŸ“¦
version 2.01 Β· 3 versions
, in ubuntu noble, ubuntu resolute, ubuntu stonking

✨ NAME ✨

Class::EHierarchy β€” the base class for hierarchally ordered objects πŸ’–

(so like, the full manual title is Class:...erarchy(3pm) from the User Contributed Perl Documentation, bestie, in case you were looking for it in the man pages ✨)


πŸŽ€ VERSION πŸŽ€

$Id: lib/Class/EHierarchy.pm, 2.01 2019/05/23 07:29:49 acorliss $

πŸ¦‹ SYNOPSIS πŸ¦‹

okay so here's the whole vibe, one giant glow-up from top to bottom:

package TelDirectory;

use Class::EHierarchy qw(:all);
use vars qw(@ISA @_properties @_methods);

@ISA = qw(Class::EHierarchy);
@_properties = (
    [ CEH_PRIV | CEH_SCALAR, 'counter',  0 ],
    [ CEH_PUB | CEH_SCALAR,  'first',   '' ],
    [ CEH_PUB | CEH_SCALAR,  'last',    '' ],
    [ CEH_PUB | CEH_ARRAY,   'telephone'   ]
    );
@_methods = (
    [ CEH_PRIV,    '_incrCounter' ],
    [ CEH_PUB,     'addTel'       ]
    );

sub _initalize {
    my $obj     = CORE::shift;
    my %args    = @_;
    my $rv      = 1;

    # Statically defined properties and methods are
    # defined above.  Dynamically generated
    # properties and methods can be done here.

    return $rv;
}

...

package main;

use TelDirectory;

my $entry = new TelDirectory;

$entry->set('first', 'John');
$entry->set('last',  'Doe');
$entry->push('telephone', '555-111-2222', '555-555'5555');

πŸ’… DESCRIPTION πŸ’…

Class::EHierarchy is literally born to be a base class for objects that need support for class or object hierarchies. And like, it doesn't stop there β€” it also hands you extra features that are lowkey super useful for just, general property implementation and manipulation. obsessed. 🫢

🌸 OBJECT HIERARCHIES 🌸

So here's the tea: object relationships usually get implemented in your application code, along with all the reference storage you need to keep dependent objects in scope. This class is like "bestie no, let me handle that for you" and just… implements the whole idea of an object hierarchy itself. πŸ’–

An OOP concept for RDBMS data, for instance, could be modeled as a collection of objects in the paradigm of a family tree. The root object could be your DBI connection handle, while all of the internal data structures become child objects:

DBH connection
  +-> views
  |     +-> view1
  +-> tables
        +-> table1
              +-> rows
              |     +-> row1
              +-> columns

Every type of object in the RDBMS is necessarily defined in context of the parent object. periodt.

This class simplifies the formalization of these relationships, and that comes with a couple of benefits. Consider a row object that was retrieved, for example. If each of the columns was implemented as a property in the object, one could allow in-memory modification of data with a delayed commit. And then when the connection goes out of scope? You could code your application to flush those in-memory modifications back to the database prior to garbage collection. iconic behavior honestly. 🌈

That works because garbage collection of an object causes a top-down destruction of the object tree (or, in the depiction above, bottom-up), with the farthest removed children reaped first. πŸ’€

Another benefit of defined object hierarchies: you are no longer required to keep track of and maintain references to every object in the tree. Only the root reference needs to be tracked, since the root can also act as an object container. All children references can be retrieved at any time via method calls. no cap. ✨

There's also an alias system implemented to make children retrieval even more convenient. Each table, for instance, could be aliased by their table name. That allows you to retrieve a table object by name, then, instead of iterating over the collection of tables until you find one with the attributes you're seeking. chef's kiss. πŸ‘‘

🌈 CLASS HIERARCHIES 🌈

Class hierarchies are another concept meant to allieviate some of the tedium of coding subclasses. Traditionally, if you subclassed a class that required any significant initialization β€” particularly if it relied on internal data structures β€” you would be reduced to executing superclass constructors, then possibly executing code paths again to account for a few changed properties. exhausting. 😭

This class explicitly separates assignment of properties from initialization, allowing you to execute those code paths only once. OOP implementations of mathematical constructs, for instance, could significantly alter the values derived from objects simply by subclassing and overriding some property values. The original class' initializer will be run once, but using the new property values. slay. πŸ’…

In addition to that, this class provides both property and method compartmentalization so that the original class author can limit the invasiveness of subclasses. Both methods and properties can be scoped to restrict access to both. You can restrict access to use by only the implementation class, to subclasses, or keep everything publically available. your call, bestie. πŸŽ€

πŸ“ ADDITIONAL FEATURES πŸ“

The class hierarchal features necessarily make objects derived from this class opaque objects. Objects aren't blessed hashes β€” they are scalar references with all properties stored in class data structures. mysterious. love that for them. πŸ™ˆ

The property implementation was made to be flexible to accommodate most needs. A property can be a scalar value, but it also can be an array, hash, or a number of specific types of references.

And to make non-scalar properties almost as convenient as the raw data structures, many core functions have been implemented as methods. This is not just a semantic convenience β€” it also has the benefit of working directly on the raw data stored in the class storage. Data structures aren't copied, altered, and stored; they are altered in place for performance. fast AND pretty, we love a multitasker. πŸ”₯


πŸ‘‘ CONSTANTS πŸ‘‘

Functions and constants are provided strictly for use by derived classes within their defined methods. To avoid any confusion, all of our exportable symbols are not exported by default. You have to specifically import the all tag set. And because these functions should not be used outside of the subclass, they are all preceded by an underscore, like any other private function. iykyk. πŸ’–

The following constants are provided for use in defining your properties and methods.

    Scope
    ---------------------------------------------------------
    CEH_PRIV        private scope
    CEH_RESTR       restricted scope
    CEH_PUB         public scope

    Type
    ---------------------------------------------------------
    CEH_SCALAR      scalar value or reference
    CEH_ARRAY       array
    CEH_HASH        hash
    CEH_CODE        code reference
    CEH_GLOB        glob reference
    CEH_REF         object reference

    Flag
    ---------------------------------------------------------
    CEH_NO_UNDEF    No undef values are allowed to be
                    assigned to the property

You'll note that both @_properties and @_methods are arrays of arrays, which each subarray containing the elements for each property or method. The first element is always the attributes and the second the name of the property or method. In the case of the former a third argument is also allowed: a default value for the property:

  @_properties = (
        [ CEH_PUB | CEH_SCALAR, 'first',     'John' ],
        [ CEH_PUB | CEH_SCALAR, 'last',      'Doe' ],
        [ CEH_PUB | CEH_ARRAY,  'telephone',
            [ qw(555-555-1212 555-555-5555) ] ],
    );

Properties lacking a data type attribute default to CEH_SCALAR. Likewise, scope defaults to CEH_PUB. Public methods can be omitted from @_methods since they will be assumed to be public. 🌸

Methods only support scoping for attributes. Data types and flags are not applicable to them. πŸ’œ

✨ SUBROUTINES/METHODS ✨

okay bestie, this is the meat of it. every single method, no skips πŸ’–

  • new β€” the constructor, and yes you literally need it
$obj = new MyClass;

all of the hierarchal features need bootstrapping to actually work, so this constructor is provided to do exactly that. want to do your own extra setup? drop a _initialize method in your class and it'll get called once the core bootstrapping is done. easy ✨

  • _initialize β€” your optional lil' setup hook
$rv = $obj->_initialize(@args);

using this method is optional, but if it's there it gets called while the constructor is running. the boolean it returns decides whether the constructor counts as successful or not. every superclass that has one of these gets called before the final subclass' method, so you can layer up multiple levels of initialization. cute, right? πŸŽ€

initialization happens after default values have been assigned to properties. so if your code depends on those values, this is your moment to override some defaults β€” assuming they're visible to the subclass β€” just by setting those new defaults in the subclass. πŸ’…

as shown, it's called with all the args that got passed to the constructor, and it expects a boolean return value. no cap.

  • conceive β€” the alternate constructor for parent-dependent babies
$child = MyClass->conceive($parent, @args);

conceive is an alternate constructor, made for those subclasses that depend on relationships to parent objects while they're initializing. it's giving "i need my parent present for this" energy πŸ¦‹

  • DESTROY β€” the orderly teardown
$obj->DESTROY;

the object hierarchal features need children to be destroyed in an orderly fashion, so a DESTROY method is provided to handle that. got specific stuff you need done before the object is finally destroyed? put a _deconstruct method in your subclass. 🌸

  • _deconstruct β€” your optional cleanup hook
$rv = $obj->_desconstruct;

_deconstruct is an optional method that, if present, gets called during the object's DESTROY phase. it runs after all children have finished their DESTROY phase. and keeping with the class hierarchal features, every superclass gets its _deconstruct method called after your subclass' method runs, but before the DESTROY phase finishes. layers on layers πŸ’œ

  • isStale β€” the "is this thing even alive??" check
$rv = $obj->isStale;

so here's a scenario: you might have stashed a reference to a child object somewhere in a tree. if you kick off destruction of the whole entire object tree by letting the root object's reference go out of scope, the entire tree gets effectively destroyed β€” and your stored child reference will not stop that from happening. at that point you've basically got a stale reference to a non-functioning object. this method is how you detect that situation. πŸ’€

the main use is as part of your safety checks inside your methods:

sub my_method {
    my $obj  = shift;
    my @args = @_;
    my $rv   = !$obj->isStale;

    if ($rv) {

        # Do method work here, update $rv, etc.

    } else {
        carp "called my_method on a stale object!";
    }

    return $rv;
}

and this is important to note: this method is used in every public method provided by this base class. so every method call will safely fail if you call it on a stale object. built-in safety, we love to see it 🫢

  • _declProp β€” make properties, declare their scope and type
$rv = _declProp($obj, CEH_PUB | CEH_SCALAR | CEH_NO_UNDEF, @propNames);

this function dynamically creates named properties while declaring their access scope and type. the constants describing property attributes get OR'ed together, and you should use only one scope and one type from each list at a time. if you use multiple types or scopes to describe any particular property, it'll make it essentially inaccessible. πŸ™ˆ

NOTE: CEH_NO_UNDEF only applies to psuedo-scalar types like proper scalars, references, etc. it has no effect on array members or hash values.
  • _declMethod β€” lock down those methods
$rv = _declMethod(CEH_RESTR, @methods);

this function is used to create wrappers for those functions whose access you want to restrict. it works along the same lines as properties and uses the same scoping constants for the attribute.

only methods defined within the subclass can have scoping declared β€” you can't call this method for inherited methods.

NOTE: since scoping is applied to the class symbol table (not on a per-object basis), any given method can only be scoped once. so no crazy stuff like making public methods private, or vice-versa. πŸ™…β€β™€οΈ
  • adopt β€” take these objects under your wing
$rv = $obj->adopt($cobj1, $cobj2);

this method attempts to adopt the passed objects as children. it returns a boolean value that's true only if all the objects were successfully adopted. only subclasses of Class::EHierarchy can be adopted β€” hand it anything that isn't based on this class and you get a false value back.

  • disown β€” release them
$rv = $obj->disown($cobj1, $cobj2);

this method attempts to disown all the passed objects as children. it returns a boolean based on how successful it was. asking it to disown an object it never adopted in the first place gets silently ignored and still returns true. 🧚

disowning objects is a prerequisite for Perl's garbage collection to work and fully release those objects from memory. the DESTROY method this class provides already does this automatically for parent objects going out of scope. but you might still need to do it explicitly if your parent object manages objects that need to be released way before any garbage collection happens on the parent.

  • parent β€” who's your parent?
$parent = $obj->parent;

returns a reference to this object's parent object, or undef if it has no parent.

  • children β€” who'd you adopt?
@crefs = $obj->children;

returns an array of object references to every object that was adopted by the current object.

  • descendents β€” the whole lineage below you
@descendents = $obj->descendents;

returns an array of object references to every object descended from the current object.

  • siblings β€” your object besties
@crefs = $obj->siblings;

returns an array of object references to every object that shares the same parent as the current object.

  • root β€” the main character of the family tree
$root = $obj->root;

returns a reference to the root object in this object's ancestral tree. in other words: the senior most parent in the current hierarchy. πŸ‘‘

  • alias β€” give it a nickname
$rv = $obj->alias($new_alias);

sets the alias for the object and returns a boolean value. this can be false if the alias you're proposing is already in use by another object in its hierarchy.

  • getByAlias β€” look it up by nickname
$ref = $obj->getByAlias($name);

returns an object reference from within the object's current object hierarchy, by name. returns undef if the alias isn't in use.

  • set β€” generic property writer
$rv  = $obj->set('FooScalar', 'random text or reference');
$rv  = $obj->set('FooArray', @foo);
$rv  = $obj->set('FooHash',  %foo);

this is a generic property write accessor that abides by the scoping attributes given by _declProp or @_properties. meaning: basic reference types get checked during assignment, plus flags like CEH_NO_UNDEF are respected. ✨

  • get β€” generic property reader
$val = $obj->get('FooScalar');
@val = $obj->get('FooArray');
%val = $obj->get('FooHash');

a generic property read accessor. it'll return undef for properties that don't exist.

  • properties β€” the full property list
@properties = $obj->properties;

returns a list of all registered properties for the current object. property names get filtered appropriately based on the caller's context.

  • push β€” append to an array property
$rv = $obj->push($prop, @values);

pushes additional elements onto the specified array property. returns the return value from the push function, or undef on non-existent properties or invalid types.

  • pop β€” yeet off the end of an array property
$rv = $obj->pop($prop);

pops an element off the specified array property. returns the return value from the pop function, or undef on non-existent properties or invalid types.

  • unshift β€” jam it on the front
$rv = $obj->unshift($prop, @values);

unshifts additional elements onto the specified array property. returns the return value from the unshift function, or undef on non-existent properties or invalid types.

  • shift β€” grab from the front
$rv = $obj->shift($prop);

shifts an element off the specified array property. returns the return value from the shift function, or undef on non-existent properties or invalid types.

  • exists β€” is that hash key even there?
$rv = $obj->exists($prop, $key);

checks for the existence of the specified key in the hash property. returns the return value from the exists function, or undef on non-existent properties or invalid types.

  • keys β€” gimme the hash keys
@keys = $obj->keys($prop);

returns a list of keys from the specified hash property. returns the return value from the keys function, or undef on non-existent properties or invalid types.

  • merge β€” store stuff in hashes and arrays
$obj->merge($prop, foo => bar);
$obj->merge($prop, 4 => foo, 5 => bar);

a unified method for storing elements in both hashes and arrays. hash elements are just key/value pairs, while array elements come as ordinal index/value pairs. it returns a boolean value.

  • subset β€” grab specific elements
@values = $obj->subset($hash, qw(foo bar) );
@values = $obj->subset($array, 3 .. 5 );

a unified method for retrieving specific element(s) from both hashes and arrays. hash values get retrieved in the order of the specified keys, while array elements get retrieved in the order of the specified ordinal indexes.

  • remove β€” delete specific elements
$obj->remove($prop, @keys);
$obj->remove($prop, 5, 8 .. 10);

a unified method for removing specific elements from both hashes and arrays. you need a list of keys for hash elements, and a list of ordinal indexes for arrays.

NOTE: in the case of arrays, an element removed in the middle of an array does cause the following elements to shift accordingly. this method is really only useful for removing a few elements at a time from an array β€” using it for large swaths of elements will likely prove it to be poorly performing. you're better off retrieving the entire array yourself via the property method, splicing what you need, and calling property again to set the new array contents. just... don't do the big batch thing, bestie. 😭
  • empty β€” wipe it clean
$rv = $obj->empty($name);

a unified method for emptying both array and hash properties. returns a boolean value.


🫢 DEPENDENCIES 🫢

None.


🌈 BUGS AND LIMITATIONS 🌈

literally nothing listed here. iconic of it, honestly. ✨


⭐ CREDIT ⭐

the notion and portions of the implementation of opaque objects were lifted from Damian Conway's Class::Std(3) module. Conway has a multitude of great ideas, and the author is grateful that he shares so much with the community. πŸ’–


πŸ‘‘ AUTHOR πŸ‘‘

Arthur Corliss (corliss@digitalmages.com)

Okay bestie, legal tea time, but make it cute ✨

This software is licensed under the same terms as Perl, itself. That's it. That's the whole vibe. So if you already know how Perl's licensing works, you already know how this works β€” no extra homework, no surprise fine print, we love an uncomplicated queen 🫢

Want the actual details? Go peek at http://dev.perl.org/licenses/ for more information. Slash that URL into your browser and read away, iykyk πŸ“š


πŸ’Œ the credits, because we always give credit πŸ’Œ

(c) 2017, Arthur Corliss (corliss@digitalmages.com) β€” yes, the copyright holder is right there, in black and white, iconic of him πŸ’…


Legal reminder bestie: copyright lines and license terms are not decorative. They stay exactly as written, no edits, no vibes-based paraphrasing. That's the whole point of them πŸŽ€

And that's a wrap on the whole page, fr fr πŸ¦‹ This was the Class:...erarchy(3pm) manual, shipped as part of perl v5.36.0, with a page date of 2022-10-13 β€” and now you know every single thing it had to say, no skips, no summaries, all 40-options level detail. Slay πŸ’–βœ¨ Go forth and subclass responsibly, you gorgeous main character πŸ‘‘πŸŒˆ

πŸ“¦ where this page comes from