use strict;
use warnings;
+use Carp 'confess';
use Sub::Name 'subname';
use Scalar::Util 'blessed';
-our $VERSION = '0.01';
+our $VERSION = '0.02';
+
+use Moose::Meta::TypeConstraint;
sub import {
shift;
my $pkg = shift || caller();
return if $pkg eq ':no_export';
no strict 'refs';
- foreach my $export (qw(
- type subtype as where
- )) {
+ foreach my $export (qw(type subtype as where coerce from via)) {
*{"${pkg}::${export}"} = \&{"${export}"};
- }
-
- foreach my $constraint (qw(
- Any
- Value Ref
- Str Int
- ScalarRef ArrayRef HashRef CodeRef RegexpRef
- Object
- )) {
- *{"${pkg}::${constraint}"} = \&{"${constraint}"};
}
-
}
-my %TYPES;
+{
+ my %TYPES;
+ sub find_type_constraint { $TYPES{$_[0]} }
+
+ sub create_type_constraint {
+ my ($name, $parent, $constraint) = @_;
+ (not exists $TYPES{$name})
+ || confess "The type constraint '$name' has already been created";
+ $parent = find_type_constraint($parent) if defined $parent;
+ $TYPES{$name} = Moose::Meta::TypeConstraint->new(
+ name => $name,
+ parent => $parent,
+ constraint => $constraint,
+ );
+ }
+
+ sub find_type_coercion {
+ my $type_name = shift;
+ $TYPES{$type_name}->coercion_code;
+ }
+
+ sub register_type_coercion {
+ my ($type_name, $type_coercion) = @_;
+ my $type = $TYPES{$type_name};
+ (!$type->has_coercion)
+ || confess "The type coercion for '$type_name' has already been registered";
+ $type->set_coercion_code($type_coercion);
+ }
+
+ sub export_type_contstraints_as_functions {
+ my $pkg = caller();
+ no strict 'refs';
+ foreach my $constraint (keys %TYPES) {
+ *{"${pkg}::${constraint}"} = $TYPES{$constraint}->constraint_code;
+ }
+ }
+}
-# might need this later
-#sub find_type_constraint { $TYPES{$_[0]} }
sub type ($$) {
my ($name, $check) = @_;
- my $pkg = caller();
- my $full_name = "${pkg}::${name}";
- no strict 'refs';
- *{$full_name} = $TYPES{$name} = subname $full_name => sub {
- return $TYPES{$name} unless defined $_[0];
- local $_ = $_[0];
- return undef unless $check->($_[0]);
- $_[0];
- };
+ create_type_constraint($name, undef, $check);
}
sub subtype ($$;$) {
- my ($name, $parent, $check) = @_;
- if (defined $check) {
- my $pkg = caller();
- my $full_name = "${pkg}::${name}";
- no strict 'refs';
- $parent = $TYPES{$parent} unless $parent && ref($parent) eq 'CODE';
- *{$full_name} = $TYPES{$name} = subname $full_name => sub {
- return $TYPES{$name} unless defined $_[0];
- local $_ = $_[0];
- return undef unless defined $parent->($_[0]) && $check->($_[0]);
- $_[0];
- };
+ if (scalar @_ == 3) {
+ my ($name, $parent, $check) = @_;
+ create_type_constraint($name, $parent, $check);
}
else {
- ($parent, $check) = ($name, $parent);
- $parent = $TYPES{$parent} unless $parent && ref($parent) eq 'CODE';
- return subname((caller() . '::__anon_subtype__') => sub {
- return $TYPES{$name} unless defined $_[0];
- local $_ = $_[0];
- return undef unless defined $parent->($_[0]) && $check->($_[0]);
- $_[0];
- });
+ my ($parent, $check) = @_;
+ $parent = find_type_constraint($parent);
+ return Moose::Meta::TypeConstraint->new(
+ name => '__ANON__',
+ parent => $parent,
+ constraint => $check,
+ );
}
}
+sub coerce ($@) {
+ my ($type_name, @coercion_map) = @_;
+ my @coercions;
+ while (@coercion_map) {
+ my ($constraint_name, $action) = splice(@coercion_map, 0, 2);
+ my $constraint = find_type_constraint($constraint_name)->constraint_code;
+ (defined $constraint)
+ || confess "Could not find the type constraint ($constraint_name)";
+ push @coercions => [ $constraint, $action ];
+ }
+ register_type_coercion($type_name, sub {
+ my $thing = shift;
+ foreach my $coercion (@coercions) {
+ my ($constraint, $converter) = @$coercion;
+ if (defined $constraint->($thing)) {
+ local $_ = $thing;
+ return $converter->($thing);
+ }
+ }
+ return $thing;
+ });
+}
+
sub as ($) { $_[0] }
+sub from ($) { $_[0] }
sub where (&) { $_[0] }
+sub via (&) { $_[0] }
# define some basic types
=head1 NAME
-Moose::Util::TypeConstraints -
+Moose::Util::TypeConstraints - Type constraint system for Moose
=head1 SYNOPSIS
subtype NaturalLessThanTen
=> as Natural
=> where { $_ < 10 };
+
+ coerce Num
+ => from Str
+ => via { 0+$_ };
=head1 DESCRIPTION
+This module provides Moose with the ability to create type contraints
+to be are used in both attribute definitions and for method argument
+validation.
+
+This is B<NOT> a type system for Perl 5.
+
+This module also provides a simple hierarchy for Perl 5 types, this
+could probably use some work, but it works for me at the moment.
+
+ Any
+ Value
+ Int
+ Str
+ Ref
+ ScalarRef
+ ArrayRef
+ HashRef
+ CodeRef
+ RegexpRef
+ Object
+
+Suggestions for improvement are welcome.
+
=head1 FUNCTIONS
+=head2 Type Constraint Registry
+
+=over 4
+
+=item B<find_type_constraint ($type_name)>
+
+=item B<create_type_constraint ($type_name, $type_constraint)>
+
+=item B<find_type_coercion>
+
+=item B<register_type_coercion>
+
+=item B<export_type_contstraints_as_functions>
+
+=item B<dump_type_constraints>
+
+=back
+
=head2 Type Constraint Constructors
=over 4
=item B<where>
+=item B<coerce>
+
+=item B<from>
+
+=item B<via>
+
=back
=head2 Built-in Type Constraints
exception. If you find a bug please either email me, or add the bug
to cpan-RT.
-=head1 CODE COVERAGE
-
-I use L<Devel::Cover> to test the code coverage of my tests, below is the
-L<Devel::Cover> report on this module's test suite.
-
-=head1 ACKNOWLEDGEMENTS
-
=head1 AUTHOR
Stevan Little E<lt>stevan@iinteractive.comE<gt>