s/container/parameterized/
[gitmo/Moose.git] / lib / Moose / Util / TypeConstraints.pm
1
2 package Moose::Util::TypeConstraints;
3
4 use strict;
5 use warnings;
6
7 use Carp         'confess';
8 use Scalar::Util 'blessed', 'reftype';
9 use B            'svref_2object';
10 use Sub::Exporter;
11
12 our $VERSION   = '0.14';
13 our $AUTHORITY = 'cpan:STEVAN';
14
15 ## --------------------------------------------------------
16 # Prototyped subs must be predeclared because we have a 
17 # circular dependency with Moose::Meta::Attribute et. al. 
18 # so in case of us being use'd first the predeclaration 
19 # ensures the prototypes are in scope when consumers are
20 # compiled.
21
22 # creation and location
23 sub find_type_constraint                 ($);
24 sub find_or_create_type_constraint       ($;$);
25 sub create_type_constraint_union         (@);
26 sub create_parameterized_type_constraint ($);
27
28 # dah sugah!
29 sub type        ($$;$$);
30 sub subtype     ($$;$$$);
31 sub coerce      ($@);
32 sub as          ($);
33 sub from        ($);
34 sub where       (&);
35 sub via         (&);
36 sub message     (&);
37 sub optimize_as (&);
38 sub enum        ($;@);
39
40 ## private stuff ...        
41 sub _create_type_constraint ($$$;$$);
42 sub _install_type_coercions ($$);
43
44 ## --------------------------------------------------------
45
46 use Moose::Meta::TypeConstraint;
47 use Moose::Meta::TypeConstraint::Union;
48 use Moose::Meta::TypeConstraint::Parameterized;
49 use Moose::Meta::TypeCoercion;
50 use Moose::Meta::TypeCoercion::Union;
51 use Moose::Meta::TypeConstraint::Registry;
52
53 my @exports = qw/
54     type subtype as where message optimize_as
55     coerce from via 
56     enum
57     find_type_constraint
58 /;
59
60 Sub::Exporter::setup_exporter({ 
61     exports => \@exports,
62     groups  => { default => [':all'] }
63 });
64
65 sub unimport {
66     no strict 'refs';    
67     my $class = caller();
68     # loop through the exports ...
69     foreach my $name (@exports) {
70         # if we find one ...
71         if (defined &{$class . '::' . $name}) {
72             my $keyword = \&{$class . '::' . $name};
73             
74             # make sure it is from Moose
75             my $pkg_name = eval { svref_2object($keyword)->GV->STASH->NAME };
76             next if $@;
77             next if $pkg_name ne 'Moose::Util::TypeConstraints';
78             
79             # and if it is from Moose then undef the slot
80             delete ${$class . '::'}{$name};
81         }
82     }
83 }
84
85 ## --------------------------------------------------------
86 ## type registry and some useful functions for it
87 ## --------------------------------------------------------
88
89 my $REGISTRY = Moose::Meta::TypeConstraint::Registry->new;
90
91 sub get_type_constraint_registry         { $REGISTRY }
92 sub list_all_type_constraints            { keys %{$REGISTRY->type_constraints} }   
93 sub export_type_constraints_as_functions {
94     my $pkg = caller();
95     no strict 'refs';
96         foreach my $constraint (keys %{$REGISTRY->type_constraints}) {
97                 *{"${pkg}::${constraint}"} = $REGISTRY->get_type_constraint($constraint)
98                                                       ->_compiled_type_constraint;
99         }        
100 }
101
102 sub create_type_constraint_union (@) {
103     my @type_constraint_names;
104     
105     if (scalar @_ == 1 && _detect_type_constraint_union($_[0])) {
106         @type_constraint_names = _parse_type_constraint_union($_[0]);
107     }
108     else {
109         @type_constraint_names = @_;
110     }
111     
112     (scalar @type_constraint_names >= 2)
113         || confess "You must pass in at least 2 type names to make a union"; 
114         
115     ($REGISTRY->has_type_constraint($_))
116         || confess "Could not locate type constraint ($_) for the union"
117             foreach @type_constraint_names;
118            
119     return Moose::Meta::TypeConstraint::Union->new(
120         type_constraints => [
121             map { 
122                 $REGISTRY->get_type_constraint($_) 
123             } @type_constraint_names        
124         ],
125     );    
126 }
127
128 sub create_parameterized_type_constraint ($) {
129     my $type_constraint_name = shift;
130     
131     my ($base_type, $type_parameter) = _parse_parameterized_type_constraint($type_constraint_name);
132     
133     (defined $base_type && defined $type_parameter)
134         || confess "Could not parse type name ($type_constraint_name) correctly";
135     
136     ($REGISTRY->has_type_constraint($base_type))
137         || confess "Could not locate the base type ($base_type)";
138     
139     return Moose::Meta::TypeConstraint::Parameterized->new(
140         name           => $type_constraint_name,
141         parent         => $REGISTRY->get_type_constraint($base_type),
142         type_parameter => find_or_create_type_constraint(
143             $type_parameter => {
144                 parent     => $REGISTRY->get_type_constraint('Object'),
145                 constraint => sub { $_[0]->isa($type_parameter) }
146             }
147         ),
148     );    
149 }
150
151 sub find_or_create_type_constraint ($;$) {
152     my ($type_constraint_name, $options_for_anon_type) = @_;
153     
154     return $REGISTRY->get_type_constraint($type_constraint_name)
155         if $REGISTRY->has_type_constraint($type_constraint_name);
156     
157     my $constraint;
158     
159     if (_detect_type_constraint_union($type_constraint_name)) {
160         $constraint = create_type_constraint_union($type_constraint_name);
161     }
162     elsif (_detect_parameterized_type_constraint($type_constraint_name)) {
163         $constraint = create_parameterized_type_constraint($type_constraint_name);       
164     }
165     else {
166         # NOTE:
167         # otherwise assume that we should create
168         # an ANON type with the $options_for_anon_type 
169         # options which can be passed in. It should
170         # be noted that these don't get registered 
171         # so we need to return it.
172         # - SL
173         return Moose::Meta::TypeConstraint->new(
174             name => '__ANON__',
175             %{$options_for_anon_type}     
176         );
177     }
178     
179     $REGISTRY->add_type_constraint($constraint);
180     return $constraint;    
181 }
182
183 ## --------------------------------------------------------
184 ## exported functions ...
185 ## --------------------------------------------------------
186
187 sub find_type_constraint ($) { $REGISTRY->get_type_constraint(@_) }
188
189 # type constructors
190
191 sub type ($$;$$) {
192     splice(@_, 1, 0, undef);
193         goto &_create_type_constraint;  
194 }
195
196 sub subtype ($$;$$$) {
197     # NOTE:
198     # this adds an undef for the name
199     # if this is an anon-subtype:
200     #   subtype(Num => where { $_ % 2 == 0 }) # anon 'even' subtype
201     # but if the last arg is not a code
202     # ref then it is a subtype alias:
203     #   subtype(MyNumbers => as Num); # now MyNumbers is the same as Num
204     # ... yeah I know it's ugly code 
205     # - SL
206         unshift @_ => undef if scalar @_ <= 2 && (reftype($_[1]) || '') eq 'CODE';      
207         goto &_create_type_constraint;
208 }
209
210 sub coerce ($@) {
211     my ($type_name, @coercion_map) = @_;   
212     _install_type_coercions($type_name, \@coercion_map);
213 }
214
215 sub as      ($) { $_[0] }
216 sub from    ($) { $_[0] }
217 sub where   (&) { $_[0] }
218 sub via     (&) { $_[0] }
219
220 sub message     (&) { +{ message   => $_[0] } }
221 sub optimize_as (&) { +{ optimized => $_[0] } }
222
223 sub enum ($;@) {
224     my ($type_name, @values) = @_;
225     (scalar @values >= 2)
226         || confess "You must have at least two values to enumerate through";
227     my $regexp = join '|' => @values;
228         _create_type_constraint(
229             $type_name,
230             'Str',
231             sub { qr/^$regexp$/i }
232         );    
233 }
234
235 ## --------------------------------------------------------
236 ## desugaring functions ...
237 ## --------------------------------------------------------
238
239 sub _create_type_constraint ($$$;$$) { 
240     my $name   = shift;
241     my $parent = shift;
242     my $check  = shift || sub { 1 };
243     
244     my ($message, $optimized);
245     for (@_) {
246         $message   = $_->{message}   if exists $_->{message};
247         $optimized = $_->{optimized} if exists $_->{optimized};            
248     }
249
250     my $pkg_defined_in = scalar(caller(0));
251     
252     if (defined $name) {
253         my $type = $REGISTRY->get_type_constraint($name);
254     
255         ($type->_package_defined_in eq $pkg_defined_in)
256             || confess ("The type constraint '$name' has already been created in " 
257                        . $type->_package_defined_in . " and cannot be created again in "
258                        . $pkg_defined_in)
259                  if defined $type;   
260     }                    
261                           
262     $parent = $REGISTRY->get_type_constraint($parent) if defined $parent;
263     
264     my $constraint = Moose::Meta::TypeConstraint->new(
265         name               => $name || '__ANON__',
266         parent             => $parent,            
267         constraint         => $check,       
268         message            => $message,    
269         optimized          => $optimized,
270         package_defined_in => $pkg_defined_in,
271     );
272
273     $REGISTRY->add_type_constraint($constraint)
274         if defined $name;
275
276     return $constraint;
277 }
278
279 sub _install_type_coercions ($$) { 
280     my ($type_name, $coercion_map) = @_;
281     my $type = $REGISTRY->get_type_constraint($type_name);
282     (!$type->has_coercion)
283         || confess "The type coercion for '$type_name' has already been registered";        
284     my $type_coercion = Moose::Meta::TypeCoercion->new(
285         type_coercion_map => $coercion_map,
286         type_constraint   => $type
287     );            
288     $type->coercion($type_coercion);
289 }
290
291 ## --------------------------------------------------------
292 ## type notation parsing ...
293 ## --------------------------------------------------------
294
295 {
296     # All I have to say is mugwump++ cause I know 
297     # do not even have enough regexp-fu to be able 
298     # to have written this (I can only barely 
299     # understand it as it is)
300     # - SL 
301     
302     use re "eval";
303
304     my $valid_chars = qr{[\w:|]};
305     my $type_atom   = qr{ $valid_chars+ };
306
307     my $type                = qr{  $valid_chars+  (?: \[  (??{$any})  \] )? }x;
308     my $type_capture_parts  = qr{ ($valid_chars+) (?: \[ ((??{$any})) \] )? }x;
309     my $type_with_parameter = qr{  $valid_chars+      \[  (??{$any})  \]    }x;
310
311     my $op_union = qr{ \s+ \| \s+ }x;
312     my $union    = qr{ $type (?: $op_union $type )+ }x;
313
314     our $any = qr{ $type | $union }x;
315
316     sub _parse_parameterized_type_constraint {
317         $_[0] =~ m{ $type_capture_parts }x;
318         return ($1, $2);
319     }
320
321     sub _detect_parameterized_type_constraint {
322         $_[0] =~ m{ ^ $type_with_parameter $ }x;
323     }
324
325     sub _parse_type_constraint_union {
326         my $given = shift;
327         my @rv;
328         while ( $given =~ m{ \G (?: $op_union )? ($type) }gcx ) {
329                 push @rv => $1;
330         }
331         (pos($given) eq length($given))
332             || confess "'$given' didn't parse (parse-pos=" 
333                      . pos($given) 
334                      . " and str-length="
335                      . length($given)
336                      . ")";
337         @rv;
338     }
339
340     sub _detect_type_constraint_union {
341         $_[0] =~ m{^ $type $op_union $type ( $op_union .* )? $}x;
342     }
343 }
344
345 ## --------------------------------------------------------
346 # define some basic built-in types
347 ## --------------------------------------------------------
348
349 type 'Any'  => where { 1 }; # meta-type including all
350 type 'Item' => where { 1 }; # base-type 
351
352 subtype 'Undef'   => as 'Item' => where { !defined($_) };
353 subtype 'Defined' => as 'Item' => where {  defined($_) };
354
355 subtype 'Bool'
356     => as 'Item' 
357     => where { !defined($_) || $_ eq "" || "$_" eq '1' || "$_" eq '0' };
358
359 subtype 'Value' 
360     => as 'Defined' 
361     => where { !ref($_) } 
362     => optimize_as { defined($_[0]) && !ref($_[0]) };
363     
364 subtype 'Ref'
365     => as 'Defined' 
366     => where {  ref($_) } 
367     => optimize_as { ref($_[0]) };
368
369 subtype 'Str' 
370     => as 'Value' 
371     => where { 1 } 
372     => optimize_as { defined($_[0]) && !ref($_[0]) };
373
374 subtype 'Num' 
375     => as 'Value' 
376     => where { Scalar::Util::looks_like_number($_) } 
377     => optimize_as { !ref($_[0]) && Scalar::Util::looks_like_number($_[0]) };
378     
379 subtype 'Int' 
380     => as 'Num'   
381     => where { "$_" =~ /^-?[0-9]+$/ }
382     => optimize_as { defined($_[0]) && !ref($_[0]) && $_[0] =~ /^-?[0-9]+$/ };
383
384 subtype 'ScalarRef' => as 'Ref' => where { ref($_) eq 'SCALAR' } => optimize_as { ref($_[0]) eq 'SCALAR' };
385 subtype 'ArrayRef'  => as 'Ref' => where { ref($_) eq 'ARRAY'  } => optimize_as { ref($_[0]) eq 'ARRAY'  };
386 subtype 'HashRef'   => as 'Ref' => where { ref($_) eq 'HASH'   } => optimize_as { ref($_[0]) eq 'HASH'   };     
387 subtype 'CodeRef'   => as 'Ref' => where { ref($_) eq 'CODE'   } => optimize_as { ref($_[0]) eq 'CODE'   };
388 subtype 'RegexpRef' => as 'Ref' => where { ref($_) eq 'Regexp' } => optimize_as { ref($_[0]) eq 'Regexp' };     
389 subtype 'GlobRef'   => as 'Ref' => where { ref($_) eq 'GLOB'   } => optimize_as { ref($_[0]) eq 'GLOB'   };
390
391 # NOTE:
392 # scalar filehandles are GLOB refs, 
393 # but a GLOB ref is not always a filehandle
394 subtype 'FileHandle' 
395     => as 'GlobRef' 
396     => where { Scalar::Util::openhandle($_) }
397     => optimize_as { ref($_[0]) eq 'GLOB' && Scalar::Util::openhandle($_[0]) };
398
399 # NOTE: 
400 # blessed(qr/.../) returns true,.. how odd
401 subtype 'Object' 
402     => as 'Ref' 
403     => where { blessed($_) && blessed($_) ne 'Regexp' }
404     => optimize_as { blessed($_[0]) && blessed($_[0]) ne 'Regexp' };
405
406 subtype 'Role' 
407     => as 'Object' 
408     => where { $_->can('does') }
409     => optimize_as { blessed($_[0]) && $_[0]->can('does') };
410     
411 subtype 'ClassName' 
412     => as 'Str' 
413     => where { eval { $_->isa('UNIVERSAL') } }
414     => optimize_as { !ref($_[0]) && eval { $_[0]->isa('UNIVERSAL') } };    
415
416 ## --------------------------------------------------------
417 # end of built-in types ...
418 ## --------------------------------------------------------
419
420 {
421     my @BUILTINS = list_all_type_constraints();
422     sub list_all_builtin_type_constraints { @BUILTINS }
423 }
424
425 1;
426
427 __END__
428
429 =pod
430
431 =head1 NAME
432
433 Moose::Util::TypeConstraints - Type constraint system for Moose
434
435 =head1 SYNOPSIS
436
437   use Moose::Util::TypeConstraints;
438
439   type 'Num' => where { Scalar::Util::looks_like_number($_) };
440   
441   subtype 'Natural' 
442       => as 'Num' 
443       => where { $_ > 0 };
444   
445   subtype 'NaturalLessThanTen' 
446       => as 'Natural'
447       => where { $_ < 10 }
448       => message { "This number ($_) is not less than ten!" };
449       
450   coerce 'Num' 
451       => from 'Str'
452         => via { 0+$_ }; 
453         
454   enum 'RGBColors' => qw(red green blue);
455
456 =head1 DESCRIPTION
457
458 This module provides Moose with the ability to create custom type 
459 contraints to be used in attribute definition. 
460
461 =head2 Important Caveat
462
463 This is B<NOT> a type system for Perl 5. These are type constraints, 
464 and they are not used by Moose unless you tell it to. No type 
465 inference is performed, expression are not typed, etc. etc. etc. 
466
467 This is simply a means of creating small constraint functions which 
468 can be used to simplify your own type-checking code.
469
470 =head2 Slightly Less Important Caveat
471
472 It is almost always a good idea to quote your type and subtype names. 
473 This is to prevent perl from trying to execute the call as an indirect 
474 object call. This issue only seems to come up when you have a subtype
475 the same name as a valid class, but when the issue does arise it tends 
476 to be quite annoying to debug. 
477
478 So for instance, this:
479   
480   subtype DateTime => as Object => where { $_->isa('DateTime') };
481
482 will I<Just Work>, while this:
483
484   use DateTime;
485   subtype DateTime => as Object => where { $_->isa('DateTime') };
486
487 will fail silently and cause many headaches. The simple way to solve 
488 this, as well as future proof your subtypes from classes which have 
489 yet to have been created yet, is to simply do this:
490
491   use DateTime;
492   subtype 'DateTime' => as 'Object' => where { $_->isa('DateTime') };
493
494 =head2 Default Type Constraints
495
496 This module also provides a simple hierarchy for Perl 5 types, this 
497 could probably use some work, but it works for me at the moment.
498
499   Any
500   Item 
501       Bool
502       Undef
503       Defined
504           Value
505               Num
506                 Int
507               Str
508                 ClassName
509           Ref
510               ScalarRef
511               ArrayRef
512               HashRef
513               CodeRef
514               RegexpRef
515               GlobRef
516                 FileHandle
517               Object    
518                   Role
519
520 Suggestions for improvement are welcome.
521
522 B<NOTE:> The C<Undef> type constraint does not work correctly 
523 in every occasion, please use it sparringly.
524
525 B<NOTE:> The C<ClassName> type constraint is simply a subtype 
526 of string which responds true to C<isa('UNIVERSAL')>. This means
527 that your class B<must> be loaded for this type constraint to 
528 pass. I know this is not ideal for all, but it is a saner 
529 restriction than most others. 
530
531 =head2 Use with Other Constraint Modules
532
533 This module should play fairly nicely with other constraint 
534 modules with only some slight tweaking. The C<where> clause 
535 in types is expected to be a C<CODE> reference which checks
536 it's first argument and returns a bool. Since most constraint
537 modules work in a similar way, it should be simple to adapt 
538 them to work with Moose.
539
540 For instance, this is how you could use it with 
541 L<Declare::Constraints::Simple> to declare a completely new type. 
542
543   type 'HashOfArrayOfObjects' 
544       => IsHashRef(
545           -keys   => HasLength,
546           -values => IsArrayRef( IsObject ));
547
548 For more examples see the F<t/204_example_w_DCS.t> test file.
549
550 Here is an example of using L<Test::Deep> and it's non-test 
551 related C<eq_deeply> function. 
552
553   type 'ArrayOfHashOfBarsAndRandomNumbers' 
554       => where {
555           eq_deeply($_, 
556               array_each(subhashof({
557                   bar           => isa('Bar'),
558                   random_number => ignore()
559               }))) 
560         };
561
562 For a complete example see the F<t/205_example_w_TestDeep.t> 
563 test file.    
564     
565 =head1 FUNCTIONS
566
567 =head2 Type Constraint Construction & Locating
568
569 =over 4
570
571 =item B<create_type_constraint_union ($pipe_seperated_types | @type_constraint_names)>
572
573 Given string with C<$pipe_seperated_types> or a list of C<@type_constraint_names>, 
574 this will return a L<Moose::Meta::TypeConstraint::Union> instance.
575
576 =item B<create_parameterized_type_constraint ($type_name)>
577
578 Given a C<$type_name> in the form of:
579
580   BaseType[ContainerType]
581
582 this will extract the base type and container type and build an instance of 
583 L<Moose::Meta::TypeConstraint::Parameterized> for it.
584
585 =item B<find_or_create_type_constraint ($type_name, ?$options_for_anon_type)>
586
587 This will attempt to find or create a type constraint given the a C<$type_name>. 
588 If it cannot find it in the registry, it will see if it should be a union or 
589 container type an create one if appropriate, and lastly if nothing can be 
590 found or created that way, it will create an anon-type using the 
591 C<$options_for_anon_type> HASH ref to populate it.
592
593 =item B<find_type_constraint ($type_name)>
594
595 This function can be used to locate a specific type constraint
596 meta-object, of the class L<Moose::Meta::TypeConstraint> or a
597 derivative. What you do with it from there is up to you :)
598
599 =item B<get_type_constraint_registry>
600
601 Fetch the L<Moose::Meta::TypeConstraint::Registry> object which 
602 keeps track of all type constraints.
603
604 =item B<list_all_type_constraints>
605
606 This will return a list of type constraint names, you can then 
607 fetch them using C<find_type_constraint ($type_name)> if you 
608 want to.
609
610 =item B<list_all_builtin_type_constraints>
611
612 This will return a list of builtin type constraints, meaning, 
613 those which are defined in this module. See the section 
614 labeled L<Default Type Constraints> for a complete list.
615
616 =item B<export_type_constraints_as_functions>
617
618 This will export all the current type constraints as functions 
619 into the caller's namespace. Right now, this is mostly used for 
620 testing, but it might prove useful to others.
621
622 =back
623
624 =head2 Type Constraint Constructors
625
626 The following functions are used to create type constraints. 
627 They will then register the type constraints in a global store 
628 where Moose can get to them if it needs to. 
629
630 See the L<SYNOPSIS> for an example of how to use these.
631
632 =over 4
633
634 =item B<type ($name, $where_clause)>
635
636 This creates a base type, which has no parent. 
637
638 =item B<subtype ($name, $parent, $where_clause, ?$message)>
639
640 This creates a named subtype. 
641
642 =item B<subtype ($parent, $where_clause, ?$message)>
643
644 This creates an unnamed subtype and will return the type 
645 constraint meta-object, which will be an instance of 
646 L<Moose::Meta::TypeConstraint>. 
647
648 =item B<enum ($name, @values)>
649
650 This will create a basic subtype for a given set of strings. 
651 The resulting constraint will be a subtype of C<Str> and 
652 will match any of the items in C<@values>. See the L<SYNOPSIS> 
653 for a simple example.
654
655 B<NOTE:> This is not a true proper enum type, it is simple 
656 a convient constraint builder.
657
658 =item B<as>
659
660 This is just sugar for the type constraint construction syntax.
661
662 =item B<where>
663
664 This is just sugar for the type constraint construction syntax.
665
666 =item B<message>
667
668 This is just sugar for the type constraint construction syntax.
669
670 =item B<optimize_as>
671
672 This can be used to define a "hand optimized" version of your 
673 type constraint which can be used to avoid traversing a subtype
674 constraint heirarchy. 
675
676 B<NOTE:> You should only use this if you know what you are doing, 
677 all the built in types use this, so your subtypes (assuming they 
678 are shallow) will not likely need to use this.
679
680 =back
681
682 =head2 Type Coercion Constructors
683
684 Type constraints can also contain type coercions as well. If you 
685 ask your accessor to coerce, then Moose will run the type-coercion 
686 code first, followed by the type constraint check. This feature 
687 should be used carefully as it is very powerful and could easily 
688 take off a limb if you are not careful.
689
690 See the L<SYNOPSIS> for an example of how to use these.
691
692 =over 4
693
694 =item B<coerce>
695
696 =item B<from>
697
698 This is just sugar for the type coercion construction syntax.
699
700 =item B<via>
701
702 This is just sugar for the type coercion construction syntax.
703
704 =back
705
706 =head2 Namespace Management
707
708 =over 4
709
710 =item B<unimport>
711
712 This will remove all the type constraint keywords from the 
713 calling class namespace.
714
715 =back
716
717 =head1 BUGS
718
719 All complex software has bugs lurking in it, and this module is no 
720 exception. If you find a bug please either email me, or add the bug
721 to cpan-RT.
722
723 =head1 AUTHOR
724
725 Stevan Little E<lt>stevan@iinteractive.comE<gt>
726
727 =head1 COPYRIGHT AND LICENSE
728
729 Copyright 2006, 2007 by Infinity Interactive, Inc.
730
731 L<http://www.iinteractive.com>
732
733 This library is free software; you can redistribute it and/or modify
734 it under the same terms as Perl itself. 
735
736 =cut