[Moose-commits] r7884 - in MooseX-Types-Structured/tags: . 0.10
0.10/lib/MooseX/Meta/TypeConstraint 0.10/lib/MooseX/Types 0.10/t
jnapiorkowski at code2.0beta.co.uk
jnapiorkowski at code2.0beta.co.uk
Thu Apr 2 16:10:45 BST 2009
Author: jnapiorkowski
Date: 2009-04-02 08:10:44 -0700 (Thu, 02 Apr 2009)
New Revision: 7884
Added:
MooseX-Types-Structured/tags/0.10/
MooseX-Types-Structured/tags/0.10/Changes
MooseX-Types-Structured/tags/0.10/lib/MooseX/Meta/TypeConstraint/Structured.pm
MooseX-Types-Structured/tags/0.10/lib/MooseX/Types/Structured.pm
MooseX-Types-Structured/tags/0.10/t/12-error.t
Removed:
MooseX-Types-Structured/tags/0.10/Changes
MooseX-Types-Structured/tags/0.10/lib/MooseX/Meta/TypeConstraint/Structured.pm
MooseX-Types-Structured/tags/0.10/lib/MooseX/Types/Structured.pm
MooseX-Types-Structured/tags/0.10/t/12-error.t
Log:
tagged for release
Copied: MooseX-Types-Structured/tags/0.10 (from rev 7879, MooseX-Types-Structured/trunk)
Property changes on: MooseX-Types-Structured/tags/0.10
___________________________________________________________________
Name: svn:mergeinfo
+
Name: svk:merge
+ 3efe9002-19ed-0310-8735-a98156148065:/MooseX-Types-Structured/tags/0.02:6432
Deleted: MooseX-Types-Structured/tags/0.10/Changes
===================================================================
--- MooseX-Types-Structured/trunk/Changes 2009-04-01 03:11:50 UTC (rev 7879)
+++ MooseX-Types-Structured/tags/0.10/Changes 2009-04-02 15:10:44 UTC (rev 7884)
@@ -1,65 +0,0 @@
-Revision history for MooseX-Types-Structured
-
-0.09 07 March 2009
- - I guess we don't support the "subtype MyType, [TypeConstraint]" syntax
- anymore. Changed the recursion test to reflect that, which should fix
- my 100% fail rate :)
-0.08 06 March 2009
- - New Feature: slurpy method for when you want a structured type
- constraint that allows trailing arguments of indeterminate length.
- Please see the documentation and the '11-overflow.t' test for more.
- - Documentation for above as well as a bunch of POD cleanups, spell
- checks and improvements to formatting.
- - Stevan Little submitted a sweet update to the '10-recursion.t' test
- that blows my mind. Will probably form the core of a to be done set
- of cookbook style PODs. Worth looking at.
- - First step at improving the error message you get when validation
- fails. A full error stacktrace is not in this release, but you now
- at least get to see part of the offending value.
-
-0.07 09 December 2008
- - Fixed typo in previous changelog
- - documentation improvements and updates
- - increased version requirement for MooseX::Types so that we can take
- advantage of the recursion support added.
- - added test for recursion.
-
-0.06 06 December 2008
- - Added a 'helper' type constraint called Optional. See docs for more.
- - added lots of tests to cover the API better, coverage and fixes for
- the ->parameterize method in particular have been clarified.
- - changes so that the type contraints are more forgiving when null
- values appear in method calls.
- - used ->make_immutable which should speed up the constraints.
- - removed some unnecessary calls to use Moose when I wasn't using Moose.
- - lots of little code cleanup work and more internal documentation.
- - This version requires a newer Moose than previous versions. The
- Makefile.PL has been updated to reflect this. This is a required
- update.
-
-0.05 08 November 2008
- - Fixed some wackiness in the documentation.
-
-0.04 07 November 2008
- - Bumped minimum required versions of Moose and MooseX-Types to solve
- problem with overloading and type constraint names (issue resolved
- in Moose code.)
- - Changed the way the required Perl version string is used to increase
- compatibility and lowered minimum required Perl
-
-0.03 29 October 2008
- - Fixed incorrect Perl version string (rafl)
- - hide the meta classes from pause. This should clarify which POD is
- the right one to read and also I want to discourage people from
- subclassing that stuff since it will probably change
- - various documentation cleanup
- - new test 'example.t' with runable versions of the code in the example
- POD section.
-
-0.02 28 October 2008
- - cleared up some typos in the test suite
- - Fixed some POD formatting issues, mostly some dumb tabs I ended up
- with when I switched editors. Added a bit more documentation
-
-0.01 27 October 2008
- - Completed basic requirements, documentation and tests.
Copied: MooseX-Types-Structured/tags/0.10/Changes (from rev 7883, MooseX-Types-Structured/trunk/Changes)
===================================================================
--- MooseX-Types-Structured/tags/0.10/Changes (rev 0)
+++ MooseX-Types-Structured/tags/0.10/Changes 2009-04-02 15:10:44 UTC (rev 7884)
@@ -0,0 +1,73 @@
+Revision history for MooseX-Types-Structured
+
+0.10 02 April 2009
+ - Minor documentation grammar fixes and one major example error fixed
+ - Much improved error reporting. Now we return the 'internal' error
+ that kicked a validation failure. It's still best to use this for
+ debugging rather than for actual user error messages, since I think
+ we are rapidly approaching a need for Moose constraints needing more
+ in the error and message reporting.
+ - Documentation for the above.
+0.09 07 March 2009
+ - I guess we don't support the "subtype MyType, [TypeConstraint]" syntax
+ anymore. Changed the recursion test to reflect that, which should fix
+ my 100% fail rate :)
+0.08 06 March 2009
+ - New Feature: slurpy method for when you want a structured type
+ constraint that allows trailing arguments of indeterminate length.
+ Please see the documentation and the '11-overflow.t' test for more.
+ - Documentation for above as well as a bunch of POD cleanups, spell
+ checks and improvements to formatting.
+ - Stevan Little submitted a sweet update to the '10-recursion.t' test
+ that blows my mind. Will probably form the core of a to be done set
+ of cookbook style PODs. Worth looking at.
+ - First step at improving the error message you get when validation
+ fails. A full error stacktrace is not in this release, but you now
+ at least get to see part of the offending value.
+
+0.07 09 December 2008
+ - Fixed typo in previous changelog
+ - documentation improvements and updates
+ - increased version requirement for MooseX::Types so that we can take
+ advantage of the recursion support added.
+ - added test for recursion.
+
+0.06 06 December 2008
+ - Added a 'helper' type constraint called Optional. See docs for more.
+ - added lots of tests to cover the API better, coverage and fixes for
+ the ->parameterize method in particular have been clarified.
+ - changes so that the type contraints are more forgiving when null
+ values appear in method calls.
+ - used ->make_immutable which should speed up the constraints.
+ - removed some unnecessary calls to use Moose when I wasn't using Moose.
+ - lots of little code cleanup work and more internal documentation.
+ - This version requires a newer Moose than previous versions. The
+ Makefile.PL has been updated to reflect this. This is a required
+ update.
+
+0.05 08 November 2008
+ - Fixed some wackiness in the documentation.
+
+0.04 07 November 2008
+ - Bumped minimum required versions of Moose and MooseX-Types to solve
+ problem with overloading and type constraint names (issue resolved
+ in Moose code.)
+ - Changed the way the required Perl version string is used to increase
+ compatibility and lowered minimum required Perl
+
+0.03 29 October 2008
+ - Fixed incorrect Perl version string (rafl)
+ - hide the meta classes from pause. This should clarify which POD is
+ the right one to read and also I want to discourage people from
+ subclassing that stuff since it will probably change
+ - various documentation cleanup
+ - new test 'example.t' with runable versions of the code in the example
+ POD section.
+
+0.02 28 October 2008
+ - cleared up some typos in the test suite
+ - Fixed some POD formatting issues, mostly some dumb tabs I ended up
+ with when I switched editors. Added a bit more documentation
+
+0.01 27 October 2008
+ - Completed basic requirements, documentation and tests.
Deleted: MooseX-Types-Structured/tags/0.10/lib/MooseX/Meta/TypeConstraint/Structured.pm
===================================================================
--- MooseX-Types-Structured/trunk/lib/MooseX/Meta/TypeConstraint/Structured.pm 2009-04-01 03:11:50 UTC (rev 7879)
+++ MooseX-Types-Structured/tags/0.10/lib/MooseX/Meta/TypeConstraint/Structured.pm 2009-04-02 15:10:44 UTC (rev 7884)
@@ -1,249 +0,0 @@
-package ## Hide from PAUSE
- MooseX::Meta::TypeConstraint::Structured;
-
-use Moose;
-use Devel::PartialDump;
-use Moose::Util::TypeConstraints ();
-use MooseX::Meta::TypeCoercion::Structured;
-extends 'Moose::Meta::TypeConstraint';
-
-=head1 NAME
-
-MooseX::Meta::TypeConstraint::Structured - Structured type constraints.
-
-=head1 DESCRIPTION
-
-A structure is a set of L<Moose::Meta::TypeConstraint> that are 'aggregated' in
-such a way as that they are all applied to an incoming list of arguments. The
-idea here is that a Type Constraint could be something like, "An Int followed by
-an Int and then a Str" and that this could be done so with a declaration like:
-
- Tuple[Int,Int,Str]; ## Example syntax
-
-So a structure is a list of Type constraints (the "Int,Int,Str" in the above
-example) which are intended to function together.
-
-=head1 ATTRIBUTES
-
-This class defines the following attributes.
-
-=head2 type_constraints
-
-A list of L<Moose::Meta::TypeConstraint> objects.
-
-=cut
-
-has 'type_constraints' => (
- is=>'ro',
- isa=>'Ref',
- predicate=>'has_type_constraints',
-);
-
-=head2 constraint_generator
-
-A subref or closure that contains the way we validate incoming values against
-a set of type constraints.
-
-=cut
-
-has 'constraint_generator' => (
- is=>'ro',
- isa=>'CodeRef',
- predicate=>'has_constraint_generator',
-);
-
-=head1 METHODS
-
-This class defines the following methods.
-
-=head2 new
-
-Initialization stuff.
-
-=cut
-
-around 'new' => sub {
- my ($new, $class, @args) = @_;
- my $self = $class->$new(@args);
- $self->coercion(MooseX::Meta::TypeCoercion::Structured->new(
- type_constraint => $self,
- ));
- return $self;
-};
-
-=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)
-
-=cut
-
-sub generate_constraint_for {
- my ($self, $type_constraints) = @_;
- return sub {
- my (@args) = @_;
- my $constraint_generator = $self->constraint_generator;
- return $constraint_generator->($type_constraints, @args);
- };
-}
-
-=head2 parameterize (@type_constraints)
-
-Given a ref of type constraints, create a structured type.
-
-=cut
-
-sub parameterize {
-
- my ($self, @type_constraints) = @_;
- my $class = ref $self;
- my $name = $self->name .'['. join(',', map {"$_"} @type_constraints) .']';
- my $constraint_generator = $self->__infer_constraint_generator;
-
- return $class->new(
- name => $name,
- parent => $self,
- type_constraints => \@type_constraints,
- constraint_generator => $constraint_generator,
- );
-}
-
-=head2 __infer_constraint_generator
-
-This returns a CODEREF which generates a suitable constraint generator. Not
-user servicable, you'll never call this directly.
-
-=cut
-
-sub __infer_constraint_generator {
- my ($self) = @_;
- if($self->has_constraint_generator) {
- return $self->constraint_generator;
- } else {
- return sub {
- ## I'm not sure about this stuff but everything seems to work
- my $tc = shift @_;
- my $merged_tc = [@$tc, @{$self->parent->type_constraints}];
- $self->constraint->($merged_tc, @_);
- };
- }
-}
-
-=head2 compile_type_constraint
-
-hook into compile_type_constraint so we can set the correct validation rules.
-
-=cut
-
-around 'compile_type_constraint' => sub {
- my ($compile_type_constraint, $self, @args) = @_;
-
- if($self->has_type_constraints) {
- my $type_constraints = $self->type_constraints;
- my $constraint = $self->generate_constraint_for($type_constraints);
- $self->_set_constraint($constraint);
- }
-
- return $self->$compile_type_constraint(@args);
-};
-
-=head2 create_child_type
-
-modifier to make sure we get the constraint_generator
-
-=cut
-
-around 'create_child_type' => sub {
- my ($create_child_type, $self, %opts) = @_;
- return $self->$create_child_type(
- %opts,
- constraint_generator => $self->__infer_constraint_generator,
- );
-};
-
-=head2 is_a_type_of
-
-=head2 is_subtype_of
-
-=head2 equals
-
-Override the base class behavior.
-
-=cut
-
-sub equals {
- my ( $self, $type_or_name ) = @_;
- my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
-
- return unless $other->isa(__PACKAGE__);
-
- return (
- $self->type_constraints_equals($other)
- and
- $self->parent->equals( $other->parent )
- );
-}
-
-=head2 type_constraints_equals
-
-Checks to see if the internal type contraints are equal.
-
-=cut
-
-sub type_constraints_equals {
- my ($self, $other) = @_;
- my @self_type_constraints = @{$self->type_constraints||[]};
- my @other_type_constraints = @{$other->type_constraints||[]};
-
- ## Incoming ay be either arrayref or hashref, need top compare both
- while(@self_type_constraints) {
- my $self_type_constraint = shift @self_type_constraints;
- my $other_type_constraint = shift @other_type_constraints
- || return; ## $other needs the same number of children.
-
- if( ref $self_type_constraint) {
- $self_type_constraint->equals($other_type_constraint)
- || return; ## type constraints obviously need top be equal
- } else {
- $self_type_constraint eq $other_type_constraint
- || return; ## strings should be equal
- }
-
- }
-
- return 1; ##If we get this far, everything is good.
-}
-
-=head2 get_message
-
-Give you a better peek into what's causing the error. For now we stringify the
-incoming deep value with L<Devel::PartialDump> and pass that on to either your
-custom error message or the default one. In the future we'll try to provide a
-more complete stack trace of the actual offending elements
-
-=cut
-
-around 'get_message' => sub {
- my ($get_message, $self, $value) = @_;
- my $new_value = Devel::PartialDump::dump($value);
- return $self->$get_message($new_value);
-};
-
-=head1 SEE ALSO
-
-The following modules or resources may be of interest.
-
-L<Moose>, L<Moose::Meta::TypeConstraint>
-
-=head1 AUTHOR
-
-John Napiorkowski, C<< <jjnapiork at cpan.org> >>
-
-=head1 COPYRIGHT & LICENSE
-
-This program is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
-=cut
-
-__PACKAGE__->meta->make_immutable;
\ No newline at end of file
Copied: MooseX-Types-Structured/tags/0.10/lib/MooseX/Meta/TypeConstraint/Structured.pm (from rev 7883, MooseX-Types-Structured/trunk/lib/MooseX/Meta/TypeConstraint/Structured.pm)
===================================================================
--- MooseX-Types-Structured/tags/0.10/lib/MooseX/Meta/TypeConstraint/Structured.pm (rev 0)
+++ MooseX-Types-Structured/tags/0.10/lib/MooseX/Meta/TypeConstraint/Structured.pm 2009-04-02 15:10:44 UTC (rev 7884)
@@ -0,0 +1,274 @@
+package ## Hide from PAUSE
+ MooseX::Meta::TypeConstraint::Structured;
+
+use Moose;
+use Devel::PartialDump;
+use Moose::Util::TypeConstraints ();
+use MooseX::Meta::TypeCoercion::Structured;
+extends 'Moose::Meta::TypeConstraint';
+
+=head1 NAME
+
+MooseX::Meta::TypeConstraint::Structured - Structured type constraints.
+
+=head1 DESCRIPTION
+
+A structure is a set of L<Moose::Meta::TypeConstraint> that are 'aggregated' in
+such a way as that they are all applied to an incoming list of arguments. The
+idea here is that a Type Constraint could be something like, "An Int followed by
+an Int and then a Str" and that this could be done so with a declaration like:
+
+ Tuple[Int,Int,Str]; ## Example syntax
+
+So a structure is a list of Type constraints (the "Int,Int,Str" in the above
+example) which are intended to function together.
+
+=head1 ATTRIBUTES
+
+This class defines the following attributes.
+
+=head2 type_constraints
+
+A list of L<Moose::Meta::TypeConstraint> objects.
+
+=cut
+
+has 'type_constraints' => (
+ is=>'ro',
+ isa=>'Ref',
+ predicate=>'has_type_constraints',
+);
+
+=head2 constraint_generator
+
+A subref or closure that contains the way we validate incoming values against
+a set of type constraints.
+
+=cut
+
+has 'constraint_generator' => (
+ is=>'ro',
+ isa=>'CodeRef',
+ predicate=>'has_constraint_generator',
+);
+
+=head1 METHODS
+
+This class defines the following methods.
+
+=head2 new
+
+Initialization stuff.
+
+=cut
+
+around 'new' => sub {
+ my ($new, $class, @args) = @_;
+ my $self = $class->$new(@args);
+ $self->coercion(MooseX::Meta::TypeCoercion::Structured->new(
+ type_constraint => $self,
+ ));
+ return $self;
+};
+
+=head2 validate
+
+Messing with validate so that we can support niced error messages.
+=cut
+
+override 'validate' => sub {
+ my ($self, @args) = @_;
+ my $compiled_type_constraint = $self->_compiled_type_constraint;
+ my $message = bless {message=>undef}, 'MooseX::Types::Structured::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)
+
+=cut
+
+sub generate_constraint_for {
+ my ($self, $type_constraints) = @_;
+ return sub {
+ my $arg = shift @_;
+ my $constraint_generator = $self->constraint_generator;
+ my $result = $constraint_generator->($type_constraints, $arg, $_[0]);
+ return $result;
+ };
+}
+
+=head2 parameterize (@type_constraints)
+
+Given a ref of type constraints, create a structured type.
+
+=cut
+
+sub parameterize {
+
+ my ($self, @type_constraints) = @_;
+ my $class = ref $self;
+ my $name = $self->name .'['. join(',', map {"$_"} @type_constraints) .']';
+ my $constraint_generator = $self->__infer_constraint_generator;
+
+ return $class->new(
+ name => $name,
+ parent => $self,
+ type_constraints => \@type_constraints,
+ constraint_generator => $constraint_generator,
+ );
+}
+
+=head2 __infer_constraint_generator
+
+This returns a CODEREF which generates a suitable constraint generator. Not
+user servicable, you'll never call this directly.
+
+=cut
+
+sub __infer_constraint_generator {
+ my ($self) = @_;
+ if($self->has_constraint_generator) {
+ return $self->constraint_generator;
+ } else {
+ return sub {
+ ## I'm not sure about this stuff but everything seems to work
+ my $tc = shift @_;
+ my $merged_tc = [@$tc, @{$self->parent->type_constraints}];
+ $self->constraint->($merged_tc, @_);
+ };
+ }
+}
+
+=head2 compile_type_constraint
+
+hook into compile_type_constraint so we can set the correct validation rules.
+
+=cut
+
+around 'compile_type_constraint' => sub {
+ my ($compile_type_constraint, $self, @args) = @_;
+
+ if($self->has_type_constraints) {
+ my $type_constraints = $self->type_constraints;
+ my $constraint = $self->generate_constraint_for($type_constraints);
+ $self->_set_constraint($constraint);
+ }
+
+ return $self->$compile_type_constraint(@args);
+};
+
+=head2 create_child_type
+
+modifier to make sure we get the constraint_generator
+
+=cut
+
+around 'create_child_type' => sub {
+ my ($create_child_type, $self, %opts) = @_;
+ return $self->$create_child_type(
+ %opts,
+ constraint_generator => $self->__infer_constraint_generator,
+ );
+};
+
+=head2 is_a_type_of
+
+=head2 is_subtype_of
+
+=head2 equals
+
+Override the base class behavior.
+
+=cut
+
+sub equals {
+ my ( $self, $type_or_name ) = @_;
+ my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
+
+ return unless $other->isa(__PACKAGE__);
+
+ return (
+ $self->type_constraints_equals($other)
+ and
+ $self->parent->equals( $other->parent )
+ );
+}
+
+=head2 type_constraints_equals
+
+Checks to see if the internal type contraints are equal.
+
+=cut
+
+sub type_constraints_equals {
+ my ($self, $other) = @_;
+ my @self_type_constraints = @{$self->type_constraints||[]};
+ my @other_type_constraints = @{$other->type_constraints||[]};
+
+ ## Incoming ay be either arrayref or hashref, need top compare both
+ while(@self_type_constraints) {
+ my $self_type_constraint = shift @self_type_constraints;
+ my $other_type_constraint = shift @other_type_constraints
+ || return; ## $other needs the same number of children.
+
+ if( ref $self_type_constraint) {
+ $self_type_constraint->equals($other_type_constraint)
+ || return; ## type constraints obviously need top be equal
+ } else {
+ $self_type_constraint eq $other_type_constraint
+ || return; ## strings should be equal
+ }
+
+ }
+
+ return 1; ##If we get this far, everything is good.
+}
+
+=head2 get_message
+
+Give you a better peek into what's causing the error. For now we stringify the
+incoming deep value with L<Devel::PartialDump> and pass that on to either your
+custom error message or the default one. In the future we'll try to provide a
+more complete stack trace of the actual offending elements
+
+=cut
+
+around 'get_message' => sub {
+ my ($get_message, $self, $value) = @_;
+ $value = Devel::PartialDump::dump($value)
+ if ref $value;
+ return $self->$get_message($value);
+};
+
+=head1 SEE ALSO
+
+The following modules or resources may be of interest.
+
+L<Moose>, L<Moose::Meta::TypeConstraint>
+
+=head1 AUTHOR
+
+John Napiorkowski, C<< <jjnapiork at cpan.org> >>
+
+=head1 COPYRIGHT & LICENSE
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
\ No newline at end of file
Deleted: MooseX-Types-Structured/tags/0.10/lib/MooseX/Types/Structured.pm
===================================================================
--- MooseX-Types-Structured/trunk/lib/MooseX/Types/Structured.pm 2009-04-01 03:11:50 UTC (rev 7879)
+++ MooseX-Types-Structured/tags/0.10/lib/MooseX/Types/Structured.pm 2009-04-02 15:10:44 UTC (rev 7884)
@@ -1,785 +0,0 @@
-package MooseX::Types::Structured;
-
-use 5.008;
-
-use Moose::Util::TypeConstraints;
-use MooseX::Meta::TypeConstraint::Structured;
-use MooseX::Types -declare => [qw(Dict Tuple Optional)];
-use Sub::Exporter -setup => { exports => [ qw(Dict Tuple Optional slurpy) ] };
-
-our $VERSION = '0.09';
-our $AUTHORITY = 'cpan:JJNAPIORK';
-
-=head1 NAME
-
-MooseX::Types::Structured - Structured Type Constraints for Moose
-
-=head1 SYNOPSIS
-
-The following is example usage for this module.
-
- package Person;
-
- use Moose;
- use MooseX::Types::Moose qw(Str Int HashRef);
- use MooseX::Types::Structured qw(Dict Tuple Optional);
-
- ## A name has a first and last part, but middle names are not required
- has name => (
- isa=>Dict[
- first => Str,
- last => Str,
- middle => Optional[Str],
- ],
- );
-
- ## description is a string field followed by a HashRef of tagged data.
- has description => (
- isa=>Tuple[
- Str,
- Optional[HashRef],
- ],
- );
-
-Then you can instantiate this class with something like:
-
- my $john = Person->new(
- name => {
- first => 'John',
- middle => 'James'
- last => 'Napiorkowski',
- },
- description => [
- 'A cool guy who loves Perl and Moose.', {
- married_to => 'Vanessa Li',
- born_in => 'USA',
- };
- ]
- );
-
-Or with:
-
- my $vanessa = Person->new(
- name => {
- first => 'Vanessa',
- last => 'Li'
- },
- description => ['A great student!'],
- );
-
-But all of these would cause a constraint error for the 'name' attribute:
-
- ## Value for 'name' not a HashRef
- Person->new( name => 'John' );
-
- ## Value for 'name' has incorrect hash key and missing required keys
- Person->new( name => {
- first_name => 'John'
- });
-
- ## Also incorrect keys
- Person->new( name => {
- first_name => 'John',
- age => 39,
- });
-
- ## key 'middle' incorrect type, should be a Str not a ArrayRef
- Person->new( name => {
- first => 'Vanessa',
- middle => [1,2],
- last => 'Li',
- });
-
-And these would cause a constraint error for the 'description' attribute:
-
- ## Should be an ArrayRef
- Person->new( description => 'Hello I am a String' );
-
- ## First element must be a string not a HashRef.
- Person->new (description => [{
- tag1 => 'value1',
- tag2 => 'value2'
- }]);
-
-Please see the test cases for more examples.
-
-=head1 DESCRIPTION
-
-A structured type constraint is a standard container L<Moose> type constraint,
-such as an ArrayRef or HashRef, which has been enhanced to allow you to
-explicitly name all the allowed type constraints inside the structure. The
-generalized form is:
-
- TypeConstraint[@TypeParameters or %TypeParameters]
-
-Where 'TypeParameters' is an array reference or hash references of
-L<Moose::Meta::TypeConstraint> objects.
-
-This type library enables structured type constraints. It is built on top of the
-L<MooseX::Types> library system, so you should review the documentation for that
-if you are not familiar with it.
-
-=head2 Comparing Parameterized types to Structured types
-
-Parameterized constraints are built into core Moose and you are probably already
-familar with the type constraints 'HashRef' and 'ArrayRef'. Structured types
-have similar functionality, so their syntax is likewise similar. For example,
-you could define a parameterized constraint like:
-
- subtype ArrayOfInts,
- as Arrayref[Int];
-
-which would constrain a value to something like [1,2,3,...] and so on. On the
-other hand, a structured type constraint explicitly names all it's allowed
-'internal' type parameter constraints. For the example:
-
- subtype StringFollowedByInt,
- as Tuple[Str,Int];
-
-would constrain it's value to things like ['hello', 111] but ['hello', 'world']
-would fail, as well as ['hello', 111, 'world'] and so on. Here's another
-example:
-
- subtype StringIntOptionalHashRef,
- as Tuple[
- Str, Int,
- Optional[HashRef]
- ];
-
-This defines a type constraint that validates values like:
-
- ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
- ['World', 200];
-
-Notice that the last type constraint in the structure is optional. This is
-enabled via the helper Optional type constraint, which is a variation of the
-core Moose type constraint 'Maybe'. The main difference is that Optional type
-constraints are required to validate if they exist, while 'Maybe' permits
-undefined values. So the following example would not validate:
-
- StringIntOptionalHashRef->validate(['Hello Undefined', 1000, undef]);
-
-Please note the subtle difference between undefined and null. If you wish to
-allow both null and undefined, you should use the core Moose 'Maybe' type
-constraint instead:
-
- use MooseX::Types -declare [qw(StringIntMaybeHashRef)];
- use MooseX::Types::Moose qw(Maybe);
- use MooseX::Types::Structured qw(Tuple);
-
- subtype StringIntMaybeHashRef,
- as Tuple[
- Str, Int, Maybe[HashRef]
- ];
-
-This would validate the following:
-
- ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
- ['World', 200, undef];
- ['World', 200];
-
-Structured constraints are not limited to arrays. You can define a structure
-against a HashRef with 'Dict' as in this example:
-
- subtype FirstNameLastName,
- as Dict[
- firstname => Str,
- lastname => Str,
- ];
-
-This would constrain a HashRef to something like:
-
- {firstname => 'Christopher', lastname= > 'Parsons'};
-
-but all the following would fail validation:
-
- ## Incorrect keys
- {first => 'Christopher', last => 'Parsons'};
-
- ## Too many keys
- {firstname => 'Christopher', lastname => 'Parsons', middlename => 'Allen'};
-
- ## Not a HashRef
- ['Christopher', 'Christopher'];
-
-These structures can be as simple or elaborate as you wish. You can even
-combine various structured, parameterized and simple constraints all together:
-
- subtype Crazy,
- as Tuple[
- Int,
- Dict[name=>Str, age=>Int],
- ArrayRef[Int]
- ];
-
-Which would match "[1, {name=>'John', age=>25},[10,11,12]]". Please notice how
-the type parameters can be visually arranged to your liking and to improve the
-clarity of your meaning. You don't need to run then altogether onto a single
-line.
-
-=head2 Alternatives
-
-You should exercise some care as to whether or not your complex structured
-constraints would be better off contained by a real object as in the following
-example:
-
- package MyApp::MyStruct;
- use Moose;
-
- ## lazy way to make a bunch of attributes
- has $_ for qw(full_name age_in_years);
-
- package MyApp::MyClass;
- use Moose;
-
- has person => (isa => 'MyApp::MyStruct');
-
- my $instance = MyApp::MyClass->new(
- person=>MyApp::MyStruct->new(
- full_name => 'John',
- age_in_years => 39,
- ),
- );
-
-This method may take some additional time to setup but will give you more
-flexibility. However, structured constraints are highly compatible with this
-method, granting some interesting possibilities for coercion. Try:
-
- package MyApp::MyClass;
-
- use Moose;
- use MyApp::MyStruct;
-
- ## It's recommended your type declarations live in a separate class in order
- ## to promote reusability and clarity. Inlined here for brevity.
-
- use MooseX::Types::DateTime qw(DateTime);
- use MooseX::Types -declare [qw(MyStruct)];
- use MooseX::Types::Moose qw(Str Int);
- use MooseX::Types::Structured qw(Dict);
-
- ## Use class_type to create an ISA type constraint if your object doesn't
- ## inherit from Moose::Object.
- class_type 'MyApp::MyStruct';
-
- ## Just a shorter version really.
- subtype MyStruct,
- as 'MyApp::MyStruct';
-
- ## Add the coercions.
- coerce MyStruct,
- from Dict[
- full_name=>Str,
- age_in_years=>Int
- ], via {
- MyApp::MyStruct->new(%$_);
- },
- from Dict[
- lastname=>Str,
- firstname=>Str,
- dob=>DateTime
- ], via {
- my $name = $_->{firstname} .' '. $_->{lastname};
- my $age = DateTime->now - $_->{dob};
-
- MyApp::MyStruct->new(
- full_name=>$name,
- age_in_years=>$age->years,
- );
- };
-
- has person => (isa=>MyStruct);
-
-This would allow you to instantiate with something like:
-
- my $obj = MyApp::MyClass->new( person => {
- full_name=>'John Napiorkowski',
- age_in_years=>39,
- });
-
-Or even:
-
- my $obj = MyApp::MyClass->new( person => {
- lastname=>'John',
- firstname=>'Napiorkowski',
- dob=>DateTime->new(year=>1969),
- });
-
-If you are not familiar with how coercions work, check out the L<Moose> cookbook
-entry L<Moose::Cookbook::Recipe5> for an explanation. The section L</Coercions>
-has additional examples and discussion.
-
-=head2 Subtyping a Structured type constraint
-
-You need to exercise some care when you try to subtype a structured type as in
-this example:
-
- subtype Person,
- as Dict[name => Str];
-
- subtype FriendlyPerson,
- as Person[
- name => Str,
- total_friends => Int,
- ];
-
-This will actually work BUT you have to take care that the subtype has a
-structure that does not contradict the structure of it's parent. For now the
-above works, but I will clarify the syntax for this at a future point, so
-it's recommended to avoid (should not really be needed so much anyway). For
-now this is supported in an EXPERIMENTAL way. Your thoughts, test cases and
-patches are welcomed for discussion. If you find a good use for this, please
-let me know.
-
-=head2 Coercions
-
-Coercions currently work for 'one level' deep. That is you can do:
-
- subtype Person,
- as Dict[
- name => Str,
- age => Int
- ];
-
- subtype Fullname,
- as Dict[
- first => Str,
- last => Str
- ];
-
- coerce Person,
- ## Coerce an object of a particular class
- from BlessedPersonObject, via {
- +{
- name=>$_->name,
- age=>$_->age,
- };
- },
-
- ## Coerce from [$name, $age]
- from ArrayRef, via {
- +{
- name=>$_->[0],
- age=>$_->[1],
- },
- },
- ## Coerce from {fullname=>{first=>...,last=>...}, dob=>$DateTimeObject}
- from Dict[fullname=>Fullname, dob=>DateTime], via {
- my $age = $_->dob - DateTime->now;
- my $firstn = $_->{fullname}->{first};
- my $lastn = $_->{fullname}->{last}
- +{
- name => $_->{fullname}->{first} .' '. ,
- age =>$age->years
- }
- };
-
-And that should just work as expected. However, if there are any 'inner'
-coercions, such as a coercion on 'Fullname' or on 'DateTime', that coercion
-won't currently get activated.
-
-Please see the test '07-coerce.t' for a more detailed example. Discussion on
-extending coercions to support this welcome on the Moose development channel or
-mailing list.
-
-=head2 Recursion
-
-Newer versions of L<MooseX::Types> support recursive type constraints. That is
-you can include a type constraint as a contained type constraint of itself. For
-example:
-
- subtype Person,
- as Dict[
- name=>Str,
- friends=>Optional[
- ArrayRef[Person]
- ],
- ];
-
-This would declare a Person subtype that contains a name and an optional
-ArrayRef of Persons who are friends as in:
-
- {
- name => 'Mike',
- friends => [
- { name => 'John' },
- { name => 'Vincent' },
- {
- name => 'Tracey',
- friends => [
- { name => 'Stephenie' },
- { name => 'Ilya' },
- ],
- },
- ],
- };
-
-Please take care to make sure the recursion node is either Optional, or declare
-a Union with an non recursive option such as:
-
- subtype Value
- as Tuple[
- Str,
- Str|Tuple,
- ];
-
-Which validates:
-
- [
- 'Hello', [
- 'World', [
- 'Is', [
- 'Getting',
- 'Old',
- ],
- ],
- ],
- ];
-
-Otherwise you will define a subtype thatis impossible to validate since it is
-infinitely recursive. For more information about defining recursive types,
-please see the documentation in L<MooseX::Types> and the test cases.
-
-=head1 TYPE CONSTRAINTS
-
-This type library defines the following constraints.
-
-=head2 Tuple[@constraints]
-
-This defines an ArrayRef based constraint which allows you to validate a specific
-list of contained constraints. For example:
-
- Tuple[Int,Str]; ## Validates [1,'hello']
- Tuple[Str|Object, Int]; ## Validates ['hello', 1] or [$object, 2]
-
-=head2 Dict[%constraints]
-
-This defines a HashRef based constraint which allowed you to validate a specific
-hashref. For example:
-
- Dict[name=>Str, age=>Int]; ## Validates {name=>'John', age=>39}
-
-=head2 Optional[$constraint]
-
-This is primarily a helper constraint for Dict and Tuple type constraints. What
-this allows if for you to assert that a given type constraint is allowed to be
-null (but NOT undefined). If the value is null, then the type constraint passes
-but if the value is defined it must validate against the type constraint. This
-makes it easy to make a Dict where one or more of the keys doesn't have to exist
-or a tuple where some of the values are not required. For example:
-
- subtype Name() => as Dict[
- first=>Str,
- last=>Str,
- middle=>Optional[Str],
- ];
-
-Creates a constraint that validates against a hashref with the keys 'first' and
-'last' being strings and required while an optional key 'middle' is must be a
-string if it appears but doesn't have to appear. So in this case both the
-following are valid:
-
- {first=>'John', middle=>'James', last=>'Napiorkowski'}
- {first=>'Vanessa', last=>'Li'}
-
-=head1 EXPORTABLE SUBROUTINES
-
-This type library makes available for export the following subroutines
-
-=head2 slurpy
-
-Structured type constraints by their nature are closed; that is validation will
-depend and an exact match between your structure definition and the arguments to
-be checked. Sometimes you might wish for a slightly looser amount of validation.
-For example, you may wish to validate the first 3 elements of an array reference
-and allow for an arbitrary number of additional elements. At first thought you
-might think you could do it this way:
-
- # I want to validate stuff like: [1,"hello", $obj, 2,3,4,5,6,...]
- subtype AllowTailingArgs,
- as Tuple[
- Int,
- Str,
- Object,
- ArrayRef[Int],
- ];
-
-However what this will actually validate are structures like this:
-
- [10,"Hello", $obj, [11,12,13,...] ]; # Notice element 4 is an ArrayRef
-
-In order to allow structured validation of, "and then some", arguments, you can
-use the </slurpy> method against a type constraint. For example:
-
- use MooseX::Types::Structured qw(Tuple slurpy);
-
- subtype AllowTailingArgs,
- as Tuple[
- Int,
- Str,
- Object,
- slurpy ArrayRef[Int],
- ];
-
-This will now work as expected, validating ArrayRef structures such as:
-
- [1,"hello", $obj, 2,3,4,5,6,...]
-
-A few caveats apply. First, the slurpy type constraint must be the last one in
-the list of type constraint parameters. Second, the parent type of the slurpy
-type constraint must match that of the containing type constraint. That means
-that a Tuple can allow a slurpy ArrayRef (or children of ArrayRefs, including
-another Tuple) and a Dict can allow a slurpy HashRef (or children/subtypes of
-HashRef, also including other Dict constraints).
-
-Please note the the technical way this works 'under the hood' is that the
-slurpy keywork transforms the target type constraint into a coderef. Please do
-not try to create your own custom coderefs; always use the slurpy method. The
-underlying technology may change in the future but the slurpy keyword will be
-supported.
-
-=head1 EXAMPLES
-
-Here are some additional example usage for structured types. All examples can
-be found also in the 't/examples.t' test. Your contributions are also welcomed.
-
-=head2 Normalize a HashRef
-
-You need a hashref to conform to a canonical structure but are required accept a
-bunch of different incoming structures. You can normalize using the Dict type
-constraint and coercions. This example also shows structured types mixed which
-other MooseX::Types libraries.
-
- package Test::MooseX::Meta::TypeConstraint::Structured::Examples::Normalize;
-
- use Moose;
- use DateTime;
-
- use MooseX::Types::Structured qw(Dict Tuple);
- use MooseX::Types::DateTime qw(DateTime);
- use MooseX::Types::Moose qw(Int Str Object);
- use MooseX::Types -declare => [qw(Name Age Person)];
-
- subtype Person,
- as Dict[
- name=>Str,
- age=>Int,
- ];
-
- coerce Person,
- from Dict[
- first=>Str,
- last=>Str,
- years=>Int,
- ], via { +{
- name => "$_->{first} $_->{last}",
- age => $_->{years},
- }},
- from Dict[
- fullname=>Dict[
- last=>Str,
- first=>Str,
- ],
- dob=>DateTime,
- ],
- ## DateTime needs to be inside of single quotes here to disambiguate the
- ## class package from the DataTime type constraint imported via the
- ## line "use MooseX::Types::DateTime qw(DateTime);"
- via { +{
- name => "$_->{fullname}{first} $_->{fullname}{last}",
- age => ($_->{dob} - 'DateTime'->now)->years,
- }};
-
- has person => (is=>'rw', isa=>Person, coerce=>1);
-
-And now you can instantiate with all the following:
-
- __PACKAGE__->new(
- name=>'John Napiorkowski',
- age=>39,
- );
-
- __PACKAGE__->new(
- first=>'John',
- last=>'Napiorkowski',
- years=>39,
- );
-
- __PACKAGE__->new(
- fullname => {
- first=>'John',
- last=>'Napiorkowski'
- },
- dob => 'DateTime'->new(
- year=>1969,
- month=>2,
- day=>13
- ),
- );
-
-This technique is a way to support various ways to instantiate your class in a
-clean and declarative way.
-
-=cut
-
-Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
- MooseX::Meta::TypeConstraint::Structured->new(
- name => "MooseX::Types::Structured::Tuple" ,
- parent => find_type_constraint('ArrayRef'),
- constraint_generator=> sub {
- ## Get the constraints and values to check
- my ($type_constraints, $values) = @_;
- my @type_constraints = defined $type_constraints ?
- @$type_constraints : ();
-
- my $overflow_handler;
- if(ref $type_constraints[-1] eq 'CODE') {
- $overflow_handler = pop @type_constraints;
- }
-
- my @values = defined $values ? @$values: ();
- ## Perform the checking
- while(@type_constraints) {
- my $type_constraint = shift @type_constraints;
- if(@values) {
- my $value = shift @values;
- unless($type_constraint->check($value)) {
- return;
- }
- } else {
- ## Test if the TC supports null values
- unless($type_constraint->check()) {
- return;
- }
- }
- }
- ## Make sure there are no leftovers.
- if(@values) {
- if($overflow_handler) {
- return $overflow_handler->([@values]);
- } else {
- return;
- }
- } elsif(@type_constraints) {
- warn "I failed due to left over TC";
- return;
- } else {
- return 1;
- }
- }
- )
-);
-
-Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
- MooseX::Meta::TypeConstraint::Structured->new(
- name => "MooseX::Types::Structured::Dict",
- parent => find_type_constraint('HashRef'),
- constraint_generator=> sub {
- ## Get the constraints and values to check
- my ($type_constraints, $values) = @_;
- my @type_constraints = defined $type_constraints ?
- @$type_constraints : ();
-
- my $overflow_handler;
- if(ref $type_constraints[-1] eq 'CODE') {
- $overflow_handler = pop @type_constraints;
- }
- my (%type_constraints) = @type_constraints;
- my %values = defined $values ? %$values: ();
- ## Perform the checking
- while(%type_constraints) {
- my($key, $type_constraint) = each %type_constraints;
- delete $type_constraints{$key};
- if(exists $values{$key}) {
- my $value = $values{$key};
- delete $values{$key};
- unless($type_constraint->check($value)) {
- return;
- }
- } else {
- ## Test to see if the TC supports null values
- unless($type_constraint->check()) {
- return;
- }
- }
- }
- ## Make sure there are no leftovers.
- if(%values) {
- if($overflow_handler) {
- return $overflow_handler->(+{%values});
- } else {
- return;
- }
- } elsif(%type_constraints) {
- return;
- } else {
- return 1;
- }
- },
- )
-);
-
-OPTIONAL: {
- my $Optional = Moose::Meta::TypeConstraint::Parameterizable->new(
- name => 'MooseX::Types::Structured::Optional',
- package_defined_in => __PACKAGE__,
- parent => find_type_constraint('Item'),
- constraint => sub { 1 },
- constraint_generator => sub {
- my ($type_parameter, @args) = @_;
- my $check = $type_parameter->_compiled_type_constraint();
- return sub {
- my (@args) = @_;
- ## Does the arg exist? Something exists if it's a 'real' value
- ## or if it is set to undef.
- if(exists($args[0])) {
- ## If it exists, we need to validate it
- $check->($args[0]);
- } else {
- ## But it's is okay if the value doesn't exists
- return 1;
- }
- }
- }
- );
-
- Moose::Util::TypeConstraints::register_type_constraint($Optional);
- Moose::Util::TypeConstraints::add_parameterizable_type($Optional);
-}
-
-sub slurpy($) {
- my $tc = shift @_;
- return sub {
- $tc->check(shift);
- };
-}
-
-=head1 SEE ALSO
-
-The following modules or resources may be of interest.
-
-L<Moose>, L<MooseX::Types>, L<Moose::Meta::TypeConstraint>,
-L<MooseX::Meta::TypeConstraint::Structured>
-
-=head1 TODO
-
-Here's a list of stuff I would be happy to get volunteers helping with:
-
-All POD examples need test cases in t/documentation/*.t
-Want to break out the examples section to a separate cookbook style POD.
-Want more examples and best practice / usage guidance for authors
-Need to clarify deep coercions,
-Need to clarify subtypes of subtypes.
-
-=head1 AUTHOR
-
-John Napiorkowski, C<< <jjnapiork at cpan.org> >>
-
-=head1 COPYRIGHT & LICENSE
-
-This program is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
-=cut
-
-1;
Copied: MooseX-Types-Structured/tags/0.10/lib/MooseX/Types/Structured.pm (from rev 7883, MooseX-Types-Structured/trunk/lib/MooseX/Types/Structured.pm)
===================================================================
--- MooseX-Types-Structured/tags/0.10/lib/MooseX/Types/Structured.pm (rev 0)
+++ MooseX-Types-Structured/tags/0.10/lib/MooseX/Types/Structured.pm 2009-04-02 15:10:44 UTC (rev 7884)
@@ -0,0 +1,826 @@
+package MooseX::Types::Structured;
+
+use 5.008;
+
+use Moose::Util::TypeConstraints;
+use MooseX::Meta::TypeConstraint::Structured;
+use MooseX::Types -declare => [qw(Dict Tuple Optional)];
+use Sub::Exporter -setup => { exports => [ qw(Dict Tuple Optional slurpy) ] };
+use Devel::PartialDump;
+
+our $VERSION = '0.10';
+our $AUTHORITY = 'cpan:JJNAPIORK';
+
+=head1 NAME
+
+MooseX::Types::Structured - Structured Type Constraints for Moose
+
+=head1 SYNOPSIS
+
+The following is example usage for this module.
+
+ package Person;
+
+ use Moose;
+ use MooseX::Types::Moose qw(Str Int HashRef);
+ use MooseX::Types::Structured qw(Dict Tuple Optional);
+
+ ## A name has a first and last part, but middle names are not required
+ has name => (
+ isa=>Dict[
+ first => Str,
+ last => Str,
+ middle => Optional[Str],
+ ],
+ );
+
+ ## description is a string field followed by a HashRef of tagged data.
+ has description => (
+ isa=>Tuple[
+ Str,
+ Optional[HashRef],
+ ],
+ );
+
+Then you can instantiate this class with something like:
+
+ my $john = Person->new(
+ name => {
+ first => 'John',
+ middle => 'James'
+ last => 'Napiorkowski',
+ },
+ description => [
+ 'A cool guy who loves Perl and Moose.', {
+ married_to => 'Vanessa Li',
+ born_in => 'USA',
+ };
+ ]
+ );
+
+Or with:
+
+ my $vanessa = Person->new(
+ name => {
+ first => 'Vanessa',
+ last => 'Li'
+ },
+ description => ['A great student!'],
+ );
+
+But all of these would cause a constraint error for the 'name' attribute:
+
+ ## Value for 'name' not a HashRef
+ Person->new( name => 'John' );
+
+ ## Value for 'name' has incorrect hash key and missing required keys
+ Person->new( name => {
+ first_name => 'John'
+ });
+
+ ## Also incorrect keys
+ Person->new( name => {
+ first_name => 'John',
+ age => 39,
+ });
+
+ ## key 'middle' incorrect type, should be a Str not a ArrayRef
+ Person->new( name => {
+ first => 'Vanessa',
+ middle => [1,2],
+ last => 'Li',
+ });
+
+And these would cause a constraint error for the 'description' attribute:
+
+ ## Should be an ArrayRef
+ Person->new( description => 'Hello I am a String' );
+
+ ## First element must be a string not a HashRef.
+ Person->new (description => [{
+ tag1 => 'value1',
+ tag2 => 'value2'
+ }]);
+
+Please see the test cases for more examples.
+
+=head1 DESCRIPTION
+
+A structured type constraint is a standard container L<Moose> type constraint,
+such as an ArrayRef or HashRef, which has been enhanced to allow you to
+explicitly name all the allowed type constraints inside the structure. The
+generalized form is:
+
+ TypeConstraint[@TypeParameters or %TypeParameters]
+
+Where 'TypeParameters' is an array reference or hash references of
+L<Moose::Meta::TypeConstraint> objects.
+
+This type library enables structured type constraints. It is built on top of the
+L<MooseX::Types> library system, so you should review the documentation for that
+if you are not familiar with it.
+
+=head2 Comparing Parameterized types to Structured types
+
+Parameterized constraints are built into core Moose and you are probably already
+familar with the type constraints 'HashRef' and 'ArrayRef'. Structured types
+have similar functionality, so their syntax is likewise similar. For example,
+you could define a parameterized constraint like:
+
+ subtype ArrayOfInts,
+ as Arrayref[Int];
+
+which would constrain a value to something like [1,2,3,...] and so on. On the
+other hand, a structured type constraint explicitly names all it's allowed
+'internal' type parameter constraints. For the example:
+
+ subtype StringFollowedByInt,
+ as Tuple[Str,Int];
+
+would constrain it's value to things like ['hello', 111] but ['hello', 'world']
+would fail, as well as ['hello', 111, 'world'] and so on. Here's another
+example:
+
+ subtype StringIntOptionalHashRef,
+ as Tuple[
+ Str, Int,
+ Optional[HashRef]
+ ];
+
+This defines a type constraint that validates values like:
+
+ ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
+ ['World', 200];
+
+Notice that the last type constraint in the structure is optional. This is
+enabled via the helper Optional type constraint, which is a variation of the
+core Moose type constraint 'Maybe'. The main difference is that Optional type
+constraints are required to validate if they exist, while 'Maybe' permits
+undefined values. So the following example would not validate:
+
+ StringIntOptionalHashRef->validate(['Hello Undefined', 1000, undef]);
+
+Please note the subtle difference between undefined and null. If you wish to
+allow both null and undefined, you should use the core Moose 'Maybe' type
+constraint instead:
+
+ use MooseX::Types -declare [qw(StringIntMaybeHashRef)];
+ use MooseX::Types::Moose qw(Maybe);
+ use MooseX::Types::Structured qw(Tuple);
+
+ subtype StringIntMaybeHashRef,
+ as Tuple[
+ Str, Int, Maybe[HashRef]
+ ];
+
+This would validate the following:
+
+ ['Hello', 100, {key1 => 'value1', key2 => 'value2'}];
+ ['World', 200, undef];
+ ['World', 200];
+
+Structured constraints are not limited to arrays. You can define a structure
+against a HashRef with 'Dict' as in this example:
+
+ subtype FirstNameLastName,
+ as Dict[
+ firstname => Str,
+ lastname => Str,
+ ];
+
+This would constrain a HashRef to something like:
+
+ {firstname => 'Christopher', lastname= > 'Parsons'};
+
+but all the following would fail validation:
+
+ ## Incorrect keys
+ {first => 'Christopher', last => 'Parsons'};
+
+ ## Too many keys
+ {firstname => 'Christopher', lastname => 'Parsons', middlename => 'Allen'};
+
+ ## Not a HashRef
+ ['Christopher', 'Christopher'];
+
+These structures can be as simple or elaborate as you wish. You can even
+combine various structured, parameterized and simple constraints all together:
+
+ subtype Crazy,
+ as Tuple[
+ Int,
+ Dict[name=>Str, age=>Int],
+ ArrayRef[Int]
+ ];
+
+Which would match "[1, {name=>'John', age=>25},[10,11,12]]". Please notice how
+the type parameters can be visually arranged to your liking and to improve the
+clarity of your meaning. You don't need to run then altogether onto a single
+line.
+
+=head2 Alternatives
+
+You should exercise some care as to whether or not your complex structured
+constraints would be better off contained by a real object as in the following
+example:
+
+ package MyApp::MyStruct;
+ use Moose;
+
+ ## lazy way to make a bunch of attributes
+ has $_ for qw(full_name age_in_years);
+
+ package MyApp::MyClass;
+ use Moose;
+
+ has person => (isa => 'MyApp::MyStruct');
+
+ my $instance = MyApp::MyClass->new(
+ person=>MyApp::MyStruct->new(
+ full_name => 'John',
+ age_in_years => 39,
+ ),
+ );
+
+This method may take some additional time to setup but will give you more
+flexibility. However, structured constraints are highly compatible with this
+method, granting some interesting possibilities for coercion. Try:
+
+ package MyApp::MyClass;
+
+ use Moose;
+ use MyApp::MyStruct;
+
+ ## It's recommended your type declarations live in a separate class in order
+ ## to promote reusability and clarity. Inlined here for brevity.
+
+ use MooseX::Types::DateTime qw(DateTime);
+ use MooseX::Types -declare [qw(MyStruct)];
+ use MooseX::Types::Moose qw(Str Int);
+ use MooseX::Types::Structured qw(Dict);
+
+ ## Use class_type to create an ISA type constraint if your object doesn't
+ ## inherit from Moose::Object.
+ class_type 'MyApp::MyStruct';
+
+ ## Just a shorter version really.
+ subtype MyStruct,
+ as 'MyApp::MyStruct';
+
+ ## Add the coercions.
+ coerce MyStruct,
+ from Dict[
+ full_name=>Str,
+ age_in_years=>Int
+ ], via {
+ MyApp::MyStruct->new(%$_);
+ },
+ from Dict[
+ lastname=>Str,
+ firstname=>Str,
+ dob=>DateTime
+ ], via {
+ my $name = $_->{firstname} .' '. $_->{lastname};
+ my $age = DateTime->now - $_->{dob};
+
+ MyApp::MyStruct->new(
+ full_name=>$name,
+ age_in_years=>$age->years,
+ );
+ };
+
+ has person => (isa=>MyStruct);
+
+This would allow you to instantiate with something like:
+
+ my $obj = MyApp::MyClass->new( person => {
+ full_name=>'John Napiorkowski',
+ age_in_years=>39,
+ });
+
+Or even:
+
+ my $obj = MyApp::MyClass->new( person => {
+ lastname=>'John',
+ firstname=>'Napiorkowski',
+ dob=>DateTime->new(year=>1969),
+ });
+
+If you are not familiar with how coercions work, check out the L<Moose> cookbook
+entry L<Moose::Cookbook::Recipe5> for an explanation. The section L</Coercions>
+has additional examples and discussion.
+
+=head2 Subtyping a Structured type constraint
+
+You need to exercise some care when you try to subtype a structured type as in
+this example:
+
+ subtype Person,
+ as Dict[name => Str];
+
+ subtype FriendlyPerson,
+ as Person[
+ name => Str,
+ total_friends => Int,
+ ];
+
+This will actually work BUT you have to take care that the subtype has a
+structure that does not contradict the structure of it's parent. For now the
+above works, but I will clarify the syntax for this at a future point, so
+it's recommended to avoid (should not really be needed so much anyway). For
+now this is supported in an EXPERIMENTAL way. Your thoughts, test cases and
+patches are welcomed for discussion. If you find a good use for this, please
+let me know.
+
+=head2 Coercions
+
+Coercions currently work for 'one level' deep. That is you can do:
+
+ subtype Person,
+ as Dict[
+ name => Str,
+ age => Int
+ ];
+
+ subtype Fullname,
+ as Dict[
+ first => Str,
+ last => Str
+ ];
+
+ coerce Person,
+ ## Coerce an object of a particular class
+ from BlessedPersonObject, via {
+ +{
+ name=>$_->name,
+ age=>$_->age,
+ };
+ },
+
+ ## Coerce from [$name, $age]
+ from ArrayRef, via {
+ +{
+ name=>$_->[0],
+ age=>$_->[1],
+ },
+ },
+ ## Coerce from {fullname=>{first=>...,last=>...}, dob=>$DateTimeObject}
+ from Dict[fullname=>Fullname, dob=>DateTime], via {
+ my $age = $_->dob - DateTime->now;
+ my $firstn = $_->{fullname}->{first};
+ my $lastn = $_->{fullname}->{last}
+ +{
+ name => $_->{fullname}->{first} .' '. ,
+ age =>$age->years
+ }
+ };
+
+And that should just work as expected. However, if there are any 'inner'
+coercions, such as a coercion on 'Fullname' or on 'DateTime', that coercion
+won't currently get activated.
+
+Please see the test '07-coerce.t' for a more detailed example. Discussion on
+extending coercions to support this welcome on the Moose development channel or
+mailing list.
+
+=head2 Recursion
+
+Newer versions of L<MooseX::Types> support recursive type constraints. That is
+you can include a type constraint as a contained type constraint of itself. For
+example:
+
+ subtype Person,
+ as Dict[
+ name=>Str,
+ friends=>Optional[
+ ArrayRef[Person]
+ ],
+ ];
+
+This would declare a Person subtype that contains a name and an optional
+ArrayRef of Persons who are friends as in:
+
+ {
+ name => 'Mike',
+ friends => [
+ { name => 'John' },
+ { name => 'Vincent' },
+ {
+ name => 'Tracey',
+ friends => [
+ { name => 'Stephenie' },
+ { name => 'Ilya' },
+ ],
+ },
+ ],
+ };
+
+Please take care to make sure the recursion node is either Optional, or declare
+a Union with an non recursive option such as:
+
+ subtype Value
+ as Tuple[
+ Str,
+ Str|Tuple,
+ ];
+
+Which validates:
+
+ [
+ 'Hello', [
+ 'World', [
+ 'Is', [
+ 'Getting',
+ 'Old',
+ ],
+ ],
+ ],
+ ];
+
+Otherwise you will define a subtype thatis impossible to validate since it is
+infinitely recursive. For more information about defining recursive types,
+please see the documentation in L<MooseX::Types> and the test cases.
+
+=head1 TYPE CONSTRAINTS
+
+This type library defines the following constraints.
+
+=head2 Tuple[@constraints]
+
+This defines an ArrayRef based constraint which allows you to validate a specific
+list of contained constraints. For example:
+
+ Tuple[Int,Str]; ## Validates [1,'hello']
+ Tuple[Str|Object, Int]; ## Validates ['hello', 1] or [$object, 2]
+
+=head2 Dict[%constraints]
+
+This defines a HashRef based constraint which allowed you to validate a specific
+hashref. For example:
+
+ Dict[name=>Str, age=>Int]; ## Validates {name=>'John', age=>39}
+
+=head2 Optional[$constraint]
+
+This is primarily a helper constraint for Dict and Tuple type constraints. What
+this allows if for you to assert that a given type constraint is allowed to be
+null (but NOT undefined). If the value is null, then the type constraint passes
+but if the value is defined it must validate against the type constraint. This
+makes it easy to make a Dict where one or more of the keys doesn't have to exist
+or a tuple where some of the values are not required. For example:
+
+ subtype Name() => as Dict[
+ first=>Str,
+ last=>Str,
+ middle=>Optional[Str],
+ ];
+
+Creates a constraint that validates against a hashref with the keys 'first' and
+'last' being strings and required while an optional key 'middle' is must be a
+string if it appears but doesn't have to appear. So in this case both the
+following are valid:
+
+ {first=>'John', middle=>'James', last=>'Napiorkowski'}
+ {first=>'Vanessa', last=>'Li'}
+
+=head1 EXPORTABLE SUBROUTINES
+
+This type library makes available for export the following subroutines
+
+=head2 slurpy
+
+Structured type constraints by their nature are closed; that is validation will
+depend on an exact match between your structure definition and the arguments to
+be checked. Sometimes you might wish for a slightly looser amount of validation.
+For example, you may wish to validate the first 3 elements of an array reference
+and allow for an arbitrary number of additional elements. At first thought you
+might think you could do it this way:
+
+ # I want to validate stuff like: [1,"hello", $obj, 2,3,4,5,6,...]
+ subtype AllowTailingArgs,
+ as Tuple[
+ Int,
+ Str,
+ Object,
+ ArrayRef[Int],
+ ];
+
+However what this will actually validate are structures like this:
+
+ [10,"Hello", $obj, [11,12,13,...] ]; # Notice element 4 is an ArrayRef
+
+In order to allow structured validation of, "and then some", arguments, you can
+use the </slurpy> method against a type constraint. For example:
+
+ use MooseX::Types::Structured qw(Tuple slurpy);
+
+ subtype AllowTailingArgs,
+ as Tuple[
+ Int,
+ Str,
+ Object,
+ slurpy ArrayRef[Int],
+ ];
+
+This will now work as expected, validating ArrayRef structures such as:
+
+ [1,"hello", $obj, 2,3,4,5,6,...]
+
+A few caveats apply. First, the slurpy type constraint must be the last one in
+the list of type constraint parameters. Second, the parent type of the slurpy
+type constraint must match that of the containing type constraint. That means
+that a Tuple can allow a slurpy ArrayRef (or children of ArrayRefs, including
+another Tuple) and a Dict can allow a slurpy HashRef (or children/subtypes of
+HashRef, also including other Dict constraints).
+
+Please note the the technical way this works 'under the hood' is that the
+slurpy keywork transforms the target type constraint into a coderef. Please do
+not try to create your own custom coderefs; always use the slurpy method. The
+underlying technology may change in the future but the slurpy keyword will be
+supported.
+
+=head1 ERROR MESSAGES
+
+Error reporting has been improved to return more useful debugging messages. Now
+I will stringify the incoming check value with L<Devel::PartialDump> so that you
+can see the actual structure that is tripping up validation. Also, I report the
+'internal' validation error, so that if a particular element inside the
+Structured Type is failing validation, you will see that. There's a limit to
+how deep this internal reporting goes, but you shouldn't see any of the "failed
+with ARRAY(XXXXXX)" that we got with earlier versions of this module.
+
+This support is continuing to expand, so it's best to use these messages for
+debugging purposes and not for creating messages that 'escape into the wild'
+such as error messages sent to the user.
+
+Please see the test '12-error.t' for a more lengthy example. Your thoughts and
+preferable tests or code patches very welcome!
+
+=head1 EXAMPLES
+
+Here are some additional example usage for structured types. All examples can
+be found also in the 't/examples.t' test. Your contributions are also welcomed.
+
+=head2 Normalize a HashRef
+
+You need a hashref to conform to a canonical structure but are required accept a
+bunch of different incoming structures. You can normalize using the Dict type
+constraint and coercions. This example also shows structured types mixed which
+other MooseX::Types libraries.
+
+ package Test::MooseX::Meta::TypeConstraint::Structured::Examples::Normalize;
+
+ use Moose;
+ use DateTime;
+
+ use MooseX::Types::Structured qw(Dict Tuple);
+ use MooseX::Types::DateTime qw(DateTime);
+ use MooseX::Types::Moose qw(Int Str Object);
+ use MooseX::Types -declare => [qw(Name Age Person)];
+
+ subtype Person,
+ as Dict[
+ name=>Str,
+ age=>Int,
+ ];
+
+ coerce Person,
+ from Dict[
+ first=>Str,
+ last=>Str,
+ years=>Int,
+ ], via { +{
+ name => "$_->{first} $_->{last}",
+ age => $_->{years},
+ }},
+ from Dict[
+ fullname=>Dict[
+ last=>Str,
+ first=>Str,
+ ],
+ dob=>DateTime,
+ ],
+ ## DateTime needs to be inside of single quotes here to disambiguate the
+ ## class package from the DataTime type constraint imported via the
+ ## line "use MooseX::Types::DateTime qw(DateTime);"
+ via { +{
+ name => "$_->{fullname}{first} $_->{fullname}{last}",
+ age => ($_->{dob} - 'DateTime'->now)->years,
+ }};
+
+ has person => (is=>'rw', isa=>Person, coerce=>1);
+
+And now you can instantiate with all the following:
+
+ __PACKAGE__->new(
+ person=>{
+ name=>'John Napiorkowski',
+ age=>39,
+ },
+ );
+
+ __PACKAGE__->new(
+ person=>{
+ first=>'John',
+ last=>'Napiorkowski',
+ years=>39,
+ },
+ );
+
+ __PACKAGE__->new(
+ person=>{
+ fullname => {
+ first=>'John',
+ last=>'Napiorkowski'
+ },
+ dob => 'DateTime'->new(
+ year=>1969,
+ month=>2,
+ day=>13
+ ),
+ },
+ );
+
+This technique is a way to support various ways to instantiate your class in a
+clean and declarative way.
+
+=cut
+
+Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
+ MooseX::Meta::TypeConstraint::Structured->new(
+ name => "MooseX::Types::Structured::Tuple" ,
+ parent => find_type_constraint('ArrayRef'),
+ constraint_generator=> sub {
+ ## Get the constraints and values to check
+ my ($type_constraints, $values) = @_;
+ my @type_constraints = defined $type_constraints ?
+ @$type_constraints : ();
+
+ my $overflow_handler;
+ if(ref $type_constraints[-1] eq 'CODE') {
+ $overflow_handler = pop @type_constraints;
+ }
+
+ my @values = defined $values ? @$values: ();
+ ## Perform the checking
+ while(@type_constraints) {
+ my $type_constraint = shift @type_constraints;
+ if(@values) {
+ my $value = shift @values;
+ unless($type_constraint->check($value)) {
+ $_[2]->{message} = $type_constraint->get_message($value)
+ if ref $_[2];
+ return;
+ }
+ } else {
+ ## Test if the TC supports null values
+ unless($type_constraint->check()) {
+ $_[2]->{message} = $type_constraint->get_message('NULL')
+ if ref $_[2];
+ return;
+ }
+ }
+ }
+ ## Make sure there are no leftovers.
+ if(@values) {
+ if($overflow_handler) {
+ return $overflow_handler->([@values], $_[2]);
+ } else {
+ $_[2]->{message} = "More values than Type Constraints!"
+ if ref $_[2];
+ return;
+ }
+ } elsif(@type_constraints) {
+ $_[2]->{message} =
+ "Not enough values for all defined type constraints. Remaining: ". join(', ', at type_constraints)
+ if ref $_[2];
+ return;
+ } else {
+ return 1;
+ }
+ }
+ )
+);
+
+Moose::Util::TypeConstraints::get_type_constraint_registry->add_type_constraint(
+ MooseX::Meta::TypeConstraint::Structured->new(
+ name => "MooseX::Types::Structured::Dict",
+ parent => find_type_constraint('HashRef'),
+ constraint_generator=> sub {
+ ## Get the constraints and values to check
+ my ($type_constraints, $values) = @_;
+ my @type_constraints = defined $type_constraints ?
+ @$type_constraints : ();
+
+ my $overflow_handler;
+ if(ref $type_constraints[-1] eq 'CODE') {
+ $overflow_handler = pop @type_constraints;
+ }
+ my (%type_constraints) = @type_constraints;
+ my %values = defined $values ? %$values: ();
+ ## Perform the checking
+ while(%type_constraints) {
+ my($key, $type_constraint) = each %type_constraints;
+ delete $type_constraints{$key};
+ if(exists $values{$key}) {
+ my $value = $values{$key};
+ delete $values{$key};
+ unless($type_constraint->check($value)) {
+ $_[2]->{message} = $type_constraint->get_message($value)
+ if ref $_[2];
+ return;
+ }
+ } else {
+ ## Test to see if the TC supports null values
+ unless($type_constraint->check()) {
+ $_[2]->{message} = $type_constraint->get_message('NULL')
+ if ref $_[2];
+ return;
+ }
+ }
+ }
+ ## Make sure there are no leftovers.
+ if(%values) {
+ if($overflow_handler) {
+ return $overflow_handler->(+{%values});
+ } else {
+ $_[2]->{message} = "More values than Type Constraints!"
+ if ref $_[2];
+ return;
+ }
+ } elsif(%type_constraints) {
+ $_[2]->{message} =
+ "Not enough values for all defined type constraints. Remaining: ". join(', ',values %values)
+ if ref $_[2];
+ return;
+ } else {
+ return 1;
+ }
+ },
+ )
+);
+
+OPTIONAL: {
+ my $Optional = Moose::Meta::TypeConstraint::Parameterizable->new(
+ name => 'MooseX::Types::Structured::Optional',
+ package_defined_in => __PACKAGE__,
+ parent => find_type_constraint('Item'),
+ constraint => sub { 1 },
+ constraint_generator => sub {
+ my ($type_parameter, @args) = @_;
+ my $check = $type_parameter->_compiled_type_constraint();
+ return sub {
+ my (@args) = @_;
+ ## Does the arg exist? Something exists if it's a 'real' value
+ ## or if it is set to undef.
+ if(exists($args[0])) {
+ ## If it exists, we need to validate it
+ $check->($args[0]);
+ } else {
+ ## But it's is okay if the value doesn't exists
+ return 1;
+ }
+ }
+ }
+ );
+
+ Moose::Util::TypeConstraints::register_type_constraint($Optional);
+ Moose::Util::TypeConstraints::add_parameterizable_type($Optional);
+}
+
+sub slurpy($) {
+ my $tc = shift @_;
+ return sub {
+ $tc->check(shift);
+ };
+}
+
+=head1 SEE ALSO
+
+The following modules or resources may be of interest.
+
+L<Moose>, L<MooseX::Types>, L<Moose::Meta::TypeConstraint>,
+L<MooseX::Meta::TypeConstraint::Structured>
+
+=head1 TODO
+
+Here's a list of stuff I would be happy to get volunteers helping with:
+
+All POD examples need test cases in t/documentation/*.t
+Want to break out the examples section to a separate cookbook style POD.
+Want more examples and best practice / usage guidance for authors
+Need to clarify deep coercions,
+Need to clarify subtypes of subtypes.
+
+=head1 AUTHOR
+
+John Napiorkowski, C<< <jjnapiork at cpan.org> >>
+
+=head1 COPYRIGHT & LICENSE
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;
Deleted: MooseX-Types-Structured/tags/0.10/t/12-error.t
===================================================================
--- MooseX-Types-Structured/trunk/t/12-error.t 2009-04-01 03:11:50 UTC (rev 7879)
+++ MooseX-Types-Structured/tags/0.10/t/12-error.t 2009-04-02 15:10:44 UTC (rev 7884)
@@ -1,20 +0,0 @@
-BEGIN {
- use strict;
- use warnings;
- use Test::More tests=>4;
-}
-
-use Moose::Util::TypeConstraints;
-use MooseX::Types::Structured qw(Dict Tuple);
-use MooseX::Types::Moose qw(Int Str ArrayRef HashRef);
-
-# Create some TCs from which errors will be generated
-my $simple_tuple = subtype 'simple_tuple', as Tuple[Int,Str];
-my $simple_dict = subtype 'simple_dict', as Dict[name=>Str,age=>Int];
-
-# We probably need more stuff here...
-ok $simple_tuple->check([1,'hello']), "simple_tuple validates: 1,'hello'";
-ok !$simple_tuple->check(['hello',1]), "simple_tuple fails: 'hello',1";
-like $simple_tuple->validate(['hello',1]), qr/"hello", 1/, 'got expected valiate message';
-like $simple_dict->validate(['hello',1]), qr/"hello", 1/, 'got expected valiate message';
-
Copied: MooseX-Types-Structured/tags/0.10/t/12-error.t (from rev 7883, MooseX-Types-Structured/trunk/t/12-error.t)
===================================================================
--- MooseX-Types-Structured/tags/0.10/t/12-error.t (rev 0)
+++ MooseX-Types-Structured/tags/0.10/t/12-error.t 2009-04-02 15:10:44 UTC (rev 7884)
@@ -0,0 +1,121 @@
+BEGIN {
+ use strict;
+ use warnings;
+ use Test::More tests=>24;
+}
+
+use Moose::Util::TypeConstraints;
+use MooseX::Types::Structured qw(Dict Tuple Optional);
+use MooseX::Types::Moose qw(Int Str ArrayRef HashRef);
+
+# Create some TCs from which errors will be generated
+
+my $simple_tuple = subtype 'simple_tuple', as Tuple[Int,Str];
+my $simple_dict = subtype 'simple_dict', as Dict[name=>Str,age=>Int];
+
+# Make sure the constraints we made validate as expected
+
+ok $simple_tuple->check([1,'hello']), "simple_tuple validates: 1,'hello'";
+ok !$simple_tuple->check(['hello',1]), "simple_tuple fails: 'hello',1";
+ok $simple_dict->check({name=>'Vanessa',age=>34}), "simple_dict validates: {name=>'Vanessa',age=>34}";
+ok !$simple_dict->check({name=>$simple_dict,age=>'hello'}), "simple_dict fails: {name=>Object, age=>String}";
+
+## Let's check all the expected validation errors for tuple
+
+like $simple_tuple->validate({a=>1,b=>2}),
+ qr/Validation failed for 'simple_tuple' failed with value { a => 1, b => 2 }/,
+ 'Wrong basic type';
+
+like $simple_tuple->validate(['a','b']),
+ qr/failed for 'simple_tuple' failed with value \[ "a", "b" \]/,
+ 'Correctly failed due to "a" not an Int';
+
+like $simple_tuple->validate([1,$simple_tuple]),
+ qr/Validation failed for 'simple_tuple' failed with value \[ 1, MooseX::Meta::TypeConstraint::Structured/,
+ 'Correctly failed due to object not a Str';
+
+like $simple_tuple->validate([1]),
+ qr/Validation failed for 'Str' failed with value NULL/,
+ 'Not enought values';
+
+like $simple_tuple->validate([1,'hello','too many']),
+ qr/More values than Type Constraints!/,
+ 'Too Many values';
+
+## And the same thing for dicts [name=>Str,age=>Int]
+
+like $simple_dict->validate([1,2]),
+ qr/ failed with value \[ 1, 2 \]/,
+ 'Wrong basic type';
+
+like $simple_dict->validate({name=>'John',age=>'a'}),
+ qr/failed for 'Int' failed with value a/,
+ 'Correctly failed due to age not an Int';
+
+like $simple_dict->validate({name=>$simple_dict,age=>1}),
+ qr/failed with value { age => 1, name => MooseX:/,
+ 'Correctly failed due to object not a Str';
+
+like $simple_dict->validate({name=>'John'}),
+ qr/failed for 'Int' failed with value NULL/,
+ 'Not enought values';
+
+like $simple_dict->validate({name=>'Vincent', age=>15,extra=>'morethanIneed'}),
+ qr/More values than Type Constraints!/,
+ 'Too Many values';
+
+ ## TODO some with Optional (or Maybe) and slurpy
+
+ my $optional_tuple = subtype 'optional_tuple', as Tuple[Int,Optional[Str]];
+ my $optional_dict = subtype 'optional_dict', as Dict[name=>Str,age=>Optional[Int]];
+
+ like $optional_tuple->validate({a=>1,b=>2}),
+ qr/Validation failed for 'optional_tuple' failed with value { a => 1, b => 2 }/,
+ 'Wrong basic type';
+
+like $optional_tuple->validate(['a','b']),
+ qr/failed for 'Int' failed with value a/,
+ 'Correctly failed due to "a" not an Int';
+
+like $optional_tuple->validate([1,$simple_tuple]),
+ qr/failed for 'MooseX::Types::Structured::Optional\[Str\]' failed with value MooseX/,
+ 'Correctly failed due to object not a Str';
+
+like $optional_tuple->validate([1,'hello','too many']),
+ qr/More values than Type Constraints!/,
+ 'Too Many values';
+
+like $optional_dict->validate([1,2]),
+ qr/ failed with value \[ 1, 2 \]/,
+ 'Wrong basic type';
+
+like $optional_dict->validate({name=>'John',age=>'a'}),
+ qr/Validation failed for 'MooseX::Types::Structured::Optional\[Int\]' failed with value a/,
+ 'Correctly failed due to age not an Int';
+
+like $optional_dict->validate({name=>$simple_dict,age=>1}),
+ qr/failed with value { age => 1, name => MooseX:/,
+ 'Correctly failed due to object not a Str';
+
+like $optional_dict->validate({name=>'Vincent', age=>15,extra=>'morethanIneed'}),
+ qr/More values than Type Constraints!/,
+ 'Too Many values';
+
+## Deeper constraints
+
+my $deep_tuple = subtype 'deep_tuple',
+ as Tuple[
+ Int,
+ HashRef,
+ Dict[
+ name=>Str,
+ age=>Int,
+ ],
+ ];
+
+ok $deep_tuple->check([1,{a=>2},{name=>'Vincent',age=>15}]),
+ 'Good Constraint';
+
+like $deep_tuple->validate([1,{a=>2},{name=>'Vincent',age=>'Hello'}]),
+ qr/Error is: Validation failed for 'MooseX::Types::Structured::Dict\[name,Str,age,Int\]'/,
+ 'Example deeper error';
More information about the Moose-commits
mailing list