NAME
Aion - a postmodern object system for Perl 5, such as “Mouse”, “Moose”, “Moo”, “Mo” and “M”, but with improvements
VERSION
0.4
SYNOPSIS
package Calc {
use Aion;
has a => (is => 'ro+', isa => Num);
has b => (is => 'ro+', isa => Num);
has op => (is => 'ro', isa => Enum[qw/+ - * \/ **/], default => '+');
sub result : Isa(Object => Num) {
my ($self) = @_;
eval "${\ $self->a} ${\ $self->op} ${\ $self->b}"
}
}
Calc->new(a => 1.1, b => 2)->result # => 3.1
DESCRIPTION
Aion is OOP-framework for creating classes with features, has aspects, roles and so on.
The properties declared through HAS are called features.
And is
,isa
, default
, and so on inhas
are called aspects.
In addition to standard aspects, roles can add their own aspects using the aspect subprogram.
The signature of the methods can be checked using the attribute :Isa(...)
.
SUBROUTINES IN CLASSES AND ROLES
Use Aion
imports types from the moduleAion::Types
and the following subprograms:
has ($name, %aspects)
Creates a method for obtaining/setting the function (properties) of the class.
lib/Animal.pm file:
package Animal;
use Aion;
has type => (is => 'ro+', isa => Str);
has name => (is => 'rw-', isa => Str, default => 'murka');
1;
use lib "lib";
use Animal;
my $cat = Animal->new(type => 'cat');
$cat->type # => cat
$cat->name # => murka
$cat->name("murzik");
$cat->name # => murzik
with
Adds to the module of the role. For each role, the import_with
method is called.
File lib/Role/Keys/Stringify.pm:
package Role::Keys::Stringify;
use Aion -role;
sub keysify {
my ($self) = @_;
join ", ", sort keys %$self;
}
1;
File lib/Role/Values/Stringify.pm:
package Role::Values::Stringify;
use Aion -role;
sub valsify {
my ($self) = @_;
join ", ", map $self->{$_}, sort keys %$self;
}
1;
File lib/Class/All/Stringify.pm:
package Class::All::Stringify;
use Aion;
with q/Role::Keys::Stringify/;
with q/Role::Values::Stringify/;
has [qw/key1 key2/] => (is => 'rw', isa => Str);
1;
use lib "lib";
use Class::All::Stringify;
my $s = Class::All::Stringify->new(key1=>"a", key2=>"b");
$s->keysify # => key1, key2
$s->valsify # => a, b
isa ($package)
Checks that $package
is a super class for a given or this class itself.
package Ex::X { use Aion; }
package Ex::A { use Aion; extends q/Ex::X/; }
package Ex::B { use Aion; }
package Ex::C { use Aion; extends qw/Ex::A Ex::B/ }
Ex::C->isa("Ex::A") # -> 1
Ex::C->isa("Ex::B") # -> 1
Ex::C->isa("Ex::X") # -> 1
Ex::C->isa("Ex::X1") # -> ""
Ex::A->isa("Ex::X") # -> 1
Ex::A->isa("Ex::A") # -> 1
Ex::X->isa("Ex::X") # -> 1
does ($package)
Checks that $package
is a role that is used in a class or another role.
package Role::X { use Aion -role; }
package Role::A { use Aion; with qw/Role::X/; }
package Role::B { use Aion; }
package Ex::Z { use Aion; with qw/Role::A Role::B/ }
Ex::Z->does("Role::A") # -> 1
Ex::Z->does("Role::B") # -> 1
Ex::Z->does("Role::X") # -> 1
Role::A->does("Role::X") # -> 1
Role::A->does("Role::X1") # -> ""
Ex::Z->does("Ex::Z") # -> ""
aspect ($aspect => sub { ... })
Adds the aspect to has
in the current class and its classroom classes or the current role and applies its classes.
package Example::Earth {
use Aion;
aspect lvalue => sub {
my ($cls, $name, $value, $construct, $feature) = @_;
$construct->{attr} .= ":lvalue";
};
has moon => (is => "rw", lvalue => 1);
}
my $earth = Example::Earth->new;
$earth->moon = "Mars";
$earth->moon # => Mars
The aspect is called every time it is indicated in has
.
The creator of the aspect has the parameters:
$cls
- a bag withhas
.$name
is the name of feature.$value
is the meaning of the aspect.$construct
- a hash with fragments of the code for joining the object method.$feature
- a hash describing a feature.
package Example::Mars {
use Aion;
aspect lvalue => sub {
my ($cls, $name, $value, $construct, $feature) = @_;
$construct->{attr} .= ":lvalue";
$cls # => Example::Mars
$name # => moon
$value # -> 1
[sort keys %$construct] # --> [qw/attr eval get name pkg ret set sub/]
[sort keys %$feature] # --> [qw/construct has name opt order/]
my $_construct = {
pkg => $cls,
name => $name,
attr => ':lvalue',
eval => 'package %(pkg)s {
%(sub)s
}',
sub => 'sub %(name)s%(attr)s {
if(@_>1) {
my ($self, $val) = @_;
%(set)s%(ret)s
} else {
my ($self) = @_;
%(get)s
}
}',
get => '$self->{%(name)s}',
set => '$self->{%(name)s} = $val',
ret => '; $self',
};
$construct # --> $_construct
my $_feature = {
has => [is => "rw", lvalue => 1],
opt => {
is => "rw",
lvalue => 1,
},
name => $name,
construct => $_construct,
order => 0,
};
$feature # --> $_feature
};
has moon => (is => "rw", lvalue => 1);
}
SUBROUTINES IN CLASSES
extends (@superclasses)
Expands the class with another class/classes. It causes from each inherited class the method of import_extends
, if it is in it.
package World { use Aion;
our $extended_by_this = 0;
sub import_extends {
my ($class, $extends) = @_;
$extended_by_this ++;
$class # => World
$extends # => Hello
}
}
package Hello { use Aion;
extends q/World/;
$World::extended_by_this # -> 1
}
Hello->isa("World") # -> 1
new (%param)
The constructor.
Installs
%param
for features.Checks that the parameters correspond to the features.
Sets default values.
package NewExample { use Aion;
has x => (is => 'ro', isa => Num);
has y => (is => 'ro+', isa => Num);
has z => (is => 'ro-', isa => Num);
}
eval { NewExample->new(f => 5) }; $@ # ~> f is not feature!
eval { NewExample->new(n => 5, r => 6) }; $@ # ~> n, r is not features!
eval { NewExample->new }; $@ # ~> Feature y is required!
eval { NewExample->new(z => 10) }; $@ # ~> Feature z cannot set in new!
my $ex = NewExample->new(y => 8);
eval { $ex->x }; $@ # ~> Get feature `x` must have the type Num. The it is undef
$ex = NewExample->new(x => 10.1, y => 8);
$ex->x # -> 10.1
SUBROUTINES IN ROLES
requires (@subroutine_names)
Checks that in classes using this role there are these subprograms or features.
package Role::Alpha { use Aion -role;
sub in {
my ($self, $s) = @_;
$s =~ /[${\ $self->abc }]/
}
requires qw/abc/;
}
eval { package Omega1 { use Aion; with Role::Alpha; } }; $@ # ~> abc requires!
package Omega { use Aion;
with Role::Alpha;
sub abc { "abc" }
}
Omega->new->in("a") # -> 1
METHODS
has ($feature)
Checks that the property is established.
Features having default => sub {...}
perform sub
during the first call of the Getter, that is: are delayed.
$object->has('feature')
allows you to check that default
has not yet been called.
package ExHas { use Aion;
has x => (is => 'rw');
}
my $ex = ExHas->new;
$ex->has("x") # -> ""
$ex->x(10);
$ex->has("x") # -> 1
clear (@features)
He removes the keys of the feature from the object by previously calling them clearer
(if exists).
package ExClearer { use Aion;
has x => (is => 'rw');
has y => (is => 'rw');
}
my $c = ExClearer->new(x => 10, y => 12);
$c->has("x") # -> 1
$c->has("y") # -> 1
$c->clear(qw/x y/);
$c->has("x") # -> ""
$c->has("y") # -> ""
METHODS IN CLASSES
Use Aion
includes the following methods in the module:
new (%parameters)
The constructor.
ASPECTS
use Aion
includes the following aspects in the module for use in has
:
is => $permissions
ro
- create only a gutter.wo
- create only a setter.rw
- Create getter and setter.
By default - rw
.
Additional permits:
+
- feature is required in the parameters of the designer.+
is not used with-
.-
- a feature cannot be installed through the constructor. '-' is not used with+
.*
- do not increase the counter of links to the value (applyweaken
to the value after installing it in a feature).
package ExIs { use Aion;
has rw => (is => 'rw');
has ro => (is => 'ro+');
has wo => (is => 'wo-');
}
eval { ExIs->new }; $@ # ~> \* Feature ro is required!
eval { ExIs->new(ro => 10, wo => -10) }; $@ # ~> \* Feature wo cannot set in new!
ExIs->new(ro => 10);
ExIs->new(ro => 10, rw => 20);
ExIs->new(ro => 10)->ro # -> 10
ExIs->new(ro => 10)->wo(30)->has("wo") # -> 1
eval { ExIs->new(ro => 10)->wo }; $@ # ~> has: wo is wo- \(not get\)
ExIs->new(ro => 10)->rw(30)->rw # -> 30
The function with *
does not hold the meaning:
package Node { use Aion;
has parent => (is => "rw*", isa => Maybe[Object["Node"]]);
}
my $root = Node->new;
my $node = Node->new(parent => $root);
$node->parent->parent # -> undef
undef $root;
$node->parent # -> undef
# And by setter:
$node->parent($root = Node->new);
$node->parent->parent # -> undef
undef $root;
$node->parent # -> undef
isa => $type
Indicates the type, or rather - a validator, feature.
package ExIsa { use Aion;
has x => (is => 'ro', isa => Int);
}
eval { ExIsa->new(x => 'str') }; $@ # ~> \* Feature x must have the type Int. The it is 'str'
eval { ExIsa->new->x }; $@ # ~> Get feature `x` must have the type Int. The it is undef
ExIsa->new(x => 10)->x # -> 10
For a list of validators, see Aion:::Type.
default => $value
The default value is set in the designer if there is no parameter with the name of the feature.
package ExDefault { use Aion;
has x => (is => 'ro', default => 10);
}
ExDefault->new->x # -> 10
ExDefault->new(x => 20)->x # -> 20
If $ Value
is a subprogram, then the subprogram is considered a designer of the meaning of the feature. Lazy calculation is used.
my $count = 10;
package ExLazy { use Aion;
has x => (default => sub {
my ($self) = @_;
++$count
});
}
my $ex = ExLazy->new;
$count # -> 10
$ex->x # -> 11
$count # -> 11
$ex->x # -> 11
$count # -> 11
trigger => $sub
$sub
is called after installing the property in the constructor (new
) or through the setter. Etymology - let in.
package ExTrigger { use Aion;
has x => (trigger => sub {
my ($self, $old_value) = @_;
$self->y($old_value + $self->x);
});
has y => ();
}
my $ex = ExTrigger->new(x => 10);
$ex->y # -> 10
$ex->x(20);
$ex->y # -> 30
release => $sub
$sub
is called before returning the property from the object through the gutter. Etymology - release.
package ExRelease { use Aion;
has x => (release => sub {
my ($self, $value) = @_;
$_[1] = $value + 1;
});
}
my $ex = ExRelease->new(x => 10);
$ex->x # -> 11
clearer => $sub
$sub
is called when the deructor is called or$object->clear("feature") ``, but only if there is a property (see
$object->has(" feature ")`).
package ExClearer { use Aion;
our $x;
has x => (clearer => sub {
my ($self) = @_;
$x = $self->x
});
}
$ExClearer::x # -> undef
ExClearer->new(x => 10);
$ExClearer::x # -> 10
my $ex = ExClearer->new(x => 12);
$ExClearer::x # -> 10
$ex->clear('x');
$ExClearer::x # -> 12
undef $ex;
$ExClearer::x # -> 12
ATTRIBUTES
Aion
adds universal attributes to the package.
Isa (@signature)
The attribute Isa
checks the signature of the function.
Attention: Using the Isa
attribute slows down the program.
COUNCIL: The use of the Isa
aspect for objects is more than enough to check the correctness of the object data.
package Anim { use Aion;
sub is_cat : Isa(Object => Str => Bool) {
my ($self, $anim) = @_;
$anim =~ /(cat)/
}
}
my $anim = Anim->new;
$anim->is_cat('cat') # -> 1
$anim->is_cat('dog') # -> ""
eval { Anim->is_cat("cat") }; $@ # ~> Arguments of method `is_cat` must have the type Tuple\[Object, Str\].
eval { my @items = $anim->is_cat("cat") }; $@ # ~> Returns of method `is_cat` must have the type Tuple\[Bool\].
AUTHOR
Yaroslav O. Kosmina mailto:dart@cpan.org
LICENSE
⚖ GPLv3
COPYRIGHT
The Aion Module Is Copyright © 2023 Yaroslav O. Kosmina. Rusland. All Rights Reserved.