Initial Commit

This commit is contained in:
Riley Schneider
2025-12-03 16:38:10 +01:00
parent c5e26bf594
commit b732d8d4b5
17680 changed files with 5977495 additions and 2 deletions

View File

@@ -0,0 +1,150 @@
package Specio::Constraint::AnyCan;
use strict;
use warnings;
our $VERSION = '0.46';
use List::Util 1.33 ();
use Role::Tiny::With;
use Scalar::Util ();
use Specio::Helpers qw( perlstring );
use Specio::Library::Builtins;
use Specio::OO;
use Specio::Constraint::Role::CanType;
with 'Specio::Constraint::Role::CanType';
{
my $Defined = t('Defined');
sub _build_parent {$Defined}
}
{
my $_inline_generator = sub {
my $self = shift;
my $val = shift;
my $methods = join ', ', map { perlstring($_) } @{ $self->methods };
return sprintf( <<'EOF', $val, $methods );
(
do {
# We need to assign this since if it's something like $_[0] then
# inside the all block @_ gets redefined and we can no longer get at
# the value.
my $v = %s;
(
Scalar::Util::blessed($v) || (
defined($v)
&& !ref($v)
&& length($v)
&& $v !~ /\A
\s*
-?[0-9]+(?:\.[0-9]+)?
(?:[Ee][\-+]?[0-9]+)?
\s*
\z/xs
# Passing a GLOB from (my $glob = *GLOB) gives us a very weird
# scalar. It's not a ref and it has a length but trying to
# call ->can on it throws an exception
&& ref( \$v ) ne 'GLOB'
)
) && List::Util::all { $v->can($_) } %s;
}
)
EOF
};
sub _build_inline_generator {$_inline_generator}
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _allow_classes {1}
## use critic
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class for constraints which require a class name or object with a set of methods
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::AnyCan - A class for constraints which require a class name or object with a set of methods
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $type = Specio::Constraint::AnyCan->new(...);
print $_, "\n" for @{ $type->methods };
=head1 DESCRIPTION
This is a specialized type constraint class for types which require a class
name or object with a defined set of methods.
=head1 API
This class provides all of the same methods as L<Specio::Constraint::Simple>,
with a few differences:
=head2 Specio::Constraint::AnyCan->new( ... )
The C<parent> parameter is ignored if it passed, as it is always set to the
C<Defined> type.
The C<inline_generator> and C<constraint> parameters are also ignored. This
class provides its own default inline generator subroutine reference.
This class overrides the C<message_generator> default if none is provided.
Finally, this class requires an additional parameter, C<methods>. This must be
an array reference of method names which the constraint requires. You can also
pass a single string and it will be converted to an array reference
internally.
=head2 $any_can->methods
Returns an array reference containing the methods this constraint requires.
=head1 ROLES
This class does the L<Specio::Constraint::Role::IsaType>,
L<Specio::Constraint::Role::Interface>, and L<Specio::Role::Inlinable> roles.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,139 @@
package Specio::Constraint::AnyDoes;
use strict;
use warnings;
our $VERSION = '0.46';
use Role::Tiny::With;
use Scalar::Util ();
use Specio::Helpers qw( perlstring );
use Specio::Library::Builtins;
use Specio::OO;
use Specio::Constraint::Role::DoesType;
with 'Specio::Constraint::Role::DoesType';
{
my $Defined = t('Defined');
sub _build_parent {$Defined}
}
{
my $_inline_generator = sub {
my $self = shift;
my $val = shift;
return sprintf( <<'EOF', ($val) x 8, perlstring( $self->role ) );
(
(
Scalar::Util::blessed(%s) || (
!ref(%s)
&& defined(%s)
&& length(%s)
&& %s !~ /\A
\s*
-?[0-9]+(?:\.[0-9]+)?
(?:[Ee][\-+]?[0-9]+)?
\s*
\z/xs
&& ref( \%s ) ne 'GLOB'
)
)
&& %s->can('does')
&& %s->does(%s)
)
EOF
};
sub _build_inline_generator {$_inline_generator}
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _allow_classes {1}
## use critic
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class for constraints which require a class name or an object that does a specific role
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::AnyDoes - A class for constraints which require a class name or an object that does a specific role
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $type = Specio::Constraint::AnyDoes->new(...);
print $type->role;
=head1 DESCRIPTION
This is a specialized type constraint class for types which require a class
name or an object that does a specific role.
=head1 API
This class provides all of the same methods as L<Specio::Constraint::Simple>,
with a few differences:
=head2 Specio::Constraint::AnyDoes->new( ... )
The C<parent> parameter is ignored if it passed, as it is always set to the
C<Defined> type.
The C<inline_generator> and C<constraint> parameters are also ignored. This
class provides its own default inline generator subroutine reference.
This class overrides the C<message_generator> default if none is provided.
Finally, this class requires an additional parameter, C<role>. This must be a
single role name.
=head2 $any_isa->role
Returns the role name passed to the constructor.
=head1 ROLES
This class does the L<Specio::Constraint::Role::DoesType>,
L<Specio::Constraint::Role::Interface>, L<Specio::Role::Inlinable>, and
L<MooseX::Clone> roles.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,142 @@
package Specio::Constraint::AnyIsa;
use strict;
use warnings;
our $VERSION = '0.46';
use Role::Tiny::With;
use Scalar::Util ();
use Specio::Helpers qw( perlstring );
use Specio::Library::Builtins;
use Specio::OO;
use Specio::Constraint::Role::IsaType;
with 'Specio::Constraint::Role::IsaType';
{
my $Defined = t('Defined');
sub _build_parent {$Defined}
}
{
my $_inline_generator = sub {
my $self = shift;
my $val = shift;
return sprintf( <<'EOF', ($val) x 7, perlstring( $self->class ) );
(
(
Scalar::Util::blessed(%s)
|| (
defined(%s)
&& !ref(%s)
&& length(%s)
&& %s !~ /\A
\s*
-?[0-9]+(?:\.[0-9]+)?
(?:[Ee][\-+]?[0-9]+)?
\s*
\z/xs
# Passing a GLOB from (my $glob = *GLOB) gives us a very weird
# scalar. It's not a ref and it has a length but trying to
# call ->can on it throws an exception
&& ref( \%s ) ne 'GLOB'
)
)
&& %s->isa(%s)
)
EOF
};
sub _build_inline_generator {$_inline_generator}
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _allow_classes {1}
## use critic
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class for constraints which require a class name or an object that inherit from a specific class
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::AnyIsa - A class for constraints which require a class name or an object that inherit from a specific class
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $type = Specio::Constraint::AnyIsa->new(...);
print $type->class;
=head1 DESCRIPTION
This is a specialized type constraint class for types which require a class
name or an object that inherit from a specific class.
=head1 API
This class provides all of the same methods as L<Specio::Constraint::Simple>,
with a few differences:
=head2 Specio::Constraint::AnyIsa->new( ... )
The C<parent> parameter is ignored if it passed, as it is always set to the
C<Defined> type.
The C<inline_generator> and C<constraint> parameters are also ignored. This
class provides its own default inline generator subroutine reference.
This class overrides the C<message_generator> default if none is provided.
Finally, this class requires an additional parameter, C<class>. This must be a
single class name.
=head2 $any_isa->class
Returns the class name passed to the constructor.
=head1 ROLES
This class does the L<Specio::Constraint::Role::IsaType>,
L<Specio::Constraint::Role::Interface>, and L<Specio::Role::Inlinable> roles.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,151 @@
package Specio::Constraint::Enum;
use strict;
use warnings;
our $VERSION = '0.46';
use Role::Tiny::With;
use Scalar::Util qw( refaddr );
use Specio::Library::Builtins;
use Specio::OO;
use Storable qw( dclone );
use Specio::Constraint::Role::Interface;
with 'Specio::Constraint::Role::Interface';
{
## no critic (Subroutines::ProtectPrivateSubs)
my $attrs = dclone( Specio::Constraint::Role::Interface::_attrs() );
## use critic
for my $name (qw( parent _inline_generator )) {
$attrs->{$name}{init_arg} = undef;
$attrs->{$name}{builder}
= $name =~ /^_/ ? '_build' . $name : '_build_' . $name;
}
$attrs->{values} = {
isa => 'ArrayRef',
required => 1,
};
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _attrs {
return $attrs;
}
}
{
my $Str = t('Str');
sub _build_parent {$Str}
}
{
my $_inline_generator = sub {
my $self = shift;
my $val = shift;
return sprintf( <<'EOF', ($val) x 2, $self->_env_var_name, $val );
( !ref( %s ) && defined( %s ) && $%s{ %s } )
EOF
};
sub _build_inline_generator {$_inline_generator}
}
sub _build_inline_environment {
my $self = shift;
my %values = map { $_ => 1 } @{ $self->values };
return { '%' . $self->_env_var_name => \%values };
}
sub _env_var_name {
my $self = shift;
return '_Specio_Constraint_Enum_' . refaddr($self);
}
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class for constraints which require a string matching one of a set of values
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::Enum - A class for constraints which require a string matching one of a set of values
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $type = Specio::Constraint::Enum->new(...);
print $_, "\n" for @{ $type->values };
=head1 DESCRIPTION
This is a specialized type constraint class for types which require a string
that matches one of a list of values.
=head1 API
This class provides all of the same methods as L<Specio::Constraint::Simple>,
with a few differences:
=head2 Specio::Constraint::Enum->new( ... )
The C<parent> parameter is ignored if it passed, as it is always set to the
C<Str> type.
The C<inline_generator> and C<constraint> parameters are also ignored. This
class provides its own default inline generator subroutine reference.
Finally, this class requires an additional parameter, C<values>. This must be a
an arrayref of valid strings for the type.
=head2 $enum->values
Returns an array reference of valid values for the type.
=head1 ROLES
This class does the L<Specio::Constraint::Role::Interface> and
L<Specio::Role::Inlinable> roles.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,199 @@
package Specio::Constraint::Intersection;
use strict;
use warnings;
our $VERSION = '0.46';
use List::Util qw( all );
use Role::Tiny::With;
use Specio::OO;
use Storable qw( dclone );
use Specio::Constraint::Role::Interface;
with 'Specio::Constraint::Role::Interface';
{
## no critic (Subroutines::ProtectPrivateSubs)
my $attrs = dclone( Specio::Constraint::Role::Interface::_attrs() );
## use critic
for my $name (qw( _constraint _inline_generator )) {
delete $attrs->{$name}{predicate};
$attrs->{$name}{init_arg} = undef;
$attrs->{$name}{lazy} = 1;
$attrs->{$name}{builder}
= $name =~ /^_/ ? '_build' . $name : '_build_' . $name;
}
delete $attrs->{parent};
delete $attrs->{name}{predicate};
$attrs->{name}{lazy} = 1;
$attrs->{name}{builder} = '_build_name';
$attrs->{of} = {
isa => 'ArrayRef',
required => 1,
};
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _attrs {
return $attrs;
}
}
sub parent {undef}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _has_parent {0}
sub _has_name {
my $self = shift;
return defined $self->name;
}
sub _build_name {
my $self = shift;
return unless all { $_->_has_name } @{ $self->of };
return join q{ & }, map { $_->name } @{ $self->of };
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _has_constraint {
my $self = shift;
return !$self->_has_inline_generator;
}
## use critic
sub _build_constraint {
return $_[0]->_optimized_constraint;
}
sub _build_optimized_constraint {
my $self = shift;
## no critic (Subroutines::ProtectPrivateSubs)
my @c = map { $_->_optimized_constraint } @{ $self->of };
return sub {
return all { $_->( $_[0] ) } @c;
};
}
sub _has_inline_generator {
my $self = shift;
## no critic (Subroutines::ProtectPrivateSubs)
return all { $_->_has_inline_generator } @{ $self->of };
}
sub _build_inline_generator {
my $self = shift;
return sub {
return '(' . (
join q{ && },
map { sprintf( '( %s )', $_->_inline_generator->( $_, $_[1] ) ) }
@{ $self->of }
) . ')';
}
}
sub _build_inline_environment {
my $self = shift;
my %env;
for my $type ( @{ $self->of } ) {
%env = (
%env,
%{ $type->inline_environment },
);
}
return \%env;
}
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class for intersection constraints
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::Intersection - A class for intersection constraints
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $type = Specio::Constraint::Untion->new(...);
=head1 DESCRIPTION
This is a specialized type constraint class for intersections, which will
allow a value which matches each one of several distinct types.
=for Pod::Coverage parent
=head1 API
This class provides all of the same methods as L<Specio::Constraint::Simple>,
with a few differences:
=head2 Specio::Constraint::Intersection->new( ... )
The C<parent> parameter is ignored if it passed, as it is always C<undef>
The C<inline_generator> and C<constraint> parameters are also ignored. This
class provides its own default inline generator subroutine reference.
Finally, this class requires an additional parameter, C<of>. This must be an
arrayref of type objects.
=head2 $union->of
Returns an array reference of the individual types which makes up this
intersection.
=head1 ROLES
This class does the L<Specio::Constraint::Role::Interface> and
L<Specio::Role::Inlinable> roles.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,131 @@
package Specio::Constraint::ObjectCan;
use strict;
use warnings;
our $VERSION = '0.46';
use List::Util 1.33 ();
use Role::Tiny::With;
use Scalar::Util ();
use Specio::Helpers qw( perlstring );
use Specio::Library::Builtins;
use Specio::OO;
use Specio::Constraint::Role::CanType;
with 'Specio::Constraint::Role::CanType';
{
my $Object = t('Object');
sub _build_parent {$Object}
}
{
my $_inline_generator = sub {
my $self = shift;
my $val = shift;
my $methods = join ', ', map { perlstring($_) } @{ $self->methods };
return sprintf( <<'EOF', $val, $methods );
(
do {
my $v = %s;
Scalar::Util::blessed($v)
&& List::Util::all { $v->can($_) } %s;
}
)
EOF
};
sub _build_inline_generator {$_inline_generator}
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _allow_classes {0}
## use critic
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class for constraints which require an object with a set of methods
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::ObjectCan - A class for constraints which require an object with a set of methods
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $type = Specio::Constraint::ObjectCan->new(...);
print $_, "\n" for @{ $type->methods };
=head1 DESCRIPTION
This is a specialized type constraint class for types which require an object
with a defined set of methods.
=head1 API
This class provides all of the same methods as L<Specio::Constraint::Simple>,
with a few differences:
=head2 Specio::Constraint::ObjectCan->new( ... )
The C<parent> parameter is ignored if it passed, as it is always set to the
C<Object> type.
The C<inline_generator> and C<constraint> parameters are also ignored. This
class provides its own default inline generator subroutine reference.
This class overrides the C<message_generator> default if none is provided.
Finally, this class requires an additional parameter, C<methods>. This must be
an array reference of method names which the constraint requires. You can also
pass a single string and it will be converted to an array reference
internally.
=head2 $object_can->methods
Returns an array reference containing the methods this constraint requires.
=head1 ROLES
This class does the L<Specio::Constraint::Role::CanType>,
L<Specio::Constraint::Role::Interface>, and L<Specio::Role::Inlinable> roles.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,121 @@
package Specio::Constraint::ObjectDoes;
use strict;
use warnings;
our $VERSION = '0.46';
use Role::Tiny::With;
use Scalar::Util ();
use Specio::Helpers qw( perlstring );
use Specio::Library::Builtins;
use Specio::OO;
use Specio::Constraint::Role::DoesType;
with 'Specio::Constraint::Role::DoesType';
{
my $Object = t('Object');
sub _build_parent {$Object}
}
{
my $_inline_generator = sub {
my $self = shift;
my $val = shift;
return sprintf( <<'EOF', ($val) x 3, perlstring( $self->role ) );
( Scalar::Util::blessed(%s) && %s->can('does') && %s->does(%s) )
EOF
};
sub _build_inline_generator {$_inline_generator}
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _allow_classes {0}
## use critic
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class for constraints which require an object that does a specific role
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::ObjectDoes - A class for constraints which require an object that does a specific role
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $type = Specio::Constraint::ObjectDoes->new(...);
print $type->role;
=head1 DESCRIPTION
This is a specialized type constraint class for types which require an object
that does a specific role.
=head1 API
This class provides all of the same methods as L<Specio::Constraint::Simple>,
with a few differences:
=head2 Specio::Constraint::ObjectDoes->new( ... )
The C<parent> parameter is ignored if it passed, as it is always set to the
C<Defined> type.
The C<inline_generator> and C<constraint> parameters are also ignored. This
class provides its own default inline generator subroutine reference.
This class overrides the C<message_generator> default if none is provided.
Finally, this class requires an additional parameter, C<role>. This must be a
single role name.
=head2 $object_isa->role
Returns the role name passed to the constructor.
=head1 ROLES
This class does the L<Specio::Constraint::Role::DoesType>,
L<Specio::Constraint::Role::Interface>, and L<Specio::Role::Inlinable> roles.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,121 @@
package Specio::Constraint::ObjectIsa;
use strict;
use warnings;
our $VERSION = '0.46';
use Role::Tiny::With;
use Scalar::Util ();
use Specio::Helpers qw( perlstring );
use Specio::Library::Builtins;
use Specio::OO;
use Specio::Constraint::Role::IsaType;
with 'Specio::Constraint::Role::IsaType';
{
my $Object = t('Object');
sub _build_parent {$Object}
}
{
my $_inline_generator = sub {
my $self = shift;
my $val = shift;
return sprintf( <<'EOF', $val, $val, perlstring( $self->class ) );
( Scalar::Util::blessed( %s ) && %s->isa(%s) )
EOF
};
sub _build_inline_generator {$_inline_generator}
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _allow_classes {0}
## use critic
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class for constraints which require an object that inherits from a specific class
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::ObjectIsa - A class for constraints which require an object that inherits from a specific class
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $type = Specio::Constraint::ObjectIsa->new(...);
print $type->class;
=head1 DESCRIPTION
This is a specialized type constraint class for types which require an object
that inherits from a specific class.
=head1 API
This class provides all of the same methods as L<Specio::Constraint::Simple>,
with a few differences:
=head2 Specio::Constraint::ObjectIsa->new( ... )
The C<parent> parameter is ignored if it passed, as it is always set to the
C<Defined> type.
The C<inline_generator> and C<constraint> parameters are also ignored. This
class provides its own default inline generator subroutine reference.
This class overrides the C<message_generator> default if none is provided.
Finally, this class requires an additional parameter, C<class>. This must be a
single class name.
=head2 $object_isa->class
Returns the class name passed to the constructor.
=head1 ROLES
This class does the L<Specio::Constraint::Role::IsaType>,
L<Specio::Constraint::Role::Interface>, and L<Specio::Role::Inlinable> roles.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,209 @@
package Specio::Constraint::Parameterizable;
use strict;
use warnings;
our $VERSION = '0.46';
use Carp qw( confess );
use Role::Tiny::With;
use Specio::Constraint::Parameterized;
use Specio::DeclaredAt;
use Specio::OO;
use Specio::TypeChecks qw( does_role isa_class );
use Specio::Constraint::Role::Interface;
with 'Specio::Constraint::Role::Interface';
{
## no critic (Subroutines::ProtectPrivateSubs)
my $role_attrs = Specio::Constraint::Role::Interface::_attrs();
## use critic
my $attrs = {
%{$role_attrs},
_parameterized_constraint_generator => {
isa => 'CodeRef',
init_arg => 'parameterized_constraint_generator',
predicate => '_has_parameterized_constraint_generator',
},
_parameterized_inline_generator => {
isa => 'CodeRef',
init_arg => 'parameterized_inline_generator',
predicate => '_has_parameterized_inline_generator',
},
};
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _attrs {
return $attrs;
}
}
sub BUILD {
my $self = shift;
if ( $self->_has_constraint ) {
die
'A parameterizable constraint with a constraint parameter must also have a parameterized_constraint_generator'
unless $self->_has_parameterized_constraint_generator;
}
if ( $self->_has_inline_generator ) {
die
'A parameterizable constraint with an inline_generator parameter must also have a parameterized_inline_generator'
unless $self->_has_parameterized_inline_generator;
}
return;
}
sub parameterize {
my $self = shift;
my %args = @_;
my ( $parameter, $declared_at ) = @args{qw( of declared_at )};
does_role( $parameter, 'Specio::Constraint::Role::Interface' )
or confess
'The "of" parameter passed to ->parameterize must be an object which does the Specio::Constraint::Role::Interface role';
if ($declared_at) {
isa_class( $declared_at, 'Specio::DeclaredAt' )
or confess
'The "declared_at" parameter passed to ->parameterize must be a Specio::DeclaredAt object';
}
$declared_at = Specio::DeclaredAt->new_from_caller(1)
unless defined $declared_at;
my %p = (
parent => $self,
parameter => $parameter,
declared_at => $declared_at,
);
if ( $self->_has_parameterized_constraint_generator ) {
$p{constraint}
= $self->_parameterized_constraint_generator->($parameter);
}
else {
confess
'The "of" parameter passed to ->parameterize must be an inlinable constraint if the parameterizable type has an inline_generator'
unless $parameter->can_be_inlined;
my $ig = $self->_parameterized_inline_generator;
$p{inline_generator} = sub { $ig->( shift, $parameter, @_ ) };
}
return Specio::Constraint::Parameterized->new(%p);
}
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class which represents parameterizable constraints
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::Parameterizable - A class which represents parameterizable constraints
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $arrayref = t('ArrayRef');
my $arrayref_of_int = $arrayref->parameterize( of => t('Int') );
=head1 DESCRIPTION
This class implements the API for parameterizable types like C<ArrayRef> and
C<Maybe>.
=for Pod::Coverage BUILD
=head1 API
This class implements the same API as L<Specio::Constraint::Simple>, with a few
additions.
=head2 Specio::Constraint::Parameterizable->new(...)
This class's constructor accepts two additional parameters:
=over 4
=item * parameterized_constraint_generator
This is a subroutine that generates a new constraint subroutine when the type
is parameterized.
It will be called as a method on the type and will be passed a single
argument, the type object for the type parameter.
This parameter is mutually exclusive with the
C<parameterized_inline_generator> parameter.
=item * parameterized_inline_generator
This is a subroutine that generates a new inline generator subroutine when the
type is parameterized.
It will be called as a method on the L<Specio::Constraint::Parameterized>
object when that object needs to generate an inline constraint. It will
receive the type parameter as the first argument and the variable name as a
string as the second.
This probably seems fairly confusing, so looking at the examples in the
L<Specio::Library::Builtins> code may be helpful.
This parameter is mutually exclusive with the
C<parameterized_constraint_generator> parameter.
=back
=head2 $type->parameterize(...)
This method takes two arguments. The C<of> argument should be an object which
does the L<Specio::Constraint::Role::Interface> role, and is required.
The other argument, C<declared_at>, is optional. If it is not given, then a
new L<Specio::DeclaredAt> object is creating using a call stack depth of 1.
This method returns a new L<Specio::Constraint::Parameterized> object.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,156 @@
package Specio::Constraint::Parameterized;
use strict;
use warnings;
our $VERSION = '0.46';
use Role::Tiny::With;
use Specio::OO;
use Storable qw( dclone );
use Specio::Constraint::Role::Interface;
with 'Specio::Constraint::Role::Interface';
{
## no critic (Subroutines::ProtectPrivateSubs)
my $attrs = dclone( Specio::Constraint::Role::Interface::_attrs() );
## use critic
$attrs->{parent}{isa} = 'Specio::Constraint::Parameterizable';
$attrs->{parent}{required} = 1;
delete $attrs->{name}{predicate};
$attrs->{name}{lazy} = 1;
$attrs->{name}{builder} = '_build_name';
$attrs->{parameter} = {
does => 'Specio::Constraint::Role::Interface',
required => 1,
};
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _attrs {
return $attrs;
}
}
sub _has_name {
my $self = shift;
return defined $self->name;
}
sub _build_name {
my $self = shift;
## no critic (Subroutines::ProtectPrivateSubs)
return unless $self->parent->_has_name && $self->parameter->_has_name;
return $self->parent->name . '[' . $self->parameter->name . ']';
}
sub can_be_inlined {
my $self = shift;
return $self->_has_inline_generator
&& $self->parameter->can_be_inlined;
}
# Moose compatibility methods - these exist as a temporary hack to make Specio
# work with Moose.
sub type_parameter {
shift->parameter;
}
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class which represents parameterized constraints
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::Parameterized - A class which represents parameterized constraints
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $arrayref = t('ArrayRef');
my $arrayref_of_int = $arrayref->parameterize( of => t('Int') );
my $parent = $arrayref_of_int->parent; # returns ArrayRef
my $parameter = $arrayref_of_int->parameter; # returns Int
=head1 DESCRIPTION
This class implements the API for parameterized types.
=for Pod::Coverage can_be_inlined type_parameter
=head1 API
This class implements the same API as L<Specio::Constraint::Simple>, with a few
additions.
=head2 Specio::Constraint::Parameterized->new(...)
This class's constructor accepts two additional parameters:
=over 4
=item * parent
This should be the L<Specio::Constraint::Parameterizable> object from which this
object was created.
This parameter is required.
=item * parameter
This is the type parameter for the parameterized type. This must be an object
which does the L<Specio::Constraint::Role::Interface> role.
This parameter is required.
=back
=head2 $type->parameter
Returns the type that was passed to the constructor.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,178 @@
package Specio::Constraint::Role::CanType;
use strict;
use warnings;
our $VERSION = '0.46';
use Scalar::Util qw( blessed );
use Specio::PartialDump qw( partial_dump );
use Storable qw( dclone );
use Role::Tiny;
use Specio::Constraint::Role::Interface;
with 'Specio::Constraint::Role::Interface';
{
## no critic (Subroutines::ProtectPrivateSubs)
my $attrs = dclone( Specio::Constraint::Role::Interface::_attrs() );
## use critic
for my $name (qw( parent _inline_generator )) {
$attrs->{$name}{init_arg} = undef;
$attrs->{$name}{builder}
= $name =~ /^_/ ? '_build' . $name : '_build_' . $name;
}
$attrs->{methods} = {
isa => 'ArrayRef',
required => 1,
};
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _attrs {
return $attrs;
}
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _wrap_message_generator {
my $self = shift;
my $generator = shift;
my $type = ( split /::/, blessed $self)[-1];
my @methods = @{ $self->methods };
my $all_word_list = _word_list(@methods);
my $allow_classes = $self->_allow_classes;
unless ( defined $generator ) {
$generator = sub {
shift;
my $value = shift;
return
"An undef will never pass an $type check (wants $all_word_list)"
unless defined $value;
my $class = blessed $value;
if ( !defined $class ) {
# If we got here we know that blessed returned undef, so if
# it's a ref then it must not be blessed.
if ( ref $value ) {
my $dump = partial_dump($value);
return
"An unblessed reference ($dump) will never pass an $type check (wants $all_word_list)";
}
# If it's defined and not an unblessed ref it must be a
# string. If we allow classes (vs just objects) then it might
# be a valid class name. But an empty string is never a valid
# class name. We cannot call q{}->can.
return
"An empty string will never pass an $type check (wants $all_word_list)"
unless length $value;
if ( ref \$value eq 'GLOB' ) {
return
"A glob will never pass an $type check (wants $all_word_list)";
}
if (
$value =~ /\A
\s*
-?[0-9]+(?:\.[0-9]+)?
(?:[Ee][\-+]?[0-9]+)?
\s*
\z/xs
) {
return
"A number ($value) will never pass an $type check (wants $all_word_list)";
}
$class = $value if $allow_classes;
# At this point we either have undef or a non-empty string in
# $class.
unless ( defined $class ) {
my $dump = partial_dump($value);
return
"A plain scalar ($dump) will never pass an $type check (wants $all_word_list)";
}
}
my @missing = grep { !$value->can($_) } @methods;
my $noun = @missing == 1 ? 'method' : 'methods';
my $list = _word_list( map {qq['$_']} @missing );
return "The $class class is missing the $list $noun";
};
}
return sub { $generator->( undef, @_ ) };
}
## use critic
sub _word_list {
my @items = sort { $a cmp $b } @_;
return $items[0] if @items == 1;
return join ' and ', @items if @items == 2;
my $final = pop @items;
my $list = join ', ', @items;
$list .= ', and ' . $final;
return $list;
}
1;
# ABSTRACT: Provides a common implementation for Specio::Constraint::AnyCan and Specio::Constraint::ObjectCan
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::Role::CanType - Provides a common implementation for Specio::Constraint::AnyCan and Specio::Constraint::ObjectCan
=head1 VERSION
version 0.46
=head1 DESCRIPTION
See L<Specio::Constraint::AnyCan> and L<Specio::Constraint::ObjectCan> for details.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,144 @@
package Specio::Constraint::Role::DoesType;
use strict;
use warnings;
our $VERSION = '0.46';
use Role::Tiny;
use Scalar::Util qw( blessed );
use Specio::PartialDump qw( partial_dump );
use Storable qw( dclone );
use Specio::Constraint::Role::Interface;
with 'Specio::Constraint::Role::Interface';
{
## no critic (Subroutines::ProtectPrivateSubs)
my $attrs = dclone( Specio::Constraint::Role::Interface::_attrs() );
## use critic
for my $name (qw( parent _inline_generator )) {
$attrs->{$name}{init_arg} = undef;
$attrs->{$name}{builder}
= $name =~ /^_/ ? '_build' . $name : '_build_' . $name;
}
$attrs->{role} = {
isa => 'Str',
required => 1,
};
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _attrs {
return $attrs;
}
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _wrap_message_generator {
my $self = shift;
my $generator = shift;
my $type = ( split /::/, blessed $self)[-1];
my $role = $self->role;
my $allow_classes = $self->_allow_classes;
unless ( defined $generator ) {
$generator = sub {
shift;
my $value = shift;
return "An undef will never pass an $type check (wants $role)"
unless defined $value;
if ( ref $value && !blessed $value ) {
my $dump = partial_dump($value);
return
"An unblessed reference ($dump) will never pass an $type check (wants $role)";
}
if ( !blessed $value) {
return
"An empty string will never pass an $type check (wants $role)"
unless length $value;
if (
$value =~ /\A
\s*
-?[0-9]+(?:\.[0-9]+)?
(?:[Ee][\-+]?[0-9]+)?
\s*
\z/xs
) {
return
"A number ($value) will never pass an $type check (wants $role)";
}
if ( !$allow_classes ) {
my $dump = partial_dump($value);
return
"A plain scalar ($dump) will never pass an $type check (wants $role)";
}
}
my $got = blessed $value;
$got ||= $value;
return "The $got class does not consume the $role role";
};
}
return sub { $generator->( undef, @_ ) };
}
## use critic
1;
# ABSTRACT: Provides a common implementation for Specio::Constraint::AnyDoes and Specio::Constraint::ObjectDoes
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::Role::DoesType - Provides a common implementation for Specio::Constraint::AnyDoes and Specio::Constraint::ObjectDoes
=head1 VERSION
version 0.46
=head1 DESCRIPTION
See L<Specio::Constraint::AnyDoes> and L<Specio::Constraint::ObjectDoes> for
details.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,667 @@
package Specio::Constraint::Role::Interface;
use strict;
use warnings;
our $VERSION = '0.46';
use Carp qw( confess );
use Eval::Closure qw( eval_closure );
use List::Util 1.33 qw( all any first );
use Specio::Exception;
use Specio::PartialDump qw( partial_dump );
use Specio::TypeChecks qw( is_CodeRef );
use Role::Tiny 1.003003;
use Specio::Role::Inlinable;
with 'Specio::Role::Inlinable';
use overload(
q{""} => sub { $_[0] },
'&{}' => '_subification',
'bool' => sub {1},
);
{
## no critic (Subroutines::ProtectPrivateSubs)
my $role_attrs = Specio::Role::Inlinable::_attrs();
## use critic
my $attrs = {
%{$role_attrs},
name => {
isa => 'Str',
predicate => '_has_name',
},
parent => {
does => 'Specio::Constraint::Role::Interface',
predicate => '_has_parent',
},
_constraint => {
isa => 'CodeRef',
init_arg => 'constraint',
predicate => '_has_constraint',
},
_optimized_constraint => {
isa => 'CodeRef',
init_arg => undef,
lazy => 1,
builder => '_build_optimized_constraint',
},
_ancestors => {
isa => 'ArrayRef',
init_arg => undef,
lazy => 1,
builder => '_build_ancestors',
},
_message_generator => {
isa => 'CodeRef',
init_arg => undef,
},
_coercions => {
builder => '_build_coercions',
clone => '_clone_coercions',
},
_subification => {
init_arg => undef,
lazy => 1,
builder => '_build_subification',
},
# Because types are cloned on import, we can't directly compare type
# objects. Because type names can be reused between packages (no global
# registry) we can't compare types based on name either.
_signature => {
isa => 'Str',
init_arg => undef,
lazy => 1,
builder => '_build_signature',
},
};
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _attrs {
return $attrs;
}
}
my $NullConstraint = sub {1};
# See Specio::OO to see how this is used.
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _Specio_Constraint_Role_Interface_BUILD {
my $self = shift;
my $p = shift;
unless ( $self->_has_constraint || $self->_has_inline_generator ) {
$self->{_constraint} = $NullConstraint;
}
die
'A type constraint should have either a constraint or inline_generator parameter, not both'
if $self->_has_constraint && $self->_has_inline_generator;
$self->{_message_generator}
= $self->_wrap_message_generator( $p->{message_generator} );
return;
}
## use critic
sub _wrap_message_generator {
my $self = shift;
my $generator = shift;
unless ( defined $generator ) {
$generator = sub {
my $description = shift;
my $value = shift;
return "Validation failed for $description with value "
. partial_dump($value);
};
}
my $d = $self->description;
return sub { $generator->( $d, @_ ) };
}
sub coercions { values %{ $_[0]->{_coercions} } }
sub coercion_from_type { $_[0]->{_coercions}{ $_[1] } }
sub _has_coercion_from_type { exists $_[0]->{_coercions}{ $_[1] } }
sub _add_coercion { $_[0]->{_coercions}{ $_[1] } = $_[2] }
sub has_coercions { scalar keys %{ $_[0]->{_coercions} } }
sub validate_or_die {
my $self = shift;
my $value = shift;
return if $self->value_is_valid($value);
Specio::Exception->throw(
message => $self->_message_generator->($value),
type => $self,
value => $value,
);
}
sub value_is_valid {
my $self = shift;
my $value = shift;
return $self->_optimized_constraint->($value);
}
sub _ancestors_and_self {
my $self = shift;
return ( ( reverse @{ $self->_ancestors } ), $self );
}
sub is_a_type_of {
my $self = shift;
my $type = shift;
return any { $_->_signature eq $type->_signature }
$self->_ancestors_and_self;
}
sub is_same_type_as {
my $self = shift;
my $type = shift;
return $self->_signature eq $type->_signature;
}
sub is_anon {
my $self = shift;
return !$self->_has_name;
}
sub has_real_constraint {
my $self = shift;
return ( $self->_has_constraint && $self->_constraint ne $NullConstraint )
|| $self->_has_inline_generator;
}
sub can_be_inlined {
my $self = shift;
return 1 if $self->_has_inline_generator;
return 0
if $self->_has_constraint && $self->_constraint ne $NullConstraint;
# If this type is an empty subtype of an inlinable parent, then we can
# inline this type as well.
return 1 if $self->_has_parent && $self->parent->can_be_inlined;
return 0;
}
sub _build_generated_inline_sub {
my $self = shift;
my $type = $self->_self_or_first_inlinable_ancestor;
my $source
= 'sub { ' . $type->_inline_generator->( $type, '$_[0]' ) . '}';
return eval_closure(
source => $source,
environment => $type->inline_environment,
description => 'inlined sub for ' . $self->description,
);
}
sub _self_or_first_inlinable_ancestor {
my $self = shift;
my $type = first { $_->_has_inline_generator }
reverse $self->_ancestors_and_self;
# This should never happen because ->can_be_inlined should always be
# checked before this builder is called.
die 'Cannot generate an inline sub' unless $type;
return $type;
}
sub _build_optimized_constraint {
my $self = shift;
if ( $self->can_be_inlined ) {
return $self->_generated_inline_sub;
}
else {
return $self->_constraint_with_parents;
}
}
sub _constraint_with_parents {
my $self = shift;
my @constraints;
for my $type ( $self->_ancestors_and_self ) {
next unless $type->has_real_constraint;
# If a type can be inlined, we can use that and discard all of the
# ancestors we've seen so far, since we can assume that the inlined
# constraint does all of the ancestor checks in addition to its own.
if ( $type->can_be_inlined ) {
@constraints = $type->_generated_inline_sub;
}
else {
push @constraints, $type->_constraint;
}
}
return $NullConstraint unless @constraints;
return sub {
all { $_->( $_[0] ) } @constraints;
};
}
# This is only used for identifying from types as part of coercions, but I
# want to leave open the possibility of using something other than
# _description in the future.
sub id {
my $self = shift;
return $self->description;
}
sub add_coercion {
my $self = shift;
my $coercion = shift;
my $from_id = $coercion->from->id;
confess "Cannot add two coercions fom the same type: $from_id"
if $self->_has_coercion_from_type($from_id);
$self->_add_coercion( $from_id => $coercion );
return;
}
sub has_coercion_from_type {
my $self = shift;
my $type = shift;
return $self->_has_coercion_from_type( $type->id );
}
sub coerce_value {
my $self = shift;
my $value = shift;
for my $coercion ( $self->coercions ) {
next unless $coercion->from->value_is_valid($value);
return $coercion->coerce($value);
}
return $value;
}
sub can_inline_coercion {
my $self = shift;
return all { $_->can_be_inlined } $self->coercions;
}
sub can_inline_coercion_and_check {
my $self = shift;
return all { $_->can_be_inlined } $self, $self->coercions;
}
sub inline_coercion {
my $self = shift;
my $arg_name = shift;
die 'Cannot inline coercion'
unless $self->can_inline_coercion;
my $source = 'do { my $value = ' . $arg_name . ';';
my ( $coerce, $env );
( $coerce, $arg_name, $env ) = $self->_inline_coercion($arg_name);
$source .= $coerce . $arg_name . '};';
return ( $source, $env );
}
sub inline_coercion_and_check {
my $self = shift;
my $arg_name = shift;
die 'Cannot inline coercion and check'
unless $self->can_inline_coercion_and_check;
my $source = 'do { my $value = ' . $arg_name . ';';
my ( $coerce, $env );
( $coerce, $arg_name, $env ) = $self->_inline_coercion($arg_name);
my ( $assert, $assert_env ) = $self->inline_assert($arg_name);
$source .= $coerce;
$source .= $assert;
$source .= $arg_name . '};';
return ( $source, { %{$env}, %{$assert_env} } );
}
sub _inline_coercion {
my $self = shift;
my $arg_name = shift;
return ( q{}, $arg_name, {} ) unless $self->has_coercions;
my %env;
$arg_name = '$value';
my $source = $arg_name . ' = ';
for my $coercion ( $self->coercions ) {
$source
.= '('
. $coercion->from->inline_check($arg_name) . ') ? ('
. $coercion->inline_coercion($arg_name) . ') : ';
%env = (
%env,
%{ $coercion->inline_environment },
%{ $coercion->from->inline_environment },
);
}
$source .= $arg_name . ';';
return ( $source, $arg_name, \%env );
}
{
my $counter = 1;
sub inline_assert {
my $self = shift;
my $type_var_name = '$_Specio_Constraint_Interface_type' . $counter;
my $message_generator_var_name
= '$_Specio_Constraint_Interface_message_generator' . $counter;
my %env = (
$type_var_name => \$self,
$message_generator_var_name => \( $self->_message_generator ),
%{ $self->inline_environment },
);
my $source = $self->inline_check( $_[0] );
$source .= ' or ';
$source .= $self->_inline_throw_exception(
$_[0],
$message_generator_var_name,
$type_var_name
);
$source .= ';';
$counter++;
return ( $source, \%env );
}
}
sub inline_check {
my $self = shift;
die 'Cannot inline' unless $self->can_be_inlined;
my $type = $self->_self_or_first_inlinable_ancestor;
return $type->_inline_generator->( $type, @_ );
}
# For some idiotic reason I called $type->_subify directly in Code::TidyAll so
# I'll leave this in here for now.
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _subify { $_[0]->_subification }
## use critic
sub _build_subification {
my $self = shift;
if ( defined &Sub::Quote::quote_sub && $self->can_be_inlined ) {
return Sub::Quote::quote_sub( $self->inline_assert('$_[0]') );
}
else {
return sub { $self->validate_or_die( $_[0] ) };
}
}
sub _inline_throw_exception {
my $self = shift;
my $value_var = shift;
my $message_generator_var_name = shift;
my $type_var_name = shift;
#<<<
return 'Specio::Exception->throw( '
. ' message => ' . $message_generator_var_name . '->(' . $value_var . '),'
. ' type => ' . $type_var_name . ','
. ' value => ' . $value_var . ' )';
#>>>
}
# This exists for the benefit of Moo
sub coercion_sub {
my $self = shift;
if ( defined &Sub::Quote::quote_sub
&& all { $_->can_be_inlined } $self->coercions ) {
my $inline = q{};
my %env;
for my $coercion ( $self->coercions ) {
$inline .= sprintf(
'$_[0] = %s if %s;' . "\n",
$coercion->inline_coercion('$_[0]'),
$coercion->from->inline_check('$_[0]')
);
%env = (
%env,
%{ $coercion->inline_environment },
%{ $coercion->from->inline_environment },
);
}
$inline .= sprintf( "%s;\n", '$_[0]' );
return Sub::Quote::quote_sub( $inline, \%env );
}
else {
return sub { $self->coerce_value(shift) };
}
}
sub _build_ancestors {
my $self = shift;
my @parents;
my $type = $self;
while ( $type = $type->parent ) {
push @parents, $type;
}
return \@parents;
}
sub _build_description {
my $self = shift;
my $desc
= $self->is_anon ? 'anonymous type' : 'type named ' . $self->name;
$desc .= q{ } . $self->declared_at->description;
return $desc;
}
sub _build_coercions { {} }
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _clone_coercions {
my $self = shift;
my $coercions = $self->_coercions;
my %clones;
for my $name ( keys %{$coercions} ) {
my $coercion = $coercions->{$name};
$clones{$name} = $coercion->clone_with_new_to($self);
}
return \%clones;
}
## use critic
sub _build_signature {
my $self = shift;
# This assumes that when a type is cloned, the underlying constraint or
# generator sub is copied by _reference_, so it has the same memory
# address and stringifies to the same value. XXX - will this break under
# threads?
return join "\n",
( $self->_has_parent ? $self->parent->_signature : () ),
(
defined $self->_constraint
? $self->_constraint
: $self->_inline_generator
);
}
# Moose compatibility methods - these exist as a temporary hack to make Specio
# work with Moose.
sub has_coercion {
shift->has_coercions;
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _inline_check {
shift->inline_check(@_);
}
sub _compiled_type_constraint {
shift->_optimized_constraint;
}
## use critic;
# This class implements the methods that Moose expects from coercions as well.
sub coercion {
return shift;
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _compiled_type_coercion {
my $self = shift;
return sub {
return $self->coerce_value(shift);
};
}
## use critic
sub has_message {
1;
}
sub message {
shift->_message_generator;
}
sub get_message {
my $self = shift;
my $value = shift;
return $self->_message_generator->( $self, $value );
}
sub check {
shift->value_is_valid(@_);
}
sub coerce {
shift->coerce_value(@_);
}
1;
# ABSTRACT: The interface all type constraints should provide
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::Role::Interface - The interface all type constraints should provide
=head1 VERSION
version 0.46
=head1 DESCRIPTION
This role defines the interface that all type constraints must provide, and
provides most (or all) of the implementation. The L<Specio::Constraint::Simple>
class simply consumes this role and provides no additional code. Other
constraint classes add features or override some of this role's functionality.
=for Pod::Coverage .*
=head1 API
See the L<Specio::Constraint::Simple> documentation for details. See the
internals of various constraint classes to see how this role can be overridden
or expanded upon.
=head1 ROLES
This role does the L<Specio::Role::Inlinable> role.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,144 @@
package Specio::Constraint::Role::IsaType;
use strict;
use warnings;
our $VERSION = '0.46';
use Scalar::Util qw( blessed );
use Specio::PartialDump qw( partial_dump );
use Storable qw( dclone );
use Role::Tiny;
use Specio::Constraint::Role::Interface;
with 'Specio::Constraint::Role::Interface';
{
## no critic (Subroutines::ProtectPrivateSubs)
my $attrs = dclone( Specio::Constraint::Role::Interface::_attrs() );
## use critic
for my $name (qw( parent _inline_generator )) {
$attrs->{$name}{init_arg} = undef;
$attrs->{$name}{builder}
= $name =~ /^_/ ? '_build' . $name : '_build_' . $name;
}
$attrs->{class} = {
isa => 'ClassName',
required => 1,
};
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _attrs {
return $attrs;
}
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _wrap_message_generator {
my $self = shift;
my $generator = shift;
my $type = ( split /::/, blessed $self)[-1];
my $class = $self->class;
my $allow_classes = $self->_allow_classes;
unless ( defined $generator ) {
$generator = sub {
shift;
my $value = shift;
return "An undef will never pass an $type check (wants $class)"
unless defined $value;
if ( ref $value && !blessed $value) {
my $dump = partial_dump($value);
return
"An unblessed reference ($dump) will never pass an $type check (wants $class)";
}
if ( !blessed $value) {
return
"An empty string will never pass an $type check (wants $class)"
unless length $value;
if (
$value =~ /\A
\s*
-?[0-9]+(?:\.[0-9]+)?
(?:[Ee][\-+]?[0-9]+)?
\s*
\z/xs
) {
return
"A number ($value) will never pass an $type check (wants $class)";
}
if ( !$allow_classes ) {
my $dump = partial_dump($value);
return
"A plain scalar ($dump) will never pass an $type check (wants $class)";
}
}
my $got = blessed $value;
$got ||= $value;
return "The $got class is not a subclass of the $class class";
};
}
return sub { $generator->( undef, @_ ) };
}
## use critic
1;
# ABSTRACT: Provides a common implementation for Specio::Constraint::AnyIsa and Specio::Constraint::ObjectIsa
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::Role::IsaType - Provides a common implementation for Specio::Constraint::AnyIsa and Specio::Constraint::ObjectIsa
=head1 VERSION
version 0.46
=head1 DESCRIPTION
See L<Specio::Constraint::AnyIsa> and L<Specio::Constraint::ObjectIsa> for details.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,351 @@
package Specio::Constraint::Simple;
use strict;
use warnings;
our $VERSION = '0.46';
use Role::Tiny::With;
use Specio::OO;
with 'Specio::Constraint::Role::Interface';
__PACKAGE__->_ooify;
1;
# ABSTRACT: Class for simple (non-parameterized or specialized) types
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::Simple - Class for simple (non-parameterized or specialized) types
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $str = t('Str');
print $str->name; # Str
my $parent = $str->parent;
if ( $str->value_is_valid($value) ) { ... }
$str->validate_or_die($value);
my $code = $str->inline_coercion_and_check('$_[0]');
=head1 DESCRIPTION
This class implements simple type constraints, constraints without special
properties or parameterization.
It does not actually contain any real code of its own. The entire
implementation is provided by the L<Specio::Constraint::Role::Interface> role,
but the primary API for type constraints is documented here.
All other type constraint classes in this distribution implement this API,
except where otherwise noted.
=head1 API
This class provides the following methods.
=head2 Specio::Constraint::Simple->new(...)
This creates a new constraint. It accepts the following named parameters:
=over 4
=item * name => $name
This is the type's name. The name is optional, but if provided it must be a
string.
=item * parent => $type
The type's parent type. This must be an object which does the
L<Specio::Constraint::Role::Interface> role.
This parameter is optional.
=item * constraint => sub { ... }
A subroutine reference implementing the constraint. It will be called as a
method on the object and passed a single argument, the value to check.
It should return true or false to indicate whether the value matches the
constraint.
This parameter is mutually exclusive with C<inline_generator>.
You can also pass this option with the key C<where> in the parameter list.
=item * inline_generator => sub { ... }
This should be a subroutine reference which returns a string containing a
single term. This code should I<not> end in a semicolon. This code should
implement the constraint.
The generator will be called as a method on the constraint with a single
argument. That argument is the name of the variable being coerced, something
like C<'$_[0]'> or C<'$var'>.
The inline generator is expected to include code to implement both the current
type and all its parents. Typically, the easiest way to do this is to write a
subroutine something like this:
sub {
my $self = shift;
my $var = shift;
return $_[0]->parent->inline_check( $_[1] )
. ' and more checking code goes here';
}
This parameter is mutually exclusive with C<constraint>.
You can also pass this option with the key C<inline> in the parameter list.
=item * inline_environment => {}
This should be a hash reference of variable names (with sigils) and values for
that variable. The values should be I<references> to the values of the
variables.
This environment will be used when compiling the constraint as part of a
subroutine. The named variables will be captured as closures in the generated
subroutine, using L<Eval::Closure>.
It should be very rare to need to set this in the constructor. It's more
likely that a special type subclass would need to provide values that it
generates internally.
If you do set this, you are responsible for generating variable names that
won't clash with anything else in the inlined code.
This parameter defaults to an empty hash reference.
=item * message_generator => sub { ... }
A subroutine to generate an error message when the type check fails. The
default message says something like "Validation failed for type named Int
declared in package Specio::Library::Builtins
(.../Specio/blib/lib/Specio/Library/Builtins.pm) at line 147 in sub named (eval)
with value 1.1".
You can override this to provide something more specific about the way the
type failed.
The subroutine you provide will be called as a subroutine, I<not as a method>,
with two arguments. The first is the description of the type (the bit in the
message above that starts with "type named Int ..." and ends with "... in sub
named (eval)". This description says what the thing is and where it was
defined.
The second argument is the value that failed the type check, after any
coercions that might have been applied.
You can also pass this option with the key C<message> in the parameter list.
=item * declared_at => $declared_at
This parameter must be a L<Specio::DeclaredAt> object.
This parameter is required.
=back
It is possible to create a type without a constraint of its own.
=head2 $type->name
Returns the name of the type as it was passed the constructor.
=head2 $type->parent
Returns the parent type passed to the constructor. If the type has no parent
this returns C<undef>.
=head2 $type->is_anon
Returns false for named types, true otherwise.
=head2 $type->is_a_type_of($other_type)
Given a type object, this returns true if the type this method is called on is
a descendant of that type or is that type.
=head2 $type->is_same_type_as($other_type)
Given a type object, this returns true if the type this method is called on is
the same as that type.
=head2 $type->coercions
Returns a list of L<Specio::Coercion> objects which belong to this constraint.
=head2 $type->coercion_from_type($name)
Given a type name, this method returns a L<Specio::Coercion> object which
coerces from that type, if such a coercion exists.
=head2 $type->validate_or_die($value)
This method does nothing if the value is valid. If it is not, it throws a
L<Specio::Exception>.
=head2 $type->value_is_valid($value)
Returns true or false depending on whether the C<$value> passes the type
constraint.
=head2 $type->has_real_constraint
This returns true if the type was created with a C<constraint> or
C<inline_generator> parameter. This is used internally to skip type checks for
types that don't actually implement a constraint.
=head2 $type->description
This returns a string describing the type. This includes the type's name and
where it was declared, so you end up with something like C<'type named Foo
declared in package My::Lib (lib/My/Lib.pm) at line 42'>. If the type is
anonymous the name will be "anonymous type".
=head2 $type->id
This is a unique id for the type as a string. This is useful if you need to
make a hash key based on a type, for example. This should be treated as an
essentially arbitrary and opaque string, and could change at any time in the
future. If you want something human-readable, use the C<< $type->description
>> method.
=head2 $type->add_coercion($coercion)
This adds a new L<Specio::Coercion> to the type. If the type already has a
coercion from the same type as the new coercion, it will throw an error.
=head2 $type->has_coercion_from_type($other_type)
This method returns true if the type can coerce from the other type.
=head2 $type->coerce_value($value)
This attempts to coerce a value into a new value that matches the type. It
checks all of the type's coercions. If it finds one which has a "from" type
that accepts the value, it runs the coercion and returns the new value.
If it cannot find a matching coercion it returns the original value.
=head2 $type->inline_coercion_and_check($var)
Given a variable name, this returns a string of code and an environment hash
that implements all of the type's coercions as well as the type check itself.
This will throw an exception unless both the type and all of its coercions are
inlinable.
The generated code will throw a L<Specio::Exception> if the type constraint
fails. If the constraint passes, then the generated code returns the (possibly
coerced) value.
The return value is a two-element list. The first element is the code. The
second is a hash reference containing variables which need to be in scope for
the code to work. This is intended to be passed to L<Eval::Closure>'s
C<eval_closure> subroutine.
The returned code is a single C<do { }> block without a terminating
semicolon.
=head2 $type->inline_assert($var)
Given a variable name, this generates code that implements the constraint and
throws an exception if the variable does not pass the constraint.
The return value is a two-element list. The first element is the code. The
second is a hash reference containing variables which need to be in scope for
the code to work. This is intended to be passed to L<Eval::Closure>'s
C<eval_closure> subroutine.
=head2 $type->inline_check($var)
Given a variable name, this returns a string of code that implements the
constraint. If the type is not inlinable, this method throws an error.
=head2 $type->inline_coercion($var)
Given a variable name, this returns a string of code and an environment hash
that implements all of the type's coercions. I<It does not check that the
resulting value is valid.>
This will throw an exception unless all of the type's coercions are inlinable.
The return value is a two-element list. The first element is the code. The
second is a hash reference containing variables which need to be in scope for
the code to work. This is intended to be passed to L<Eval::Closure>'s
C<eval_closure> subroutine.
The returned code is a single C<do { }> block without a terminating
semicolon.
=head2 $type->inline_environment()
This returns a hash defining the variables that need to be closed over when
inlining the type. The keys are full variable names like C<'$foo'> or
C<'@bar'>. The values are I<references> to a variable of the matching type.
=head2 $type->coercion_sub
This method returns a sub ref that takes a single argument and applied all
relevant coercions to it. This sub ref will use L<Sub::Quote> if all the
type's coercions are inlinable.
This method exists primarily for the benefit of L<Moo>.
=head1 OVERLOADING
All constraints overloading subroutine de-referencing for the benefit of
L<Moo>. The returned subroutine uses L<Sub::Quote> if the type constraint is
inlinable.
=head1 ROLES
This role does the L<Specio::Constraint::Role::Interface> and
L<Specio::Role::Inlinable> roles.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,267 @@
package Specio::Constraint::Structurable;
use strict;
use warnings;
our $VERSION = '0.46';
use Carp qw( confess );
use Role::Tiny::With;
use Scalar::Util qw( blessed );
use Specio::DeclaredAt;
use Specio::OO;
use Specio::Constraint::Structured;
use Specio::TypeChecks qw( does_role isa_class );
use Specio::Constraint::Role::Interface;
with 'Specio::Constraint::Role::Interface';
{
## no critic (Subroutines::ProtectPrivateSubs)
my $role_attrs = Specio::Constraint::Role::Interface::_attrs();
## use critic
my $attrs = {
%{$role_attrs},
_parameterization_args_builder => {
isa => 'CodeRef',
init_arg => 'parameterization_args_builder',
required => 1,
},
_name_builder => {
isa => 'CodeRef',
init_arg => 'name_builder',
required => 1,
},
_structured_constraint_generator => {
isa => 'CodeRef',
init_arg => 'structured_constraint_generator',
predicate => '_has_structured_constraint_generator',
},
_structured_inline_generator => {
isa => 'CodeRef',
init_arg => 'structured_inline_generator',
predicate => '_has_structured_inline_generator',
},
};
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _attrs {
return $attrs;
}
}
sub BUILD {
my $self = shift;
if ( $self->_has_constraint ) {
die
'A structurable constraint with a constraint parameter must also have a structured_constraint_generator'
unless $self->_has_structured_constraint_generator;
}
if ( $self->_has_inline_generator ) {
die
'A structurable constraint with an inline_generator parameter must also have a structured_inline_generator'
unless $self->_has_structured_inline_generator;
}
return;
}
sub parameterize {
my $self = shift;
my %args = @_;
my $declared_at = $args{declared_at};
if ($declared_at) {
isa_class( $declared_at, 'Specio::DeclaredAt' )
or confess
q{The "declared_at" parameter passed to ->parameterize must be a Specio::DeclaredAt object};
}
my %parameters
= $self->_parameterization_args_builder->( $self, $args{of} );
$declared_at = Specio::DeclaredAt->new_from_caller(1)
unless defined $declared_at;
my %new_p = (
parent => $self,
parameters => \%parameters,
declared_at => $declared_at,
name => $self->_name_builder->( $self, \%parameters ),
);
if ( $self->_has_structured_constraint_generator ) {
$new_p{constraint}
= $self->_structured_constraint_generator->(%parameters);
}
else {
for my $p (
grep {
blessed($_)
&& does_role('Specio::Constraint::Role::Interface')
} values %parameters
) {
confess
q{Any type objects passed to ->parameterize must be inlinable constraints if the structurable type has an inline_generator}
unless $p->can_be_inlined;
}
my $ig = $self->_structured_inline_generator;
$new_p{inline_generator}
= sub { $ig->( shift, shift, %parameters, @_ ) };
}
return Specio::Constraint::Structured->new(%new_p);
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _name_or_anon {
return $_[1]->_has_name ? $_[1]->name : 'ANON';
}
## use critic
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class which represents structurable constraints
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::Structurable - A class which represents structurable constraints
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $tuple = t('Tuple');
my $tuple_of_str_int = $tuple->parameterize( of => [ t('Str'), t('Int') ] );
=head1 DESCRIPTION
This class implements the API for structurable types like C<Dict>, C<Map>< and
C<Tuple>.
=for Pod::Coverage BUILD
=head1 API
This class implements the same API as L<Specio::Constraint::Simple>, with a few
additions.
=head2 Specio::Constraint::Structurable->new(...)
This class's constructor accepts two additional parameters:
=over 4
=item * parameterization_args_builder
This is a subroutine that takes the values passed to C<of> and returns a hash
of named arguments. These arguments will then be passed into the
C<structured_constraint_generator> or C<structured_inline_generator>.
This should also do argument checking to make sure that the argument passed
are valid. For example, the C<Tuple> type turns the arrayref passed to C<of>
into a hash, along the way checking that the caller did not do things like
interleave optional and required elements or mix optional and slurpy together
in the definition.
This parameter is required.
=item * name_builder
This is a subroutine that is called to generate a name for the structured type
when it is created. This will be called as a method on the
C<Specio::Constraint::Structurable> object. It will be passed the hash of
arguments returned by the C<parameterization_args_builder>.
This parameter is required.
=item * structured_constraint_generator
This is a subroutine that generates a new constraint subroutine when the type
is structured.
It will be called as a method on the type and will be passed the hash of
arguments returned by the C<parameterization_args_builder>.
This parameter is mutually exclusive with the C<structured_inline_generator>
parameter.
This parameter or the C<structured_inline_generator> parameter is required.
=item * structured_inline_generator
This is a subroutine that generates a new inline generator subroutine when the
type is structured.
It will be called as a method on the L<Specio::Constraint::Structured> object
when that object needs to generate an inline constraint. It will receive the
type parameter as the first argument and the variable name as a string as the
second.
The remaining arguments will be the parameter hash returned by the
C<parameterization_args_builder>.
This probably seems fairly confusing, so looking at the examples in the
L<Specio::Library::Structured::*> code may be helpful.
This parameter is mutually exclusive with the
C<structured_constraint_generator> parameter.
This parameter or the C<structured_constraint_generator> parameter is
required.
=back
=head2 $type->parameterize(...)
This method takes two arguments. The C<of> argument should be an object which
does the L<Specio::Constraint::Role::Interface> role, and is required.
The other argument, C<declared_at>, is optional. If it is not given, then a
new L<Specio::DeclaredAt> object is creating using a call stack depth of 1.
This method returns a new L<Specio::Constraint::Structured> object.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,134 @@
package Specio::Constraint::Structured;
use strict;
use warnings;
our $VERSION = '0.46';
use List::Util qw( all );
use Role::Tiny::With;
use Specio::OO;
use Specio::TypeChecks qw( does_role );
use Storable qw( dclone );
use Specio::Constraint::Role::Interface;
with 'Specio::Constraint::Role::Interface';
{
## no critic (Subroutines::ProtectPrivateSubs)
my $attrs = dclone( Specio::Constraint::Role::Interface::_attrs() );
## use critic
$attrs->{parent}{isa} = 'Specio::Constraint::Structurable';
$attrs->{parent}{required} = 1;
$attrs->{parameters} = {
isa => 'HashRef',
required => 1,
};
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _attrs {
return $attrs;
}
}
sub can_be_inlined {
my $self = shift;
return $self->_has_inline_generator;
}
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class which represents structured constraints
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::Structured - A class which represents structured constraints
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $tuple = t('Tuple');
my $tuple_of_str_int = $tuple->parameterize( of => [ t('Str'), t('Int') ] );
my $parent = $tuple_of_str_int->parent; # returns Tuple
my $parameters = $arrayref_of_int->parameters; # returns { of => [ t('Str'), t('Int') ] }
=head1 DESCRIPTION
This class implements the API for structured types.
=for Pod::Coverage can_be_inlined type_parameter
=head1 API
This class implements the same API as L<Specio::Constraint::Simple>, with a few
additions.
=head2 Specio::Constraint::Structured->new(...)
This class's constructor accepts two additional parameters:
=over 4
=item * parent
This should be the L<Specio::Constraint::Structurable> object from which this
object was created.
This parameter is required.
=item * parameters
This is the hashref of parameters for the structured type. These are the
parameters returned by the C<Structurable> type's
C<parameterization_args_builder>. The exact form of this hashref will vary for
each structured type.
This parameter is required.
=back
=head2 $type->parameters
Returns the hashref that was passed to the constructor.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut

View File

@@ -0,0 +1,198 @@
package Specio::Constraint::Union;
use strict;
use warnings;
our $VERSION = '0.46';
use List::Util qw( all any );
use Role::Tiny::With;
use Specio::OO;
use Storable qw( dclone );
use Specio::Constraint::Role::Interface;
with 'Specio::Constraint::Role::Interface';
{
## no critic (Subroutines::ProtectPrivateSubs)
my $attrs = dclone( Specio::Constraint::Role::Interface::_attrs() );
## use critic
for my $name (qw( _constraint _inline_generator )) {
delete $attrs->{$name}{predicate};
$attrs->{$name}{init_arg} = undef;
$attrs->{$name}{lazy} = 1;
$attrs->{$name}{builder}
= $name =~ /^_/ ? '_build' . $name : '_build_' . $name;
}
delete $attrs->{parent};
delete $attrs->{name}{predicate};
$attrs->{name}{lazy} = 1;
$attrs->{name}{builder} = '_build_name';
$attrs->{of} = {
isa => 'ArrayRef',
required => 1,
};
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _attrs {
return $attrs;
}
}
sub parent {undef}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _has_parent {0}
sub _has_name {
my $self = shift;
return defined $self->name;
}
sub _build_name {
my $self = shift;
return unless all { $_->_has_name } @{ $self->of };
return join q{ | }, map { $_->name } @{ $self->of };
}
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
sub _has_constraint {
my $self = shift;
return !$self->_has_inline_generator;
}
## use critic
sub _build_constraint {
return $_[0]->_optimized_constraint;
}
sub _build_optimized_constraint {
my $self = shift;
## no critic (Subroutines::ProtectPrivateSubs)
my @c = map { $_->_optimized_constraint } @{ $self->of };
return sub {
return any { $_->( $_[0] ) } @c;
};
}
sub _has_inline_generator {
my $self = shift;
## no critic (Subroutines::ProtectPrivateSubs)
return all { $_->_has_inline_generator } @{ $self->of };
}
sub _build_inline_generator {
my $self = shift;
return sub {
return '(' . (
join q{ || },
map { sprintf( '( %s )', $_->_inline_generator->( $_, $_[1] ) ) }
@{ $self->of }
) . ')';
}
}
sub _build_inline_environment {
my $self = shift;
my %env;
for my $type ( @{ $self->of } ) {
%env = (
%env,
%{ $type->inline_environment },
);
}
return \%env;
}
__PACKAGE__->_ooify;
1;
# ABSTRACT: A class for union constraints
__END__
=pod
=encoding UTF-8
=head1 NAME
Specio::Constraint::Union - A class for union constraints
=head1 VERSION
version 0.46
=head1 SYNOPSIS
my $type = Specio::Constraint::Untion->new(...);
=head1 DESCRIPTION
This is a specialized type constraint class for unions, which will allow a
value which matches any one of several distinct types.
=for Pod::Coverage parent
=head1 API
This class provides all of the same methods as L<Specio::Constraint::Simple>,
with a few differences:
=head2 Specio::Constraint::Union->new( ... )
The C<parent> parameter is ignored if it passed, as it is always C<undef>
The C<inline_generator> and C<constraint> parameters are also ignored. This
class provides its own default inline generator subroutine reference.
Finally, this class requires an additional parameter, C<of>. This must be an
arrayref of type objects.
=head2 $union->of
Returns an array reference of the individual types which makes up this union.
=head1 ROLES
This class does the L<Specio::Constraint::Role::Interface> and
L<Specio::Role::Inlinable> roles.
=head1 SUPPORT
Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
=head1 SOURCE
The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
=head1 AUTHOR
Dave Rolsky <autarch@urth.org>
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2012 - 2020 by Dave Rolsky.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)
The full text of the license can be found in the
F<LICENSE> file included with this distribution.
=cut