File Coverage

blib/lib/Declare/Constraints/Simple/Library/Operators.pm
Criterion Covered Total %
statement 12 12 100.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 16 16 100.0


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Declare::Constraints::Simple::Library::Operators - Operators
4              
5             =cut
6              
7             package Declare::Constraints::Simple::Library::Operators;
8 12     12   150 use warnings;
  12         21  
  12         417  
9 12     12   66 use strict;
  12         27  
  12         485  
10              
11 12     12   71 use Declare::Constraints::Simple-Library;
  12         25  
  12         184  
12              
13 12     12   87 use Carp::Clan qw(^Declare::Constraints::Simple);
  12         18  
  12         84  
14              
15             =head1 SYNOPSIS
16              
17             # all hast to be valid
18             my $and_constraint = And( IsInt,
19             Matches(qr/0$/) );
20              
21             # at least one has to be valid
22             my $or_constraint = Or( IsInt, HasLength );
23              
24             # only one can be valid
25             my $xor_constraint = XOr( IsClass, IsObject );
26              
27             # reverse validity
28             my $not_an_integer = Not( IsInt );
29              
30             # case valid, validate 'bar' key depending on 'foo' keys value
31             my $struct_prof =
32             And( IsHashRef,
33             CaseValid( OnHashKeys(foo => IsEq("FooArray")),
34             OnHashKeys(bar => IsArrayRef),
35             OnHashKeys(foo => IsEq("FooHash")),
36             OnHashKeys(bar => IsHashRef) ));
37              
38             =head1 DESCRIPTION
39              
40             This module contains the frameworks operators. These constraint like
41             elements act on the validity of passed constraints.
42              
43             =head1 OPERATORS
44              
45             =head2 And(@constraints)
46              
47             Is true if all passed C<@constraints> are true on the value. Returns
48             the result of the first failing constraint.
49              
50             =cut
51              
52             constraint 'And',
53             sub {
54             my @vc = @_;
55             return sub {
56             for (@vc) {
57             my $r = $_->($_[0]);
58             return $r unless $r->is_valid;
59             }
60             return _true;
61             };
62             };
63              
64             =head2 Or(@constraints)
65              
66             Is true if at least one of the passed C<@contraints> is true. Returns the
67             last failing constraint's result if false.
68              
69             =cut
70              
71             constraint 'Or',
72             sub {
73             my @vc = @_;
74             return sub {
75             my $last_r;
76             for (0 .. $#vc) {
77             my $v = $vc[$_];
78             my $r = $v->($_[0]);
79             return _true if $r->is_valid;
80             return $r if $_ == $#vc;
81             }
82             return _false('No constraints');
83             };
84             };
85              
86             =head2 XOr(@constraints)
87              
88             Valid only if a single one of the passed C<@constraints> is valid. Returns
89             the last failing constraint's result if false.
90              
91             =cut
92              
93             constraint 'XOr',
94             sub {
95             my @vc = @_;
96             return sub {
97             my $m = 0;
98             for (@vc) {
99             my $r = $_->($_[0]);
100             $m++ if $r->is_valid;
101             }
102             return _result(($m == 1), sprintf 'Got %d true returns', $m);
103             };
104             };
105              
106             =head2 Not($constraint)
107              
108             This is valid if the passed C<$constraint> is false. The main purpose
109             of this operator is to allow the easy reversion of a constraint's
110             trueness.
111              
112             =cut
113              
114             constraint 'Not',
115             sub {
116             my ($c) = @_;
117             croak '\'Not\' only accepts only a constraint as argument'
118             if defined $c and not ref($c) eq 'CODE';
119             return sub {
120             return _true unless $c;
121             my $r = $c->($_[0]);
122             return _false('Constraint returned true') if $r->is_valid;
123             return _true;
124             };
125             };
126              
127             =head2 CaseValid($test, $conseq, $test2, $conseq2, ...)
128              
129             This runs every given C<$test> argument on the value, until it finds
130             one that returns true. If none is found, false is returned. On a true
131             result, howver, the corresponding C<$conseq> constraint is applied to
132             the value and it's result returned. This allows validation depending
133             on other properties of the value:
134              
135             my $flexible = CaseValid( IsArrayRef,
136             And( HasArraySize(1,5),
137             OnArrayElements(0 => IsInt) ),
138             IsHashRef,
139             And( HasHashElements(qw( head tail )),
140             OnHashKeys(head => IsInt) ));
141              
142             Of course, you could model most of it probably with the other
143             operators, but this is a bit more readable. For default cases use
144             C from L
145             as test.
146              
147             =cut
148              
149             constraint 'CaseValid',
150             sub {
151             my @defs = @_;
152             my ($c, @cases);
153             while (my $test = shift @defs) {
154             $c++;
155             croak "CaseValid test nr $c is not a constraint"
156             unless ref($test) eq 'CODE';
157              
158             my $conseq = shift @defs;
159             croak "CaseValid consequence nr $c is not a constraint"
160             unless ref($test) eq 'CODE';
161              
162             push @cases, [$test, $conseq];
163             }
164              
165             return sub {
166             for my $case (@cases) {
167             my ($test, $conseq) = @$case;
168             next unless $test->($_[0])->is_valid;
169             return $conseq->($_[0]);
170             }
171             _false('No matching case');
172             };
173             };
174              
175             =head1 SEE ALSO
176              
177             L, L
178              
179             =head1 AUTHOR
180              
181             Robert 'phaylon' Sedlacek Cphaylon@dunkelheit.atE>
182              
183             =head1 LICENSE AND COPYRIGHT
184              
185             This module is free software, you can redistribute it and/or modify it
186             under the same terms as perl itself.
187              
188             =cut
189              
190             1;