NAME
Aion::Pleroma - container of aeons
SYNOPSIS
use Aion::Pleroma;
my $pleroma = Aion::Pleroma->new;
$pleroma->get('user') # -> undef
$pleroma->resolve('user') # @-> user is'nt eon!
DESCRIPTION
Implements the dependency container pattern.
An eon is created when requesting from a container via the get or resolve method, or via the eon aspect as a lazy default. Laziness can be canceled via the lazy aspect.
The container can be obtained using Aion->pleroma.
The configuration for creating eons is obtained from the PLEROMA config and the annotation file (created by the Aion::Annotation package). The annotation file can be replaced via the INI config.
CONFIG
Module settings that can be set in .env:
AION_PLEROMA_INI – annotation file. Defaults to
etc/annotation/eon.ann.AION_PLEROMA_AUTOWARE – load modules automatically, even if they are not specified in the configuration. Default is
1.
EONS FROM CONFIG
You can add the aion.eon key to etc/*.yml with a description of additional eons. This allows you to assemble eons declaratively: specify constructor arguments (named or ordered), call methods after creation, and pass references to other eons via @.
Let us describe the classes of eons.
File lib/Ex/Eon/Astronomer.pm:
package Ex::Eon::Astronomer;
use strict; use warnings;
sub new { my ($class, $name, $telescope) = @_; bless { name => $name, telescope => $telescope, seen => [] }, $class }
sub name { $_[0]{name} }
sub telescope { $_[0]{telescope} }
sub seen { $_[0]{seen} }
sub observe {
my ($self, $body) = @_;
die "body is'nt Planet!" unless $body && $body->isa('Ex::Eon::Planet');
push @{ $self->{seen} }, $body;
$body
}
1;
File lib/Ex/Eon/Planet.pm:
package Ex::Eon::Planet;
use common::sense;
use Aion;
has name => (is => 'ro');
has moons => (is => 'ro', default => 0);
has discoverer => (is => 'ro');
1;
Now the configuration of the eons. The eon-scientist Ex::Eon::Galileo has arguments specified in order (arguments is an array), after creation the observe method is called with a link to another eon (@Ex::Eon::Saturn). For planets, arguments is a hash, and the discoverer argument refers (@) to the scientist's eon.
File etc/aion/include.yml:
aion:
eon:
Ex::Eon::Jupiter:
class: 'Ex::Eon::Planet'
arguments:
name: 'Jupiter'
moons: 95
discoverer: '@Ex::Eon::Galileo'
Ex::Eon::Saturn:
class: 'Ex::Eon::Planet'
arguments:
name: 'Saturn'
moons: 146
Ex::Eon::Galileo:
class: 'Ex::Eon::Astronomer'
arguments: [ 'Galileo Galilei', 'refracting telescope' ]
calls:
- [observe, '@Ex::Eon::Saturn']
In the configuration, the eon Ex::Eon::Jupiter is described before Ex::Eon::Galileo (which it refers to via @). The order of description is not important: eons are generated lazily when requested, so the @ link is resolved to a ready-made eon.
Let's read the configuration from etc/aion/include.yml and pass it to the container.
use Aion::Pleroma;
use Aion::Env::Etc ();
my $etc = Aion::Env::Etc::parse('etc/aion/include.yml');
my $pleroma = Aion::Pleroma->new(pleroma => $etc->{aion}{eon});
my $jupiter = $pleroma->resolve('Ex::Eon::Jupiter');
$jupiter->name # => Jupiter
$jupiter->moons # => 95
$jupiter->discoverer->name # => Galileo Galilei
ref($jupiter->discoverer) # => Ex::Eon::Astronomer
my $galileo = $pleroma->resolve('Ex::Eon::Galileo');
$galileo->name # => Galileo Galilei
$galileo->telescope # => refracting telescope
ref($galileo->seen->[0]) # => Ex::Eon::Planet
$galileo->seen->[0]->name # => Saturn
Circular dependency
If eons refer to each other, then the container will not be able to spawn them. The cycle is calculated already when adding an eon (autoware) - at the stage of assembling the configuration - and an exception with a chain of references is thrown.
my $cnt = Aion::Pleroma->new;
$cnt->autoware({ class => 'Ex::Eon::Galileo', arguments => [ 'Galileo Galilei', 'refracting telescope' ], calls => [[ 'observe', '@Ex::Eon::Jupiter' ]] }, 'Ex::Eon::Galileo');
$cnt->autoware({ class => 'Ex::Eon::Planet', arguments => { name => 'Jupiter', moons => 95, discoverer => '@Ex::Eon::Galileo' } }, 'Ex::Eon::Jupiter') # @~> Circular eon dependency: .+Ex::Eon::Jupiter
Eon description keys
Each eon in aion.eon is described by a string or hash.
The line specifies the constructor 'class#method' (or just 'class'), the default method is new. This is how the simplest eons are created without arguments.
The hash can contain the keys:
class– class (package) of the eon. If not specified and the eon key is similar to the class name (/^[\w:]+$/), the key itself is used.method– method of the constructor class. Defaults tonew.arguments– constructor arguments:hash – named arguments (
new => %hash);array – ordered arguments (
new => @array).calls– list of method calls after eon creation. Each call is a method name (without arguments) or an array[method_name, arguments...].
An argument (or call element) value starting with @ is treated as a reference to another eon: @key is replaced by the child eon from the container.
FEATURES
ini
Annotation file.
Aion::Pleroma->new->ini # => etc/annotation/eon.ann
pleroma
Configuration: key => 'class#class_method'.
File lib/Ex/Eon/AnimalEon.pm:
package Ex::Eon::AnimalEon;
#@eon
use common::sense;
use Aion;
has role => (is => 'ro');
#@eon ex.cat
sub cat { __PACKAGE__->new(role => 'cat') }
#@eon
sub dog { __PACKAGE__->new(role => 'dog') }
1;
File etc/annotation/eon.ann:
Ex::Eon::AnimalEon#,2=
Ex::Eon::AnimalEon#cat,10=ex.cat
Ex::Eon::AnimalEon#dog,13=Ex::Eon::AnimalEon#dog
Aion::Pleroma->new->pleroma # --> { "Ex::Eon::AnimalEon#dog" => { class => "Ex::Eon::AnimalEon", method => "dog" }, "Ex::Eon::AnimalEon" => { class => "Ex::Eon::AnimalEon", method => "new" }, "ex.cat" => { class => "Ex::Eon::AnimalEon", method => "cat" }, "Aion::Pleroma" => { class => "Aion::Pleroma", method => "new" } }
eon
The totality of generated eons.
my $pleroma = Aion::Pleroma->new;
$pleroma->eon # --> { "Aion::Pleroma" => $pleroma }
my $cat = $pleroma->resolve('ex.cat');
$pleroma->eon # --> { "ex.cat" => $cat, "Aion::Pleroma" => $pleroma }
SUBROUTINES
get ($key)
Receive an eon from the container.
my $pleroma = Aion::Pleroma->new;
$pleroma->get('') # -> undef
$pleroma->get('Ex::Eon::AnimalEon#dog')->role # => dog
resolve ($key)
Get an eon from the container or an exception if it is not there.
my $pleroma = Aion::Pleroma->new;
$pleroma->resolve('e.ibex') # @=> e.ibex is'nt eon!
$pleroma->resolve('Ex::Eon::AnimalEon#dog')->role # => dog
autoware ($config, [$key])
Add an aeon to the pleroma. $config – the string 'class#method' (or just 'class') or a hash with the keys class, method, and optionally arguments/calls (see above). If $key is not specified, it is output from the configuration.
File lib/Ex/Eon/AstroEon.pm:
package Ex::Eon::AstroEon;
use common::sense;
use Aion;
has role => (is => 'ro', default => 'upiter');
sub mars { __PACKAGE__->new(role => 'mars') }
sub venus { __PACKAGE__->new(role => 'venus') }
1;
my $pleroma = Aion::Pleroma->new;
$pleroma->autoware('Ex::Eon::AstroEon')->get('Ex::Eon::AstroEon')->role # => upiter
$pleroma->autoware('Ex::Eon::AstroEon#mars', 'ex.mars')->get('ex.mars')->role # => mars
$pleroma->autoware('Ex::Eon::AstroEon#venus')->get('Ex::Eon::AstroEon#venus')->role # => venus
$pleroma->autoware({class => 'Ex::Eon::AstroEon', method => 'venus'})->get('Ex::Eon::AstroEon#venus')->role # => venus
$pleroma->autoware('Ex::Eon::AstroEon')->get('Ex::Eon::AstroEon')->role # => upiter
$pleroma->autoware('Ex::Eon::AstroEon#mars', 'Ex::Eon::AstroEon#venus') # @-> Added eon Ex::Eon::AstroEon#venus twice, with Ex::Eon::AstroEon#mars ne Ex::Eon::AstroEon#venus
AUTHOR
Yaroslav O. Kosmina mailto:dart@cpan.org
LICENSE
⚖ GPLv3
COPYRIGHT
The Aion::Pleroma module is copyright © 2025 Yaroslav O. Kosmina. Rusland. All rights reserved.