File Coverage

blib/lib/Type/Simple.pm
Criterion Covered Total %
statement 123 124 99.1
branch 57 64 89.0
condition n/a
subroutine 57 57 100.0
pod 24 28 85.7
total 261 273 95.6


line stmt bran cond sub pod time code
1             package Type::Simple;
2              
3 9     9   177917 use 5.006;
  9         42  
4 9     9   62 use strict;
  9         23  
  9         253  
5 9     9   52 use warnings;
  9         27  
  9         368  
6              
7 9     9   4380 use parent 'Exporter';
  9         2931  
  9         56  
8              
9 9     9   639 use Scalar::Util qw(blessed looks_like_number);
  9         26  
  9         1017  
10 9     9   66 use Carp qw(croak);
  9         22  
  9         19191  
11              
12             our @EXPORT_OK = qw(
13             validate
14             Any
15             Bool
16             Maybe
17             Undef
18             Defined
19             Value
20             Str
21             Alpha
22             Alnum
23             Ascii
24             Num
25             Int
26             Print
27             Punct
28             Space
29             Word
30             Ref
31             ScalarRef
32             ArrayRef
33             HashRef
34             HashRefWith
35             CodeRef
36             RegexpRef
37             Object
38             );
39              
40             =head1 NAME
41              
42             Type::Simple - simple type validation system for Perl
43              
44             =head1 VERSION
45              
46             Version 0.02
47              
48             =cut
49              
50             our $VERSION = '0.02';
51              
52             *validate = \&apply;
53              
54             sub apply {
55 1420     1420 0 25500 my ( $fn, $x ) = @_;
56              
57 1420 100       2749 if ( ref $fn eq 'CODE' ) {
    50          
58 1414         2531 return $fn->($x);
59             } elsif ( ref $fn eq 'Regexp' ) {
60 6 100       59 return $x =~ $fn ? 1 : 0;
61             } else {
62 0         0 croak "Invalid type check $fn (expected CODE or Regexp)";
63             }
64             }
65              
66             sub AND {
67 417     417 0 909 my (@fn) = @_;
68             return sub {
69 495     495   814 my ($x) = @_;
70 495         802 foreach my $fn (@fn) {
71 947 100       1747 return 0 if not apply( $fn, $x );
72             }
73 302         947 return 1;
74 417         2259 };
75             }
76              
77             sub OR {
78 11     11 0 26 my (@fn) = @_;
79             return sub {
80 22     22   34 my ($x) = @_;
81 22         38 foreach my $fn (@fn) {
82 35 100       66 return 1 if apply( $fn, $x );
83             }
84 3         26 return 0;
85 11         65 };
86             }
87              
88             sub NOT {
89 116     116 0 218 my ($fn) = @_;
90             return sub {
91 134     134   217 my ($x) = @_;
92 134 100       276 return apply( $fn, $x ) ? 0 : 1;
93 116         381 };
94             }
95              
96             sub Any {
97 12     12 1 7005 return sub {1};
  81     81   209  
98             }
99              
100             sub Bool {
101             return sub {
102 9     9   19 my ($x) = @_;
103              
104 9 100       30 return 1 if not defined $x;
105 8 100       36 return 1 if $x eq '';
106 7 100       30 return 1 if $x =~ /^[01]$/;
107              
108 5         22 return 0;
109 9     9 1 48 };
110             }
111              
112             sub Defined {
113             return sub {
114 325     325   534 my ($x) = @_;
115 325 100       1154 return defined $x ? 1 : 0;
116 276     276 1 1211 };
117             }
118              
119             sub Undef {
120 17     17 1 44 return NOT( Defined() );
121             }
122              
123             sub Maybe {
124 8     8 1 19 my ($fn) = @_;
125              
126 8         14 return OR( Undef(), $fn, );
127             }
128              
129             sub Ref {
130             return AND(
131             Defined(),
132             sub {
133 183     183   312 my ($x) = @_;
134 183 100       805 return ref $x ? 1 : 0;
135             },
136 158     158 1 333 );
137             }
138              
139             sub Value { # defined and not reference
140 92     92 1 178 return AND( Defined(), NOT( Ref() ), );
141             }
142              
143             sub Str {
144 83     83 1 203 return Value(); # same thing as value?
145             }
146              
147             sub Alpha {
148             return AND(
149             Str(),
150             sub {
151 6     6   12 my ($x) = @_;
152 6 100       47 return $x =~ /^[[:alpha:]]+$/ ? 1 : 0;
153             },
154 4     4 1 18 );
155             }
156              
157             sub Alnum {
158             return AND(
159             Str(),
160             sub {
161 2     2   4 my ($x) = @_;
162 2 100       13 return $x =~ /^[[:alnum:]]+$/ ? 1 : 0;
163             },
164 2     2 1 5 );
165             }
166              
167             sub Ascii {
168             return AND(
169             Str(),
170             sub {
171 2     2   5 my ($x) = @_;
172 2 100       15 return $x =~ /^[[:ascii:]]+$/ ? 1 : 0;
173             },
174 2     2 1 5 );
175             }
176              
177             sub Print {
178             return AND(
179             Str(),
180             sub {
181 2     2   4 my ($x) = @_;
182 2 50       24 return $x =~ /^[[:print:]]+$/ ? 1 : 0;
183             },
184 2     2 1 5 );
185             }
186              
187             sub Punct {
188             return AND(
189             Str(),
190             sub {
191 2     2   11 my ($x) = @_;
192 2 100       17 return $x =~ /^[[:punct:]]+$/ ? 1 : 0;
193             },
194 2     2 1 7 );
195             }
196              
197             sub Space {
198             return AND(
199             Str(),
200             sub {
201 1     1   3 my ($x) = @_;
202 1 50       6 return $x =~ /^[[:space:]]+$/ ? 1 : 0;
203             },
204 1     1 1 3 );
205             }
206              
207             sub Word {
208             return AND(
209             Str(),
210             sub {
211 1     1   3 my ($x) = @_;
212 1 50       6 return $x =~ /^[[:word:]]+$/ ? 1 : 0;
213             },
214 1     1 1 5 );
215             }
216              
217             sub Num {
218             return AND(
219             Str(),
220             sub {
221 53     53   100 my ($x) = @_;
222 53 100       303 return looks_like_number($x) ? 1 : 0;
223             },
224 54     54 1 114 );
225             }
226              
227             sub Int {
228             return AND(
229             Num(),
230             sub {
231 30     30   61 my ($x) = @_;
232 30 100       198 return 1 if $x =~ /^[0-9]+$/;
233 2         11 return 0;
234             },
235 35     35 1 638 );
236             }
237              
238             sub ScalarRef {
239             return AND(
240             Ref(),
241             sub {
242 1     1   2 my ($x) = @_;
243 1 50       5 return ref $x eq 'SCALAR' ? 1 : 0;
244             },
245 1     1 1 2 );
246             }
247              
248             sub ArrayRef {
249 21     21 1 67 my ($fn) = @_;
250              
251             return AND(
252             Ref(),
253             sub {
254 14     14   40 my ($x) = @_;
255 14 100       57 return 0 unless ref $x eq 'ARRAY';
256 13 100       41 return 1 unless $fn;
257              
258             # check items
259 11         23 foreach my $item ( @{$x} ) {
  11         32  
260 46 100       94 return 0 unless apply( $fn, $item );
261             }
262              
263 9         26 return 1;
264             },
265 21         61 );
266             }
267              
268             sub HashRef {
269 29     29 1 87 my ($fn) = @_;
270              
271             return AND(
272             Ref(),
273             sub {
274 43     43   81 my ($x) = @_;
275 43 100       124 return 0 unless ref $x eq 'HASH';
276 42 100       114 return 1 unless $fn;
277              
278             # check items
279 35         52 foreach my $key ( keys %{$x} ) {
  35         115  
280 93 100       175 return 0 unless apply( $fn, $x->{$key} );
281             }
282              
283 33         100 return 1;
284             },
285 29         80 );
286             }
287              
288             sub HashRefWith {
289 4     4 1 13 my (%params) = @_;
290              
291             return AND(
292             HashRef(),
293             sub {
294 4     4   6 my ($x) = @_;
295 4         12 foreach my $key (keys %params) {
296 8 100       22 if ($key =~ /^CODE\(0x[0-9a-f]+\)$/) {
297 1         153 croak qq{Key "$key" should be a string, not a CODE reference (did you try to use a validation type as a key?)};
298             }
299 7         10 my $fn = $params{$key};
300 7 100       16 return 0 unless apply( $fn, $x->{$key} );
301             }
302              
303 2         5 return 1;
304             },
305 4         8 );
306             }
307              
308             sub CodeRef {
309             return AND(
310             Ref(),
311             sub {
312 1     1   2 my ($x) = @_;
313 1 50       5 return ref $x eq 'CODE' ? 1 : 0;
314             },
315 1     1 1 4 );
316             }
317              
318             sub RegexpRef {
319             return AND(
320             Ref(),
321             sub {
322 1     1   3 my ($x) = @_;
323 1 50       4 return ref $x eq 'Regexp' ? 1 : 0;
324             },
325 1     1 1 3 );
326             }
327              
328             sub Object {
329             return AND(
330             Ref(),
331             sub {
332 2     2   4 my ($x) = @_;
333 2 100       20 return blessed $x ? 1 : 0;
334             },
335 2     2 1 5 );
336             }
337              
338             =head1 SYNOPSIS
339              
340             use Type::Simple qw(:all);
341              
342             # simple values
343             validate( Int(), 123 ); # -> true
344             validate( Str(), 'xyz' ); # -> false
345              
346             # array and hash references
347             validate( ArrayRef(Int()), [ 1, 2, 3 ] ); # -> true
348             validate( HashRef(Bool()), { foo => 1, bar => 0 } ); # -> true
349              
350             # hash references with specific keys and value types
351             validate(
352             HashRefWith( foo => Int(), bar => Int() ),
353             { foo => 1 },
354             ); # -> false, because you didn't provide key "bar"
355              
356             validate(
357             HashRefWith( foo => Int(), bar => Maybe(Int()) ),
358             { foo => 1 },
359             ); # -> true, because "bar" is Maybe()
360              
361             Check the test suite for many more examples!
362              
363             You can pass your own validation functions as code references:
364              
365             my $greater_than_one = sub { $_[0] > 1 };
366             my $less_than_ten = sub { $_[0] < 10 };
367              
368             validate( $greater_than_one, 50 ); # -> true
369             validate( $less_than_ten, 50 ); # -> false
370              
371             It's possible to combine and modify tests using the boolean functions
372             C, C and C:
373              
374             validate(
375             Type::Simple::OR( CodeRef(), RegexpRef() ),
376             $code_or_regexp,
377             );
378              
379             validate(
380             Type::Simple::AND(
381             Num(),
382             $greater_than_one,
383             $less_than_ten
384             ),
385             $number
386             );
387              
388             validate(
389             Type::Simple::AND(
390             Num(),
391             Type::Simple::NOT(Int()),
392             ),
393             $non_integer_number
394             );
395              
396             =head1 DESCRIPTION
397              
398             Any
399             Bool
400             Maybe(`a)
401             Undef
402             Defined
403             Value
404             Str
405             Alpha
406             Alnum
407             Ascii
408             Num
409             Int
410             Print
411             Punct
412             Space
413             Word
414             Ref
415             ScalarRef
416             ArrayRef(`a)
417             HashRef(`a)
418             HashRefWith( k1 => `a, k2 => `b, ... )
419             CodeRef
420             RegexpRef
421             Object
422              
423             =head1 EXPORT
424              
425             None by default.
426              
427             All the subroutines below can be exported:
428              
429             =head1 SUBROUTINES
430              
431             =head2 validate( type, value)
432              
433             Try to validate a value using a type. Example:
434              
435             validate( Num(), 123 ); # -> true
436             validate( Num(), 'x' ); # -> false
437              
438             The validation functions are the following:
439              
440             =head2 Any()
441              
442             Anything.
443              
444             =head2 Bool()
445              
446             Perl boolean values: C<1>, C<0>, C<''> and C.
447              
448             =head2 Maybe(`a)
449              
450             Type `a or C.
451              
452             =head2 Undef()
453              
454             C.
455              
456             =head2 Defined()
457              
458             Defined value. (Not C)
459              
460             =head2 Value()
461              
462             Number or string. (Not references)
463              
464             =head2 Str()
465              
466             String.
467              
468             Since numbers can be stringified, it will also accept numbers.
469             If you want a non-numeric string, you can use:
470              
471             Type::Simple::AND(
472             Str(),
473             Type::Simple::NOT(Num()),
474             );
475              
476             =head2 Alpha()
477              
478             A string of alphabetical characters (C<[A-Za-z]>).
479              
480             =head2 Alnum()
481              
482             A string of alphanumeric characters (C<[A-Za-z0-9]>)
483              
484             =head2 Ascii()
485              
486             A string of characters in the ASCII character set.
487              
488             =head2 Num()
489              
490             Looks like a number.
491              
492             =head2 Int()
493              
494             An integer.
495              
496             =head2 Print()
497              
498             A string of printable characters, including spaces.
499              
500             =head2 Punct()
501              
502             A string of non-alphanumeric, non-space characters
503             (C<<[-!"#$%&'()*+,./:;<=>?@[\\\]^_`{|}~]>>).
504              
505             =head2 Space()
506              
507             A string of whitespace characters (equivalent to C<\s>).
508              
509             =head2 Word()
510              
511             A string of word characters (C<[A-Za-z0-9_]>, equivalent to C<\w>).
512              
513             =head2 Ref()
514              
515             A reference.
516              
517             =head2 ScalarRef()
518              
519             A scalar reference.
520              
521             =head2 ArrayRef(`a)
522              
523             An array reference.
524              
525             If you specify `a, the array elements should be of type `a.
526              
527             =head2 HashRef(`a)
528              
529             A hash reference.
530              
531             If you specify `a, all values should be of type `a.
532              
533             =head2 HashRefWith( k1 => `a, k2 => `b, ... )
534              
535             A hash reference with a given set of keys and value types.
536              
537             Attention: keys MUST be strings!
538              
539             HashRefWith( foo => Int() ); # good
540             HashRefWith( Str() => Int() ); # bad! Key should be a string, not a CODE reference
541              
542             =head2 CodeRef()
543              
544             A code reference.
545              
546             =head2 RegexpRef()
547              
548             A regexp reference.
549              
550             =head2 Object()
551              
552             A blessed object.
553              
554             =head1 AUTHOR
555              
556             Nelson Ferraz, C<< >>
557              
558             =head1 BUGS
559              
560             Please report any bugs or feature requests to
561             L. I will be notified,
562             and then you'll automatically be notified of progress on your bug
563             as I make changes.
564              
565             =head1 SUPPORT
566              
567             You can find documentation for this module with the perldoc command.
568              
569             perldoc Type::Simple
570              
571             You can also look for information at:
572              
573             =over 4
574              
575             =item * GitHub
576              
577             L
578              
579             =item * Search CPAN
580              
581             L
582              
583             =back
584              
585              
586             =head1 ACKNOWLEDGEMENTS
587              
588              
589             =head1 LICENSE AND COPYRIGHT
590              
591             Copyright 2017 Nelson Ferraz.
592              
593             This program is distributed under the MIT (X11) License:
594             L
595              
596             Permission is hereby granted, free of charge, to any person
597             obtaining a copy of this software and associated documentation
598             files (the "Software"), to deal in the Software without
599             restriction, including without limitation the rights to use,
600             copy, modify, merge, publish, distribute, sublicense, and/or sell
601             copies of the Software, and to permit persons to whom the
602             Software is furnished to do so, subject to the following
603             conditions:
604              
605             The above copyright notice and this permission notice shall be
606             included in all copies or substantial portions of the Software.
607              
608             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
609             EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
610             OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
611             NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
612             HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
613             WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
614             FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
615             OTHER DEALINGS IN THE SOFTWARE.
616              
617              
618             =cut
619              
620             1; # End of Type::Simple