rollback some stuff to reset my brain a bit
[gitmo/MooseX-Types-Structured.git] / lib / MooseX / Types / Structured.pm
CommitLineData
d24da8ec 1package MooseX::Types::Structured;
2
6c2f284c 3use Moose;
4use Moose::Util::TypeConstraints;
5use MooseX::Meta::TypeConstraint::Structured::Positional;
6use MooseX::Meta::TypeConstraint::Structured::Named;
011bacc6 7
dbd75632 8#use MooseX::Types -declare => [qw(Dict Tuple Optional)];
9use Sub::Exporter
10 -setup => { exports => [ qw( Dict Tuple Optional) ] };
6c2f284c 11
d24da8ec 12our $VERSION = '0.01';
13our $AUTHORITY = 'cpan:JJNAPIORK';
14
15=head1 NAME
16
17MooseX::Types::Structured; Structured Type Constraints for Moose
18
19=head1 SYNOPSIS
20
6c2f284c 21The following is example usage for this module. You can define a class that has
22an attribute with a structured type like so:
23
24 package MyApp::MyClass;
25
26 use Moose;
27 use MooseX::Types::Moose qw(Str Int);
28 use MooseX::Types::Structured qw(Dict Tuple);
29
30 has name => (isa=>Dict[first_name=>Str, last_name=>Str]);
31
32Then you can instantiate this class with something like:
33
34 my $instance = MyApp::MyClass->new(
35 name=>{first_name=>'John', last_name=>'Napiorkowski'},
36 );
d24da8ec 37
6c2f284c 38But all of these would cause an error:
39
40 my $instance = MyApp::MyClass->new(name=>'John');
41 my $instance = MyApp::MyClass->new(name=>{first_name=>'John'});
42 my $instance = MyApp::MyClass->new(name=>{first_name=>'John', age=>39});
43
44Please see the test cases for more examples.
d24da8ec 45
46=head1 DESCRIPTION
47
6c2f284c 48This type library enables structured type constraints. Basically, this is very
49similar to parameterized constraints that are built into the core Moose types,
50except that you are allowed to define the container's entire structure. For
51example, you could define a parameterized constraint like so:
52
53 subtype HashOfInts, as Hashref[Int];
54
55which would constraint a value to something like [1,2,3,...] and so one. A
56structured constraint like so:
57
58 subtype StringFollowedByInt, as Tuple[Str,Int];
59
60would constrain it's value to something like ['hello', 111];
61
62These structures can be as simple or elaborate as you wish. You can even
63combine various structured, parameterized and simple constraints all together:
64
65 subtype crazy, as Tuple[Int, Dict[name=>Str, age=>Int], ArrayRef[Int]];
66
67Which would match "[1, {name=>'John', age=>25},[10,11,12]]".
68
69You should exercise some care as to whether or not your complex structured
70constraints would be better off contained by a real object as in the following
71example:
72
73 {
74 package MyApp::MyStruct;
75 use Moose;
76
77 has $_ for qw(name age);
78
79 package MyApp::MyClass;
80 use Moose;
81
82 has person => (isa=>'MyApp::MyStruct');
83 }
84
85 my $instance = MyApp::MyClass
86 ->new( person=>MyApp::MyStruct->new(name=>'John', age=>39) );
87
88This method may take some additional time to setup but will give you more
89flexibility. However, structured constraints are highly compatible with this
90method, granting some interesting possibilities for coercion. Try:
91
92 subtype 'MyStruct',
93 as 'MyApp::MyStruct';
94
95 coerce 'MyStruct',
96 from (Dict[name=>Str, age=>Int]),
97 via {
98 MyApp::MyStruct->new(%$_);
99 },
100 from (Dict[last_name=>Str, first_name=>Str, dob=>DateTime]),
101 via {
102 my $name = _->{first_name} .' '. $_->{last_name};
103 my $age = $_->{dob} - DateTime->now;
104 MyApp::MyStruct->new(
105 name=>$name,
106 age=>$age->years );
107 };
108
d24da8ec 109
110=head1 TYPES
111
112This class defines the following types and subtypes.
113
114=cut
115
13cf7c3b 116sub Optional($) {
117 return bless {args=>shift}, 'MooseX::Types::Optional';
118}
119
dbd75632 120sub Tuple($) {
13cf7c3b 121 my ($args, $optional) = _normalize_args(@_);
6c2f284c 122 my @args = @$args;
123 my @optional = ref $optional eq 'ARRAY' ? @$optional : ();
124
125 return MooseX::Meta::TypeConstraint::Structured::Positional->new(
126 name => 'Tuple',
127 parent => find_type_constraint('ArrayRef'),
128 package_defined_in => __PACKAGE__,
129 signature => [map {
130 _normalize_type_constraint($_);
131 } @args],
132 optional_signature => [map {
133 _normalize_type_constraint($_);
134 } @optional],
135 );
136}
78f55946 137use Data::Dump qw/dump/;
dbd75632 138sub Dict($) {
13cf7c3b 139 my ($args, $optional) = _normalize_args(@_);
6c2f284c 140 my %args = @$args;
141 my %optional = ref $optional eq 'ARRAY' ? @$optional : ();
142
78f55946 143
6c2f284c 144 return MooseX::Meta::TypeConstraint::Structured::Named->new(
145 name => 'Dict',
146 parent => find_type_constraint('HashRef'),
147 package_defined_in => __PACKAGE__,
148 signature => {map {
149 $_ => _normalize_type_constraint($args{$_});
150 } keys %args},
151 optional_signature => {map {
78f55946 152
153 warn dump $_;
154 warn dump $optional{$_};
155 warn dump _normalize_type_constraint($optional{$_});
156
6c2f284c 157 $_ => _normalize_type_constraint($optional{$_});
78f55946 158
6c2f284c 159 } keys %optional},
160 );
161}
162
13cf7c3b 163sub _normalize_args {
164 my $args = shift @_;
165 confess "Structured Type Constraints can only accept an ArrayRef as arguments"
166 unless ref $args eq 'ARRAY';
167
168 my @args = @$args;
169 my $last = pop @args;
170
171 if(blessed $last && $last->isa('MooseX::Types::Optional')) {
172 return ([@args], $last->{args});
173 } else {
174 return ([@args, $last]);
175 }
176
177}
6c2f284c 178sub _normalize_type_constraint {
78f55946 179 my ($tc) = @_;
180
424215da 181 ## If incoming is an object, we will assume it's something that implements
182 ## what a type constraint is. We should probably have a Role for this...
183 if(defined $tc && blessed $tc) {
6c2f284c 184 return $tc;
185 } elsif($tc) {
186 return Moose::Util::TypeConstraints::find_or_parse_type_constraint($tc);
187 }
188}
d24da8ec 189
190=head1 SEE ALSO
191
192The following modules or resources may be of interest.
193
194L<Moose>, L<MooseX::TypeLibrary>, L<Moose::Meta::TypeConstraint>
195
196=head1 BUGS
197
198No known or reported bugs.
199
200=head1 AUTHOR
201
202John Napiorkowski, C<< <jjnapiork@cpan.org> >>
203
204=head1 COPYRIGHT & LICENSE
205
206This program is free software; you can redistribute it and/or modify
207it under the same terms as Perl itself.
208
209=cut
210
6c2f284c 2111;