X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMoose%2FMeta%2FTypeConstraint%2FUnion.pm;h=0ee81659dcc94141c869b168598c41db0f1aa33d;hb=c05704596921f27fba4b1148dfed3ddd0d15795e;hp=77c91f8001ad0fd79a54f3203d75e95f4ed6b1c4;hpb=a7be0f8593e4e7b7f570f49027ee4f8f25d4d8bc;p=gitmo%2FMoose.git diff --git a/lib/Moose/Meta/TypeConstraint/Union.pm b/lib/Moose/Meta/TypeConstraint/Union.pm index 77c91f8..0ee8165 100644 --- a/lib/Moose/Meta/TypeConstraint/Union.pm +++ b/lib/Moose/Meta/TypeConstraint/Union.pm @@ -7,41 +7,102 @@ use metaclass; use Moose::Meta::TypeCoercion::Union; -our $VERSION = '0.69'; -$VERSION = eval $VERSION; -our $AUTHORITY = 'cpan:STEVAN'; +use List::MoreUtils qw(all); +use List::Util qw(first); use base 'Moose::Meta::TypeConstraint'; __PACKAGE__->meta->add_attribute('type_constraints' => ( accessor => 'type_constraints', - default => sub { [] } + default => sub { [] }, + Class::MOP::_definition_context(), )); -sub new { +sub new { my ($class, %options) = @_; + + my $name = join '|' => sort { $a cmp $b } + map { $_->name } @{ $options{type_constraints} }; + my $self = $class->SUPER::new( - name => (join '|' => sort {$a cmp $b} - map { $_->name } @{$options{type_constraints}}), - parent => undef, - message => undef, - hand_optimized_type_constraint => undef, - compiled_type_constraint => sub { - my $value = shift; - foreach my $type (@{$options{type_constraints}}) { - return 1 if $type->check($value); - } - return undef; - }, - %options + name => $name, + %options, ); - $self->_set_constraint(sub { $self->check($_[0]) }); - $self->coercion(Moose::Meta::TypeCoercion::Union->new( - type_constraint => $self - )); + + $self->_set_constraint( $self->_compiled_type_constraint ); + return $self; } +# XXX - this is a rather gross implementation of laziness for the benefit of +# MX::Types. If we try to call ->has_coercion on the objects during object +# construction, this does not work when defining a recursive constraint with +# MX::Types. +sub coercion { + my $self = shift; + + return $self->{coercion} if exists $self->{coercion}; + + # Using any instead of grep here causes a weird error with some corner + # cases when MX::Types is in use. See RT #61001. + if ( grep { $_->has_coercion } @{ $self->type_constraints } ) { + return $self->{coercion} = Moose::Meta::TypeCoercion::Union->new( + type_constraint => $self ); + } + else { + return $self->{coercion} = undef; + } +} + +sub has_coercion { + return defined $_[0]->coercion; +} + +sub _actually_compile_type_constraint { + my $self = shift; + + my @constraints = @{ $self->type_constraints }; + + return sub { + my $value = shift; + foreach my $type (@constraints) { + return 1 if $type->check($value); + } + return undef; + }; +} + +sub can_be_inlined { + my $self = shift; + + # This was originally done with all() from List::MoreUtils, but that + # caused some sort of bizarro parsing failure under 5.10. + for my $tc ( @{ $self->type_constraints } ) { + return 0 unless $tc->can_be_inlined; + } + + return 1; +} + +sub _inline_check { + my $self = shift; + my $val = shift; + + return '(' + . ( + join ' || ', map { '(' . $_->_inline_check($val) . ')' } + @{ $self->type_constraints } + ) + . ')'; +} + +sub inline_environment { + my $self = shift; + + return { map { %{ $_->inline_environment } } + @{ $self->type_constraints } }; +} + sub equals { my ( $self, $type_or_name ) = @_; @@ -67,9 +128,16 @@ sub equals { return @other_constraints == 0; } -sub parents { +sub parent { my $self = shift; - $self->type_constraints; + + my ($first, @rest) = @{ $self->type_constraints }; + + for my $parent ( $first->_collect_all_parents ) { + return $parent if all { $_->is_a_type_of($parent) } @rest; + } + + return; } sub validate { @@ -81,23 +149,25 @@ sub validate { $message .= ($message ? ' and ' : '') . $err if defined $err; } - return ($message . ' in (' . $self->name . ')') ; + return ($message . ' in (' . $self->name . ')') ; +} + +sub find_type_for { + my ($self, $value) = @_; + + return first { $_->check($value) } @{ $self->type_constraints }; } 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; + + return all { $_->is_a_type_of($type_name) } @{ $self->type_constraints }; } 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; + + return all { $_->is_subtype_of($type_name) } @{ $self->type_constraints }; } sub create_child_type { @@ -125,107 +195,92 @@ sub create_child_type { 1; +# ABSTRACT: A union of Moose type constraints + __END__ =pod -=head1 NAME - -Moose::Meta::TypeConstraint::Union - A union of Moose type constraints - =head1 DESCRIPTION -This metaclass represents a union of Moose type constraints. More -details to be explained later (possibly in a Cookbook recipe). +This metaclass represents a union of type constraints. A union takes +multiple type constraints, and is true if any one of its member +constraints is true. -This actually used to be part of Moose::Meta::TypeConstraint, but it -is now better off in it's own file. +=head1 INHERITANCE -=head1 METHODS - -This class is not a subclass of Moose::Meta::TypeConstraint, -but it does provide the same API +C is a subclass of +L. =over 4 -=item B - -=item B - -=item B - -=item B +=item B<< Moose::Meta::TypeConstraint::Union->new(%options) >> -=item B +This creates a new class type constraint based on the given +C<%options>. -=item B +It takes the same options as its parent. It also requires an +additional option, C. This is an array reference +containing the L objects that are the +members of the union type. The C option defaults to the names +all of these member types sorted and then joined by a pipe (|). -=item B +The constructor sets the implementation of the constraint so that is +simply calls C on the newly created object. -=item B +Finally, the constructor also makes sure that the object's C +attribute is a L object. -=back - -=head2 Overridden methods - -=over 4 +=item B<< $constraint->type_constraints >> -=item B +This returns the array reference of C provided to +the constructor. -=item B +=item B<< $constraint->parent >> -=item B +This returns the nearest common ancestor of all the components of the union. -=item B +=item B<< $constraint->check($value) >> -=item B +=item B<< $constraint->validate($value) >> -=back +These two methods simply call the relevant method on each of the +member type constraints in the union. If any type accepts the value, +the value is valid. -=head2 Empty or Stub methods +With C the error message returned includes all of the error +messages returned by the member type constraints. -These methods tend to not be very relevant in -the context of a union. Either that or they are -just difficult to specify and not very useful -anyway. They are here for completeness. +=item B<< $constraint->equals($type_name_or_object) >> -=over 4 +A type is considered equal if it is also a union type, and the two +unions have the same member types. -=item B +=item B<< $constraint->find_type_for($value) >> -=item B +This returns the first member type constraint for which C is +true, allowing you to determine which of the Union's member type constraints +a given value matches. -=item B +=item B<< $constraint->is_a_type_of($type_name_or_object) >> -=item B +This returns true if all of the member type constraints return true +for the C method. -=item B +=item B<< $constraint->is_subtype_of >> -=item B +This returns true if all of the member type constraints return true +for the C method. -=item B +=item B<< $constraint->create_child_type(%options) >> -=item B +This returns a new L object with the type +as its parent. =back =head1 BUGS -All complex software has bugs lurking in it, and this module is no -exception. If you find a bug please either email me, or add the bug -to cpan-RT. - -=head1 AUTHOR - -Stevan Little Estevan@iinteractive.comE - -=head1 COPYRIGHT AND LICENSE - -Copyright 2006-2009 by Infinity Interactive, Inc. - -L - -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself. +See L for details on reporting bugs. =cut