coercion happens during obj construction too
[gitmo/Moose.git] / lib / Moose / Util / TypeConstraints.pm
CommitLineData
a15dff8d 1
2package Moose::Util::TypeConstraints;
3
4use strict;
5use warnings;
6
e90c03d0 7use Carp 'confess';
a15dff8d 8use Sub::Name 'subname';
9use Scalar::Util 'blessed';
10
5569c072 11our $VERSION = '0.02';
a15dff8d 12
13sub import {
14 shift;
15 my $pkg = shift || caller();
16 return if $pkg eq ':no_export';
17 no strict 'refs';
d6e2d9a1 18 foreach my $export (qw(type subtype as where coerce from via)) {
a15dff8d 19 *{"${pkg}::${export}"} = \&{"${export}"};
a15dff8d 20 }
a15dff8d 21}
22
182134e8 23{
24 my %TYPES;
25 sub find_type_constraint {
26 my $type_name = shift;
27 $TYPES{$type_name};
28 }
29
30 sub register_type_constraint {
31 my ($type_name, $type_constraint) = @_;
32 $TYPES{$type_name} = $type_constraint;
33 }
34
4b598ea3 35 sub dump_type_constraints {
36 require Data::Dumper;
37 $Data::Dumper::Deparse = 1;
38 Data::Dumper::Dumper(\%TYPES);
39 }
40
182134e8 41 sub export_type_contstraints_as_functions {
42 my $pkg = caller();
43 no strict 'refs';
44 foreach my $constraint (keys %TYPES) {
45 *{"${pkg}::${constraint}"} = $TYPES{$constraint};
46 }
47 }
48}
49
50{
51 my %COERCIONS;
52 sub find_type_coercion {
53 my $type_name = shift;
54 $COERCIONS{$type_name};
55 }
56
57 sub register_type_coercion {
58 my ($type_name, $type_coercion) = @_;
59 $COERCIONS{$type_name} = $type_coercion;
60 }
61}
a15dff8d 62
a15dff8d 63
64sub type ($$) {
65 my ($name, $check) = @_;
182134e8 66 my $full_name = caller() . "::${name}";
67 register_type_constraint($name => subname $full_name => sub {
a15dff8d 68 local $_ = $_[0];
69 return undef unless $check->($_[0]);
70 $_[0];
182134e8 71 });
a15dff8d 72}
73
74sub subtype ($$;$) {
75 my ($name, $parent, $check) = @_;
76 if (defined $check) {
182134e8 77 my $full_name = caller() . "::${name}";
78 $parent = find_type_constraint($parent)
79 unless $parent && ref($parent) eq 'CODE';
6b8bd8d3 80 register_type_constraint($name => subname $full_name => sub {
a15dff8d 81 local $_ = $_[0];
82 return undef unless defined $parent->($_[0]) && $check->($_[0]);
83 $_[0];
182134e8 84 });
a15dff8d 85 }
86 else {
87 ($parent, $check) = ($name, $parent);
182134e8 88 $parent = find_type_constraint($parent)
89 unless $parent && ref($parent) eq 'CODE';
90 return subname '__anon_subtype__' => sub {
a15dff8d 91 local $_ = $_[0];
92 return undef unless defined $parent->($_[0]) && $check->($_[0]);
93 $_[0];
5569c072 94 };
a15dff8d 95 }
96}
97
4b598ea3 98sub coerce ($@) {
e90c03d0 99 my ($type_name, @coercion_map) = @_;
4b598ea3 100 #use Data::Dumper;
101 #warn Dumper \@coercion_map;
e90c03d0 102 my @coercions;
103 while (@coercion_map) {
104 my ($constraint_name, $action) = splice(@coercion_map, 0, 2);
105 my $constraint = find_type_constraint($constraint_name);
106 (defined $constraint)
107 || confess "Could not find the type constraint ($constraint_name)";
108 push @coercions => [ $constraint, $action ];
109 }
182134e8 110 register_type_coercion($type_name, sub {
e90c03d0 111 my $thing = shift;
112 foreach my $coercion (@coercions) {
113 my ($constraint, $converter) = @$coercion;
114 if (defined $constraint->($thing)) {
115 return $converter->($thing);
116 }
117 }
118 return $thing;
182134e8 119 });
120}
121
a15dff8d 122sub as ($) { $_[0] }
d6e2d9a1 123sub from ($) { $_[0] }
a15dff8d 124sub where (&) { $_[0] }
d6e2d9a1 125sub via (&) { $_[0] }
a15dff8d 126
127# define some basic types
128
129type Any => where { 1 };
130
131type Value => where { !ref($_) };
132type Ref => where { ref($_) };
133
134subtype Int => as Value => where { Scalar::Util::looks_like_number($_) };
135subtype Str => as Value => where { !Scalar::Util::looks_like_number($_) };
136
137subtype ScalarRef => as Ref => where { ref($_) eq 'SCALAR' };
138subtype ArrayRef => as Ref => where { ref($_) eq 'ARRAY' };
139subtype HashRef => as Ref => where { ref($_) eq 'HASH' };
140subtype CodeRef => as Ref => where { ref($_) eq 'CODE' };
141subtype RegexpRef => as Ref => where { ref($_) eq 'Regexp' };
142
143# NOTE:
144# blessed(qr/.../) returns true,.. how odd
145subtype Object => as Ref => where { blessed($_) && blessed($_) ne 'Regexp' };
146
1471;
148
149__END__
150
151=pod
152
153=head1 NAME
154
e522431d 155Moose::Util::TypeConstraints - Type constraint system for Moose
a15dff8d 156
157=head1 SYNOPSIS
158
159 use Moose::Util::TypeConstraints;
160
161 type Num => where { Scalar::Util::looks_like_number($_) };
162
163 subtype Natural
164 => as Num
165 => where { $_ > 0 };
166
167 subtype NaturalLessThanTen
168 => as Natural
169 => where { $_ < 10 };
6b8bd8d3 170
171 coerce Num
d6e2d9a1 172 => from Str
173 => via { 0+$_ };
a15dff8d 174
175=head1 DESCRIPTION
176
e522431d 177This module provides Moose with the ability to create type contraints
178to be are used in both attribute definitions and for method argument
179validation.
180
181This is B<NOT> a type system for Perl 5.
182
e522431d 183This module also provides a simple hierarchy for Perl 5 types, this
184could probably use some work, but it works for me at the moment.
185
186 Any
187 Value
188 Int
189 Str
190 Ref
191 ScalarRef
192 ArrayRef
193 HashRef
194 CodeRef
195 RegexpRef
196 Object
197
198Suggestions for improvement are welcome.
199
a15dff8d 200=head1 FUNCTIONS
201
182134e8 202=head2 Type Constraint Registry
203
204=over 4
205
206=item B<find_type_constraint ($type_name)>
207
208=item B<register_type_constraint ($type_name, $type_constraint)>
209
210=item B<find_type_coercion>
211
212=item B<register_type_coercion>
213
214=item B<export_type_contstraints_as_functions>
215
4b598ea3 216=item B<dump_type_constraints>
217
182134e8 218=back
219
a15dff8d 220=head2 Type Constraint Constructors
221
222=over 4
223
224=item B<type>
225
226=item B<subtype>
227
228=item B<as>
229
230=item B<where>
231
182134e8 232=item B<coerce>
233
d6e2d9a1 234=item B<from>
235
236=item B<via>
182134e8 237
a15dff8d 238=back
239
240=head2 Built-in Type Constraints
241
242=over 4
243
244=item B<Any>
245
246=item B<Value>
247
248=item B<Int>
249
250=item B<Str>
251
252=item B<Ref>
253
254=item B<ArrayRef>
255
256=item B<CodeRef>
257
258=item B<HashRef>
259
260=item B<RegexpRef>
261
262=item B<ScalarRef>
263
264=item B<Object>
265
266=back
267
268=head1 BUGS
269
270All complex software has bugs lurking in it, and this module is no
271exception. If you find a bug please either email me, or add the bug
272to cpan-RT.
273
a15dff8d 274=head1 AUTHOR
275
276Stevan Little E<lt>stevan@iinteractive.comE<gt>
277
278=head1 COPYRIGHT AND LICENSE
279
280Copyright 2006 by Infinity Interactive, Inc.
281
282L<http://www.iinteractive.com>
283
284This library is free software; you can redistribute it and/or modify
285it under the same terms as Perl itself.
286
287=cut