Officially document sugar-free form for type and subtype where it
Dave Rolsky [Sat, 21 Feb 2009 22:28:17 +0000 (22:28 +0000)]
accepts a hashref of named parameters, rather than positional params.

This preserves the safety of non-positional params, but lets you
integrate easily with other modules, for doing

  subtype( 'Foo', { where => sub_returner() } );

Added tests for this new style and made sure the docs show it too.

lib/Moose/Util/TypeConstraints.pm
t/040_type_constraints/001_util_type_constraints.t
t/200_examples/004_example_w_DCS.t

index 1046c44..733e297 100644 (file)
@@ -5,7 +5,7 @@ use strict;
 use warnings;
 
 use Carp ();
-use List::MoreUtils qw( all );
+use List::MoreUtils qw( all any );
 use Scalar::Util qw( blessed reftype );
 use Moose::Exporter;
 
@@ -256,17 +256,16 @@ sub register_type_constraint {
 # type constructors
 
 sub type {
-    if ( all { ( reftype($_) || '' ) eq 'CODE' || ! ref $_ } @_ ) {
-        # back-compat version, called without sugar
-        _create_type_constraint( $_[0], undef, $_[1] );
+    # back-compat version, called without sugar
+    if ( ! any { ( reftype($_) || '' ) eq 'HASH' } @_ ) {
+        return _create_type_constraint( $_[0], undef, $_[1] );
     }
-    else {
-        my $name = shift;
 
-        my %p = map { %{$_} } @_;
+    my $name = shift;
 
-        _create_type_constraint( $name, undef, $p{check}, $p{message}, $p{optimized} );
-    }
+    my %p = map { %{$_} } @_;
+
+    return _create_type_constraint( $name, undef, $p{where}, $p{message}, $p{optimize_as} );
 }
 
 sub subtype {
@@ -294,12 +293,12 @@ sub subtype {
     my %p = map { %{$_} } @_;
 
     # subtype Str => where { ... };
-    if ( ! exists $p{parent} ) {
-        $p{parent} = $name;
+    if ( ! exists $p{as} ) {
+        $p{as} = $name;
         $name = undef;
     }
 
-    _create_type_constraint( $name, $p{parent}, $p{check}, $p{message}, $p{optimized} );
+    return _create_type_constraint( $name, $p{as}, $p{where}, $p{message}, $p{optimize_as} );
 }
 
 sub class_type {
@@ -333,10 +332,10 @@ sub coerce {
     _install_type_coercions($type_name, \@coercion_map);
 }
 
-sub as ($)          { { parent    => $_[0] } }
-sub where (&)       { { check     => $_[0] } }
-sub message (&)     { { message   => $_[0] } }
-sub optimize_as (&) { { optimized => $_[0] } }
+sub as ($)          { { as          => $_[0] } }
+sub where (&)       { { where       => $_[0] } }
+sub message (&)     { { message     => $_[0] } }
+sub optimize_as (&) { { optimize_as => $_[0] } }
 
 sub from    {@_}
 sub via (&) { $_[0] }
@@ -836,10 +835,13 @@ them to work with Moose.
 For instance, this is how you could use it with
 L<Declare::Constraints::Simple> to declare a completely new type.
 
-  type 'HashOfArrayOfObjects'
-      => IsHashRef(
+  type 'HashOfArrayOfObjects',
+      {
+      where => IsHashRef(
           -keys   => HasLength,
-          -values => IsArrayRef( IsObject ));
+          -values => IsArrayRef(IsObject)
+      )
+  };
 
 For more examples see the F<t/200_examples/204_example_w_DCS.t>
 test file.
@@ -875,8 +877,13 @@ See the L<SYNOPSIS> for an example of how to use these.
 
 This creates a base type, which has no parent.
 
-Note that calling C<type> I<without> the sugar helpers (C<where>,
-C<message>, etc), is deprecated.
+The C<type> function should either be called with the sugar helpers
+(C<where>, C<message>, etc), or with a name and a hashref of
+parameters:
+
+  type( 'Foo', { where => ..., message => ... } );
+
+The valid hashref keys are C<where>, C<message>, and C<optimize_as>.
 
 =item B<subtype 'Name' => as 'Parent' => where { } ...>
 
@@ -885,8 +892,14 @@ This creates a named subtype.
 If you provide a parent that Moose does not recognize, it will
 automatically create a new class type constraint for this name.
 
-Note that calling C<subtype> I<without> the sugar helpers (C<where>,
-C<message>, etc), is deprecated.
+When creating a named type, the C<subtype> function should either be
+called with the sugar helpers (C<where>, C<message>, etc), or with a
+name and a hashref of parameters:
+
+ subtype( 'Foo', { where => ..., message => ... } );
+
+The valid hashref keys are C<as> (the parent), C<where>, C<message>,
+and C<optimize_as>.
 
 =item B<subtype as 'Parent' => where { } ...>
 
@@ -894,6 +907,12 @@ This creates an unnamed subtype and will return the type
 constraint meta-object, which will be an instance of
 L<Moose::Meta::TypeConstraint>.
 
+When creating an anonymous type, the C<subtype> function should either
+be called with the sugar helpers (C<where>, C<message>, etc), or with
+just a hashref of parameters:
+
+ subtype( { where => ..., message => ... } );
+
 =item B<class_type ($class, ?$options)>
 
 Creates a new subtype of C<Object> with the name C<$class> and the
index 9b463e9..b9e044c 100644 (file)
@@ -3,7 +3,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 73;
+use Test::More tests => 84;
 use Test::Exception;
 
 use Scalar::Util ();
@@ -161,6 +161,31 @@ throws_ok {$r->add_type_constraint(bless {}, 'SomeClass')} qr/not a valid type c
     ok( $subtype->has_message, 'subtype does have a message' );
 }
 
+# alternative sugar-less calling style which is documented as legit:
+{
+    my $subtype = subtype( 'MyStr', { as => 'Str' } );
+    isa_ok( $subtype, 'Moose::Meta::TypeConstraint', 'got a subtype' );
+    is( $subtype->name, 'MyStr', 'name is MyStr' );
+    is( $subtype->parent->name, 'Str', 'parent is Str' );
+}
+
+{
+    my $subtype = subtype( { as => 'Str' } );
+    isa_ok( $subtype, 'Moose::Meta::TypeConstraint', 'got a subtype' );
+    is( $subtype->name, '__ANON__', 'name is __ANON__' );
+    is( $subtype->parent->name, 'Str', 'parent is Str' );
+}
+
+{
+    my $subtype = subtype( { as => 'Str', where => sub { /X/ } } );
+    isa_ok( $subtype, 'Moose::Meta::TypeConstraint', 'got a subtype' );
+    is( $subtype->name, '__ANON__', 'name is __ANON__' );
+    is( $subtype->parent->name, 'Str', 'parent is Str' );
+    ok( $subtype->check('FooX'), 'constraint accepts FooX' );
+    ok( ! $subtype->check('Foo'), 'constraint reject Foo' );
+}
+
+
 # Back-compat for being called without sugar. Previously, calling with
 # sugar was indistinguishable from calling directly.
 
index 3dd0e6a..e9edc2e 100644 (file)
@@ -27,13 +27,16 @@ use Test::Exception;
     use Moose;
     use Moose::Util::TypeConstraints;
     use Declare::Constraints::Simple -All;
-    
+
     # define your own type ...
-    type 'HashOfArrayOfObjects' 
-        => IsHashRef(
+    type( 'HashOfArrayOfObjects',
+        {
+        where => IsHashRef(
             -keys   => HasLength,
-            -values => IsArrayRef( IsObject ));    
-    
+            -values => IsArrayRef(IsObject)
+        )
+    } );
+
     has 'bar' => (
         is  => 'rw',
         isa => 'HashOfArrayOfObjects',
@@ -42,7 +45,7 @@ use Test::Exception;
     # inline the constraints as anon-subtypes
     has 'baz' => (
         is  => 'rw',
-        isa => subtype('ArrayRef' => IsArrayRef(IsInt)),
+        isa => subtype( { as => 'ArrayRef', where => IsArrayRef(IsInt) } ),
     );
 
     package Bar;