Aion

 view release on metacpan or  search on metacpan

LICENSE  view on Meta::CPAN

    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an

i18n/Aion/Types.ru-en.po  view on Meta::CPAN

msgid "Подпрограмма с телом."
msgstr "Subroutine with body."

msgid "Подпрограмма без тела."
msgstr "Subroutine without body."

msgid "Ссылка на подпрограмму с соответствующей сигнатурой."
msgstr "A link to a subroutine with the corresponding signature."

msgid "Подпрограммы без тела не оборачиваются в обработчик сигнатуры, а сигнатура запоминается для валидации соответствия впоследствии оÐ...
msgstr "Subroutines without a body are not wrapped in a signature handler, and the signature is remembered to validate the conformity of a subsequently declared subroutine with a body. Therefore the function has no signature."

msgid "Регулярное выражение."
msgstr "Regular expression."

msgid "Ссылка на скаляр или ссылка на ссылку."
msgstr "A reference to a scalar or a reference to a reference."

msgid "Ссылка на скаляр."
msgstr "Reference to a scalar."

lib/Aion.pm  view on Meta::CPAN

}

# isa => Type
sub isa_aspect {
	my ($isa, $feature) = @_;
	my ($construct, $name) = @$feature{qw/construct name/};
	die "has: $name - isa maybe Aion::Type" unless UNIVERSAL::isa($isa, 'Aion::Type');

	$feature->{isa} = $isa;

	$construct->add_release("${\$feature->meta}\{isa}->validate(\$val, 'Get feature $name');") if ISA =~ /ro|rw/;

	$construct->add_preset("${\$feature->meta}\{isa}->validate(\$val, 'Set feature $name');") if ISA =~ /wo|rw/;
}

# coerce => 1
sub coerce_aspect {
	my ($coerce, $feature) = @_;

	return unless $coerce;

	die "coerce: isa not present!" unless $feature->{isa};

lib/Aion.pm  view on Meta::CPAN

sub default_aspect {
	my ($default, $feature) = @_;

	my $name = $feature->name;
	my $default_is_code = ref $default eq "CODE";

	if($default_is_code) {
		$feature->{builder} = $default;
	} else {
		$feature->{default} = $default;
		$feature->{opt}{isa}->validate($default, $name) if $feature->{opt}{isa};
	}

	if($feature->{opt}{lazy} // $default_is_code) {
		$feature->{lazy} = 1;

		if ($default_is_code) {
			$feature->construct->add_access("unless(%(has)s) {
				my \$val = ${\$feature->meta}\{builder}->(\$self);
				%(write)s
			}");

lib/Aion/Meta/Subroutine.pm  view on Meta::CPAN

	my $return_of_meth = "Return of method `$subname`";

	my @signature = @$signature;
	my $ret = pop @signature;

	my ($ret_array, $ret_scalar) = exists $ret->{is_wantarray}? @{$ret->{args}}: (Tuple([$ret]), $ret);

	my $args = Tuple(\@signature);

	my $sub = sub {
		$args->validate(\@_, $args_of_meth);
		wantarray? do {
			my @returns = $referent->(@_);
			$ret_array->validate(\@returns, $returns_of_meth);
			@returns
		}: do {
			my $return = $referent->(@_);
			$ret_scalar->validate($return, $return_of_meth);
			$return
		}
	};

	Sub::Util::set_prototype Sub::Util::prototype($referent), $sub;
	Sub::Util::set_subname Sub::Util::subname($referent), $sub;
	
	*{"$pkg\::$subname"} = $sub if $subname ne '__ANON__';
	
	$self->{wrapsub} = $sub;

lib/Aion/Type.md  view on Meta::CPAN

"a"  ~~ $IntOrChar # => 1
"ab" ~~ $IntOrChar # -> ""

my $Digit = $Int & $Char;
7  ~~ $Digit # => 1
77 ~~ $Digit # -> ""

"a" ~~ ~$Int; # => 1
5   ~~ ~$Int; # -> ""

eval { $Int->validate("a", "..Eval..") }; $@	# ~> ..Eval.. must have the type Int. The it is 'a'
```

# DESCRIPTION

Порождает валидаторы. Используется в `Aion::Types::subtype`.

# METHODS

## new (%ARGUMENTS)

lib/Aion/Type.md  view on Meta::CPAN


my $Num = Aion::Type->new(name => "Num", message => sub {
	"Error: $_ is'nt $Aion::Type::SELF->{N}!"
});

$Num->detail("x", "car") # => Error: x is'nt car!
```

`$Aion::Type::SELF->{N}` equivalent to `N` in context of `Aion::Types`.

## validate ($element, $feature)

Проверяет `$element` и выбрасывает сообщение `detail`, если элемент не принадлежит классу.

```perl
my $PositiveInt = Aion::Type->new(
	name => "PositiveInt",
	test => sub { /^\d+$/ },
);

eval {
	$PositiveInt->validate(-1, "Neg")
};
$@ # ~> Neg must have the type PositiveInt. The it is -1
```

## val_to_str ($val)

Переводит `$val` в строку.

```perl
Aion::Type->new->val_to_str([1,2,{x=>6}]) # => [1, 2, {x => 6}]

lib/Aion/Type.pm  view on Meta::CPAN

	(my $self, local $_, my $name) = @_;
	local $Aion::Type::SELF = $self;
	local $Aion::Type::SELF->{N} = $name;
	$self->{message}? $self->{message}->():
		"$name must have the type $self. The it is ${\
			Aion::Meta::Util::val_to_str($_)
		}!"
}

# Валидировать значение в параметре
sub validate {
	(my $self, local $_, my $name) = @_;
	die $self->detail($_, $name) if !$self->test;
	$_
}

# Преобразовать значение в строку
sub val_to_str {
	my ($self, $val) = @_;
	Aion::Meta::Util::val_to_str($val)
}

lib/Aion/Type.pm  view on Meta::CPAN

	"a"  ~~ $IntOrChar # => 1
	"ab" ~~ $IntOrChar # -> ""
	
	my $Digit = $Int & $Char;
	7  ~~ $Digit # => 1
	77 ~~ $Digit # -> ""
	
	"a" ~~ ~$Int; # => 1
	5   ~~ ~$Int; # -> ""
	
	eval { $Int->validate("a", "..Eval..") }; $@	# ~> ..Eval.. must have the type Int. The it is 'a'

=head1 DESCRIPTION

Spawns validators. Used in C<Aion::Types::subtype>.

=head1 METHODS

=head2 new (%ARGUMENTS)

Constructor.

lib/Aion/Type.pm  view on Meta::CPAN

	$Int->detail(-5, "Feature car") # => Feature car must have the type Int. The it is -5!
	
	my $Num = Aion::Type->new(name => "Num", message => sub {
		"Error: $_ is'nt $Aion::Type::SELF->{N}!"
	});
	
	$Num->detail("x", "car") # => Error: x is'nt car!

C<< $Aion::Type::SELF-E<gt>{N} >> equivalent to C<N> in context of C<Aion::Types>.

=head2 validate ($element, $feature)

Checks C<$element> and throws a C<detail> message if the element does not belong to the class.

	my $PositiveInt = Aion::Type->new(
		name => "PositiveInt",
		test => sub { /^\d+$/ },
	);
	
	eval {
		$PositiveInt->validate(-1, "Neg")
	};
	$@ # ~> Neg must have the type PositiveInt. The it is -1

=head2 val_to_str ($val)

Converts C<$val> to a string.

	Aion::Type->new->val_to_str([1,2,{x=>6}]) # => [1, 2, {x => 6}]

=head2 instanceof ($type)

lib/Aion/Types.md  view on Meta::CPAN

use Aion::Types;

BEGIN {
	subtype SpeakOfKitty => as StrMatch[qr/\bkitty\b/i],
		message { "Speak is'nt included kitty!" };
}

"Kitty!" ~~ SpeakOfKitty # -> 1
"abc"    ~~ SpeakOfKitty # -> ""

SpeakOfKitty->validate("abc", "This") # @-> Speak is'nt included kitty!


BEGIN {
	subtype IntOrArrayRef => as (Int | ArrayRef);
}

[] ~~ IntOrArrayRef  # -> 1
35 ~~ IntOrArrayRef  # -> 1
"" ~~ IntOrArrayRef  # -> ""

lib/Aion/Types.md  view on Meta::CPAN


Создаёт новый тип.

```perl
BEGIN {
	subtype One => where { $_ == 1 } message { "Actual 1 only!" };
}

1 ~~ One	 # -> 1
0 ~~ One	 # -> ""
eval { One->validate(0) }; $@ # ~> Actual 1 only!
```

`where` и `message` — это синтаксический сахар, а `subtype` можно использовать без них.

```perl
BEGIN {
	subtype Many => (where => sub { $_ > 1 });
}

2 ~~ Many  # -> 1

lib/Aion/Types.md  view on Meta::CPAN


Используется с `subtype` для расширения создаваемого типа `$super_type`.

## init_where ($code)

Инициализирует тип с новыми аргументами. Используется с `subtype`.

```perl
BEGIN {
	subtype 'LessThen[A]',
		init_where { Num->validate(A, "Argument LessThen[A]") }
		where { $_ < A };
}

eval { LessThen["string"] }; $@  # ~> Argument LessThen\[A\]

5 ~~ LessThen[5]  # -> ""
```

## where ($code)

lib/Aion/Types.pm  view on Meta::CPAN

	subtype "Control", as &Any;
		subtype "Union[A, B...]", as &Control,
			where { my $val = $_; any { $_->include($val) } ARGS };
		subtype "Intersection[A, B...]", as &Control,
			where { my $val = $_; all { $_->include($val) } ARGS };
		subtype "Exclude[A, B...]", as &Control,
			where { my $val = $_; !any { $_->include($val) } ARGS };
		subtype "Option[A]", as &Control,
			init_where {
				SELF->{is_option} = 1;
				Tuple([Object(["Aion::Type"])])->validate(scalar ARGS, "Arguments Option[A]")
			}
			where { A->test };
		subtype "Wantarray[A, S]", as &Control,
			init_where {
				SELF->{is_wantarray} = 1;
				Tuple([Object(["Aion::Type"]), Object(["Aion::Type"])])->validate(scalar ARGS, "Arguments Wantarray[A, S]")
			}
			where { ... };


	subtype "Item", as &Any;
		subtype "Bool", as &Item, where { ref $_ eq "" and /^(1|0|)\z/ };
		subtype "BoolLike", as &Item, where {
			return 1 if overload::Method($_, 'bool');
			my $m = overload::Method($_, '0+');
			Bool()->include($m ? $m->($_) : $_) };

lib/Aion/Types.pm  view on Meta::CPAN

					where {
						my ($K, $V) = ARGS;
						while(my ($k, $v) = each %$_) {
							return "" unless $K->include($k) && $V->include($v);
						}
						return 1;
					};

				my $tuple_args = ArrayRef([Object(['Aion::Type'])]);
				subtype "Tuple[A...]", as &ArrayRef,
					init_where { $tuple_args->validate(scalar ARGS, "Arguments Tuple[A...]") }
					where {
						my $k = 0;
						for my $A (ARGS) {
							return "" if $A->exclude($_->[$k++]);
						}
						$k == @$_
					};
				subtype "CycleTuple[A...]", as &ArrayRef,
					init_where { $tuple_args->validate(scalar ARGS, "Arguments CycleTuple[A...]") }
					where {
						my $k = 0;
						while($k < @$_) {
							for my $A (ARGS) {
								return "" if $A->exclude($_->[$k++]);
							}
						}
						$k == @$_
					};
				my $dict_args = CycleTuple([&Str, Object(['Aion::Type'])]);
				subtype "Dict[k => A, ...]", as &HashRef,
					init_where { $dict_args->validate(scalar ARGS, "Arguments Dict[k => A, ...]") }
					where {
						my $count = 0; my $k;
						for my $A (ARGS) {
							$k = $A, next unless ref $A;
							if(exists $_->{$k}) {
								return "" if $A->exclude($_->{$k});
								$count++;
							} else {
								return "" if !exists $A->{is_option};
							}

lib/Aion/Types.pm  view on Meta::CPAN

	use Aion::Types;
	
	BEGIN {
		subtype SpeakOfKitty => as StrMatch[qr/\bkitty\b/i],
			message { "Speak is'nt included kitty!" };
	}
	
	"Kitty!" ~~ SpeakOfKitty # -> 1
	"abc"    ~~ SpeakOfKitty # -> ""
	
	SpeakOfKitty->validate("abc", "This") # @-> Speak is'nt included kitty!
	
	
	BEGIN {
		subtype IntOrArrayRef => as (Int | ArrayRef);
	}
	
	[] ~~ IntOrArrayRef  # -> 1
	35 ~~ IntOrArrayRef  # -> 1
	"" ~~ IntOrArrayRef  # -> ""
	

lib/Aion/Types.pm  view on Meta::CPAN

=head2 subtype ($name, @paraphernalia)

Creates a new type.

	BEGIN {
		subtype One => where { $_ == 1 } message { "Actual 1 only!" };
	}
	
	1 ~~ One	 # -> 1
	0 ~~ One	 # -> ""
	eval { One->validate(0) }; $@ # ~> Actual 1 only!

C<where> and C<message> are syntactic sugar, and C<subtype> can be used without them.

	BEGIN {
		subtype Many => (where => sub { $_ > 1 });
	}
	
	2 ~~ Many  # -> 1
	
	eval { subtype Many => (where1 => sub { $_ > 1 }) }; $@ # ~> subtype Many unused keys left: where1

lib/Aion/Types.pm  view on Meta::CPAN

=head2 as ($super_type)

Used with C<subtype> to extend the created C<$super_type> type.

=head2 init_where ($code)

Initializes a type with new arguments. Used with C<subtype>.

	BEGIN {
		subtype 'LessThen[A]',
			init_where { Num->validate(A, "Argument LessThen[A]") }
			where { $_ < A };
	}
	
	eval { LessThen["string"] }; $@  # ~> Argument LessThen\[A\]
	
	5 ~~ LessThen[5]  # -> ""

=head2 where ($code)

Uses C<$code> as a test. The value for the test is passed to C<$_>.

lib/Aion/Types.pm  view on Meta::CPAN

=head2 Isa[A...]

A link to a subroutine with the corresponding signature.

	sub sig_ex :Isa(Int => Str) {}
	
	\&sig_ex ~~ Isa[Int => Str]        # -> 1
	\&sig_ex ~~ Isa[Int => Str => Num] # -> ""
	\&sig_ex ~~ Isa[Int => Num]        # -> ""

Subroutines without a body are not wrapped in a signature handler, and the signature is remembered to validate the conformity of a subsequently declared subroutine with a body. Therefore the function has no signature.

	sub unreachable_sig_ex :Isa(Int => Str);
	
	\&unreachable_sig_ex ~~ Isa[Int => Str] # -> ""

=head2 RegexpRef

Regular expression.

	qr// ~~ RegexpRef # -> 1

t/aion/type.t  view on Meta::CPAN

::is scalar do {"a"  ~~ $IntOrChar}, "1", '"a"  ~~ $IntOrChar # => 1';
::is scalar do {"ab" ~~ $IntOrChar}, scalar do{""}, '"ab" ~~ $IntOrChar # -> ""';

my $Digit = $Int & $Char;
::is scalar do {7  ~~ $Digit}, "1", '7  ~~ $Digit # => 1';
::is scalar do {77 ~~ $Digit}, scalar do{""}, '77 ~~ $Digit # -> ""';

::is scalar do {"a" ~~ ~$Int;}, "1", '"a" ~~ ~$Int; # => 1';
::is scalar do {5   ~~ ~$Int;}, scalar do{""}, '5   ~~ ~$Int; # -> ""';

::like scalar do {eval { $Int->validate("a", "..Eval..") }; $@}, qr{..Eval.. must have the type Int. The it is 'a'}, 'eval { $Int->validate("a", "..Eval..") }; $@	# ~> ..Eval.. must have the type Int. The it is \'a\'';

# 
# # DESCRIPTION
# 
# Порождает валидаторы. Используется в `Aion::Types::subtype`.
# 
# # METHODS
# 
# ## new (%ARGUMENTS)
# 

t/aion/type.t  view on Meta::CPAN


my $Num = Aion::Type->new(name => "Num", message => sub {
	"Error: $_ is'nt $Aion::Type::SELF->{N}!"
});

::is scalar do {$Num->detail("x", "car")}, "Error: x is'nt car!", '$Num->detail("x", "car") # => Error: x is\'nt car!';

# 
# `$Aion::Type::SELF->{N}` equivalent to `N` in context of `Aion::Types`.
# 
# ## validate ($element, $feature)
# 
# Проверяет `$element` и выбрасывает сообщение `detail`, если элемент не принадлежит классу.
# 
::done_testing; }; subtest 'validate ($element, $feature)' => sub { 
my $PositiveInt = Aion::Type->new(
	name => "PositiveInt",
	test => sub { /^\d+$/ },
);

eval {
	$PositiveInt->validate(-1, "Neg")
};
::like scalar do {$@}, qr{Neg must have the type PositiveInt. The it is -1}, '$@ # ~> Neg must have the type PositiveInt. The it is -1';

# 
# ## val_to_str ($val)
# 
# Переводит `$val` в строку.
# 
::done_testing; }; subtest 'val_to_str ($val)' => sub { 
::is scalar do {Aion::Type->new->val_to_str([1,2,{x=>6}])}, "[1, 2, {x => 6}]", 'Aion::Type->new->val_to_str([1,2,{x=>6}]) # => [1, 2, {x => 6}]';

t/aion/types.t  view on Meta::CPAN

use Aion::Types;

BEGIN {
	subtype SpeakOfKitty => as StrMatch[qr/\bkitty\b/i],
		message { "Speak is'nt included kitty!" };
}

::is scalar do {"Kitty!" ~~ SpeakOfKitty}, scalar do{1}, '"Kitty!" ~~ SpeakOfKitty # -> 1';
::is scalar do {"abc"    ~~ SpeakOfKitty}, scalar do{""}, '"abc"    ~~ SpeakOfKitty # -> ""';

eval {SpeakOfKitty->validate("abc", "This")}; ok defined($@), 'SpeakOfKitty->validate("abc", "This") # @-> Speak is\'nt included kitty!'; ::cmp_ok $@, '=~', '^' . quotemeta 'Speak is\'nt included kitty!', 'SpeakOfKitty->validate("abc", "This") # @-> ...


BEGIN {
	subtype IntOrArrayRef => as (Int | ArrayRef);
}

::is scalar do {[] ~~ IntOrArrayRef}, scalar do{1}, '[] ~~ IntOrArrayRef  # -> 1';
::is scalar do {35 ~~ IntOrArrayRef}, scalar do{1}, '35 ~~ IntOrArrayRef  # -> 1';
::is scalar do {"" ~~ IntOrArrayRef}, scalar do{""}, '"" ~~ IntOrArrayRef  # -> ""';

t/aion/types.t  view on Meta::CPAN

# 
# Создаёт новый тип.
# 
::done_testing; }; subtest 'subtype ($name, @paraphernalia)' => sub { 
BEGIN {
	subtype One => where { $_ == 1 } message { "Actual 1 only!" };
}

::is scalar do {1 ~~ One}, scalar do{1}, '1 ~~ One	 # -> 1';
::is scalar do {0 ~~ One}, scalar do{""}, '0 ~~ One	 # -> ""';
::like scalar do {eval { One->validate(0) }; $@}, qr{Actual 1 only\!}, 'eval { One->validate(0) }; $@ # ~> Actual 1 only!';

# 
# `where` и `message` — это синтаксический сахар, а `subtype` можно использовать без них.
# 

BEGIN {
	subtype Many => (where => sub { $_ > 1 });
}

::is scalar do {2 ~~ Many}, scalar do{1}, '2 ~~ Many  # -> 1';

t/aion/types.t  view on Meta::CPAN

# 
# Используется с `subtype` для расширения создаваемого типа `$super_type`.
# 
# ## init_where ($code)
# 
# Инициализирует тип с новыми аргументами. Используется с `subtype`.
# 
::done_testing; }; subtest 'init_where ($code)' => sub { 
BEGIN {
	subtype 'LessThen[A]',
		init_where { Num->validate(A, "Argument LessThen[A]") }
		where { $_ < A };
}

::like scalar do {eval { LessThen["string"] }; $@}, qr{Argument LessThen\[A\]}, 'eval { LessThen["string"] }; $@  # ~> Argument LessThen\[A\]';

::is scalar do {5 ~~ LessThen[5]}, scalar do{""}, '5 ~~ LessThen[5]  # -> ""';

# 
# ## where ($code)
# 



( run in 1.488 second using v1.01-cache-2.11-cpan-2e29ac893d0 )