fix ClassName constraint to use symbol table
Matt S Trout [Wed, 14 Nov 2007 19:27:33 +0000 (19:27 +0000)]
Changes
lib/Moose/Util/TypeConstraints.pm
t/040_type_constraints/003_util_std_type_constraints.t

diff --git a/Changes b/Changes
index 78fee19..105dd00 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,15 +1,18 @@
 Revision history for Perl extension Moose
 
+    * Moose::Util::TypeConstraints
+      - fix ClassName constraint to introspect symbol table
+
 0.29 Tues. Nov. 13, 2007
     * Moose::Meta::Attribute
-      -Fix error message on missing builder method (groditi)
+      - Fix error message on missing builder method (groditi)
 
     * Moose::Meta::Method::Accessor
-      -Fix error message on missing builder method (groditi)
+      - Fix error message on missing builder method (groditi)
 
     * t/ 
-      -Add test to check for the correct error message when
-       builder method is missing (groditi)
+      - Add test to check for the correct error message when
+        builder method is missing (groditi)
 
 0.28 Tues. Nov. 13, 2007
     - 0.27 packaged incorrectly (groditi)     
index 807e61a..44690df 100644 (file)
@@ -411,8 +411,61 @@ subtype 'Role'
 
 subtype 'ClassName'
     => as 'Str'
-    => where { eval { $_->isa('UNIVERSAL') } }
-    => optimize_as { !ref($_[0]) && eval { $_[0]->isa('UNIVERSAL') } };
+    => where {
+         return unless defined($_) && length($_);
+
+         # walk the symbol table tree to avoid autovififying
+         # \*{${main::}{"Foo::"}} == \*main::Foo::
+
+         my $pack = \*::;
+         foreach my $part (split('::', $_)) {
+           return unless exists ${$$pack}{"${part}::"};
+           $pack = \*{${$$pack}{"${part}::"}};
+         }
+
+         # check for $VERSION or @ISA
+         return 1 if exists ${$$pack}{VERSION}
+                     && defined *{${$$pack}{VERSION}}{SCALAR};
+         return 1 if exists ${$$pack}{ISA}
+                     && defined *{${$$pack}{ISA}}{ARRAY};
+
+         # check for any method
+         foreach ( keys %{$$pack} ) {
+           next if substr($_, -2, 2) eq '::';
+           return 1 if defined *{${$$pack}{$_}}{CODE};
+         }
+
+         # fail
+         return;
+       }
+    => optimize_as {
+         return if ref($_[0]);
+         return unless defined($_[0]) && length($_[0]);
+
+         # walk the symbol table tree to avoid autovififying
+         # \*{${main::}{"Foo::"}} == \*main::Foo::
+
+         my $pack = \*::;
+         foreach my $part (split('::', $_[0])) {
+           return unless exists ${$$pack}{"${part}::"};
+           $pack = \*{${$$pack}{"${part}::"}};
+         }
+
+         # check for $VERSION or @ISA
+         return 1 if exists ${$$pack}{VERSION}
+                     && defined *{${$$pack}{VERSION}}{SCALAR};
+         return 1 if exists ${$$pack}{ISA}
+                     && defined *{${$$pack}{ISA}}{ARRAY};
+
+         # check for any method
+         foreach ( keys %{$$pack} ) {
+           next if substr($_, -2, 2) eq '::';
+           return 1 if defined *{${$$pack}{$_}}{CODE};
+         }
+
+         # fail
+         return;
+       };
 
 ## --------------------------------------------------------
 # end of built-in types ...
index 3341efc..2961044 100644 (file)
@@ -3,7 +3,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 270;
+use Test::More tests => 271;
 use Test::Exception;
 
 use Scalar::Util ();
@@ -306,6 +306,8 @@ ok(!defined ClassName('Baz'),           '... ClassName rejects anything which is
 
 {
   package Quux::Wibble; # this makes Quux symbol table exist
+
+  sub foo {}
 }
 
 ok(!defined ClassName('Quux'),           '... ClassName rejects anything which is not a ClassName');
@@ -319,6 +321,7 @@ ok(!defined ClassName(qr/../),          '... ClassName rejects anything which is
 ok(!defined ClassName(bless {}, 'Foo'), '... ClassName rejects anything which is not a ClassName');
 ok(!defined ClassName(undef),           '... ClassName rejects anything which is not a ClassName');
 ok(defined ClassName('UNIVERSAL'),      '... ClassName accepts anything which is a ClassName');
+ok(defined ClassName('Quux::Wibble'),      '... ClassName accepts anything which is a ClassName');
 ok(defined ClassName('Moose::Meta::TypeConstraint'), '... ClassName accepts anything which is a ClassName');
 
 close($fh) || die "Could not close the filehandle $0 for test";