NAME
Aion - a postmodern object system for Perl 5, such as “Mouse”, “Moose”, “Moo”, “Mo” and “M”, but with improvements
VERSION
0.3
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.