File Coverage

blib/lib/Trickster/Validator.pm
Criterion Covered Total %
statement 64 82 78.0
branch 35 54 64.8
condition 3 5 60.0
subroutine 14 19 73.6
pod 0 5 0.0
total 116 165 70.3


line stmt bran cond sub pod time code
1             package Trickster::Validator;
2              
3 2     2   370784 use strict;
  2         5  
  2         85  
4 2     2   22 use warnings;
  2         5  
  2         133  
5 2     2   26 use v5.14;
  2         7  
6              
7 2     2   619 use Trickster::Exception;
  2         6  
  2         3316  
8              
9             sub new {
10 7     7 0 1853 my ($class, $rules) = @_;
11            
12 7   50     66 return bless {
13             rules => $rules || {},
14             errors => {},
15             }, $class;
16             }
17              
18             sub validate {
19 16     16 0 73 my ($self, $data) = @_;
20            
21 16         56 $self->{errors} = {};
22            
23 16         35 for my $field (keys %{$self->{rules}}) {
  16         61  
24 25         55 my $rules = $self->{rules}{$field};
25 25         46 my $value = $data->{$field};
26            
27 25         51 for my $rule (@$rules) {
28 46 100       154 my ($rule_name, @args) = ref($rule) eq 'ARRAY' ? @$rule : ($rule);
29            
30 46         111 my $method = "_validate_$rule_name";
31 46 50       174 if ($self->can($method)) {
32 46         135 my $error = $self->$method($field, $value, @args);
33 46 100       145 if ($error) {
34 11         16 push @{$self->{errors}{$field}}, $error;
  11         70  
35             }
36             }
37             }
38             }
39            
40 16         34 return keys %{$self->{errors}} == 0;
  16         97  
41             }
42              
43             sub errors {
44 3     3 0 28 my ($self) = @_;
45 3         20 return $self->{errors};
46             }
47              
48             sub throw_if_invalid {
49 0     0 0 0 my ($self) = @_;
50            
51 0 0       0 unless ($self->is_valid) {
52 0         0 Trickster::Exception::BadRequest->throw(
53             message => 'Validation failed',
54             details => $self->errors,
55             );
56             }
57             }
58              
59             sub is_valid {
60 0     0 0 0 my ($self) = @_;
61 0         0 return keys %{$self->{errors}} == 0;
  0         0  
62             }
63              
64             # Validation rules
65              
66             sub _validate_required {
67 8     8   20 my ($self, $field, $value) = @_;
68            
69 8 100 66     55 return "$field is required" unless defined $value && $value ne '';
70 5         13 return undef;
71             }
72              
73             sub _validate_min {
74 12     12   32 my ($self, $field, $value, $min) = @_;
75            
76 12 100       34 return undef unless defined $value;
77            
78 7 100       32 if ($value =~ /^\d+$/) {
79 3 100       11 return "$field must be at least $min" if $value < $min;
80             } else {
81 4 100       22 return "$field must be at least $min characters" if length($value) < $min;
82             }
83            
84 4         11 return undef;
85             }
86              
87             sub _validate_max {
88 10     10   20 my ($self, $field, $value, $max) = @_;
89            
90 10 100       25 return undef unless defined $value;
91            
92 5 100       17 if ($value =~ /^\d+$/) {
93 3 100       25 return "$field must be at most $max" if $value > $max;
94             } else {
95 2 50       8 return "$field must be at most $max characters" if length($value) > $max;
96             }
97            
98 4         9 return undef;
99             }
100              
101             sub _validate_email {
102 6     6   16 my ($self, $field, $value) = @_;
103            
104 6 100       18 return undef unless defined $value;
105            
106 4 100       35 return "$field must be a valid email" unless $value =~ /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
107 3         8 return undef;
108             }
109              
110             sub _validate_regex {
111 0     0   0 my ($self, $field, $value, $pattern) = @_;
112            
113 0 0       0 return undef unless defined $value;
114            
115 0 0       0 return "$field has invalid format" unless $value =~ $pattern;
116 0         0 return undef;
117             }
118              
119             sub _validate_in {
120 2     2   6 my ($self, $field, $value, @allowed) = @_;
121            
122 2 50       7 return undef unless defined $value;
123            
124             return "$field must be one of: " . join(', ', @allowed)
125 2 100       5 unless grep { $_ eq $value } @allowed;
  6         20  
126            
127 1         4 return undef;
128             }
129              
130             sub _validate_numeric {
131 5     5   12 my ($self, $field, $value) = @_;
132            
133 5 100       16 return undef unless defined $value;
134            
135 3 50       22 return "$field must be numeric" unless $value =~ /^-?\d+\.?\d*$/;
136 3         8 return undef;
137             }
138              
139             sub _validate_integer {
140 0     0   0 my ($self, $field, $value) = @_;
141            
142 0 0       0 return undef unless defined $value;
143            
144 0 0       0 return "$field must be an integer" unless $value =~ /^-?\d+$/;
145 0         0 return undef;
146             }
147              
148             sub _validate_url {
149 0     0   0 my ($self, $field, $value) = @_;
150            
151 0 0       0 return undef unless defined $value;
152            
153 0 0       0 return "$field must be a valid URL"
154             unless $value =~ m{^https?://[^\s/$.?#].[^\s]*$}i;
155            
156 0         0 return undef;
157             }
158              
159             sub _validate_custom {
160 3     3   8 my ($self, $field, $value, $callback) = @_;
161            
162 3 50       9 return undef unless defined $value;
163            
164 3         9 my $result = $callback->($value);
165 3 100       34 return $result if $result; # Return error message if validation fails
166 1         3 return undef;
167             }
168              
169             1;
170              
171             __END__