File Coverage

blib/lib/Data/Predicate/Predicates.pm
Criterion Covered Total %
statement 90 90 100.0
branch 38 38 100.0
condition 12 12 100.0
subroutine 33 33 100.0
pod 16 16 100.0
total 189 189 100.0


line stmt bran cond sub pod time code
1             package Data::Predicate::Predicates;
2              
3 6     6   124252 use strict;
  6         15  
  6         211  
4 6     6   30 use warnings;
  6         13  
  6         161  
5              
6 6     6   2920 use Data::Predicate::ClosurePredicate;
  6         13  
  6         169  
7 6     6   36 use Scalar::Util qw(blessed looks_like_number);
  6         7  
  6         752  
8 6     6   5028 use Readonly;
  6         25924  
  6         346  
9 6     6   44 use base 'Exporter';
  6         11  
  6         10511  
10              
11             our @EXPORT_OK = qw(
12             p_and
13             p_or
14             p_not
15             p_always_true
16             p_always_false
17             p_undef
18             p_defined
19             p_blessed
20             p_is_number
21             p_ref_type
22             p_isa
23             p_can
24             p_numeric_equals
25             p_string_equals
26             p_regex
27             p_substring
28             );
29             our %EXPORT_TAGS = (
30             all => [@EXPORT_OK],
31             logic => [qw(p_and p_or p_not)],
32             defaults => [qw(p_always_true p_always_false)],
33             tests => [qw(p_defined p_undef p_blessed p_is_number p_ref_type
34             p_isa p_can p_numeric_equals p_string_equals
35             p_regex p_substring)],
36             );
37              
38             #Set of predicates which never change so we build once & cache; others may & do
39             Readonly::Hash my %STATIC_PREDICATES => (
40             'true' => Data::Predicate::ClosurePredicate->new(
41             closure => sub { 1 } ,
42             description => 'true'
43             ),
44             'false' => Data::Predicate::ClosurePredicate->new(
45             closure => sub { 0 },
46             description => 'false'
47             ),
48             'defined' => Data::Predicate::ClosurePredicate->new(
49             closure => sub {
50             my ($object) = @_;
51             return ( defined $object ) ? 1 : 0;
52             },
53             description => 'defined'
54             ),
55             'undef' => Data::Predicate::ClosurePredicate->new(
56             closure => sub {
57             my ($object) = @_;
58             return ( defined $object ) ? 0 : 1;
59             },
60             description => 'undef'
61             ),
62             'blessed' => Data::Predicate::ClosurePredicate->new(
63             closure => sub {
64             my ($object) = @_;
65             return ( blessed($object) ) ? 1 : 0;
66             },
67             description => 'blessed'
68             ),
69             'number' => Data::Predicate::ClosurePredicate->new(
70             closure => sub {
71             my ($object) = @_;
72             return ( defined $object && looks_like_number($object) ) ? 1 : 0;
73             },
74             description => 'number'
75             )
76             );
77              
78             sub p_and {
79 2     2 1 21 my ( @predicates ) = @_;
80             return Data::Predicate::ClosurePredicate->new(
81             closure => sub {
82 41     41   52 my ($object) = @_;
83 41         60 foreach my $pred (@predicates) {
84 76 100       192 if(!$pred->apply($object)) {
85 23         102 return 0;
86             }
87             }
88 18         74 return 1;
89             },
90 2         38 description => 'and'
91             );
92             }
93              
94             sub p_or {
95 1     1 1 4 my ( @predicates ) = @_;
96             return Data::Predicate::ClosurePredicate->new(
97             closure => sub {
98 5     5   10 my ($object) = @_;
99 5         8 foreach my $pred (@predicates) {
100 9 100       27 if($pred->apply($object)) {
101 2         10 return 1;
102             }
103             }
104 3         24 return 0;
105             },
106 1         8 description => 'or'
107             );
108             }
109              
110             sub p_not {
111 2     2 1 15 my ($predicate) = @_;
112             return Data::Predicate::ClosurePredicate->new(
113             closure => sub {
114 2     2   4 my ($object) = @_;
115 2 100       6 return ($predicate->apply($object)) ? 0 : 1;
116             },
117 2         17 description => 'not'
118             );
119             }
120              
121             sub p_always_true {
122 2     2 1 12 return $STATIC_PREDICATES{'true'};
123             }
124              
125             sub p_always_false {
126 2     2 1 10 return $STATIC_PREDICATES{'false'};
127             }
128              
129             sub p_defined {
130 31     31 1 185 return $STATIC_PREDICATES{'defined'};
131             }
132              
133             sub p_undef {
134 2     2 1 428 return $STATIC_PREDICATES{'undef'};
135             }
136              
137             sub p_blessed {
138 31     31 1 163 return $STATIC_PREDICATES{'blessed'};
139             }
140              
141             sub p_is_number {
142 11     11 1 90 return $STATIC_PREDICATES{'number'};
143             }
144              
145             sub p_ref_type {
146 3     3 1 19 my ($ref_type) = @_;
147             return Data::Predicate::ClosurePredicate->new(
148             closure => sub {
149 10     10   14 my ($object) = @_;
150 10         19 my $ref = ref($object);
151 10 100       39 return 0 unless $ref;
152 6 100       36 return ( $ref eq $ref_type ) ? 1 : 0;
153             },
154 3         39 description => 'ref_type predicate with type '.$ref_type
155             );
156             }
157              
158             sub p_isa {
159 4     4 1 18 my ($isa) = @_;
160             return Data::Predicate::ClosurePredicate->new(
161             closure => sub {
162 11     11   18 my ($object) = @_;
163 11 100       34 return 0 unless p_blessed()->apply($object);
164 4 100       60 return ( $object->isa($isa) ) ? 1 : 0;
165             },
166 4         47 description => 'isa predicate with type '.$isa
167             );
168             }
169              
170             sub p_can {
171 14     14 1 33 my ($method) = @_;
172             return Data::Predicate::ClosurePredicate->new(
173             closure => sub {
174 20     20   31 my ($object) = @_;
175 20 100       63 return 0 unless p_blessed()->apply($object);
176 15 100       172 return ( $object->can($method) ) ? 1 : 0;
177             },
178 14         133 description => 'Can predicate with method '.$method
179             );
180             }
181              
182             sub p_string_equals {
183 2     2 1 383 my ($str, $method) = @_;
184 2 100       11 my $target = (defined $method) ? "against method ${method}" : 'against values';
185             return Data::Predicate::ClosurePredicate->new(
186             closure => sub {
187 8     8   17 my ($object) = @_;
188 8         16 my $val = _invoke($object, $method);
189 7 100 100     17 return ( p_defined->apply($val) && $val eq $str ) ? 1 : 0;
190             },
191 2         29 description => "String equals predicate $target with number $str"
192             );
193             }
194              
195             sub p_numeric_equals {
196 2     2 1 721 my ($number, $method) = @_;
197 2 100       10 my $target = (defined $method) ? "against method ${method}" : 'against values';
198             return Data::Predicate::ClosurePredicate->new(
199             closure => sub {
200 9     9   12 my ($object) = @_;
201 9         19 my $val = _invoke($object, $method);
202 8 100 100     17 return ( p_is_number->apply($val) && $val == $number ) ? 1 : 0;
203             },
204 2         21 description => "Numeric equals predicate $target with number $number"
205             );
206             }
207              
208             sub p_regex {
209 3     3 1 656 my ($regex, $method) = @_;
210 3 100       12 my $target = (defined $method) ? "against method ${method}" : 'against values';
211             return Data::Predicate::ClosurePredicate->new(
212             closure => sub {
213 14     14   21 my ($object) = @_;
214 14         27 my $val = _invoke($object, $method);
215 13 100 100     31 return ( p_defined->apply($val) && $val =~ $regex) ? 1 : 0;
216             },
217 3         33 description => "Regular expression predicate $target with regex $regex"
218             );
219             }
220              
221             sub p_substring {
222 2     2 1 650 my ($substring, $method) = @_;
223 2 100       8 my $target = (defined $method) ? "against method ${method}" : 'against values';
224             return Data::Predicate::ClosurePredicate->new(
225             closure => sub {
226 10     10   17 my ($object) = @_;
227 10         20 my $val = _invoke($object, $method);
228 9 100 100     19 return ( p_defined->apply($val) && index($val, $substring) > -1) ? 1 : 0;
229             },
230 2         25 description => "Substring predicate $target with substring $substring"
231             );
232             }
233              
234             sub _invoke {
235 41     41   57 my ($object, $method) = @_;
236 41         40 my $val;
237 41 100       98 if($method) {
238 12 100       34 if(p_can($method)->apply($object)) {
239 8         35 $val = $object->can($method)->($object);
240             }
241             else {
242 4         106 confess("Cannot call the method '${method}' on the given object ${object}");
243             }
244             }
245             else {
246 29         39 $val = $object;
247             }
248 37         137 return $val;
249             }
250              
251             1;
252             __END__