use warnings;
use Carp 'confess';
-use Scalar::Util 'blessed';
+use Scalar::Util 'blessed', 'reftype';
use B 'svref_2object';
use Sub::Exporter;
-our $VERSION = '0.12';
+our $VERSION = '0.14';
our $AUTHORITY = 'cpan:STEVAN';
+# Prototyped subs must be predeclared because we have a circular dependency
+# with Moose::Meta::Attribute et. al. so in case of us being use'd first the
+# predeclaration ensures the prototypes are in scope when consumers are
+# compiled
+
+sub find_type_constraint ($);
+sub _create_type_constraint ($$$;$$);
+sub _install_type_coercions ($$);
+sub create_type_constraint_union (@);
+sub type ($$;$$);
+sub subtype ($$;$$$);
+sub coerce ($@);
+sub as ($);
+sub from ($);
+sub where (&);
+sub via (&);
+sub message (&);
+sub optimize_as (&);
+sub enum ($;@);
+
use Moose::Meta::TypeConstraint;
use Moose::Meta::TypeCoercion;
+use Moose::Meta::TypeConstraint::Registry;
my @exports = qw/
type subtype as where message optimize_as
}
}
-{
- my %TYPES;
- sub find_type_constraint ($) {
- return $TYPES{$_[0]}->[1]
- if exists $TYPES{$_[0]};
- return;
- }
-
- sub _dump_type_constraints {
- require Data::Dumper;
- Data::Dumper::Dumper(\%TYPES);
- }
-
- sub _create_type_constraint ($$$;$$) {
- my $name = shift;
- my $parent = shift;
- my $check = shift;;
-
- my ($message, $optimized);
- for (@_) {
- $message = $_->{message} if exists $_->{message};
- $optimized = $_->{optimized} if exists $_->{optimized};
- }
+my $REGISTRY = Moose::Meta::TypeConstraint::Registry->new;
- my $pkg_defined_in = scalar(caller(0));
-
- ($TYPES{$name}->[0] eq $pkg_defined_in)
- || confess ("The type constraint '$name' has already been created in "
- . $TYPES{$name}->[0] . " and cannot be created again in "
- . $pkg_defined_in)
- if defined $name && exists $TYPES{$name};
-
- $parent = find_type_constraint($parent) if defined $parent;
- my $constraint = Moose::Meta::TypeConstraint->new(
- name => $name || '__ANON__',
- parent => $parent,
- constraint => $check,
- message => $message,
- optimized => $optimized,
- );
- $TYPES{$name} = [ $pkg_defined_in, $constraint ] if defined $name;
- return $constraint;
- }
+sub _get_type_constraint_registry { $REGISTRY }
+sub _dump_type_constraints { $REGISTRY->dump }
- sub _install_type_coercions ($$) {
- my ($type_name, $coercion_map) = @_;
- my $type = find_type_constraint($type_name);
- (!$type->has_coercion)
- || confess "The type coercion for '$type_name' has already been registered";
- my $type_coercion = Moose::Meta::TypeCoercion->new(
- type_coercion_map => $coercion_map,
- type_constraint => $type
- );
- $type->coercion($type_coercion);
- }
+# NOTE:
+# this method breaks down the sugar
+# from the functions below.
+sub _create_type_constraint ($$$;$$) {
+ my $name = shift;
+ my $parent = shift;
+ my $check = shift || sub { 1 };
- sub create_type_constraint_union (@) {
- my (@type_constraint_names) = @_;
- return Moose::Meta::TypeConstraint->union(
- map {
- find_type_constraint($_)
- } @type_constraint_names
- );
+ my ($message, $optimized);
+ for (@_) {
+ $message = $_->{message} if exists $_->{message};
+ $optimized = $_->{optimized} if exists $_->{optimized};
}
+
+ my $pkg_defined_in = scalar(caller(0));
- sub export_type_constraints_as_functions {
- my $pkg = caller();
- no strict 'refs';
- foreach my $constraint (keys %TYPES) {
- *{"${pkg}::${constraint}"} = find_type_constraint($constraint)->_compiled_type_constraint;
- }
- }
+ if (defined $name) {
+ my $type = $REGISTRY->get_type_constraint($name);
- *Moose::Util::TypeConstraints::export_type_contstraints_as_functions = \&export_type_constraints_as_functions;
+ ($type->_package_defined_in eq $pkg_defined_in)
+ || confess ("The type constraint '$name' has already been created in "
+ . $type->_package_defined_in . " and cannot be created again in "
+ . $pkg_defined_in)
+ if defined $type;
+ }
+
+ $parent = $REGISTRY->get_type_constraint($parent) if defined $parent;
- sub list_all_type_constraints { keys %TYPES }
+ my $constraint = Moose::Meta::TypeConstraint->new(
+ name => $name || '__ANON__',
+ parent => $parent,
+ constraint => $check,
+ message => $message,
+ optimized => $optimized,
+ package_defined_in => $pkg_defined_in,
+ );
+
+ $REGISTRY->add_type_constraint($constraint)
+ if defined $name;
+
+ return $constraint;
+}
+
+sub _install_type_coercions ($$) {
+ my ($type_name, $coercion_map) = @_;
+ my $type = $REGISTRY->get_type_constraint($type_name);
+ (!$type->has_coercion)
+ || confess "The type coercion for '$type_name' has already been registered";
+ my $type_coercion = Moose::Meta::TypeCoercion->new(
+ type_coercion_map => $coercion_map,
+ type_constraint => $type
+ );
+ $type->coercion($type_coercion);
+}
+
+sub create_type_constraint_union (@) {
+ my (@type_constraint_names) = @_;
+ return Moose::Meta::TypeConstraint->union(
+ map {
+ $REGISTRY->get_type_constraint($_)
+ } @type_constraint_names
+ );
}
+sub export_type_constraints_as_functions {
+ my $pkg = caller();
+ no strict 'refs';
+ foreach my $constraint (keys %{$REGISTRY->type_constraints}) {
+ *{"${pkg}::${constraint}"} = $REGISTRY->get_type_constraint($constraint)
+ ->_compiled_type_constraint;
+ }
+}
+
+*Moose::Util::TypeConstraints::export_type_contstraints_as_functions = \&export_type_constraints_as_functions;
+
+sub list_all_type_constraints { keys %{$REGISTRY->type_constraints} }
+
+## --------------------------------------------------------
+## exported functions ...
+## --------------------------------------------------------
+
+sub find_type_constraint ($) { $REGISTRY->get_type_constraint(@_) }
+
# type constructors
sub type ($$;$$) {
}
sub subtype ($$;$$$) {
- unshift @_ => undef if scalar @_ <= 2;
+ # NOTE:
+ # this adds an undef for the name
+ # if this is an anon-subtype:
+ # subtype(Num => where { $_ % 2 == 0 }) # anon 'even' subtype
+ # but if the last arg is not a code
+ # ref then it is a subtype alias:
+ # subtype(MyNumbers => as Num); # now MyNumbers is the same as Num
+ # ... yeah I know it's ugly code
+ # - SL
+ unshift @_ => undef if scalar @_ <= 2 && (reftype($_[1]) || '') eq 'CODE';
goto &_create_type_constraint;
}
=> as 'Object'
=> where { $_->can('does') }
=> optimize_as { blessed($_[0]) && $_[0]->can('does') };
+
+subtype 'ClassName'
+ => as 'Str'
+ => where { eval { $_->isa('UNIVERSAL') } }
+ => optimize_as { !ref($_[0]) && eval { $_[0]->isa('UNIVERSAL') } };
{
my @BUILTINS = list_all_type_constraints();
Num
Int
Str
+ ClassName
Ref
ScalarRef
ArrayRef
B<NOTE:> The C<Undef> type constraint does not work correctly
in every occasion, please use it sparringly.
+B<NOTE:> The C<ClassName> type constraint is simply a subtype
+of string which responds true to C<isa('UNIVERSAL')>. This means
+that your class B<must> be loaded for this type constraint to
+pass. I know this is not ideal for all, but it is a saner
+restriction than most others.
+
=head2 Use with Other Constraint Modules
This module should play fairly nicely with other constraint
=item B<find_type_constraint ($type_name)>
-This function can be used to locate a specific type constraint
-meta-object. What you do with it from there is up to you :)
+This function can be used to locate a specific type constraint
+meta-object, of the class L<Moose::Meta::TypeConstraint> or a
+derivative. What you do with it from there is up to you :)
=item B<create_type_constraint_union (@type_constraint_names)>
They will then register the type constraints in a global store
where Moose can get to them if it needs to.
-See the L<SYNOPOSIS> for an example of how to use these.
+See the L<SYNOPSIS> for an example of how to use these.
=over 4
=head2 Type Coercion Constructors
Type constraints can also contain type coercions as well. If you
-ask your accessor too coerce, the Moose will run the type-coercion
+ask your accessor to coerce, then Moose will run the type-coercion
code first, followed by the type constraint check. This feature
should be used carefully as it is very powerful and could easily
take off a limb if you are not careful.
-See the L<SYNOPOSIS> for an example of how to use these.
+See the L<SYNOPSIS> for an example of how to use these.
=over 4