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