doubt we need the concept file anymore
[gitmo/MooseX-Types-Structured.git] / lib / MooseX / Meta / TypeConstraint / Structured / Named.pm
CommitLineData
9a491c80 1package MooseX::Meta::TypeConstraint::Structured::Named;
2
3use Moose;
4use Moose::Meta::TypeConstraint ();
5use Moose::Util::TypeConstraints;
6
7extends 'Moose::Meta::TypeConstraint';
bc5c0758 8with 'MooseX::Meta::TypeConstraint::Role::Structured';
9a491c80 9
10=head1 NAME
11
12MooseX::Meta::TypeConstraint::Structured::Named - Structured Type Constraints
13
14=head1 VERSION
15
160.01
17
18=cut
19
20our $VERSION = '0.01';
21
22=head1 DESCRIPTION
23
24Structured type constraints let you assign an internal pattern of type
25constraints to a 'container' constraint. The goal is to make it easier to
26declare constraints like "ArrayRef[Int, Int, Str]" where the constraint is an
27ArrayRef of three elements and the internal constraint on the three is Int, Int
28and Str.
29
30To accomplish this, we add an attribute to the base L<Moose::Meta::TypeConstraint>
31to hold a L</signature>, which is a reference to a pattern of type constraints.
32We then override L</constraint> to check our incoming value to the attribute
33against this signature pattern.
34
35Named structured Constraints expect the internal constraints to be in keys or
36fields similar to what we expect in a HashRef.
37
38=head1 TYPES
39
40The following types are defined in this class.
41
42=head2 Moose::Meta::TypeConstraint
43
44Used to make sure we can properly validate incoming signatures.
45
46=cut
47
48class_type 'Moose::Meta::TypeConstraint';
49
50=head1 ATTRIBUTES
51
52This class defines the following attributes.
53
54=head2 signature
55
56This is a signature of internal contraints for the contents of the outer
57contraint container.
58
59=cut
60
24dd1d2e 61has '+signature' => (isa=>'HashRef[Moose::Meta::TypeConstraint]');
9a491c80 62
63=head2 optional_signature
64
65This is a signature of internal contraints for the contents of the outer
66contraint container. These are optional constraints.
67
68=cut
69
24dd1d2e 70has '+optional_signature' => (isa=>'HashRef[Moose::Meta::TypeConstraint]');
9a491c80 71
72=head1 METHODS
73
74This class defines the following methods.
75
76=head2 _normalize_args
77
78Get arguments into a known state or die trying. Ideally we try to make this
79into a HashRef so we can match it up with the L</signature> HashRef.
80
81=cut
82
83sub _normalize_args {
84 my ($self, $args) = @_;
85 if(defined $args) {
86 if(ref $args eq 'HASH') {
87 %$args
88 } else {
89 confess 'Signature must be an HashRef type';
90 }
91 } else {
92 confess 'Signature cannot be empty';
93 }
94}
95
96=head2 constraint
97
98The constraint is basically validating the L</signature> against the incoming
99
100=cut
101
102sub constraint {
103 my $self = shift;
104 return sub {
105 my %args = $self->_normalize_args(shift);
106 my @signature = keys %{$self->signature};
107 my @ptional_signature = keys %{$self->optional_signature}
108 if $self->has_optional_signature;
109
110 ## First make sure all the required type constraints match
111 while( my $type_constraint_key = shift @signature) {
112 my $type_constraint = $self->signature->{$type_constraint_key};
113 if(my $error = $type_constraint->validate($args{$type_constraint_key})) {
114 confess $error;
115 }
116 delete $args{$type_constraint_key};
117 }
118
119 ## Now test the option type constraints.
120 while( my $arg_key = keys %args) {
121 my $optional_type_constraint = $self->signature->{$arg_key};
122 if(my $error = $optional_type_constraint->validate($args{$arg_key})) {
123 confess $error;
124 }
125 }
126
127 ## If we got this far we passed!
128 return 1;
129 };
130}
131
132=head2 signature_equals
133
134Check that the signature equals another signature.
135
136=cut
137
138sub signature_equals {
139 my ($self, $compared_type_constraint) = @_;
140
141 foreach my $idx (keys %{$self->signature}) {
142 my $this = $self->signature->{$idx};
143 my $that = $compared_type_constraint->signature->{$idx};
144 return unless $this->equals($that);
145 }
146
147 if($self->has_optional_signature) {
148 foreach my $idx (keys %{$self->optional_signature}) {
149 my $this = $self->optional_signature->{$idx};
150 my $that = $compared_type_constraint->optional_signature->{$idx};
151 return unless $this->equals($that);
152 }
153 }
154
155 return 1;
156}
157
158=head2 equals
159
160modifier to make sure equals descends into the L</signature>
161
162=cut
163
164around 'equals' => sub {
165 my ($equals, $self, $compared_type_constraint) = @_;
166
167 ## Make sure we are comparing typeconstraints of the same base class
168 return unless $compared_type_constraint->isa(__PACKAGE__);
169
170 ## Make sure the base equals is also good
171 return unless $self->$equals($compared_type_constraint);
172
173 ## Make sure the signatures match
174 return unless $self->signature_equals($compared_type_constraint);
175
176 ## If we get this far, the two are equal
177 return 1;
178};
179
180=head1 AUTHOR
181
182John James Napiorkowski <jjnapiork@cpan.org>
183
184=head1 LICENSE
185
186You may distribute this code under the same terms as Perl itself.
187
188=cut
189
190no Moose; 1;