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