moved around the subroutines that generate constraints
[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;
7#use MooseX::Types::Moose qw();
8#use MooseX::Types -declare => [qw( Dict Tuple Optional )];
9 use Sub::Exporter
10 -setup => { exports => [ qw(Dict Tuple) ] };
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
6c2f284c 116sub Tuple {
117 my ($args, $optional) = @_;
118 my @args = @$args;
119 my @optional = ref $optional eq 'ARRAY' ? @$optional : ();
120
121 return MooseX::Meta::TypeConstraint::Structured::Positional->new(
122 name => 'Tuple',
123 parent => find_type_constraint('ArrayRef'),
124 package_defined_in => __PACKAGE__,
125 signature => [map {
126 _normalize_type_constraint($_);
127 } @args],
128 optional_signature => [map {
129 _normalize_type_constraint($_);
130 } @optional],
131 );
132}
133
134sub Dict {
135 my ($args, $optional) = @_;
136 my %args = @$args;
137 my %optional = ref $optional eq 'ARRAY' ? @$optional : ();
138
139 return MooseX::Meta::TypeConstraint::Structured::Named->new(
140 name => 'Dict',
141 parent => find_type_constraint('HashRef'),
142 package_defined_in => __PACKAGE__,
143 signature => {map {
144 $_ => _normalize_type_constraint($args{$_});
145 } keys %args},
146 optional_signature => {map {
147 $_ => _normalize_type_constraint($optional{$_});
148 } keys %optional},
149 );
150}
151
152sub _normalize_type_constraint {
153 my $tc = shift @_;
154 if(defined $tc && blessed $tc && $tc->isa('Moose::Meta::TypeConstraint')) {
155 return $tc;
156 } elsif($tc) {
157 return Moose::Util::TypeConstraints::find_or_parse_type_constraint($tc);
158 }
159}
d24da8ec 160
161=head1 SEE ALSO
162
163The following modules or resources may be of interest.
164
165L<Moose>, L<MooseX::TypeLibrary>, L<Moose::Meta::TypeConstraint>
166
167=head1 BUGS
168
169No known or reported bugs.
170
171=head1 AUTHOR
172
173John Napiorkowski, C<< <jjnapiork@cpan.org> >>
174
175=head1 COPYRIGHT & LICENSE
176
177This program is free software; you can redistribute it and/or modify
178it under the same terms as Perl itself.
179
180=cut
181
6c2f284c 1821;