support for recursive types, documentation and tests for that, random documentation...
John Napiorkowski [Wed, 10 Dec 2008 00:09:28 +0000 (00:09 +0000)]
Changes
lib/MooseX/Types.pm
lib/MooseX/Types/TypeDecorator.pm
lib/MooseX/Types/UndefinedType.pm
t/14_compatibility-sub-exporter.t
t/15_recursion.t
t/lib/SubExporterCompatibility.pm

diff --git a/Changes b/Changes
index 75f7f1c..2a70deb 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,3 +1,9 @@
+0.08    Mon Dec  09 19:00:00 EST 2008
+        - Added experimental support for recursive type constraints.  Pod and
+          tests for this feature.  Let the madness begin.
+        - Documentation updates.
+        - Workaround test case for the SUb::Exporter compatibility issue.  Also
+          documented in Pod.
 
 0.07    Fri Oct  07 23:00:00 EST 2008
         - Bumped minimum allowed Moose version to fix problem with overloading
index 7eb0fec..cda0f45 100644 (file)
@@ -20,7 +20,7 @@ use Carp::Clan                      qw( ^MooseX::Types );
 use namespace::clean -except => [qw( meta )];
 
 use 5.008;
-our $VERSION = 0.07;
+our $VERSION = 0.08;
 my $UndefMsg = q{Action for type '%s' not yet defined in library '%s'};
 
 =head1 SYNOPSIS
@@ -263,6 +263,21 @@ The fully qualified name of this type as L<Moose> knows it.
 A message that will be thrown when type functionality is used but the
 type does not yet exist.
 
+=head1 RECURSIVE SUBTYPES
+
+As of version 0.08, L<Moose::Types> has experimental support for Recursive
+subtypes.  This will allow:
+
+    subtype Tree() => as HashRef[Str|Tree];
+
+Which validates things like:
+
+    {key=>'value'};
+    {key=>{subkey1=>'value', subkey2=>'value'}}
+    
+And so on.  This feature is new and there may be lurking bugs so don't be afraid
+to hunt me down with patches and test cases if you have trouble.
+
 =head1 NOTES REGARDING TYPE UNIONS
 
 L<MooseX::Types> uses L<MooseX::Types::TypeDecorator> to do some overloading
@@ -482,6 +497,42 @@ documention and examples nearly uniformly use the '=>' version of the comma
 operator and this could be an issue if you are converting code.
 
 Patches welcome for discussion.
+
+=head2 Compatibility with Sub::Exporter
+
+If you want to use L<Sub::Exporter> with a Type Library, you need to make sure
+you export all the type constraints declared AS WELL AS any additional export
+targets. For example if you do:
+
+    package TypeAndSubExporter; {
+        
+        use MooseX::Types::Moose qw(Str);
+        use MooseX::Types -declare => [qw(MyStr)];
+        use Sub::Exporter -setup => { exports => [ qw(something) ] };
+        
+        subtype MyStr,
+         as Str;
+         
+        sub something {
+            return 1;
+        }    
+        
+    } 1;
+    
+    package Foo; {
+        use TypeAndSubExporter qw(MyStr);
+    } 1;
+
+You'll get a '"MyStr" is not exported by the TypeAndSubExporter module' error.
+Upi can workaround by:
+
+        - use Sub::Exporter -setup => { exports => [ qw(something) ] };
+        + use Sub::Exporter -setup => { exports => [ qw(something MyStr) ] };
+
+This is a workaround and I am exploring how to make these modules work better
+together.  I realize this workaround will lead a lot of duplication in your
+export declarations and will be onerous for large type libraries.  Patches and
+detailed test cases welcome. See the tests directory for a start on this.
     
 =head1 SEE ALSO
 
index 519cad7..46a3fbd 100644 (file)
@@ -133,12 +133,22 @@ Delegate to the decorator targe
 =cut
 
 sub AUTOLOAD {
+    
     my ($self, @args) = @_;
     my ($method) = (our $AUTOLOAD =~ /([^:]+)$/);
-    if($self->__type_constraint->can($method)) {
-        return $self->__type_constraint->$method(@args);
+    
+    ## We delegate with this method in an attempt to support a value of
+    ## __type_constraint which is also AUTOLOADing, in particular the class
+    ## MooseX::Types::UndefinedType which AUTOLOADs during autovivication.
+    
+    my $return;
+    
+    eval {
+        $return = $self->__type_constraint->$method(@args);
+    }; if($@) {
+        croak $@;
     } else {
-        croak "Method '$method' is not supported for ". ref($self->__type_constraint);   
+        return $return;
     }
 }
 
index b35d340..4045c19 100644 (file)
@@ -9,6 +9,9 @@ MooseX::Types::UndefinedType - Represents a not yet defined type
 use warnings;
 use strict;
 
+use Moose::Util::TypeConstraints ();
+use Carp::Clan qw( ^MooseX::Types );
+
 use overload '""'     => sub { shift->name },
              fallback => 1;
 
@@ -19,6 +22,9 @@ constraint under it's full name, it assumes it has not yet been defined.
 It will then return an instance of this class, handling only 
 stringification, name and possible identification of undefined types.
 
+Later, when you try to use the Undefined Type Constraint, autovivification will
+be attempted.
+
 =head1 METHODS
 
 =head2 new
@@ -28,7 +34,9 @@ class.
 
 =cut
 
-sub new { bless { name => $_[1] }, $_[0] }
+sub new {
+    return bless { name => $_[1] }, $_[0];
+}
 
 =head2 name
 
@@ -36,19 +44,70 @@ Returns the stored type name.
 
 =cut
 
-sub name { $_[0]->{name} }
+sub name {
+    return $_[0]->{name};
+}
+
+=head2 __autovivify
+
+Try to see if the type constraint has yet been defined and if so create it.
+
+=cut
+
+sub __autovivify {
+    my ($self) = @_;
+    if(my $tc = $self->{instance}) {
+        return $tc;
+    } elsif( my $new_tc = Moose::Util::TypeConstraints::find_type_constraint($self->name)) {
+        $self->{instance} = $new_tc;
+        return $new_tc;
+    } else {
+        return;
+    }
+}
+
+=head2 AUTOLOAD
+
+Try to autovivify and delegate
+
+=cut
+
+sub AUTOLOAD {
+    my ($self, @args)  = @_;
+    my ($method) = our $AUTOLOAD =~ /([^:]+)$/;    
+
+    if(my $type_constraint = $self->__autovivify) {
+        return $type_constraint->$method(@args);
+    } else {
+        croak "Method '$method' is not supported for " . $self->name;
+    }
+}
+
+=head2 DESTROY
+
+Moose::Meta::TypeConstraint::Parameterizable complains if this isn't here. TODO
+to find out why.
+
+=cut
+
+sub DESTROY {
+    return;
+}
 
 =head1 SEE ALSO
 
 L<MooseX::Types::Moose>,
 L<Moose::Util::TypeConstraints>, 
-L<Moose::Meta::TypeConstraint>
+L<Moose::Meta::TypeConstraint>,
+L<Carp::Clan>
 
 =head1 AUTHOR AND COPYRIGHT
 
 Robert 'phaylon' Sedlacek C<E<lt>rs@474.atE<gt>>, with many thanks to
 the C<#moose> cabal on C<irc.perl.org>.
 
+Additional features by John Napiorkowski (jnapiorkowski) <jjnapiork@cpan.org>
+
 =head1 LICENSE
 
 This program is free software; you can redistribute it and/or modify
index b2d2fb3..f220be3 100644 (file)
@@ -12,9 +12,8 @@ BEGIN {
         : ( tests => 3 );
 }
 
-use SubExporterCompatibility qw(MyStr);
-
+use SubExporterCompatibility qw(MyStr something);
+       
 ok MyStr->check('aaa'), "Correctly passed";
 ok !MyStr->check([1]), "Correctly fails";
-ok something(), "Found the something method";
-
+ok something(), "Found the something method";
\ No newline at end of file
index 4c6ed41..f98709f 100644 (file)
@@ -20,9 +20,10 @@ BEGIN {
     package MooseX::Types::Test::Recursion::TestRunner;
     
     BEGIN {
-        use Test::More 'no_plan';
-        use Data::Dump qw/dump/;
+        use Test::More tests=>5;
+        use Test::Exception;
         
+        ## Grab the newly created test type constraint
         MooseX::Types::Test::Recursion->import(':all');
     };
 
@@ -33,8 +34,21 @@ BEGIN {
     ok RecursiveHashRef->check({key=>{subkey=>"value"}})
      => 'properly validated {key=>{subkey=>"value"}}';
      
+    ok RecursiveHashRef->check({
+        key=>{
+            subkey=>"value",
+            subkey2=>{
+                ssubkey1=>"value3",
+                ssubkey2=>"value4"
+            }
+        }
+    }) => 'properly validated deeper recursive values';
+    
+    ok ! RecursiveHashRef->check({key=>[1,2,3]})
+     => 'Properly invalidates bad value';
      
-    warn dump RecursiveHashRef();
+    ok ! RecursiveHashRef->check({key=>{subkey=>"value",subkey2=>{ssubkey=>[1,2,3]}}})
+     => 'Properly invalidates bad value deeply';
 }
 
 
index 9456929..1db281c 100644 (file)
@@ -2,7 +2,7 @@ package SubExporterCompatibility; {
     
     use MooseX::Types::Moose qw(Str);
     use MooseX::Types -declare => [qw(MyStr)];
-    use Sub::Exporter -setup => { exports => [ qw(something) ] };
+    use Sub::Exporter -setup => { exports => [ qw(something MyStr) ] };
     
     subtype MyStr,
      as Str;