4 use Test::More tests=>42;
9 package Test::MooseX::Meta::TypeConstraint::Structured;
12 use Moose::Util::TypeConstraints;
13 use MooseX::Meta::TypeConstraint::Structured::Named;
14 use MooseX::Meta::TypeConstraint::Structured::Positional;
21 my ($args, $optional) = @_;
23 my @optional = ref $optional eq 'ARRAY' ? @$optional : ();
25 return MooseX::Meta::TypeConstraint::Structured::Positional->new(
27 parent => find_type_constraint('ArrayRef'),
28 package_defined_in => __PACKAGE__,
30 _normalize_type_constraint($_);
32 optional_signature => [map {
33 _normalize_type_constraint($_);
39 my ($args, $optional) = @_;
41 my %optional = ref $optional eq 'ARRAY' ? @$optional : ();
43 return MooseX::Meta::TypeConstraint::Structured::Named->new(
45 parent => find_type_constraint('HashRef'),
46 package_defined_in => __PACKAGE__,
48 $_ => _normalize_type_constraint($args{$_});
50 optional_signature => {map {
51 $_ => _normalize_type_constraint($optional{$_});
56 sub _normalize_type_constraint {
58 if(defined $tc && blessed $tc && $tc->isa('Moose::Meta::TypeConstraint')) {
61 return Moose::Util::TypeConstraints::find_or_parse_type_constraint($tc);
65 has 'tuple' => (is=>'rw', isa=>Tuple['Int', 'Str', 'MyString']);
66 has 'dict' => (is=>'rw', isa=>Dict[name=>'Str', age=>'Int']);
67 has 'dict_with_maybe' => (is=>'rw', isa=>Dict[name=>'Str', age=>'Maybe[Int]']);
68 has 'tuple_with_param' => (is=>'rw', isa=>Tuple['Int', 'Str', 'ArrayRef[Int]']);
69 has 'tuple_with_maybe' => (is=>'rw', isa=>Tuple['Int', 'Str', 'Maybe[Int]']);
70 has 'dict_with_tuple' => (is=>'rw', isa=>Dict[key1=>'Str', key2=>Tuple['Int','Str']]);
71 has 'optional_tuple' => (is=>'rw', isa=>Tuple(['Int', 'Int'],['Int']) );
72 has 'optional_dict' => (is=>'rw', isa=>Dict([key1=>'Int'],[key2=>'Int']) );
77 ## First ArrayRef Arg is the required type constraints for the top
82 ## The third required element is a Dict type constraint, which
83 ## itself has two required keys and a third optional key.
84 Dict([name=>'Str',age=>'Int'],[visits=>'Int'])
86 ## Second ArrayRef Arg defines the optional constraints for the top
90 ## This Tuple has one required type constraint and two optional.
99 ##has 'sugered' => ();
102 ## Instantiate a new test object
104 ok my $record = Test::MooseX::Meta::TypeConstraint::Structured->new
105 => 'Instantiated new Record test class.';
107 isa_ok $record => 'Test::MooseX::Meta::TypeConstraint::Structured'
108 => 'Created correct object type.';
113 $record->crazy([1,'hello.abc.world', {name=>'John', age=>39}]);
114 } => 'Set crazy attribute with no optionals used';
116 is_deeply $record->crazy, [1, 'hello.abc.world', {name=>'John', age=>39}]
117 => 'correct values for crazy attributes no optionals';
120 $record->crazy([1,'hello.abc.world', {name=>'John', age=>39, visits=>10},10, [1,2,{key=>'value'}]]);
121 } => 'Set crazy attribute with all optionals used';
123 is_deeply $record->crazy, [1,'hello.abc.world', {name=>'John', age=>39, visits=>10},10, [1,2,{key=>'value'}]]
124 => 'correct values for crazy attributes all optionals';
127 $record->crazy([1,'hello.abc.world', {name=>'John', age=>39},10, [1,2]]);
128 } => 'Set crazy attribute with some optionals used';
131 $record->crazy([1,'hello', 'test.xxx.test']);
132 }, qr/Validation failed for 'MyString'/
133 => 'Properly failed for bad value in crazy attribute 01';
136 $record->crazy([1,'hello.abc.world', {notname=>'John', notage=>39}]);
137 }, qr/Validation failed for 'Str'/
138 => 'Properly failed for bad value in crazy attribute 02';
140 ## Test Tuple type constraint
143 $record->tuple([1,'hello', 'test.abc.test']);
144 } => 'Set tuple attribute without error';
146 is $record->tuple->[0], 1
147 => 'correct set the tuple attribute index 0';
149 is $record->tuple->[1], 'hello'
150 => 'correct set the tuple attribute index 1';
152 is $record->tuple->[2], 'test.abc.test'
153 => 'correct set the tuple attribute index 2';
156 $record->tuple([1,'hello', 'test.xxx.test']);
157 }, qr/Validation failed for 'MyString'/
158 => 'Properly failed for bad value in custom type constraint';
161 $record->tuple(['asdasd',2, 'test.abc.test']);
162 }, qr/Validation failed for 'Int'/
163 => 'Got Expected Error for violating constraints';
165 ## Test the Dictionary type constraint
168 $record->dict({name=>'frith', age=>23});
169 } => 'Set dict attribute without error';
171 is $record->dict->{name}, 'frith'
172 => 'correct set the dict attribute name';
174 is $record->dict->{age}, 23
175 => 'correct set the dict attribute age';
178 $record->dict({name=>[1,2,3], age=>'sdfsdfsd'});
179 }, qr/Validation failed for 'Str'/
180 => 'Got Expected Error for bad value in dict';
182 ## Test tuple_with_maybe
185 $record->tuple_with_maybe([1,'hello', 1]);
186 } => 'Set tuple attribute without error';
189 $record->tuple_with_maybe([1,'hello', 'a']);
190 }, qr/Validation failed for 'Maybe\[Int\]'/
191 => 'Properly failed for bad value parameterized constraint';
194 $record->tuple_with_maybe([1,'hello']);
195 } => 'Set tuple attribute without error skipping optional parameter';
197 ## Test Tuple with parameterized type
200 $record->tuple_with_param([1,'hello', [1,2,3]]);
201 } => 'Set tuple attribute without error';
204 $record->tuple_with_param([1,'hello', [qw/a b c/]]);
205 }, qr/Validation failed for 'ArrayRef\[Int\]'/
206 => 'Properly failed for bad value parameterized constraint';
208 ## Test dict_with_maybe
211 $record->dict_with_maybe({name=>'frith', age=>23});
212 } => 'Set dict attribute without error';
214 is $record->dict_with_maybe->{name}, 'frith'
215 => 'correct set the dict attribute name';
217 is $record->dict_with_maybe->{age}, 23
218 => 'correct set the dict attribute age';
221 $record->dict_with_maybe({name=>[1,2,3], age=>'sdfsdfsd'});
222 }, qr/Validation failed for 'Str'/
223 => 'Got Expected Error for bad value in dict';
226 $record->dict_with_maybe({age=>30});
227 }, qr/Validation failed for 'Str'/
228 => 'Got Expected Error for missing named parameter';
231 $record->dict_with_maybe({name=>'usal'});
232 } => 'Set dict attribute without error, skipping optional';
234 ## Test dict_with_tuple
237 $record->dict_with_tuple({key1=>'Hello', key2=>[1,'World']});
238 } => 'Set tuple attribute without error';
241 $record->dict_with_tuple({key1=>'Hello', key2=>['World',2]});
242 }, qr/Validation failed for 'Int'/
243 => 'Threw error on bad constraint';
245 ## Test optional_tuple
248 $record->optional_tuple([1,2,3]);
249 } => 'Set tuple attribute with optional bits';
251 is_deeply $record->optional_tuple, [1,2,3]
252 => 'correct values set';
255 $record->optional_tuple([4,5]);
256 } => 'Set tuple attribute withOUT optional bits';
258 is_deeply $record->optional_tuple, [4,5]
259 => 'correct values set again';
262 $record->optional_tuple([1,2,'bad']);
263 }, qr/Validation failed for 'Int'/
264 => 'Properly failed for bad value in optional bit';
269 $record->optional_dict({key1=>1,key2=>2});
270 } => 'Set tuple attribute with optional bits';
272 is_deeply $record->optional_dict, {key1=>1,key2=>2}
273 => 'correct values set';
276 $record->optional_dict({key1=>3});
277 } => 'Set tuple attribute withOUT optional bits';
279 is_deeply $record->optional_dict, {key1=>3}
280 => 'correct values set again';
283 $record->optional_dict({key1=>1,key2=>'bad'});
284 }, qr/Validation failed for 'Int'/
285 => 'Properly failed for bad value in optional bit';