RT#83929: fix memory leak in union types
[gitmo/Moose.git] / lib / Moose / Meta / TypeCoercion.pm
CommitLineData
6bf30233 1
2package Moose::Meta::TypeCoercion;
3
4use strict;
5use warnings;
6use metaclass;
7
a27aa600 8use Moose::Meta::Attribute;
a3c7e2fe 9use Moose::Util::TypeConstraints ();
a27aa600 10
a27aa600 11__PACKAGE__->meta->add_attribute('type_coercion_map' => (
12 reader => 'type_coercion_map',
dc2b7cc8 13 default => sub { [] },
14 Class::MOP::_definition_context(),
a27aa600 15));
d44714be 16
a27aa600 17__PACKAGE__->meta->add_attribute(
18 Moose::Meta::Attribute->new('type_constraint' => (
19 reader => 'type_constraint',
dc2b7cc8 20 weak_ref => 1,
21 Class::MOP::_definition_context(),
a27aa600 22 ))
23);
24
25# private accessor
26__PACKAGE__->meta->add_attribute('compiled_type_coercion' => (
dc2b7cc8 27 accessor => '_compiled_type_coercion',
28 Class::MOP::_definition_context(),
a27aa600 29));
30
d03bd989 31sub new {
a27aa600 32 my $class = shift;
d4db37e2 33 my $self = Class::MOP::class_of($class)->new_object(@_);
41e007e4 34 $self->compile_type_coercion;
a27aa600 35 return $self;
36}
37
38sub compile_type_coercion {
39 my $self = shift;
40 my @coercion_map = @{$self->type_coercion_map};
41 my @coercions;
42 while (@coercion_map) {
43 my ($constraint_name, $action) = splice(@coercion_map, 0, 2);
9c637fca 44 my $type_constraint = ref $constraint_name ? $constraint_name : Moose::Util::TypeConstraints::find_or_parse_type_constraint($constraint_name);
70ea9161 45
46 unless ( defined $type_constraint ) {
47 require Moose;
48 Moose->throw_error("Could not find the type constraint ($constraint_name) to coerce from");
49 }
50
d03bd989 51 push @coercions => [
52 $type_constraint->_compiled_type_constraint,
53 $action
e95c7c42 54 ];
a27aa600 55 }
d03bd989 56 $self->_compiled_type_coercion(sub {
a27aa600 57 my $thing = shift;
58 foreach my $coercion (@coercions) {
59 my ($constraint, $converter) = @$coercion;
42bc21a4 60 if ($constraint->($thing)) {
d03bd989 61 local $_ = $thing;
a27aa600 62 return $converter->($thing);
63 }
64 }
65 return $thing;
d03bd989 66 });
a27aa600 67}
68
41e007e4 69sub has_coercion_for_type {
70 my ($self, $type_name) = @_;
71 my %coercion_map = @{$self->type_coercion_map};
72 exists $coercion_map{$type_name} ? 1 : 0;
73}
74
75sub add_type_coercions {
76 my ($self, @new_coercion_map) = @_;
d03bd989 77
78 my $coercion_map = $self->type_coercion_map;
41e007e4 79 my %has_coercion = @$coercion_map;
d03bd989 80
41e007e4 81 while (@new_coercion_map) {
d03bd989 82 my ($constraint_name, $action) = splice(@new_coercion_map, 0, 2);
70ea9161 83
84 if ( exists $has_coercion{$constraint_name} ) {
85 require Moose;
86 Moose->throw_error("A coercion action already exists for '$constraint_name'")
87 }
88
41e007e4 89 push @{$coercion_map} => ($constraint_name, $action);
90 }
d03bd989 91
41e007e4 92 # and re-compile ...
93 $self->compile_type_coercion;
94}
95
a27aa600 96sub coerce { $_[0]->_compiled_type_coercion->($_[1]) }
97
98
6bf30233 991;
100
ad46f524 101# ABSTRACT: The Moose Type Coercion metaclass
102
6bf30233 103__END__
104
105=pod
106
6bf30233 107=head1 DESCRIPTION
108
3f961962 109A type coercion object is basically a mapping of one or more type
110constraints and the associated coercions subroutines.
6ba6d68c 111
3f961962 112It's unlikely that you will need to instantiate an object of this
113class directly, as it's part of the deep internals of Moose.
6ba6d68c 114
6bf30233 115=head1 METHODS
116
117=over 4
118
3f961962 119=item B<< Moose::Meta::TypeCoercion->new(%options) >>
6bf30233 120
3f961962 121Creates a new type coercion object, based on the options provided.
a27aa600 122
3f961962 123=over 8
a27aa600 124
3f961962 125=item * type_constraint
6ba6d68c 126
3f961962 127This is the L<Moose::Meta::TypeConstraint> object for the type that is
128being coerced I<to>.
a27aa600 129
3f961962 130=back
131
132=item B<< $coercion->type_coercion_map >>
133
134This returns the map of type constraints to coercions as an array
135reference. The values of the array alternate between type names and
136subroutine references which implement the coercion.
137
138The value is an array reference because coercions are tried in the
139order they are added.
140
141=item B<< $coercion->type_constraint >>
142
143This returns the L<Moose::Meta::TypeConstraint> that was passed to the
144constructor.
145
146=item B<< $coercion->has_coercion_for_type($type_name) >>
147
148Returns true if the coercion can coerce the named type.
149
150=item B<< $coercion->add_type_coercions( $type_name => $sub, ... ) >>
151
152This method takes a list of type names and subroutine references. If
153the coercion already has a mapping for a given type, it throws an
154exception.
155
156Coercions are actually
157
158=item B<< $coercion->coerce($value) >>
159
160This method takes a value and applies the first valid coercion it
161finds.
162
163This means that if the value could belong to more than type in the
164coercion object, the first coercion added is used.
a27aa600 165
3f961962 166=item B<< Moose::Meta::TypeCoercion->meta >>
41e007e4 167
3f961962 168This will return a L<Class::MOP::Class> instance for this class.
41e007e4 169
6bf30233 170=back
171
172=head1 BUGS
173
d4048ef3 174See L<Moose/BUGS> for details on reporting bugs.
6bf30233 175
42bc21a4 176=cut