X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMoose%2FMeta%2FTypeConstraint.pm;h=e67e6f3983fde0c1a0b3e98af4ccf1aa0fd53e14;hb=825a80dec36c7a359993cc8dc60af9463fdeb0c7;hp=e2109c6c5d6d039d744a0a74dbb2e0ca929f64a1;hpb=cce8198ba52519eaf60ebb1121fb8304537cb4a5;p=gitmo%2FMoose.git diff --git a/lib/Moose/Meta/TypeConstraint.pm b/lib/Moose/Meta/TypeConstraint.pm index e2109c6..e67e6f3 100644 --- a/lib/Moose/Meta/TypeConstraint.pm +++ b/lib/Moose/Meta/TypeConstraint.pm @@ -9,7 +9,7 @@ use Sub::Name 'subname'; use Carp 'confess'; use Scalar::Util 'blessed'; -our $VERSION = '0.03'; +our $VERSION = '0.06'; __PACKAGE__->meta->add_attribute('name' => (reader => 'name' )); __PACKAGE__->meta->add_attribute('parent' => (reader => 'parent' )); @@ -35,6 +35,21 @@ sub new { return $self; } +sub coerce { + ((shift)->coercion || confess "Cannot coerce without a type coercion")->coerce(@_) +} + +sub _collect_all_parents { + my $self = shift; + my @parents; + my $current = $self->parent; + while (defined $current) { + unshift @parents => $current; + $current = $current->parent; + } + return @parents; +} + sub compile_type_constraint { my $self = shift; my $check = $self->constraint; @@ -42,13 +57,21 @@ sub compile_type_constraint { || confess "Could not compile type constraint '" . $self->name . "' because no constraint check"; my $parent = $self->parent; if (defined $parent) { - # we have a subtype ... - $parent = $parent->_compiled_type_constraint; + # we have a subtype ... + # so we gather all the parents in order + # and grab their constraints ... + my @parents = map { $_->constraint } $self->_collect_all_parents; + # then we compile them to run without + # having to recurse as we did before $self->_compiled_type_constraint(subname $self->name => sub { local $_ = $_[0]; - return undef unless defined $parent->($_[0]) && $check->($_[0]); + foreach my $parent (@parents) { + return undef unless $parent->($_[0]); + } + return undef unless $check->($_[0]); 1; }); + } else { # we have a type .... @@ -78,6 +101,11 @@ sub validate { } } +sub is_a_type_of { + my ($self, $type_name) = @_; + ($self->name eq $type_name || $self->is_subtype_of($type_name)); +} + sub is_subtype_of { my ($self, $type_name) = @_; my $current = $self; @@ -96,7 +124,7 @@ sub union { || confess "You must pass in only Moose::Meta::TypeConstraint instances to make unions" foreach @type_constraints; return Moose::Meta::TypeConstraint::Union->new( - type_constraints => \@type_constraints + type_constraints => \@type_constraints, ); } @@ -106,7 +134,7 @@ use strict; use warnings; use metaclass; -our $VERSION = '0.01'; +our $VERSION = '0.03'; __PACKAGE__->meta->add_attribute('type_constraints' => ( accessor => 'type_constraints', @@ -131,18 +159,54 @@ sub constraint { # conform to the TypeConstraint API sub parent { undef } -sub coercion { undef } -sub has_coercion { 0 } sub message { undef } sub has_message { 0 } -sub check { +# FIXME: +# not sure what this should actually do here +sub coercion { undef } + +# this should probably be memoized +sub has_coercion { + my $self = shift; + foreach my $type (@{$self->type_constraints}) { + return 1 if $type->has_coercion + } + return 0; +} + +# NOTE: +# this feels too simple, and may not always DWIM +# correctly, especially in the presence of +# close subtype relationships, however it should +# work for a fair percentage of the use cases +sub coerce { my $self = shift; my $value = shift; foreach my $type (@{$self->type_constraints}) { - return 1 if $type->check($value); + if ($type->has_coercion) { + my $temp = $type->coerce($value); + return $temp if $self->check($temp); + } + } + return undef; +} + +sub _compiled_type_constraint { + my $self = shift; + return sub { + my $value = shift; + foreach my $type (@{$self->type_constraints}) { + return 1 if $type->check($value); + } + return undef; } - return undef; +} + +sub check { + my $self = shift; + my $value = shift; + $self->_compiled_type_constraint->($value); } sub validate { @@ -158,6 +222,22 @@ sub validate { return ($message . ' in (' . $self->name . ')') ; } +sub is_a_type_of { + my ($self, $type_name) = @_; + foreach my $type (@{$self->type_constraints}) { + return 1 if $type->is_a_type_of($type_name); + } + return 0; +} + +sub is_subtype_of { + my ($self, $type_name) = @_; + foreach my $type (@{$self->type_constraints}) { + return 1 if $type->is_subtype_of($type_name); + } + return 0; +} + 1; __END__ @@ -187,10 +267,19 @@ If you wish to use features at this depth, please come to the =item B -=item B +=item B + +This checks the current type name, and if it does not match, +checks if it is a subtype of it. + +=item B =item B +=item B + +This will apply the type-coercion if applicable. + =item B This method will return a true (C<1>) if the C<$value> passes the