File Coverage

blib/lib/Poz/Types/number.pm
Criterion Covered Total %
statement 119 119 100.0
branch 24 24 100.0
condition 27 47 57.4
subroutine 30 30 100.0
pod 14 16 87.5
total 214 236 90.6


line stmt bran cond sub pod time code
1             package Poz::Types::number;
2 11     11   199 use 5.032;
  11         44  
3 11     11   107 use strict;
  11         23  
  11         332  
4 11     11   52 use warnings;
  11         19  
  11         612  
5 11     11   58 use parent 'Poz::Types::scalar';
  11         21  
  11         84  
6              
7             sub new {
8 44     44 1 92 my ($class, $opts) = @_;
9 44   50     100 $opts = $opts || {};
10 44   100     235 $opts->{required_error} //= "required";
11 44   100     243 $opts->{invalid_type_error} //= "Not a number";
12 44         161 my $self = $class->SUPER::new($opts);
13 44         244 return $self;
14             }
15              
16             sub rule {
17 148     148 1 277 my ($self, $value) = @_;
18 148 100       328 return $self->{required_error} unless defined $value;
19 145 100       918 return $self->{invalid_type_error} unless $value =~ /^-?\d+\.?\d*$/;
20 125         282 return;
21             };
22              
23             sub coerce {
24 4     4 1 7 my ($self, $value) = @_;
25 4         10 return $value +0;
26             }
27              
28             sub gt {
29 1     1 1 2 my ($self, $min, $opts) = @_;
30 1   50     38 $opts = $opts || {};
31 1   50     8 $opts->{message} //= "Too small";
32 1         5 push @{$self->{rules}}, sub {
33 3     3   3 my ($self, $value) = @_;
34 3 100       9 return $opts->{message} if $value <= $min;
35 1         1 return;
36 1         1 };
37 1         2 return $self;
38             }
39              
40             sub gte {
41 5     5 1 8 my ($self, $min, $opts) = @_;
42 5   50     13 $opts = $opts || {};
43 5   50     19 $opts->{message} //= "Too small";
44 5         15 push @{$self->{rules}}, sub {
45 9     9   11 my ($self, $value) = @_;
46 9 100       14 return $opts->{message} if $value < $min;
47 8         11 return;
48 5         6 };
49 5         13 return $self;
50             }
51              
52             sub min {
53 4     4 0 5 my ($self, $min, $opts) = @_;
54 4         8 return $self->gte($min, $opts);
55             }
56              
57             sub lt {
58 1     1 1 2 my ($self, $max, $opts) = @_;
59 1   50     4 $opts = $opts || {};
60 1   50     5 $opts->{message} //= "Too large";
61 1         5 push @{$self->{rules}}, sub {
62 3     3   5 my ($self, $value) = @_;
63 3 100       7 return $opts->{message} if $value >= $max;
64 1         1 return;
65 1         1 };
66 1         2 return $self;
67             }
68              
69             sub lte {
70 4     4 1 6 my ($self, $max, $opts) = @_;
71 4   50     26 $opts = $opts || {};
72 4   50     14 $opts->{message} //= "Too large";
73 4         17 push @{$self->{rules}}, sub {
74 8     8   10 my ($self, $value) = @_;
75 8 100       33 return $opts->{message} if $value > $max;
76 7         7 return;
77 4         5 };
78 4         13 return $self;
79             }
80              
81             sub max {
82 3     3 0 5 my ($self, $max, $opts) = @_;
83 3         4 return $self->lte($max, $opts);
84             }
85              
86             # value must be an integer
87             sub int {
88 2     2 1 4 my ($self, $opts) = @_;
89 2   100     7 $opts = $opts || {};
90 2   100     7 $opts->{message} //= "Not an integer";
91 2         12 push @{$self->{rules}}, sub {
92 6     6   12 my ($self, $value) = @_;
93 6 100       29 return $opts->{message} if $value !~ /^-?\d+$/;
94 3         6 return;
95 2         3 };
96 2         6 return $self;
97             }
98              
99             sub positive {
100 1     1 1 4 my ($self, $opts) = @_;
101 1   50     7 $opts = $opts || {};
102 1   50     8 $opts->{message} //= "Not a positive number";
103 1         7 push @{$self->{rules}}, sub {
104 2     2   5 my ($self, $value) = @_;
105 2 100       8 return $opts->{message} if $value <= 0;
106 1         2 return;
107 1         2 };
108 1         4 return $self;
109             }
110              
111             sub negative {
112 1     1 1 4 my ($self, $opts) = @_;
113 1   50     7 $opts = $opts || {};
114 1   50     8 $opts->{message} //= "Not a negative number";
115 1         7 push @{$self->{rules}}, sub {
116 2     2   6 my ($self, $value) = @_;
117 2 100       9 return $opts->{message} if $value >= 0;
118 1         3 return;
119 1         2 };
120 1         4 return $self;
121             }
122              
123             sub nonpositive {
124 1     1 1 4 my ($self, $opts) = @_;
125 1   50     7 $opts = $opts || {};
126 1   50     7 $opts->{message} //= "Not a non-positive number";
127 1         6 push @{$self->{rules}}, sub {
128 3     3   8 my ($self, $value) = @_;
129 3 100       14 return $opts->{message} if $value > 0;
130 2         5 return;
131 1         3 };
132 1         4 return $self;
133             }
134              
135             sub nonnegative {
136 1     1 1 4 my ($self, $opts) = @_;
137 1   50     7 $opts = $opts || {};
138 1   50     9 $opts->{message} //= "Not a non-negative number";
139 1         7 push @{$self->{rules}}, sub {
140 3     3   8 my ($self, $value) = @_;
141 3 100       11 return $opts->{message} if $value < 0;
142 2         5 return;
143 1         2 };
144 1         4 return $self;
145             }
146              
147             # Evenly divisible by 5.
148             sub multipleOf {
149 4     4 1 12 my ($self, $divisor, $opts) = @_;
150 4   50     20 $opts = $opts || {};
151 4   33     30 $opts->{message} //= "Not a multiple of $divisor";
152 4         52 push @{$self->{rules}}, sub {
153 12     12   26 my ($self, $value) = @_;
154 12 100       47 return $opts->{message} if $value % $divisor != 0;
155 7         41 return;
156 4         8 };
157 4         18 return $self;
158             }
159              
160             # synonym for multipleOf
161             sub step {
162 1     1 1 4 my ($self, $divisor, $opts) = @_;
163 1         4 return $self->multipleOf($divisor, $opts);
164             }
165              
166             1;
167              
168             =head1 NAME
169              
170             Poz::Types::number - A module for number type validation and coercion
171              
172             =head1 SYNOPSIS
173              
174             use Poz qw/z/;
175              
176             my $number = z->number;
177              
178             # Validate a number
179             $number->rule(42); # No error
180              
181             # Coerce a value to a number
182             my $coerced_value = $number->coerce("42.5");
183              
184             # Add validation rules
185             $number->gt(10)->lt(100);
186              
187             # Validate with custom rules
188             $number->rule(50); # No error
189              
190             =head1 DESCRIPTION
191              
192             Poz::Types::number is a module for validating and coercing number types. It provides various methods to enforce constraints on numbers, such as greater than, less than, integer, positive, negative, and multiples of a given number.
193              
194             =head1 METHODS
195              
196             =head2 rule
197              
198             $number->rule($value);
199              
200             Validates the given value against the defined rules. Throws an error if the value is invalid.
201              
202             =head2 coerce
203              
204             my $coerced_value = $number->coerce($value);
205              
206             Coerces the given value to a number.
207              
208             =head2 gt
209              
210             $number->gt($min, \%opts);
211              
212             Adds a rule to ensure the value is greater than the specified minimum.
213              
214             =head2 gte
215              
216             $number->gte($min, \%opts);
217              
218             Adds a rule to ensure the value is greater than or equal to the specified minimum.
219              
220             =head2 lt
221              
222             $number->lt($max, \%opts);
223              
224             Adds a rule to ensure the value is less than the specified maximum.
225              
226             =head2 lte
227              
228             $number->lte($max, \%opts);
229              
230             Adds a rule to ensure the value is less than or equal to the specified maximum.
231              
232             =head2 int
233              
234             $number->int(\%opts);
235              
236             Adds a rule to ensure the value is an integer.
237              
238             =head2 positive
239              
240             $number->positive(\%opts);
241              
242             Adds a rule to ensure the value is a positive number.
243              
244             =head2 negative
245              
246             $number->negative(\%opts);
247              
248             Adds a rule to ensure the value is a negative number.
249              
250             =head2 nonpositive
251              
252             $number->nonpositive(\%opts);
253              
254             Adds a rule to ensure the value is a non-positive number.
255              
256             =head2 nonnegative
257              
258             $number->nonnegative(\%opts);
259              
260             Adds a rule to ensure the value is a non-negative number.
261              
262             =head2 multipleOf
263              
264             $number->multipleOf($divisor, \%opts);
265              
266             Adds a rule to ensure the value is a multiple of the specified divisor.
267              
268             =head2 step
269              
270             $number->step($divisor, \%opts);
271              
272             Synonym for `multipleOf`.
273              
274             =head1 LICENSE
275              
276             Copyright (C) ytnobody.
277              
278             This library is free software; you can redistribute it and/or modify
279             it under the same terms as Perl itself.
280              
281             =head1 AUTHOR
282              
283             ytnobody E<lt>ytnobody@gmail.comE<gt>
284              
285             =cut