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 with has.

  • $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 (apply weaken 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.