reader presedence bug and tests
[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 Scalar::Util 'blessed';
571dd39f 9use B 'svref_2object';
10use Sub::Exporter;
a15dff8d 11
c899258b 12our $VERSION = '0.12';
d44714be 13our $AUTHORITY = 'cpan:STEVAN';
a15dff8d 14
4e036ee4 15use Moose::Meta::TypeConstraint;
2ca63f5d 16use Moose::Meta::TypeCoercion;
4e036ee4 17
571dd39f 18my @exports = qw/
8ecb1fa0 19 type subtype as where message optimize_as
571dd39f 20 coerce from via
21 enum
22 find_type_constraint
23/;
24
25Sub::Exporter::setup_exporter({
26 exports => \@exports,
27 groups => { default => [':all'] }
28});
29
30sub unimport {
31 no strict 'refs';
32 my $class = caller();
33 # loop through the exports ...
34 foreach my $name (@exports) {
35 # if we find one ...
36 if (defined &{$class . '::' . $name}) {
37 my $keyword = \&{$class . '::' . $name};
38
39 # make sure it is from Moose
40 my $pkg_name = eval { svref_2object($keyword)->GV->STASH->NAME };
41 next if $@;
42 next if $pkg_name ne 'Moose::Util::TypeConstraints';
43
44 # and if it is from Moose then undef the slot
45 delete ${$class . '::'}{$name};
46 }
2c0cbef7 47 }
571dd39f 48}
a15dff8d 49
182134e8 50{
51 my %TYPES;
2c0cbef7 52 sub find_type_constraint ($) {
446e850f 53 return $TYPES{$_[0]}->[1]
54 if exists $TYPES{$_[0]};
55 return;
56 }
57
58 sub _dump_type_constraints {
59 require Data::Dumper;
256903b6 60 Data::Dumper::Dumper(\%TYPES);
446e850f 61 }
62
8ecb1fa0 63 sub _create_type_constraint ($$$;$$) {
64 my $name = shift;
65 my $parent = shift;
66 my $check = shift;;
67
68 my ($message, $optimized);
69 for (@_) {
815ec671 70 $message = $_->{message} if exists $_->{message};
8ecb1fa0 71 $optimized = $_->{optimized} if exists $_->{optimized};
72 }
587ae0d2 73
a05c504e 74 my $pkg_defined_in = scalar(caller(0));
75
0e6614c3 76 ($TYPES{$name}->[0] eq $pkg_defined_in)
a05c504e 77 || confess ("The type constraint '$name' has already been created in "
78 . $TYPES{$name}->[0] . " and cannot be created again in "
79 . $pkg_defined_in)
80 if defined $name && exists $TYPES{$name};
81
0e6614c3 82 $parent = find_type_constraint($parent) if defined $parent;
a27aa600 83 my $constraint = Moose::Meta::TypeConstraint->new(
84 name => $name || '__ANON__',
66811d63 85 parent => $parent,
76d37e5a 86 constraint => $check,
87 message => $message,
8ecb1fa0 88 optimized => $optimized,
4e036ee4 89 );
0e6614c3 90 $TYPES{$name} = [ $pkg_defined_in, $constraint ] if defined $name;
a27aa600 91 return $constraint;
182134e8 92 }
182134e8 93
2c0cbef7 94 sub _install_type_coercions ($$) {
a27aa600 95 my ($type_name, $coercion_map) = @_;
0e6614c3 96 my $type = find_type_constraint($type_name);
4e036ee4 97 (!$type->has_coercion)
d46a48f3 98 || confess "The type coercion for '$type_name' has already been registered";
a27aa600 99 my $type_coercion = Moose::Meta::TypeCoercion->new(
100 type_coercion_map => $coercion_map,
101 type_constraint => $type
102 );
103 $type->coercion($type_coercion);
182134e8 104 }
66811d63 105
2c0cbef7 106 sub create_type_constraint_union (@) {
c07af9d2 107 my (@type_constraint_names) = @_;
108 return Moose::Meta::TypeConstraint->union(
109 map {
110 find_type_constraint($_)
111 } @type_constraint_names
112 );
113 }
114
429ccc11 115 sub export_type_constraints_as_functions {
66811d63 116 my $pkg = caller();
117 no strict 'refs';
118 foreach my $constraint (keys %TYPES) {
0e6614c3 119 *{"${pkg}::${constraint}"} = find_type_constraint($constraint)->_compiled_type_constraint;
66811d63 120 }
429ccc11 121 }
122
123 *Moose::Util::TypeConstraints::export_type_contstraints_as_functions = \&export_type_constraints_as_functions;
b1e01e3c 124
125 sub list_all_type_constraints { keys %TYPES }
182134e8 126}
a15dff8d 127
7c13858b 128# type constructors
a15dff8d 129
815ec671 130sub type ($$;$$) {
1b7df21f 131 splice(@_, 1, 0, undef);
132 goto &_create_type_constraint;
a15dff8d 133}
134
8ecb1fa0 135sub subtype ($$;$$$) {
136 unshift @_ => undef if scalar @_ <= 2;
2c0cbef7 137 goto &_create_type_constraint;
a15dff8d 138}
139
4b598ea3 140sub coerce ($@) {
66811d63 141 my ($type_name, @coercion_map) = @_;
7c13858b 142 _install_type_coercions($type_name, \@coercion_map);
182134e8 143}
144
76d37e5a 145sub as ($) { $_[0] }
146sub from ($) { $_[0] }
147sub where (&) { $_[0] }
148sub via (&) { $_[0] }
8ecb1fa0 149
150sub message (&) { +{ message => $_[0] } }
151sub optimize_as (&) { +{ optimized => $_[0] } }
a15dff8d 152
2c0cbef7 153sub enum ($;@) {
fcec2383 154 my ($type_name, @values) = @_;
2c0cbef7 155 (scalar @values >= 2)
156 || confess "You must have at least two values to enumerate through";
fcec2383 157 my $regexp = join '|' => @values;
158 _create_type_constraint(
159 $type_name,
160 'Str',
161 sub { qr/^$regexp$/i }
162 );
163}
164
a15dff8d 165# define some basic types
166
f65cb534 167type 'Any' => where { 1 }; # meta-type including all
168type 'Item' => where { 1 }; # base-type
a15dff8d 169
f65cb534 170subtype 'Undef' => as 'Item' => where { !defined($_) };
171subtype 'Defined' => as 'Item' => where { defined($_) };
a15dff8d 172
8ecb1fa0 173subtype 'Bool'
174 => as 'Item'
175 => where { !defined($_) || $_ eq "" || "$_" eq '1' || "$_" eq '0' };
5a4c5493 176
8ecb1fa0 177subtype 'Value'
178 => as 'Defined'
179 => where { !ref($_) }
180 => optimize_as { defined($_[0]) && !ref($_[0]) };
181
182subtype 'Ref'
183 => as 'Defined'
184 => where { ref($_) }
185 => optimize_as { ref($_[0]) };
186
187subtype 'Str'
188 => as 'Value'
189 => where { 1 }
190 => optimize_as { defined($_[0]) && !ref($_[0]) };
191
192subtype 'Num'
193 => as 'Value'
194 => where { Scalar::Util::looks_like_number($_) }
195 => optimize_as { !ref($_[0]) && Scalar::Util::looks_like_number($_[0]) };
196
197subtype 'Int'
198 => as 'Num'
199 => where { "$_" =~ /^-?[0-9]+$/ }
200 => optimize_as { defined($_[0]) && !ref($_[0]) && $_[0] =~ /^-?[0-9]+$/ };
201
202subtype 'ScalarRef' => as 'Ref' => where { ref($_) eq 'SCALAR' } => optimize_as { ref($_[0]) eq 'SCALAR' };
203subtype 'ArrayRef' => as 'Ref' => where { ref($_) eq 'ARRAY' } => optimize_as { ref($_[0]) eq 'ARRAY' };
204subtype 'HashRef' => as 'Ref' => where { ref($_) eq 'HASH' } => optimize_as { ref($_[0]) eq 'HASH' };
205subtype 'CodeRef' => as 'Ref' => where { ref($_) eq 'CODE' } => optimize_as { ref($_[0]) eq 'CODE' };
206subtype 'RegexpRef' => as 'Ref' => where { ref($_) eq 'Regexp' } => optimize_as { ref($_[0]) eq 'Regexp' };
207subtype 'GlobRef' => as 'Ref' => where { ref($_) eq 'GLOB' } => optimize_as { ref($_[0]) eq 'GLOB' };
a15dff8d 208
0a5bd159 209# NOTE:
210# scalar filehandles are GLOB refs,
211# but a GLOB ref is not always a filehandle
8ecb1fa0 212subtype 'FileHandle'
213 => as 'GlobRef'
214 => where { Scalar::Util::openhandle($_) }
215 => optimize_as { ref($_[0]) eq 'GLOB' && Scalar::Util::openhandle($_[0]) };
0a5bd159 216
a15dff8d 217# NOTE:
218# blessed(qr/.../) returns true,.. how odd
8ecb1fa0 219subtype 'Object'
220 => as 'Ref'
221 => where { blessed($_) && blessed($_) ne 'Regexp' }
222 => optimize_as { blessed($_[0]) && blessed($_[0]) ne 'Regexp' };
a15dff8d 223
8ecb1fa0 224subtype 'Role'
225 => as 'Object'
226 => where { $_->can('does') }
227 => optimize_as { blessed($_[0]) && $_[0]->can('does') };
02a0fb52 228
943596a6 229{
230 my @BUILTINS = list_all_type_constraints();
231 sub list_all_builtin_type_constraints { @BUILTINS }
232}
233
a15dff8d 2341;
235
236__END__
237
238=pod
239
240=head1 NAME
241
e522431d 242Moose::Util::TypeConstraints - Type constraint system for Moose
a15dff8d 243
244=head1 SYNOPSIS
245
246 use Moose::Util::TypeConstraints;
247
2c0cbef7 248 type 'Num' => where { Scalar::Util::looks_like_number($_) };
a15dff8d 249
2c0cbef7 250 subtype 'Natural'
251 => as 'Num'
a15dff8d 252 => where { $_ > 0 };
253
2c0cbef7 254 subtype 'NaturalLessThanTen'
255 => as 'Natural'
79592a54 256 => where { $_ < 10 }
257 => message { "This number ($_) is not less than ten!" };
6b8bd8d3 258
2c0cbef7 259 coerce 'Num'
260 => from 'Str'
d6e2d9a1 261 => via { 0+$_ };
98aae381 262
2c0cbef7 263 enum 'RGBColors' => qw(red green blue);
a15dff8d 264
265=head1 DESCRIPTION
266
d44714be 267This module provides Moose with the ability to create custom type
268contraints to be used in attribute definition.
e522431d 269
6ba6d68c 270=head2 Important Caveat
271
272This is B<NOT> a type system for Perl 5. These are type constraints,
273and they are not used by Moose unless you tell it to. No type
274inference is performed, expression are not typed, etc. etc. etc.
275
276This is simply a means of creating small constraint functions which
a7d0cd00 277can be used to simplify your own type-checking code.
6ba6d68c 278
2c0cbef7 279=head2 Slightly Less Important Caveat
280
281It is almost always a good idea to quote your type and subtype names.
43d599e5 282This is to prevent perl from trying to execute the call as an indirect
2c0cbef7 283object call. This issue only seems to come up when you have a subtype
284the same name as a valid class, but when the issue does arise it tends
285to be quite annoying to debug.
286
287So for instance, this:
288
289 subtype DateTime => as Object => where { $_->isa('DateTime') };
290
291will I<Just Work>, while this:
292
293 use DateTime;
294 subtype DateTime => as Object => where { $_->isa('DateTime') };
295
296will fail silently and cause many headaches. The simple way to solve
297this, as well as future proof your subtypes from classes which have
298yet to have been created yet, is to simply do this:
299
300 use DateTime;
d44714be 301 subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
2c0cbef7 302
6ba6d68c 303=head2 Default Type Constraints
e522431d 304
e522431d 305This module also provides a simple hierarchy for Perl 5 types, this
306could probably use some work, but it works for me at the moment.
307
308 Any
f65cb534 309 Item
5a4c5493 310 Bool
f65cb534 311 Undef
312 Defined
5a4c5493 313 Value
314 Num
315 Int
316 Str
317 Ref
318 ScalarRef
451c8248 319 ArrayRef
320 HashRef
5a4c5493 321 CodeRef
322 RegexpRef
3f7376b0 323 GlobRef
0a5bd159 324 FileHandle
5a4c5493 325 Object
326 Role
e522431d 327
6ba6d68c 328Suggestions for improvement are welcome.
2c0cbef7 329
330B<NOTE:> The C<Undef> type constraint does not work correctly
331in every occasion, please use it sparringly.
703e92fb 332
333=head2 Use with Other Constraint Modules
334
335This module should play fairly nicely with other constraint
336modules with only some slight tweaking. The C<where> clause
337in types is expected to be a C<CODE> reference which checks
338it's first argument and returns a bool. Since most constraint
339modules work in a similar way, it should be simple to adapt
340them to work with Moose.
341
342For instance, this is how you could use it with
66c57662 343L<Declare::Constraints::Simple> to declare a completely new type.
703e92fb 344
345 type 'HashOfArrayOfObjects'
346 => IsHashRef(
347 -keys => HasLength,
348 -values => IsArrayRef( IsObject ));
349
350For more examples see the F<t/204_example_w_DCS.t> test file.
351
352Here is an example of using L<Test::Deep> and it's non-test
353related C<eq_deeply> function.
354
355 type 'ArrayOfHashOfBarsAndRandomNumbers'
356 => where {
357 eq_deeply($_,
358 array_each(subhashof({
359 bar => isa('Bar'),
360 random_number => ignore()
361 })))
362 };
363
364For a complete example see the F<t/205_example_w_TestDeep.t>
365test file.
e522431d 366
a15dff8d 367=head1 FUNCTIONS
368
182134e8 369=head2 Type Constraint Registry
370
371=over 4
372
373=item B<find_type_constraint ($type_name)>
374
6ba6d68c 375This function can be used to locate a specific type constraint
376meta-object. What you do with it from there is up to you :)
182134e8 377
c07af9d2 378=item B<create_type_constraint_union (@type_constraint_names)>
379
380Given a list of C<@type_constraint_names>, this will return a
381B<Moose::Meta::TypeConstraint::Union> instance.
382
429ccc11 383=item B<export_type_constraints_as_functions>
182134e8 384
6ba6d68c 385This will export all the current type constraints as functions
386into the caller's namespace. Right now, this is mostly used for
387testing, but it might prove useful to others.
388
429ccc11 389=item B<export_type_contstraints_as_functions>
390
391Alias for the above function.
392
b1e01e3c 393=item B<list_all_type_constraints>
394
395This will return a list of type constraint names, you can then
396fetch them using C<find_type_constraint ($type_name)> if you
397want to.
398
943596a6 399=item B<list_all_builtin_type_constraints>
400
401This will return a list of builtin type constraints, meaning,
402those which are defined in this module. See the section
403labeled L<Default Type Constraints> for a complete list.
404
182134e8 405=back
406
a15dff8d 407=head2 Type Constraint Constructors
408
6ba6d68c 409The following functions are used to create type constraints.
410They will then register the type constraints in a global store
411where Moose can get to them if it needs to.
a15dff8d 412
25f2c3fc 413See the L<SYNOPSIS> for an example of how to use these.
a15dff8d 414
6ba6d68c 415=over 4
a15dff8d 416
6ba6d68c 417=item B<type ($name, $where_clause)>
a15dff8d 418
6ba6d68c 419This creates a base type, which has no parent.
a15dff8d 420
79592a54 421=item B<subtype ($name, $parent, $where_clause, ?$message)>
182134e8 422
6ba6d68c 423This creates a named subtype.
d6e2d9a1 424
79592a54 425=item B<subtype ($parent, $where_clause, ?$message)>
182134e8 426
6ba6d68c 427This creates an unnamed subtype and will return the type
428constraint meta-object, which will be an instance of
429L<Moose::Meta::TypeConstraint>.
a15dff8d 430
fcec2383 431=item B<enum ($name, @values)>
432
2c0cbef7 433This will create a basic subtype for a given set of strings.
434The resulting constraint will be a subtype of C<Str> and
435will match any of the items in C<@values>. See the L<SYNOPSIS>
436for a simple example.
437
438B<NOTE:> This is not a true proper enum type, it is simple
439a convient constraint builder.
440
6ba6d68c 441=item B<as>
a15dff8d 442
6ba6d68c 443This is just sugar for the type constraint construction syntax.
a15dff8d 444
6ba6d68c 445=item B<where>
a15dff8d 446
6ba6d68c 447This is just sugar for the type constraint construction syntax.
76d37e5a 448
449=item B<message>
450
451This is just sugar for the type constraint construction syntax.
a15dff8d 452
8ecb1fa0 453=item B<optimize_as>
454
d44714be 455This can be used to define a "hand optimized" version of your
456type constraint which can be used to avoid traversing a subtype
457constraint heirarchy.
458
459B<NOTE:> You should only use this if you know what you are doing,
460all the built in types use this, so your subtypes (assuming they
461are shallow) will not likely need to use this.
462
6ba6d68c 463=back
a15dff8d 464
6ba6d68c 465=head2 Type Coercion Constructors
a15dff8d 466
587ae0d2 467Type constraints can also contain type coercions as well. If you
468ask your accessor too coerce, the Moose will run the type-coercion
469code first, followed by the type constraint check. This feature
470should be used carefully as it is very powerful and could easily
471take off a limb if you are not careful.
a15dff8d 472
25f2c3fc 473See the L<SYNOPSIS> for an example of how to use these.
a15dff8d 474
6ba6d68c 475=over 4
a15dff8d 476
6ba6d68c 477=item B<coerce>
a15dff8d 478
6ba6d68c 479=item B<from>
a15dff8d 480
6ba6d68c 481This is just sugar for the type coercion construction syntax.
482
483=item B<via>
a15dff8d 484
6ba6d68c 485This is just sugar for the type coercion construction syntax.
a15dff8d 486
487=back
488
571dd39f 489=head2 Namespace Management
490
491=over 4
492
493=item B<unimport>
494
495This will remove all the type constraint keywords from the
496calling class namespace.
497
498=back
499
a15dff8d 500=head1 BUGS
501
502All complex software has bugs lurking in it, and this module is no
503exception. If you find a bug please either email me, or add the bug
504to cpan-RT.
505
a15dff8d 506=head1 AUTHOR
507
508Stevan Little E<lt>stevan@iinteractive.comE<gt>
509
510=head1 COPYRIGHT AND LICENSE
511
b77fdbed 512Copyright 2006, 2007 by Infinity Interactive, Inc.
a15dff8d 513
514L<http://www.iinteractive.com>
515
516This library is free software; you can redistribute it and/or modify
517it under the same terms as Perl itself.
518
81dc201f 519=cut