NAME
Venus::Hook - Venus Lifecycle Hooks
ABSTRACT
Lifecycle Hooks for Perl 5
SYNOPSIS
package User;
use base 'Venus::Core';
# Define attributes
User->ATTR('name');
User->ATTR('email');
# Post-construction initialization
sub BUILD {
my ($self) = @_;
$self->{created} = time;
return $self;
}
# Pre-destruction cleanup
sub DECONSTRUCT {
my ($self) = @_;
$self->log("User destroyed");
return $self;
}
package main;
my $user = User->BLESS(name => 'Elliot', email => 'e@example.com');
# bless({name => 'Elliot', email => 'e@example.com', created => ...}, 'User')
DESCRIPTION
This document provides a comprehensive reference for all lifecycle hooks available in the Venus OOP framework. Venus provides a rich set of hooks that allow you to customize class building, object construction, and object destruction behavior.
Venus lifecycle hooks are special methods that are automatically invoked at specific points during class building, object construction, and object destruction. These hooks provide extension points for customizing behavior without modifying core Venus code. Hooks are conventionally named in UPPERCASE to distinguish them from regular methods.
SUPER
When overriding lifecycle hooks in a subclass, you should almost always call the parent implementation using SUPER, or risk breaking the framework in unexpected ways.
The Venus framework relies on proper hook execution throughout the inheritance chain. Failing to call the parent implementation can break functionality in subtle ways, including:
Breaking role/mixin composition and their lifecycle hooks
Skipping critical initialization or cleanup code from parent classes
Bypassing framework-level validations and setup
Causing memory leaks by not cleaning up instance data properly
Required
When overriding any of the following hooks, you should call SUPER unless you have a very specific reason not to:
ARGS, ATTR, AUDIT, BASE, BLESS, BUILD, BUILDARGS, CONSTRUCT, CLONE,
DECONSTRUCT, DESTROY, DOES, EXPORT, FROM, GET, IMPORT, ITEM, META,
MIXIN, MASK, NAME, ROLE, SET, SUBS, TEST, UNIMPORT, USE
Correct Pattern
Always call the parent implementation first, then add your custom logic:
package Child;
use base 'Parent';
sub BUILD {
my ($self, $data) = @_;
# CRITICAL: Call parent implementation first
$self->SUPER::BUILD($data);
# Then add your custom logic
$self->{child_field} = 'value';
return $self;
}
Incorrect Pattern
DO NOT skip the SUPER call:
# WRONG - This breaks the framework!
sub BUILD {
my ($self, $data) = @_;
# Missing: $self->SUPER::BUILD($data);
$self->{child_field} = 'value'; # Parent's BUILD never runs!
return $self;
}
When You Might Skip SUPER
There are very rare cases where you might intentionally skip calling SUPER:
You are replacing the parent's behavior entirely (use with extreme caution)
You are implementing a base hook that has no parent (e.g., in Venus::Core itself)
You are implementing certain introspection hooks like
DATAthat are meant to be completely overridden
Rule of thumb: If you're unsure whether to call SUPER, call it. It's almost always the right choice.
HOOKS
This package provides the following hooks:
Object Construction Hooks
These hooks control how objects are constructed and initialized.
- args
-
ARGS(any @args) (hashref)The ARGS hook accepts constructor arguments and returns a blessable data structure. It is called during object construction, before blessing.
Since
1.00 - bless
-
BLESS(any @args) (object)The BLESS hook is the main object construction hook that creates and returns an instance. It is called when you call the constructor (or
BLESSdirectly). The default behavior is equivalent to callingBUILD(bless(ARGS(BUILDARGS(@args) || DATA), $class)).Since
1.00 - buildargs
-
BUILDARGS(any @args) (any @args | hashref $data)The BUILDARGS hook pre-processes constructor arguments before they are converted to a data structure. It is called before
ARGS, and receives raw constructor arguments.Since
1.00- buildargs example 1
-
package User; use base 'Venus::Core'; sub BUILDARGS { my ($self, @args) = @_; # Convert single string arg to hashref my $data = @args == 1 && !ref $args[0] ? {name => $args[0]} : {}; return $data; } package main; my $user = User->BLESS('Elliot'); # bless({name => 'Elliot'}, 'User')
- build
-
BUILD(hashref $data) (object)The BUILD hook is a post-construction initialization hook. It receives the blessed object and can modify it. It is called after the object is blessed, before it's returned to the caller. While the return value is ignored by
BLESS, it should return$selffor chaining.Note: When inheriting, you should call the parent's BUILD using
$self->SUPER::BUILD($data).Since
1.00 - construct
-
CONSTRUCT() (any)The CONSTRUCT hook is an additional post-construction hook for instance preparation, separate from the build process. It is called automatically after
BLESS, without arguments. The return value is not used in subsequent processing.Since
4.15 - data
-
DATA() (Ref)The DATA hook returns the default data structure to bless when no arguments are provided. It is called during object construction when no arguments are given. The default implementation returns an empty hashref
{}.Since
1.00 - clone
-
CLONE() (object)The CLONE hook creates a deep clone of the invocant. It is called when you call
cloneorCLONE. It must be called on an object instance (not a class). Private data (fromMASK) is not included in the clone.Since
4.15- clone example 1
-
package User; use base 'Venus::Core'; User->MASK('password'); sub get_password { my ($self) = @_; $self->password; } sub set_password { my ($self) = @_; $self->password('secret'); } package main; my $user = User->BLESS(name => 'Elliot'); $user->set_password; my $clone = $user->CLONE; # bless({name => 'Elliot'}, 'User') # Note: Private data (password) is not cloned
Object Destruction Hooks
These hooks are invoked when objects are being destroyed.
- deconstruct
-
DECONSTRUCT() (any)The DECONSTRUCT hook is a pre-destruction cleanup hook for releasing resources before the object is destroyed. It is called just before
DESTROY. The return value is not used in subsequent processing.Since
4.15 - destroy
-
DESTROY() (any)The DESTROY hook is the object destruction lifecycle hook. It is called when the last reference to the object goes away.
Since
1.00
Class Building Hooks
These hooks are used during class definition to set up the class structure.
- attr
-
ATTR(string $name, any @args) (string | object)The ATTR hook installs attribute accessors in the calling package. It is called during class building when you call
attrorATTR. It creates getter/setter methods for the named attribute.Since
1.00 - base
-
BASE(string $name) (string | object)The BASE hook registers one or more base classes for the calling package. It is called during class building. Note: Unlike
FROM, this does NOT invoke theAUDIThook.Since
1.00 - mask
-
MASK(string $name, any @args) (string | object)The MASK hook installs private instance data accessors that can only be accessed within the class or its subclasses. It is called during class building. It creates accessors that raise an exception if accessed outside the class hierarchy.
Since
4.15- mask example 1
-
package User; use base 'Venus::Core'; User->MASK('password'); sub get_password { my ($self) = @_; $self->password; # OK: called from within class } sub set_password { my ($self, $value) = @_; $self->password($value); # OK: called from within class } package main; my $user = User->BLESS(name => 'Elliot'); $user->set_password('secret'); # Works # $user->password; # Exception! Can't access private variable
Composition Hooks
These hooks handle role and mixin composition.
- role
-
ROLE(string $name) (string | object)The ROLE hook consumes a role into the calling package. It is called during class building via the
rolefunction.Behavior:
Methods are copied from the role to the consumer
Methods are NOT copied if they already exist in the consumer
First method wins in naming collisions
Automatically invokes the role's
IMPORThookDoes NOT invoke the role's
AUDIThook
Since
1.00 - mixin
-
MIXIN(string $name) (string | object)The MIXIN hook consumes a mixin into the calling package. It is called during class building via the
mixinfunction.Behavior:
Methods are ALWAYS copied, even if they already exist (override)
Last mixin wins in naming collisions
Automatically invokes the mixin's
IMPORThookMore aggressive than roles
Since
1.02 - test
-
TEST(string $name) (string | object)The TEST hook consumes a role and invokes its
AUDIThook for interface validation. It is called during class building via thetestorwithfunction.Behavior:
Same as
ROLEbut also invokesAUDIThookAllows roles to act as interfaces
Since
1.00- test example 1
-
package Admin; use base 'Venus::Core'; package IsAdmin; use base 'Venus::Core'; sub shutdown { return; } sub AUDIT { my ($self, $from) = @_; die "${from} is not a super-user" if !$from->DOES('Admin'); } sub EXPORT { ['shutdown'] } package User; use base 'Venus::Core'; User->ROLE('Admin'); User->TEST('IsAdmin'); # Will validate User->DOES('Admin') package main; my $user = User->BLESS; # OK
- from
-
FROM(string $name) (string | object)The FROM hook registers a base class and invokes its
AUDITandIMPORThooks. It is called during class building via thefromfunction.Behavior:
Registers inheritance
Automatically invokes
AUDIThook for validationAutomatically invokes
IMPORThook
Since
1.00- from example 1
-
package Entity; use base 'Venus::Core'; sub AUDIT { my ($self, $from) = @_; die "Missing startup" if !$from->can('startup'); die "Missing shutdown" if !$from->can('shutdown'); } package User; use base 'Venus::Core'; User->ATTR('startup'); User->ATTR('shutdown'); User->FROM('Entity'); # Will validate interface package main; my $user = User->BLESS; # OK
Interface and Validation Hooks
- audit
-
AUDIT(string $role) (string | object)The AUDIT hook is an interface validation callback executed when a role is consumed via
TESTorFROM. It is called when the consumer invokesTESTorFROMhooks. It receives the role itself and the consuming class name, and should die/throw if the interface contract is not satisfied.Since
1.00- audit example 1
-
package HasType; use base 'Venus::Core'; sub AUDIT { my ($self, $from) = @_; die 'Consumer missing "type" attribute' if !$from->can('type'); } package User; use base 'Venus::Core'; User->ATTR('type'); User->TEST('HasType'); # OK: User has 'type' package Admin; use base 'Venus::Core'; # Admin->TEST('HasType'); # Would die: Admin missing 'type'
Import/Export Hooks
These hooks control what gets exported when roles/mixins are composed.
- export
-
EXPORT(any @args) (arrayref)The EXPORT hook returns an arrayref of routine names to be automatically imported by consumers. It is called when a class is used via
use, or wheneverimportis called, or during role/mixin composition viaROLE,TEST, orMIXINhooks. Only methods listed in EXPORT are copied to the consumer. Note: By default, if noEXPORTroutine is declared, and if a package variable exists named `@EXPORT`, the package variable is used as the list of routines to be automatically exported.Since
1.00- export example 1
-
package Admin; use base 'Venus::Core'; sub shutdown { return; } sub restart { return; } sub EXPORT { ['shutdown'] # Only shutdown will be exported } package User; use base 'Venus::Core'; User->ROLE('Admin'); package main; my $user = User->BLESS; # $user->can('shutdown') is true # $user->can('restart') is false
- import
-
IMPORT(string $into, any @args) (string | object)The IMPORT hook dispatches the
EXPORThook when roles/mixins are consumed. It is called when a class is used viause, or wheneverimportis called, or during role/mixin composition viaROLE,TEST, orMIXINhooks. Override this hook to track or customize the import process.Since
1.00- import example 1
-
package Admin; use base 'Venus::Core'; our $USES = 0; sub shutdown { return; } sub EXPORT { ['shutdown'] } sub IMPORT { my ($self, $into) = @_; $self->SUPER::IMPORT($into); $USES++; return $self; } package User; use base 'Venus::Core'; User->ROLE('Admin'); package main; # $Admin::USES is now 1
- use
-
USE(string $into, any @args) (any)The USE hook is invoked when the Perl
usedeclaration is used. It is called during compilation when youusea package.Since
2.91 - unimport
-
UNIMPORT(string $into, any @args) (any)The UNIMPORT hook is invoked when the Perl
nodeclaration is used. It is called when younoa package.Since
2.91
Instance Data Hooks
These hooks control how instance data (attributes) are accessed and modified.
- get
-
GET(string $name) (any)The GET hook is responsible for getting instance attribute values. By default, all attribute getters dispatch to this method. Override this hook to implement custom getter logic for all attributes.
Since
2.91 - set
-
SET(string $name, any @args) (any)The SET hook is responsible for setting instance attribute values. By default, all attribute setters dispatch to this method. Override this hook to implement custom setter logic for all attributes.
Since
2.91 - item
-
ITEM(string $name, any @args) (string | object)The ITEM hook is responsible for both getting and setting instance attributes. By default, all attribute accessors dispatch to this method. Without extra args, it acts as a getter; with args, it acts as a setter.
Since
1.11
Introspection Hooks
These hooks provide information about the package/object structure.
- meta
-
META() (Venus::Meta)The META hook returns a Venus::Meta object describing the package configuration. It is called when you call
metaorMETAon a class or instance.Since
1.00 - name
-
NAME() (string)The NAME hook returns the name of the package. It is called whenever the package name is accessed via
NAME.Since
1.00 - does
-
DOES(string $name) (boolean)The DOES hook returns true if the invocant consumed the specified role or mixin. It is called on-demand, typically to check role composition.
Since
1.00 - subs
-
SUBS() (arrayref)The SUBS hook returns the routines defined on the package and consumed from roles (excluding inherited methods). It is called on-demand, typically for introspection of available methods, whenever
SUBSis accessed.Since
1.00
Hook Execution Order
Understanding the order in which hooks are executed is crucial for proper initialization.
Class Building Phase
- 1.
BASE/FROM- Register base classes -
FROMalso triggers:AUDIT(from parent),IMPORT(from parent) - 2.
ATTR/MASK- Define attributes - 3.
ROLE/TEST/MIXIN- Compose roles/mixins -
ROLEtriggers:IMPORT(from role)TESTtriggers:AUDIT(from role),IMPORT(from role)MIXINtriggers:IMPORT(from mixin)
Object Construction Phase
- 1.
BUILDARGS- Pre-process constructor arguments - 2.
ARGS- Convert arguments to blessable data structure - 3.
DATA- Provide default data structure (if no args) - 4.
bless- Perl's built-in blessing - 5.
BUILD- Post-construction initialization - 6.
CONSTRUCT- Additional post-construction setup - 7. Return object to caller
Object Destruction Phase
Role/Mixin Composition Phase
When a role/mixin is consumed:
- 1. Consumer calls
ROLE/TEST/MIXIN - 2. If TEST or FROM:
AUDITis called on the role/parent - 3.
IMPORTis called on the role/mixin - 4.
EXPORTis called to determine what to copy - 5. Methods are copied according to composition rules
Best Practices
1. Always Return $self in BUILD
sub BUILD {
my ($self) = @_;
# ... initialization ...
return $self; # Always return $self
}
2. Call SUPER in Inherited BUILD
sub BUILD {
my ($self, $data) = @_;
$self->SUPER::BUILD($data); # Call parent's BUILD
# ... your initialization ...
return $self;
}
3. Explicitly Declare EXPORT
sub EXPORT {
# Be explicit about what you export
['method1', 'method2']
}
4. Use AUDIT for Interface Enforcement
sub AUDIT {
my ($self, $from) = @_;
die "Missing required method 'foo'" if !$from->can('foo');
die "Missing required method 'bar'" if !$from->can('bar');
}
5. Use MASK for Encapsulation
# Instead of documenting "don't use this attribute"
# Use MASK to enforce it programmatically
User->MASK('internal_cache');
6. Keep BUILDARGS Simple
sub BUILDARGS {
my ($self, @args) = @_;
# Simple transformations only
# Complex logic goes in BUILD
return @args == 1 && !ref $args[0] ? {id => $args[0]} : {@args};
}
SUMMARY
Venus provides a comprehensive set of lifecycle hooks organized into several categories:
Construction: ARGS, BLESS, BUILDARGS, BUILD, CONSTRUCT, DATA, CLONE
Destruction: DECONSTRUCT, DESTROY
Class Building: ATTR, BASE, MASK
Composition: ROLE, MIXIN, TEST, FROM
Validation: AUDIT
Import/Export: EXPORT, IMPORT, USE, UNIMPORT
Instance Data: GET, SET, ITEM, STORE
Introspection: META, METACACHE, NAME, DOES, SUBS
These hooks provide fine-grained control over every aspect of object-oriented programming in Venus, from class construction to object lifecycle management.
AUTHORS
Awncorp, awncorp@cpan.org
LICENSE
Copyright (C) 2022, Awncorp, awncorp@cpan.org.
This program is free software, you can redistribute it and/or modify it under the terms of the Apache license version 2.0.