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