File Coverage

blib/lib/Brick/General.pm
Criterion Covered Total %
statement 18 100 18.0
branch 0 18 0.0
condition 0 22 0.0
subroutine 6 30 20.0
pod 0 7 0.0
total 24 177 13.5


line stmt bran cond sub pod time code
1             package Brick::General;
2 5     5   33 use strict;
  5         20  
  5         251  
3              
4 5     5   45 use base qw(Exporter);
  5         11  
  5         582  
5 5     5   30 use vars qw($VERSION);
  5         9  
  5         363  
6              
7             $VERSION = '0.904';
8              
9             package Brick::Bucket;
10 5     5   26 use strict;
  5         10  
  5         143  
11              
12 5     5   22 use Carp qw(croak confess);
  5         23  
  5         5914  
13              
14             =encoding utf8
15              
16             =head1 NAME
17              
18             Brick::General - constraints for domain-nonspecific stuff
19              
20             =head1 SYNOPSIS
21              
22             use Brick;
23              
24             =head1 DESCRIPTION
25              
26             =head2 Single fields
27              
28             =over 4
29              
30             =item _is_blank( HASHREF )
31              
32              
33             =cut
34              
35             sub _is_blank {
36 0     0     my( $bucket, $setup ) = @_;
37              
38 0           $setup->{fields} = [ $setup->{field} ];
39              
40 0           $bucket->_fields_are_blank( $setup );
41             }
42              
43             =item _is_true( HASHREF )
44              
45              
46             =cut
47              
48             sub _is_true {
49 0     0     my( $bucket, $setup ) = @_;
50              
51 0           $setup->{fields} = [ $setup->{field} ];
52              
53 0           $bucket->_fields_are_true( $setup );
54             }
55              
56              
57             =item _is_defined( HASHREF )
58              
59              
60             =cut
61              
62             sub _is_defined {
63 0     0     my( $bucket, $setup ) = @_;
64              
65 0           $setup->{fields} = [ $setup->{field} ];
66              
67 0           $bucket->_fields_are_defined( $setup );
68             }
69              
70             =back
71              
72             =head2 Multiple field conditions
73              
74             =over 4
75              
76             =item defined_fields( HASHREF )
77              
78             A wrapper around __fields_are_something to supply the code reference
79             to verify that each field for definedness. It takes the same input.
80              
81             =cut
82              
83              
84             sub defined_fields {
85 0     0 0   my( $bucket, $setup ) = @_;
86              
87 0           my $sub = $bucket->_fields_are_defined( $setup );
88 0           $bucket->__make_constraint( $sub, $setup );
89             }
90              
91             =item true_fields( HASHREF )
92              
93             A wrapper around __fields_are_something to supply the code reference
94             to verify that each field for true values. It takes the same input.
95              
96             =cut
97              
98             sub true_fields {
99 0     0 0   my( $bucket, $setup ) = @_;
100              
101 0           my $sub = $bucket->_fields_are_true( $setup );
102 0           $bucket->__make_constraint( $sub, $setup );
103             }
104              
105             =item false_fields( HASHREF )
106              
107             A wrapper around __fields_are_something to supply the code reference
108             to verify that each field for false values. It takes the same input.
109              
110             =cut
111              
112             sub false_fields {
113 0     0 0   my( $bucket, $setup ) = @_;
114              
115 0           my $sub = $bucket->_fields_are_false( $setup );
116 0           $bucket->__make_constraint( $sub, $setup );
117             }
118              
119             =item blank_fields( HASHREF )
120              
121             A wrapper around __fields_are_something to supply the code reference
122             to verify that each field has blank values. It takes the same input.
123              
124             =cut
125              
126             sub blank_fields {
127 0     0 0   my( $bucket, $setup ) = @_;
128              
129 0           my $sub = $bucket->_fields_are_blank( $setup );
130 0           $bucket->__make_constraint( $sub, $setup );
131             }
132              
133             =item exist_fields( HASHREF )
134              
135             A wrapper around __fields_are_something to supply the code reference
136             to verify that each field has blank values. It takes the same input.
137              
138             =cut
139              
140             sub exist_fields {
141 0     0 0   my( $bucket, $setup ) = @_;
142              
143 0           my $sub = $bucket->_fields_exist( $setup );
144 0           $bucket->__make_constraint( $sub, $setup );
145             }
146              
147             =item allowed_fields( HASHREF )
148              
149             A wrapper around _remove_extra_fields to remove anything not in the
150             list of the key 'allowed_fields' in HASHREF.
151              
152             This constraint only cares about fields that do not belong in the
153             input. It does not, for instance, ensure that all the fields that
154             should be there are. Use required fields for that.
155              
156             =cut
157              
158             sub allowed_fields {
159 0     0 0   my( $bucket, $setup ) = @_;
160              
161             my $filter_sub = $bucket->_remove_extra_fields(
162             {
163             %$setup,
164             filter_fields => $setup->{allowed_fields}
165             }
166 0           );
167              
168 0           $bucket->__make_constraint( $filter_sub, $setup );
169             }
170              
171             =item required_fields( HASHREF )
172              
173             A wrapper around _fields_are_defined_and_not_null_string to check for
174             the presence of the required fields. A required field must exist in
175             the input hash and have a defined value that is not the null string.
176              
177             =cut
178              
179             sub required_fields {
180 0     0 0   my( $bucket, $setup ) = @_;
181              
182             my $sub = $bucket->_fields_are_defined_and_not_null_string(
183             {
184             %$setup,
185             fields => $setup->{required_fields},
186             }
187 0           );
188              
189 0           $bucket->__make_constraint( $sub, $setup );
190             }
191              
192             =item _fields_exist( HASHREF )
193              
194             fields - an anonymous array of fields that must exist in input
195              
196             If all of the fields satisfy the condition, it does not die. If some of the
197             fields do not satisfy the condition, it dies with a hash reference whose keys
198             are:
199              
200             message - message about the error
201             errors - anonymous array of fields that failed the condition
202             handler - anonymous array of fields that satisfy the condition
203              
204             If a code error occurs, it dies with a simple scalar.
205              
206             =cut
207              
208             sub _fields_exist {
209 0     0     my( $bucket, $setup, $sub ) = @_;
210              
211 0           my @caller = $bucket->__caller_chain_as_list();
212              
213             #print STDERR Data::Dumper->Dump( [\@caller], [qw(caller)] );
214              
215 0 0 0       unless( eval { $setup->{fields}->isa( ref [] ) } or
  0            
216             UNIVERSAL::isa( $setup->{fields}, ref [] ) ) {
217 0           croak( "Argument to $caller[0]{'sub'} must be an anonymous array of field names!" );
218             }
219              
220             my $composed = $bucket->add_to_bucket ( {
221             name => $setup->{name} || $caller[0]{'sub'},
222             description => ( $setup->{description} || "Fields exist" ),
223             fields => [ $setup->{fields} ],
224             code => sub {
225 0     0     my @errors;
226             my @missing;
227 0           foreach my $f ( @{ $setup->{fields} } ) {
  0            
228 0 0         next if exists $_[0]->{ $f };
229              
230             push @errors, {
231 0   0       handler => $caller[1]{'sub'} || $caller[0]{'sub'},
232             message => "Field [$f] was not in input",
233             };
234              
235 0           push @missing, $f;
236             }
237              
238             die {
239             message => "These fields were missing in the input: [@missing]",
240             errors => \@errors,
241 0 0 0       handler => $caller[1]{'sub'} || $caller[0]{'sub'},
242             } if @missing;
243             },
244 0   0       } );
      0        
245              
246 0           $bucket->comprise( $composed, $sub );
247              
248 0           $composed;
249             }
250              
251             =item __fields_are_something( HASHREF, CODEREF )
252              
253             Applies CODEREF to all of the fields in HASHREF->{fields}.
254              
255             fields - an anonymous array of fields to apply CODEREF to
256             description - a textual description of the test (has default)
257             test_name - short (couple word) description of test (e.g. "defined")
258              
259             If all of the fields satisfy the condition, it does not die. If some of the
260             fields do not satisfy the condition, it dies with a hash reference whose keys
261             are:
262              
263             message - message about the error
264             errors - anonymous array of fields that failed the condition
265             handler - anonymous array of fields that satisfy the condition
266              
267             If a code error occurs, it dies with a simple scalar.
268              
269             =cut
270              
271             sub __fields_are_something {
272 0     0     my( $bucket, $setup, $sub ) = @_;
273              
274 0           my @caller = $bucket->__caller_chain_as_list();
275              
276 0 0 0       unless( eval { $setup->{fields}->isa( ref [] ) } or
  0            
277             UNIVERSAL::isa( $setup->{fields}, ref [] ) ) {
278 0           croak( "Argument to $caller[0]{'sub'} must be an anonymous array of field names!" );
279             }
280              
281             my $composed = $bucket->add_to_bucket ( {
282             name => $setup->{name} || $caller[0]{'sub'},
283             description => ( $setup->{description} || "Fields exist" ),
284             fields => [ $setup->{fields} ],
285             code => sub {
286              
287             #print STDERR Data::Dumper->Dump( [$_[0]], [qw(input)] );
288 0     0     my @errors;
289             my @bad;
290 0           foreach my $f ( @{ $setup->{fields} } )
  0            
291             {
292 5     5   44 no warnings 'uninitialized';
  5         17  
  5         3233  
293             #print STDERR "Checking field $f ... ";
294 0           my $result = $sub->( $_[0]->{$f} );
295             #print STDERR "$result\n";
296 0           my $at = $@;
297              
298             push @errors, {
299 0 0         handler => $caller[1]{'sub'},
300             message => "Field [$f] was not $setup->{test_name}. It was [$_[0]->{$f}]",
301             } unless $result;
302              
303 0 0         push @bad, $f unless $result;
304             }
305              
306             die {
307             message => "Not all fields were $setup->{test_name}: [@bad]",
308             errors => \@errors,
309 0 0         handler => $caller[0]{'sub'},
310             } if @bad;
311              
312 0           return 1;
313             },
314 0   0       } );
      0        
315              
316 0           $bucket->comprise( $composed, $sub );
317              
318 0           $composed;
319             }
320              
321             =item _fields_are_defined_and_not_null_string( HASHREF )
322              
323             Check that all fields in HASHREF->{fields) are defined and
324             have a true value. See __fields_are_something for details.
325              
326             =cut
327              
328             sub _fields_are_defined_and_not_null_string {
329 0     0     my( $bucket, $setup ) = @_;
330              
331             #print STDERR "_fields_are_defined_and_not_null_string: ", Data::Dumper->Dump( [$setup], [qw(setup)] );
332              
333 0           $setup->{test_name} = 'defined but not null';
334              
335 0 0   0     $bucket->__fields_are_something( $setup, sub { defined $_[0] and $_[0] ne '' } );
  0            
336             }
337              
338              
339             =item _fields_are_defined( HASHREF )
340              
341             Check that all fields in HASHREF->{fields) are defined. See
342             __fields_are_something for details.
343              
344             =cut
345              
346             sub _fields_are_defined {
347 0     0     my( $bucket, $setup ) = @_;
348              
349 0           $setup->{test_name} = 'defined';
350              
351 0     0     $bucket->__fields_are_something( $setup, sub { defined $_[0] } );
  0            
352             }
353              
354             =item _fields_are_blank( HASHREF )
355              
356             Check that all fields in HASHREF->{fields) are blank (either
357             undefined or the empty string). See __fields_are_something for details.
358              
359             =cut
360              
361             sub _fields_are_blank {
362 0     0     my( $bucket, $setup ) = @_;
363              
364 0           $setup->{test_name} = 'blank';
365              
366 0 0   0     $bucket->__fields_are_something( $setup, sub { ! defined $_[0] or $_[0] eq '' } );
  0            
367             }
368              
369             =item _fields_are_false( HASHREF )
370              
371             Check that all fields in HASHREF->{fields) are false (in the Perl
372             sense). See __fields_are_something for details.
373              
374             =cut
375              
376             sub _fields_are_false {
377 0     0     my( $bucket, $setup ) = @_;
378              
379 0           $setup->{test_name} = 'false';
380              
381 0     0     $bucket->__fields_are_something( $setup, sub { ! $_[0] } );
  0            
382             }
383              
384             =item _fields_are_true( HASHREF )
385              
386             Check that all fields in HASHREF->{fields) are true (in the Perl
387             sense). See __fields_are_something for details.
388              
389             =cut
390              
391             sub _fields_are_true {
392 0     0     my( $bucket, $setup ) = @_;
393              
394 0           $setup->{test_name} = 'true';
395              
396 0     0     $bucket->__fields_are_something( $setup, sub { $_[0] } );
  0            
397             }
398              
399             =back
400              
401             =head1 TO DO
402              
403             TBA
404              
405             =head1 SEE ALSO
406              
407             TBA
408              
409             =head1 SOURCE AVAILABILITY
410              
411             This source is in Github:
412              
413             https://github.com/briandfoy/brick
414              
415             =head1 AUTHOR
416              
417             brian d foy, C<< >>
418              
419             =head1 COPYRIGHT
420              
421             Copyright © 2007-2025, brian d foy . All rights reserved.
422              
423             You may redistribute this under the terms of the Artistic License 2.0.
424              
425             =cut
426              
427             1;