adding ->parent_registry to the TC registry object
[gitmo/Moose.git] / lib / Moose / Util / TypeConstraints.pm
index 7fed316..cc36e8e 100644 (file)
@@ -5,15 +5,36 @@ use strict;
 use warnings;
 
 use Carp         'confess';
-use Scalar::Util 'blessed';
+use Scalar::Util 'blessed', 'reftype';
 use B            'svref_2object';
 use Sub::Exporter;
 
-our $VERSION   = '0.12';
+our $VERSION   = '0.14';
 our $AUTHORITY = 'cpan:STEVAN';
 
+# Prototyped subs must be predeclared because we have a circular dependency
+# with Moose::Meta::Attribute et. al. so in case of us being use'd first the
+# predeclaration ensures the prototypes are in scope when consumers are
+# compiled
+
+sub find_type_constraint         ($);
+sub _create_type_constraint      ($$$;$$);
+sub _install_type_coercions      ($$);
+sub create_type_constraint_union (@);
+sub type                         ($$;$$);
+sub subtype                      ($$;$$$);
+sub coerce                       ($@);
+sub as                           ($);
+sub from                         ($);
+sub where                        (&);
+sub via                          (&);
+sub message                      (&);
+sub optimize_as                  (&);
+sub enum                         ($;@);
+
 use Moose::Meta::TypeConstraint;
 use Moose::Meta::TypeCoercion;
+use Moose::Meta::TypeConstraint::Registry;
 
 my @exports = qw/
     type subtype as where message optimize_as
@@ -47,84 +68,94 @@ sub unimport {
     }
 }
 
-{
-    my %TYPES;
-    sub find_type_constraint ($) { 
-        return $TYPES{$_[0]}->[1] 
-            if exists $TYPES{$_[0]};
-        return;
-    }
-    
-    sub _dump_type_constraints {
-        require Data::Dumper;        
-        Data::Dumper::Dumper(\%TYPES);
-    }
-    
-    sub _create_type_constraint ($$$;$$) { 
-        my $name   = shift;
-        my $parent = shift;
-        my $check  = shift;;
-        
-        my ($message, $optimized);
-        for (@_) {
-            $message   = $_->{message}   if exists $_->{message};
-            $optimized = $_->{optimized} if exists $_->{optimized};            
-        }
+my $REGISTRY = Moose::Meta::TypeConstraint::Registry->new;
 
-        my $pkg_defined_in = scalar(caller(0));
-        
-        ($TYPES{$name}->[0] eq $pkg_defined_in)
-            || confess ("The type constraint '$name' has already been created in " 
-                       . $TYPES{$name}->[0] . " and cannot be created again in "
-                       . $pkg_defined_in)
-                 if defined $name && exists $TYPES{$name};   
-                              
-        $parent = find_type_constraint($parent) if defined $parent;
-        my $constraint = Moose::Meta::TypeConstraint->new(
-            name       => $name || '__ANON__',
-            parent     => $parent,            
-            constraint => $check,       
-            message    => $message,    
-            optimized  => $optimized,
-        );
-        $TYPES{$name} = [ $pkg_defined_in, $constraint ] if defined $name;
-        return $constraint;
-    }
+sub _get_type_constraint_registry { $REGISTRY }
+sub _dump_type_constraints        { $REGISTRY->dump }
 
-    sub _install_type_coercions ($$) { 
-        my ($type_name, $coercion_map) = @_;
-        my $type = find_type_constraint($type_name);
-        (!$type->has_coercion)
-            || confess "The type coercion for '$type_name' has already been registered";        
-        my $type_coercion = Moose::Meta::TypeCoercion->new(
-            type_coercion_map => $coercion_map,
-            type_constraint   => $type
-        );            
-        $type->coercion($type_coercion);
-    }
+# NOTE:
+# this method breaks down the sugar 
+# from the functions below.
+sub _create_type_constraint ($$$;$$) { 
+    my $name   = shift;
+    my $parent = shift;
+    my $check  = shift || sub { 1 };
     
-    sub create_type_constraint_union (@) {
-        my (@type_constraint_names) = @_;
-        return Moose::Meta::TypeConstraint->union(
-            map { 
-                find_type_constraint($_) 
-            } @type_constraint_names
-        );
+    my ($message, $optimized);
+    for (@_) {
+        $message   = $_->{message}   if exists $_->{message};
+        $optimized = $_->{optimized} if exists $_->{optimized};            
     }
+
+    my $pkg_defined_in = scalar(caller(0));
     
-    sub export_type_constraints_as_functions {
-        my $pkg = caller();
-           no strict 'refs';
-       foreach my $constraint (keys %TYPES) {
-               *{"${pkg}::${constraint}"} = find_type_constraint($constraint)->_compiled_type_constraint;
-       }        
-    }
+    if (defined $name) {
+        my $type = $REGISTRY->get_type_constraint($name);
     
-    *Moose::Util::TypeConstraints::export_type_contstraints_as_functions = \&export_type_constraints_as_functions;
+        ($type->_package_defined_in eq $pkg_defined_in)
+            || confess ("The type constraint '$name' has already been created in " 
+                       . $type->_package_defined_in . " and cannot be created again in "
+                       . $pkg_defined_in)
+                 if defined $type;   
+    }                    
+                          
+    $parent = $REGISTRY->get_type_constraint($parent) if defined $parent;
     
-    sub list_all_type_constraints { keys %TYPES }   
+    my $constraint = Moose::Meta::TypeConstraint->new(
+        name               => $name || '__ANON__',
+        parent             => $parent,            
+        constraint         => $check,       
+        message            => $message,    
+        optimized          => $optimized,
+        package_defined_in => $pkg_defined_in,
+    );
+
+    $REGISTRY->add_type_constraint($constraint)
+        if defined $name;
+
+    return $constraint;
+}
+
+sub _install_type_coercions ($$) { 
+    my ($type_name, $coercion_map) = @_;
+    my $type = $REGISTRY->get_type_constraint($type_name);
+    (!$type->has_coercion)
+        || confess "The type coercion for '$type_name' has already been registered";        
+    my $type_coercion = Moose::Meta::TypeCoercion->new(
+        type_coercion_map => $coercion_map,
+        type_constraint   => $type
+    );            
+    $type->coercion($type_coercion);
+}
+
+sub create_type_constraint_union (@) {
+    my (@type_constraint_names) = @_;
+    return Moose::Meta::TypeConstraint->union(
+        map { 
+            $REGISTRY->get_type_constraint($_) 
+        } @type_constraint_names
+    );
 }
 
+sub export_type_constraints_as_functions {
+    my $pkg = caller();
+    no strict 'refs';
+       foreach my $constraint (keys %{$REGISTRY->type_constraints}) {
+               *{"${pkg}::${constraint}"} = $REGISTRY->get_type_constraint($constraint)
+                                                     ->_compiled_type_constraint;
+       }        
+}
+
+*Moose::Util::TypeConstraints::export_type_contstraints_as_functions = \&export_type_constraints_as_functions;
+
+sub list_all_type_constraints { keys %{$REGISTRY->type_constraints} }   
+
+## --------------------------------------------------------
+## exported functions ...
+## --------------------------------------------------------
+
+sub find_type_constraint ($) { $REGISTRY->get_type_constraint(@_) }
+
 # type constructors
 
 sub type ($$;$$) {
@@ -133,7 +164,16 @@ sub type ($$;$$) {
 }
 
 sub subtype ($$;$$$) {
-       unshift @_ => undef if scalar @_ <= 2;  
+    # NOTE:
+    # this adds an undef for the name
+    # if this is an anon-subtype:
+    #   subtype(Num => where { $_ % 2 == 0 }) # anon 'even' subtype
+    # but if the last arg is not a code
+    # ref then it is a subtype alias:
+    #   subtype(MyNumbers => as Num); # now MyNumbers is the same as Num
+    # ... yeah I know it's ugly code 
+    # - SL
+       unshift @_ => undef if scalar @_ <= 2 && (reftype($_[1]) || '') eq 'CODE';      
        goto &_create_type_constraint;
 }
 
@@ -225,6 +265,11 @@ subtype 'Role'
     => as 'Object' 
     => where { $_->can('does') }
     => optimize_as { blessed($_[0]) && $_[0]->can('does') };
+    
+subtype 'ClassName' 
+    => as 'Str' 
+    => where { eval { $_->isa('UNIVERSAL') } }
+    => optimize_as { !ref($_[0]) && eval { $_[0]->isa('UNIVERSAL') } };    
 
 {
     my @BUILTINS = list_all_type_constraints();
@@ -314,6 +359,7 @@ could probably use some work, but it works for me at the moment.
               Num
                 Int
               Str
+                ClassName
           Ref
               ScalarRef
               ArrayRef
@@ -330,6 +376,12 @@ Suggestions for improvement are welcome.
 B<NOTE:> The C<Undef> type constraint does not work correctly 
 in every occasion, please use it sparringly.
 
+B<NOTE:> The C<ClassName> type constraint is simply a subtype 
+of string which responds true to C<isa('UNIVERSAL')>. This means
+that your class B<must> be loaded for this type constraint to 
+pass. I know this is not ideal for all, but it is a saner 
+restriction than most others. 
+
 =head2 Use with Other Constraint Modules
 
 This module should play fairly nicely with other constraint 
@@ -372,8 +424,9 @@ test file.
 
 =item B<find_type_constraint ($type_name)>
 
-This function can be used to locate a specific type constraint 
-meta-object. What you do with it from there is up to you :)
+This function can be used to locate a specific type constraint
+meta-object, of the class L<Moose::Meta::TypeConstraint> or a
+derivative. What you do with it from there is up to you :)
 
 =item B<create_type_constraint_union (@type_constraint_names)>
 
@@ -410,7 +463,7 @@ The following functions are used to create type constraints.
 They will then register the type constraints in a global store 
 where Moose can get to them if it needs to. 
 
-See the L<SYNOPOSIS> for an example of how to use these.
+See the L<SYNOPSIS> for an example of how to use these.
 
 =over 4
 
@@ -465,12 +518,12 @@ are shallow) will not likely need to use this.
 =head2 Type Coercion Constructors
 
 Type constraints can also contain type coercions as well. If you 
-ask your accessor too coerce, the Moose will run the type-coercion 
+ask your accessor to coerce, then Moose will run the type-coercion 
 code first, followed by the type constraint check. This feature 
 should be used carefully as it is very powerful and could easily 
 take off a limb if you are not careful.
 
-See the L<SYNOPOSIS> for an example of how to use these.
+See the L<SYNOPSIS> for an example of how to use these.
 
 =over 4