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