Fix subtype()
[gitmo/Mouse.git] / lib / Mouse / Meta / TypeConstraint.pm
CommitLineData
684db121 1package Mouse::Meta::TypeConstraint;
2use strict;
3use warnings;
9c85e9dc 4
f5ee065f 5use overload
6 '""' => sub { shift->{name} }, # stringify to tc name
7 fallback => 1;
684db121 8
f5ee065f 9use Carp qw(confess);
10use Scalar::Util qw(blessed reftype);
6d28c5cf 11
53875581 12use Mouse::Util qw(:meta);
6d28c5cf 13
f5ee065f 14my $null_check = sub { 1 };
15
684db121 16sub new {
f5ee065f 17 my($class, %args) = @_;
18
19 $args{name} = '__ANON__' if !defined $args{name};
684db121 20
3b89ea91 21 my $check = delete $args{optimized};
22
23 if($args{_compiled_type_constraint}){
24 Carp::cluck("'_compiled_type_constraint' has been deprecated, use 'optimized' instead");
25 $check = $args{_compiled_type_constraint};
26
27 if(blessed($check)){
28 Carp::cluck("Constraint must be a CODE reference");
29 $check = $check->{compiled_type_constraint};
30 }
31 }
32
33 if($check){
34 $args{hand_optimized_type_constraint} = $check;
35 $args{compiled_type_constraint} = $check;
36 }
37
38 $check = $args{constraint};
f5ee065f 39
f5ee065f 40 if(blessed($check)){
3b89ea91 41 Carp::cluck("Constraint for $args{name} must be a CODE reference");
42 $check = $check->{compiled_type_constraint};
684db121 43 }
44
f5ee065f 45 if(defined($check) && ref($check) ne 'CODE'){
3b89ea91 46 confess("Constraint for $args{name} is not a CODE reference");
f5ee065f 47 }
48
3b89ea91 49 $args{package_defined_in} ||= caller;
50
f5ee065f 51 my $self = bless \%args, $class;
3b89ea91 52 $self->compile_type_constraint() if !$self->{hand_optimized_type_constraint};
f5ee065f 53
54 return $self;
55}
56
57sub create_child_type{
58 my $self = shift;
e98220ab 59 # XXX: FIXME
60 return ref($self)->new(
3b89ea91 61 # a child inherits its parent's attributes
62 %{$self},
63
64 # but does not inherit 'compiled_type_constraint' and 'hand_optimized_type_constraint'
65 compiled_type_constraint => undef,
66 hand_optimized_type_constraint => undef,
67
68 # and is given child-specific args, of course.
69 @_,
70
71 # and its parent
72 parent => $self,
e98220ab 73 );
684db121 74}
75
f5ee065f 76sub name { $_[0]->{name} }
77sub parent { $_[0]->{parent} }
78sub message { $_[0]->{message} }
684db121 79
3b89ea91 80sub _compiled_type_constraint{ $_[0]->{compiled_type_constraint} }
81
684db121 82sub check {
83 my $self = shift;
3b89ea91 84 $self->_compiled_type_constraint->(@_);
684db121 85}
86
9c85e9dc 87sub validate {
bc71de54 88 my ($self, $value) = @_;
3b89ea91 89 if ($self->_compiled_type_constraint->($value)) {
bc71de54 90 return undef;
91 }
92 else {
93 $self->get_message($value);
94 }
9c85e9dc 95}
96
bc71de54 97sub assert_valid {
98 my ($self, $value) = @_;
9c85e9dc 99
bc71de54 100 my $error = $self->validate($value);
101 return 1 if ! defined $error;
102
f5ee065f 103 confess($error);
29607c02 104}
105
106sub get_message {
107 my ($self, $value) = @_;
108 if ( my $msg = $self->message ) {
109 local $_ = $value;
110 return $msg->($value);
111 }
112 else {
113 $value = ( defined $value ? overload::StrVal($value) : 'undef' );
114 return
115 "Validation failed for '"
116 . $self->name
117 . "' failed with value $value";
118 }
119}
120
90fe520e 121sub is_a_type_of{
f5ee065f 122 my($self, $other) = @_;
123
124 # ->is_a_type_of('__ANON__') is always false
125 return 0 if !blessed($other) && $other eq '__ANON__';
126
127 (my $other_name = $other) =~ s/\s+//g;
90fe520e 128
f5ee065f 129 return 1 if $self->name eq $other_name;
130
131 if(exists $self->{type_constraints}){ # union
132 foreach my $type(@{$self->{type_constraints}}){
133 return 1 if $type->name eq $other_name;
134 }
135 }
136
137 for(my $parent = $self->parent; defined $parent; $parent = $parent->parent){
138 return 1 if $parent->name eq $other_name;
139 }
140
141 return 0;
142}
143
3b89ea91 144sub compile_type_constraint{
f5ee065f 145 my($self) = @_;
146
147 # add parents first
148 my @checks;
149 for(my $parent = $self->parent; defined $parent; $parent = $parent->parent){
3b89ea91 150 if($parent->{hand_optimized_type_constraint}){
151 push @checks, $parent->{hand_optimized_type_constraint};
152 last; # a hand optimized constraint must include all the parents
153 }
154 elsif($parent->{constraint}){
f5ee065f 155 push @checks, $parent->{constraint};
f5ee065f 156 }
157 }
3b89ea91 158
f5ee065f 159 # then add child
160 if($self->{constraint}){
161 push @checks, $self->{constraint};
162 }
163
3b89ea91 164 if($self->{type_constraints}){ # Union
165 my @types = map{ $_->_compiled_type_constraint } @{ $self->{type_constraints} };
166 push @checks, sub{
167 foreach my $c(@types){
168 return 1 if $c->($_[0]);
169 }
170 return 0;
171 };
172 }
173
f5ee065f 174 if(@checks == 0){
3b89ea91 175 $self->{compiled_type_constraint} = $null_check;
f5ee065f 176 }
177 elsif(@checks == 1){
178 my $c = $checks[0];
3b89ea91 179 $self->{compiled_type_constraint} = sub{
f5ee065f 180 my(@args) = @_;
181 local $_ = $args[0];
182 return $c->(@args);
183 };
184 }
185 else{
3b89ea91 186 $self->{compiled_type_constraint} = sub{
f5ee065f 187 my(@args) = @_;
188 local $_ = $args[0];
189 foreach my $c(@checks){
190 return undef if !$c->(@args);
191 }
192 return 1;
193 };
194 }
3b89ea91 195 return;
90fe520e 196}
197
684db121 1981;
199__END__
200
201=head1 NAME
202
1820fffe 203Mouse::Meta::TypeConstraint - The Mouse Type Constraint metaclass
684db121 204
205=head1 DESCRIPTION
206
207For the most part, the only time you will ever encounter an
208instance of this class is if you are doing some serious deep
209introspection. This API should not be considered final, but
210it is B<highly unlikely> that this will matter to a regular
211Mouse user.
212
213Don't use this.
214
215=head1 METHODS
216
217=over 4
218
219=item B<new>
220
221=item B<name>
222
223=back
224
1820fffe 225=head1 SEE ALSO
226
227L<Moose::Meta::TypeConstraint>
228
684db121 229=cut
230