finished extended type examples
john napiorkowski [Fri, 22 May 2009 16:43:21 +0000 (12:43 -0400)]
lib/MooseX/Dependent.pm
lib/MooseX/Dependent/Meta/TypeConstraint/Dependent.pm
lib/MooseX/Dependent/Types.pm
t/01-types-dependent.t

index e90e299..9ab310e 100644 (file)
@@ -45,8 +45,9 @@ You can then use this $set_obj as a parameter on the previously declared type
 constraint 'UniqueID'.  This $set_obj become part of the constraint (you can't
 actually use the constraint without it.)
 
-    UniqueID[$set_obj]->check(1); ## Not OK, since one isn't unique in $set_obj
-    UniqueID[$set_obj]->check(100); ## OK, since 100 isn't in the set.
+    UniqueID([$set_obj])->check(1); ## Not OK, since one isn't unique in $set_obj
+    UniqueID([$set_obj])->check('AAA'); ## Not OK, since AAA is not an Int
+    UniqueID([$set_obj])->check(100); ## OK, since 100 isn't in the set.
     
 You can assign the result of a parameterized dependent type to a variable or to
 another type constraint, as like any other type constraint:
index 9cfc50f..b05a3e2 100644 (file)
@@ -36,6 +36,7 @@ has 'parent_type_constraint' => (
     required=>1,
 );
 
+
 =head2 constraining_value_type_constraint
 
 This is a type constraint which defines what kind of value is allowed to be the
@@ -63,79 +64,10 @@ has 'constraining_value' => (
     predicate=>'has_constraining_value',
 );
 
-=head2 constraint_generator
-
-A subref or closure that contains the way we validate incoming values against
-a set of type constraints.
-
-
-has 'constraint_generator' => (
-    is=>'ro',
-    isa=>'CodeRef',
-    predicate=>'has_constraint_generator',
-    required=>1,
-);
-
 =head1 METHODS
 
 This class defines the following methods.
 
-=head2 validate
-
-We intercept validate in order to custom process the message.
-
-override 'validate' => sub {
-    my ($self, @args) = @_;
-    my $compiled_type_constraint = $self->_compiled_type_constraint;
-    my $message = bless {message=>undef}, 'MooseX::Types::Dependent::Message';
-    my $result = $compiled_type_constraint->(@args, $message);
-
-    if($result) {
-        return $result;
-    } else {
-        my $args = Devel::PartialDump::dump(@args);
-        if(my $message = $message->{message}) {
-            return $self->get_message("$args, Internal Validation Error is: $message");
-        } else {
-            return $self->get_message($args);
-        }
-    }
-};
-
-=head2 generate_constraint_for ($type_constraints)
-
-Given some type constraints, use them to generate validation rules for an ref
-of values (to be passed at check time)
-
-
-sub generate_constraint_for {
-    my ($self, $callback) = @_;
-    return sub {   
-        my $dependent_pair = shift @_;
-        my ($dependent, $constraining) = @$dependent_pair;
-        
-        ## First need to test the bits
-        unless($self->check_dependent($dependent)) {
-            $_[0]->{message} = $self->get_message_dependent($dependent)
-             if $_[0];
-            return;
-        }
-    
-        unless($self->check_constraining($constraining)) {
-            $_[0]->{message} = $self->get_message_constraining($constraining)
-             if $_[0];
-            return;
-        }
-    
-        my $constraint_generator = $self->constraint_generator;
-        return $constraint_generator->(
-            $dependent,
-            $callback,
-            $constraining,
-        );
-    };
-}
-
 =head2 parameterize (@args)
 
 Given a ref of type constraints, create a structured type.
@@ -145,28 +77,48 @@ Given a ref of type constraints, create a structured type.
 sub parameterize {
     my $self = shift @_;
     my $class = ref $self;
-    
+
+    Moose->throw_error("$self already has a constraining value.") if
+     $self->has_constraining_value;
+         
     if(blessed $_[0] && $_[0]->isa('Moose::Meta::TypeConstraint')) {
         my $arg1 = shift @_;
-        my $arg2 = shift @_ || $self->constraining_value_type_constraint;
-        
-        Moose->throw_error("$arg2 is not a type constraint")
-         unless $arg2->isa('Moose::Meta::TypeConstraint');
          
-        Moose->throw_error('Too Many Args!  Two are allowed.') if @_;
-        
-        return $class->new(
-            name => $self->_generate_subtype_name($arg1, $arg2),
-            parent => $self,
-            constraint => $self->constraint,
-            parent_type_constraint=>$arg1,
-            constraining_value_type_constraint => $arg2,
-        );
-
+        if(blessed $_[0] && $_[0]->isa('Moose::Meta::TypeConstraint')) {
+            my $arg2 = shift @_ || $self->constraining_value_type_constraint;
+            
+            ## TODO fix this crap!
+            Moose->throw_error("$arg2 is not a type constraint")
+             unless $arg2->isa('Moose::Meta::TypeConstraint');
+             
+            Moose->throw_error("$arg1 is not a type of: ".$self->parent_type_constraint->name)
+             unless $arg1->is_a_type_of($self->parent_type_constraint);
+
+            Moose->throw_error("$arg2 is not a type of: ".$self->constraining_value_type_constraint->name)
+             unless $arg2->is_a_type_of($self->constraining_value_type_constraint);
+             
+            Moose->throw_error('Too Many Args!  Two are allowed.') if @_;
+            
+            return $class->new(
+                name => $self->_generate_subtype_name($arg1, $arg2),
+                parent => $self,
+                constraint => $self->constraint,
+                parent_type_constraint=>$arg1,
+                constraining_value_type_constraint => $arg2,
+            );
+        } else {
+            Moose->throw_error("$arg1 is not a type of: ".$self->constraining_value_type_constraint->name)
+             unless $arg1->is_a_type_of($self->constraining_value_type_constraint);
+             
+            return $class->new(
+                name => $self->_generate_subtype_name($self->parent_type_constraint, $arg1),
+                parent => $self,
+                constraint => $self->constraint,
+                parent_type_constraint=>$self->parent_type_constraint,
+                constraining_value_type_constraint => $arg1,
+            );
+        }
     } else {
-        Moose->throw_error("$self already has a constraining value.") if
-         $self->has_constraining_value;
-        
         my $args;
         ## Jump through some hoops to let them do tc[key=>10] and tc[{key=>10}]
         if(@_) {
@@ -283,21 +235,34 @@ sub is_a_type_of {
 
 around 'check' => sub {
     my ($check, $self, @args) = @_;
-    return $self->parent_type_constraint->check(@args) && $self->$check(@args)
+    return (
+        $self->parent_type_constraint->check(@args) &&
+        $self->$check(@args)
+    );
 };
 
 around 'validate' => sub {
     my ($validate, $self, @args) = @_;
-    return $self->parent_type_constraint->validate(@args) || $self->$validate(@args);
+    return (
+        $self->parent_type_constraint->validate(@args) ||
+        $self->$validate(@args)
+    );
 };
 
 around '_compiled_type_constraint' => sub {
     my ($method, $self, @args) = @_;
     my $coderef = $self->$method(@args);
-    my @extra_args = $self->has_constraining_value ? $self->constraining_value : ();
+    my $constraining;
+    if($self->has_constraining_value) {
+        $constraining = $self->constraining_value;
+    } 
+    
     return sub {
         my @local_args = @_;
-        $coderef->(@local_args, @extra_args);
+        if(my $err = $self->constraining_value_type_constraint->validate($constraining)) {
+            Moose->throw_error($err);
+        }
+        $coderef->(@local_args, $constraining);
     };
 };
 
index 371459a..cec413e 100644 (file)
@@ -20,7 +20,7 @@ Within your L<MooseX::Types> declared library module:
             my ($int, $set) = @_;
             return $set->find($int) ? 0:1;
         };
-
+               
 =head1 DESCRIPTION
 
 A L<MooseX::Types> library for creating dependent types.  A dependent type
@@ -44,25 +44,48 @@ for a integer, such as in:
                where {
                        my ($value, $range) = @_;
                        return ($value >= $range->{min} &&
-                        $value =< $range->{max});
+                        $value <= $range->{max});
                };
                
-       RangedInt[{min=>10,max=>100}]->check(50); ## OK
-       RangedInt[{min=>50, max=>75}]->check(99); ## Not OK, 99 exceeds max
-       RangedInt[{min=>99, max=>10}]->check(10); ## Not OK, not a valid Range!
+       RangedInt([{min=>10,max=>100}])->check(50); ## OK
+       RangedInt([{min=>50, max=>75}])->check(99); ## Not OK, 99 exceeds max
+       
+This throws a hard Moose exception.  You'll need to capture it in an eval or
+related exception catching system (see L<Try::Catch>).
+
+       RangedInt([{min=>99, max=>10}])->check(10); ## Not OK, not a valid Range!
+
+If you can't accept a hard exception here, you'll need to test the constraining
+values first, as in:
+
+       my $range = {min=>99, max=>10};
+       if(my $err = Range->validate($range)) {
+               ## Handle #$err
+       } else {
+               RangedInt($range)->check(99);
+       }
        
 Please note that for ArrayRef or HashRef dependent type constraints, as in the
 example above, as a convenience we automatically ref the incoming type
 parameters, so that the above could also be written as:
 
-       RangedInt[min=>10,max=>100]->check(50); ## OK
-       RangedInt[min=>50, max=>75]->check(99); ## Not OK, 99 exceeds max
-       RangedInt[min=>99, max=>10]->check(10); ## Not OK, not a valid Range!
+       RangedInt([min=>10,max=>100])->check(50); ## OK
+       RangedInt([min=>50, max=>75])->check(99); ## Not OK, 99 exceeds max
+       RangedInt([min=>99, max=>10])->check(10); ## Exception, not a valid Range!
 
 This is the preferred syntax, as it improve readability and adds to the
 conciseness of your type constraint declarations.  An exception wil be thrown if
 your type parameters don't match the required reference type.
 
+Also not that if you 'chain' parameterization results with a method call like:
+
+       TypeConstraint([$ob])->method;
+       
+You need to have the "(...)" around the ArrayRef in the Type Constraint
+parameters.  This seems to have something to do with the precendent level of
+"->".  Patches or thoughts welcomed.  You only need to do this in the above
+case which I imagine is not a very common case.
+
 ==head2 Subtyping a Dependent type constraints
 
 When subclassing a dependent type you must be careful to match either the
@@ -91,7 +114,8 @@ Or you could have done the following instead (example of re-paramterizing)
        subtype PositiveInt,
                as Int,
                where {
-                       shift >= 0;
+                       my ($value, $range) = @_;
+                       return $value >= 0;
                };
 
        ## subtype Range to re-parameterize Range with subtypes
@@ -103,7 +127,7 @@ Or you could have done the following instead (example of re-paramterizing)
                as RangedInt[PositiveRange];
 
 Notice how re-parameterizing the dependent type 'RangedInt' works slightly
-differently from re-parameterizing 'PositiveRange'?  Although it initially takes
+differently from re-parameterizing 'PositiveRange'  Although it initially takes
 two type constraint values to declare a dependent type, should you wish to
 later re-parameterize it, you only use a subtype of the second type parameter
 (the dependent type constraint) since the first type constraint sets the parent
@@ -143,8 +167,8 @@ what you are actually coercion, the unparameterized or parameterized constraint.
 
 Which should work like:
 
-       OlderThanAge[{older_than=>25}]->check(39); ## is OK
-       OlderThanAge[older_than=>1]->check(9); ## OK, using reference type inference
+       OlderThanAge([{older_than=>25}])->check(39); ## is OK
+       OlderThanAge([older_than=>1])->check(9); ## OK, using reference type inference
 
 And you can create coercions like:
 
@@ -214,40 +238,3 @@ it under the same terms as Perl itself.
 =cut
 
 1;
-
-__END__
-
-oose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
-    Moose::Meta::TypeConstraint::Parameterizable->new(
-        name => 'MooseX::Dependent::Types::Dependent',
-        parent => find_type_constraint('Any'),
-               constraint => sub { 0 },
-        constraint_generator=> sub { 
-                       my ($dependent_val, $callback, $constraining_val) = @_;
-                       return $callback->($dependent_val, $constraining_val);
-        },
-    )
-);
-
-
-
-$REGISTRY->add_type_constraint(
-    Moose::Meta::TypeConstraint::Parameterizable->new(
-        name               => 'HashRef',
-        package_defined_in => __PACKAGE__,
-        parent             => find_type_constraint('Ref'),
-        constraint         => sub { ref($_) eq 'HASH' },
-        optimized =>
-            \&Moose::Util::TypeConstraints::OptimizedConstraints::HashRef,
-        constraint_generator => sub {
-            my $type_parameter = shift;
-            my $check          = $type_parameter->_compiled_type_constraint;
-            return sub {
-                foreach my $x ( values %$_ ) {
-                    ( $check->($x) ) || return;
-                }
-                1;
-                }
-        }
-    )
-);
\ No newline at end of file
index 90cd1ee..3429d11 100644 (file)
@@ -1,15 +1,17 @@
 
-use Test::More tests=>62; {
+use Test::More tests=>79; {
        
        use strict;
        use warnings;
        
        use MooseX::Dependent::Types qw(Dependent);
-       use MooseX::Types::Moose qw(Int Any);
-       use MooseX::Types -declare=>[qw(SubDependent IntLessThan EvenInt
-               LessThan100GreatThen5andEvenIntNot44)];
+       use MooseX::Types::Moose qw(Int Any Maybe);
        use Moose::Util::TypeConstraints;
        
+       use MooseX::Types -declare=>[qw(SubDependent IntLessThan EvenInt
+               LessThan100GreatThen5andEvenIntNot44 IntNot54
+               GreatThen5andEvenIntNot54or64)];
+       
        ok Dependent->check(1),
          'Dependent is basically an "Any"';
          
@@ -92,10 +94,10 @@ use Test::More tests=>62; {
        ok EvenInt->check(2), 'but 2 is!';
          
        ok subtype( IntLessThan,
-               as Dependent[EvenInt, Int],
+               as Dependent[EvenInt, Maybe[Int]],
                where {
                        my $value = shift @_;
-                       my $constraining = shift @_ || 200;  #warn "..... $constraining ......";
+                       my $constraining = shift @_ || 200;
                        return ($value < $constraining && $value > 5);
                }),
          'Created IntLessThan subtype';
@@ -132,13 +134,13 @@ use Test::More tests=>62; {
        is IntLessThan->name, 'main::IntLessThan',
          'Got correct name for IntLessThan';
        
-       is IntLessThan->parent, 'MooseX::Dependent::Types::Dependent[main::EvenInt, Int]',
+       is IntLessThan->parent, 'MooseX::Dependent::Types::Dependent[main::EvenInt, Maybe[Int]]',
          'IntLessThan is a Dependent';
          
        is IntLessThan->parent_type_constraint, EvenInt,
          'Parent is an Int';
          
-       is IntLessThan->constraining_value_type_constraint, Int,
+       is IntLessThan->constraining_value_type_constraint, (Maybe[Int]),
          'constraining is an Int';
          
        ok IntLessThan->equals(IntLessThan),
@@ -184,7 +186,7 @@ use Test::More tests=>62; {
                as IntLessThan[100],
                where {
                        my $value = shift @_;
-                       return $value == 44 ? 0:1;
+                       return $value != 44;
                }),
          'Created LessThan100GreatThen5andEvenIntNot44 subtype';
 
@@ -212,6 +214,70 @@ use Test::More tests=>62; {
        ok !LessThan100GreatThen5andEvenIntNot44->check(44),
          'is Int, is even, greater than 5, less than 100 BUT 44!';
          
+       ok subtype( IntNot54,
+               as Maybe[Int],
+               where {
+                       my $val = shift @_ || 200;
+                       return $val != 54
+               }),
+         'Created a subtype of Int';
+         
+       ok IntNot54->check(100), 'Not 54';
+       ok !IntNot54->check(54), '54!!';
+       
+       ok( subtype( GreatThen5andEvenIntNot54or64,
+               as IntLessThan[IntNot54],
+               where {
+                       my $value = shift @_;
+                       return $value != 64;
+               }),
+         'Created GreatThen5andEvenIntNot54or64 subtype');
+         
+       is( (GreatThen5andEvenIntNot54or64->name),
+          'main::GreatThen5andEvenIntNot54or64',
+          'got expected name');
+       
+       ok GreatThen5andEvenIntNot54or64->check(150),
+               '150 is even, less than 200, not 54 or 64 but > 5';
+
+       ok !GreatThen5andEvenIntNot54or64->check(202),
+               '202 is even, exceeds 200, not 54 or 64 but > 5';
+               
+       is( ((GreatThen5andEvenIntNot54or64[100])->name),
+         'main::GreatThen5andEvenIntNot54or64[100]',
+         'got expected name');
+         
+       ok !GreatThen5andEvenIntNot54or64([100])->check(150),
+         '150 Not less than 100';
+         
+       ok !GreatThen5andEvenIntNot54or64([100])->check(300),
+         '300 Not less than 100 (check to make sure we are not defaulting 200)';
+         
+       ok !GreatThen5andEvenIntNot54or64([100])->check(151),
+         '151 Not less than 100';
+         
+       ok !GreatThen5andEvenIntNot54or64([100])->check(2),
+         'Not greater than 5';
+
+       ok !GreatThen5andEvenIntNot54or64([100])->check(51),
+         'Not even';
+
+       ok !GreatThen5andEvenIntNot54or64([100])->check('aaa'),
+         'Not Int';
+         
+       ok GreatThen5andEvenIntNot54or64([100])->check(42),
+         'is Int, is even, greater than 5, less than 100';
+         
+       ok !GreatThen5andEvenIntNot54or64([100])->check(64),
+         'is Int, is even, greater than 5, less than 100 BUT 64!';
+       
+       CHECKPARAM: {
+               eval { GreatThen5andEvenIntNot54or64([54])->check(32) };
+               like $@,
+                 qr/Validation failed for 'main::IntNot54' failed with value 54/,
+                 'Got Expected Error'; 
+       }
+
        #die IntLessThan->validate(100);
        #use Data::Dump qw/dump/;
        #warn dump IntLessThan;