use MooseX::Meta::TypeConstraint::Structured;
use MooseX::Types -declare => [qw(Dict Tuple Optional)];
-our $VERSION = '0.05';
+our $VERSION = '0.06';
our $AUTHORITY = 'cpan:JJNAPIORK';
=head1 NAME
use Moose;
use MooseX::Types::Moose qw(Str Int);
- use MooseX::Types::Structured qw(Dict Tuple);
-
- has name => (isa=>Dict[first_name=>Str, last_name=>Str]);
+ use MooseX::Types::Structured qw(Dict 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],
+ ],
+ );
Then you can instantiate this class with something like:
- my $instance = MyApp::MyClass->new(
+ my $john = MyApp::MyClass->new(
+ name => {
+ first=>'John',
+ middle=>'James'
+ last=>'Napiorkowski',
+ },
+ );
+
+ my $vanessa = MyApp::MyClass->new(
name => {
- first_name=>'John',
- last_name=>'Napiorkowski',
+ first=>'Vanessa',
+ last=>'Li'
},
);
MyApp::MyClass->new( name=>'John' );
MyApp::MyClass->new( name=>{first_name=>'John'} );
MyApp::MyClass->new( name=>{first_name=>'John', age=>39} );
-
+ MyApp::MyClass->new( name=>{first=>'Vanessa', middle=>[1,2], last=>'Li'} );
+
Please see the test cases for more examples.
=head1 DESCRIPTION
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 EXAMPLES
Here are some additional example usage for structured types. All examples can
return;
}
} else {
- return;
+ unless($type_constraint->check()) {
+ return;
+ }
}
}
## Make sure there are no leftovers.
return;
}
} else {
- return;
+ unless($type_constraint->check()) {
+ return;
+ }
}
}
## Make sure there are no leftovers.
use strict;
use warnings;
-use Test::More tests=>26;
+use Test::More tests=>46;
+use Test::Exception;
use Moose::Util::TypeConstraints;
use MooseX::Types::Structured qw(Optional);
-## Setup Stuff
-ok my $Optional = Moose::Util::TypeConstraints::find_or_parse_type_constraint('MooseX::Types::Structured::Optional')
- => 'Got Optional';
+APITEST: {
-isa_ok $Optional
- => 'Moose::Meta::TypeConstraint::Parameterizable';
-
-ok my $int = Moose::Util::TypeConstraints::find_or_parse_type_constraint('Int')
- => 'Got Int';
-
-ok my $arrayref = Moose::Util::TypeConstraints::find_or_parse_type_constraint('ArrayRef[Int]')
- => 'Got ArrayRef[Int]';
-
-BASIC: {
- ok my $Optional_Int = $Optional->parameterize($int), 'Parameterized Int';
- ok my $Optional_ArrayRef = $Optional->parameterize($arrayref), 'Parameterized ArrayRef';
+ ok my $Optional = Moose::Util::TypeConstraints::find_or_parse_type_constraint('MooseX::Types::Structured::Optional')
+ => 'Got Optional';
- ok $Optional_Int->check() => 'Optional is allowed to not exist';
+ isa_ok $Optional
+ => 'Moose::Meta::TypeConstraint::Parameterizable';
- ok !$Optional_Int->check(undef) => 'Optional is NOT allowed to be undef';
- ok $Optional_Int->check(199) => 'Correctly validates 199';
- ok !$Optional_Int->check("a") => 'Correctly fails "a"';
+ ok my $int = Moose::Util::TypeConstraints::find_or_parse_type_constraint('Int')
+ => 'Got Int';
- ok $Optional_ArrayRef->check() => 'Optional is allowed to not exist';
- ok !$Optional_ArrayRef->check(undef) => 'Optional is NOT allowed to be undef';
- ok $Optional_ArrayRef->check([1,2,3]) => 'Correctly validates [1,2,3]';
- ok !$Optional_ArrayRef->check("a") => 'Correctly fails "a"';
- ok !$Optional_ArrayRef->check(["a","b"]) => 'Correctly fails ["a","b"]';
-}
-
-SUBREF: {
- ok my $Optional_Int = Optional->parameterize($int),'Parameterized Int';
- ok my $Optional_ArrayRef = Optional->parameterize($arrayref), 'Parameterized ArrayRef';
+ ok my $arrayref = Moose::Util::TypeConstraints::find_or_parse_type_constraint('ArrayRef[Int]')
+ => 'Got ArrayRef[Int]';
- ok $Optional_Int->check() => 'Optional is allowed to not exist';
+ BASIC: {
+ ok my $Optional_Int = $Optional->parameterize($int), 'Parameterized Int';
+ ok my $Optional_ArrayRef = $Optional->parameterize($arrayref), 'Parameterized ArrayRef';
+
+ ok $Optional_Int->check() => 'Optional is allowed to not exist';
+
+ ok !$Optional_Int->check(undef) => 'Optional is NOT allowed to be undef';
+ ok $Optional_Int->check(199) => 'Correctly validates 199';
+ ok !$Optional_Int->check("a") => 'Correctly fails "a"';
+
+ ok $Optional_ArrayRef->check() => 'Optional is allowed to not exist';
+ ok !$Optional_ArrayRef->check(undef) => 'Optional is NOT allowed to be undef';
+ ok $Optional_ArrayRef->check([1,2,3]) => 'Correctly validates [1,2,3]';
+ ok !$Optional_ArrayRef->check("a") => 'Correctly fails "a"';
+ ok !$Optional_ArrayRef->check(["a","b"]) => 'Correctly fails ["a","b"]';
+ }
- ok !$Optional_Int->check(undef) => 'Optional is NOT allowed to be undef';
- ok $Optional_Int->check(199) => 'Correctly validates 199';
- ok !$Optional_Int->check("a") => 'Correctly fails "a"';
+ SUBREF: {
+ ok my $Optional_Int = Optional->parameterize($int),'Parameterized Int';
+ ok my $Optional_ArrayRef = Optional->parameterize($arrayref), 'Parameterized ArrayRef';
+
+ ok $Optional_Int->check() => 'Optional is allowed to not exist';
+
+ ok !$Optional_Int->check(undef) => 'Optional is NOT allowed to be undef';
+ ok $Optional_Int->check(199) => 'Correctly validates 199';
+ ok !$Optional_Int->check("a") => 'Correctly fails "a"';
+
+ ok $Optional_ArrayRef->check() => 'Optional is allowed to not exist';
+ ok !$Optional_ArrayRef->check(undef) => 'Optional is NOT allowed to be undef';
+ ok $Optional_ArrayRef->check([1,2,3]) => 'Correctly validates [1,2,3]';
+ ok !$Optional_ArrayRef->check("a") => 'Correctly fails "a"';
+ ok !$Optional_ArrayRef->check(["a","b"]) => 'Correctly fails ["a","b"]';
+ }
+}
+
+OBJECTTEST: {
+ package Test::MooseX::Meta::TypeConstraint::Structured::Optional;
+
+ use Moose;
+ use MooseX::Types::Structured qw(Dict Tuple Optional);
+ use MooseX::Types::Moose qw(Int Str Object ArrayRef HashRef Maybe);
+ use MooseX::Types -declare => [qw(
+ MoreThanFive TupleOptional1 TupleOptional2 Gender DictOptional1 Insane
+ )];
- ok $Optional_ArrayRef->check() => 'Optional is allowed to not exist';
- ok !$Optional_ArrayRef->check(undef) => 'Optional is NOT allowed to be undef';
- ok $Optional_ArrayRef->check([1,2,3]) => 'Correctly validates [1,2,3]';
- ok !$Optional_ArrayRef->check("a") => 'Correctly fails "a"';
- ok !$Optional_ArrayRef->check(["a","b"]) => 'Correctly fails ["a","b"]';
+ subtype MoreThanFive,
+ as Int,
+ where { $_ > 5};
+
+ enum Gender,
+ qw/male female transgendered/;
+
+ subtype TupleOptional1,
+ as Tuple[Int, MoreThanFive, Optional[Str|Object]];
+
+ subtype TupleOptional2,
+ as Tuple[Int, MoreThanFive, Optional[HashRef[Int|Object]]];
+
+ subtype DictOptional1,
+ as Dict[name=>Str, age=>Int, gender=>Optional[Gender]];
+
+ subtype Insane,
+ as Tuple[
+ Int,
+ Optional[Str|Object],
+ DictOptional1,
+ Optional[ArrayRef[Int]]
+ ];
+
+ has 'TupleOptional1Attr' => (is=>'rw', isa=>TupleOptional1);
+ has 'TupleOptional2Attr' => (is=>'rw', isa=>TupleOptional2);
+ has 'DictOptional1Attr' => (is=>'rw', isa=>DictOptional1);
+ has 'InsaneAttr' => (is=>'rw', isa=>Insane);
}
-## Test via the subref Optional()
+ok my $obj = Test::MooseX::Meta::TypeConstraint::Structured::Optional->new
+ => 'Instantiated new test class.';
+
+isa_ok $obj => 'Test::MooseX::Meta::TypeConstraint::Structured::Optional'
+ => 'Created correct object type.';
+
+# Test Insane
+
+lives_ok sub {
+ $obj->InsaneAttr([1,"hello",{name=>"John",age=>39,gender=>"male"},[1,2,3]]);
+} => 'Set InsaneAttr attribute without error [1,"hello",{name=>"John",age=>39,gender=>"male"},[1,2,3]]';
+
+lives_ok sub {
+ $obj->InsaneAttr([1,$obj,{name=>"John",age=>39},[1,2,3]]);
+} => 'Set InsaneAttr attribute without error [1,$obj,{name=>"John",age=>39},[1,2,3]]';
+
+lives_ok sub {
+ $obj->InsaneAttr([1,$obj,{name=>"John",age=>39}]);
+} => 'Set InsaneAttr attribute without error [1,$obj,{name=>"John",age=>39}]';
+
+throws_ok sub {
+ $obj->InsaneAttr([1,$obj,{name=>"John",age=>39},[qw/a b c/]]);
+}, qr/Attribute \(InsaneAttr\) does not pass the type constraint/
+ => q{InsaneAttr correctly fails [1,$obj,{name=>"John",age=>39},[qw/a b c/]]};
+
+throws_ok sub {
+ $obj->InsaneAttr([1,"hello",{name=>"John",age=>39,gender=>undef},[1,2,3]]);
+}, qr/Attribute \(InsaneAttr\) does not pass the type constraint/
+ => q{InsaneAttr correctly fails [1,"hello",{name=>"John",age=>39,gender=>undef},[1,2,3]]};
+
+# Test TupleOptional1Attr
+
+lives_ok sub {
+ $obj->TupleOptional1Attr([1,10,"hello"]);
+} => 'Set TupleOptional1Attr attribute without error [1,10,"hello"]';
+
+lives_ok sub {
+ $obj->TupleOptional1Attr([1,10,$obj]);
+} => 'Set TupleOptional1Attr attribute without error [1,10,$obj]';
+
+lives_ok sub {
+ $obj->TupleOptional1Attr([1,10]);
+} => 'Set TupleOptional1Attr attribute without error [1,10]';
+
+throws_ok sub {
+ $obj->TupleOptional1Attr([1,10,[1,2,3]]);
+}, qr/Attribute \(TupleOptional1Attr\) does not pass the type constraint/
+ => q{TupleOptional1Attr correctly fails [1,10,[1,2,3]]};
+
+throws_ok sub {
+ $obj->TupleOptional1Attr([1,10,undef]);
+}, qr/Attribute \(TupleOptional1Attr\) does not pass the type constraint/
+ => q{TupleOptional1Attr correctly fails [1,10,undef]};
+
+# Test TupleOptional2Attr
+
+lives_ok sub {
+ $obj->TupleOptional2Attr([1,10,{key1=>1,key2=>$obj}]);
+} => 'Set TupleOptional2Attr attribute without error [1,10,{key1=>1,key2=>$obj}]';
+
+lives_ok sub {
+ $obj->TupleOptional2Attr([1,10]);
+} => 'Set TupleOptional2Attr attribute without error [1,10]';
+
+throws_ok sub {
+ $obj->TupleOptional2Attr([1,10,[1,2,3]]);
+}, qr/Attribute \(TupleOptional2Attr\) does not pass the type constraint/
+ => q{TupleOptional2Attr correctly fails [1,10,[1,2,3]]};
+
+throws_ok sub {
+ $obj->TupleOptional2Attr([1,10,undef]);
+}, qr/Attribute \(TupleOptional2Attr\) does not pass the type constraint/
+ => q{TupleOptional2Attr correctly fails [1,10,undef]};
+
+# Test DictOptional1Attr: Dict[name=>Str, age=>Int, gender=>Optional[Gender]];
+
+lives_ok sub {
+ $obj->DictOptional1Attr({name=>"John",age=>39,gender=>"male"});
+} => 'Set DictOptional1Attr attribute without error {name=>"John",age=>39,gender=>"male"}';
+
+lives_ok sub {
+ $obj->DictOptional1Attr({name=>"Vanessa",age=>34});
+} => 'Set DictOptional1Attr attribute without error {name=>"Vanessa",age=>34}';
+
+throws_ok sub {
+ $obj->DictOptional1Attr({name=>"John",age=>39,gender=>undef});
+}, qr/Attribute \(DictOptional1Attr\) does not pass the type constraint/
+ => q{TupleOptional2Attr correctly fails {name=>"John",age=>39,gender=>undef}};
+throws_ok sub {
+ $obj->DictOptional1Attr({name=>"John",age=>39,gender=>"aaa"});
+}, qr/Attribute \(DictOptional1Attr\) does not pass the type constraint/
+ => q{TupleOptional2Attr correctly fails {name=>"John",age=>39,gender=>"aaa"}};