File Coverage

blib/lib/Data/Constraint.pm
Criterion Covered Total %
statement 25 26 96.1
branch 2 2 100.0
condition n/a
subroutine 12 13 92.3
pod n/a
total 39 41 95.1


line stmt bran cond sub pod time code
1 7     7   5493 use 5.008;
  7         25  
2              
3             package Data::Constraint;
4 7     7   52 use strict;
  7         12  
  7         154  
5              
6 7     7   46 use warnings;
  7         23  
  7         258  
7 7     7   39 no warnings;
  7         16  
  7         672  
8              
9             =encoding utf8
10              
11             =head1 NAME
12              
13             Data::Constraint - prototypical value checking
14              
15             =head1 SYNOPSIS
16              
17             use Data::Constraint;
18              
19             my $constraint = Data::Constraint->add_constraint(
20             'name_of_condition',
21             run => sub { $_[1] =~ /Perl/ },
22             description => "String should have 'Perl' in it";
23             );
24              
25             if( $constraint->check( 'Java' ) )
26             {
27             ...
28             }
29              
30             =head1 DESCRIPTION
31              
32             A constraint is some sort of condition on a datum. This module checks
33             one condition against one value at a time, and I call the thing that
34             checks that condition the "constraint". A constraint returns true or
35             false, and that's it. It should have no side effects, it should not
36             change program flow, and it should mind its own business. Let the
37             thing that calls the constraint figure out what to do with it. I want
38             something that says "yes" or "no" (and I discuss why this needs a
39             fancy module later).
40              
41             For instance, the constraint may state that the value has to be a
42             number. The condition may be something that ensures the value does
43             not have non-digits.
44              
45             $value =~ /^\d+\z/
46              
47             The value may have additional constraints, such as a lower limit.
48              
49             $value > $minimum
50              
51             Although I designed constraints to be a single condition, you
52             may want to create contraints that check more than one thing.
53              
54             $value > $minimum and $value < $maximum
55              
56             In the previous examples, we could tell what was wrong with the value
57             if the return value was false: the value didn't satisfy it's single
58             condition. If it was supposed to be all digits and wasn't, then it
59             had non-digits. If it was supposed to be greater than the minimum
60             value, but wasn't, it was less than (or equal to) the minimal value.
61             With more than one condition, like the last example, I cannot tell
62             which one failed. I might be able to say that a value of out of range,
63             but I think it is nicer to know if the value should have been larger
64             or smaller so I can pass that on to the user. Having said that, I
65             give you enough rope to do what you wish.
66              
67             =head2 Why I need a fancy, high-falutin' module
68              
69             This module is a sub-class of C. In brief, that
70             means constraints are class-objects even if they don't look like they
71             are. Each constraint is a self-contained class, and I can modify
72             a constraint by adding data and behaviour without affecting any of
73             the other constraints. I can also make a list of constraints that
74             I store for later use (also known as "delayed" execution).
75              
76             Several data may need the same conditions, so they can share the same
77             constraint. Other data that need different constraints can get
78             their own, or modify copies of ones that exist.
79              
80             I can also associate several constraints with some data, and each
81             one has its own constraint. In the compelling case for this module,
82             I needed to generate different warnings for different failures.
83              
84             =head2 Interacting with a constraint
85              
86             I can get a constraint object by asking for it.
87              
88             my $constraint = Data::Constraint->get_by_name( $name );
89              
90             If no constraint has that name, I get back the default constraint which
91             always returns true. Or should it be false? I guess that depends on
92             what you are doing.
93              
94             If I don't know which constraints exist, I can get all the
95             names. The names are just simple strings, so they have no
96             magic. Maybe this should be a hash so you can immediately use
97             the value of the key you want.
98              
99             my @names = Data::Constraint->get_all_names;
100              
101             Once I have the constraint, I give it a value to check if
102              
103             $constraint->check( $value );
104              
105             I can do this all in one step.
106              
107             Data::Constraint->get_by_name( $name )->check( $value );
108              
109             =head2 Predefined constraints
110              
111             I previously had some pre-loaded contraints (C, C,
112             and C) but that got in the way of things that didn't want them.
113             You can still find them defined in the test files though.
114              
115             =head2 Adding a new constraint
116              
117             Add a new constraint with the class method C. The
118             first argument is the name you want to give the constraint. The
119             rest of the arguments are optional, although I need to add a
120             C key if I want the constraint to do anything useful: its
121             value should be something that returns true when the value
122             satisfies the condition (so a constant is probably not what
123             you want). An anonymous subroutine is probably what you want.
124              
125             Data::Constraint->add_constraint(
126             $name_of_constraint,
127             'run' => sub {...},
128             [ @optional_arguments ],
129             );
130              
131             Once I create the constraint, it exists forever (for now). I get
132             back the constraint object:
133              
134             my $constraint = Data::Constraint->add_constraint( ... );
135              
136             The object sticks around after C<$constraint> goes out of scope.
137             The C<$constraint> is just a reference to the object. I can get
138             another reference to it through C. See L<"Deleting
139             a constraint"> if you want to get rid of them.
140              
141             =head2 Modifying a constraint
142              
143             Um, don't do that yet unless you know what you are doing.
144              
145             =head2 Deleting a constraint
146              
147             Data::Constraint->delete_by_name( $name );
148              
149             Data::Constraint->delete_all();
150              
151             =head2 Doing anything you want
152              
153             You wish! This module can't help you there.
154              
155             =head1 METHODS
156              
157             =cut
158              
159             our $VERSION = '1.203';
160              
161 7     7   49 use base qw(Class::Prototyped);
  7         14  
  7         6130  
162              
163             =over 4
164              
165             =item check( VALUE )
166              
167             Apply the constraint to the VALUE.
168              
169             =item add_constraint( NAME, KEY-VALUES )
170              
171             Added a constraint with name NAME. Possible keys and values:
172              
173             run reference to subroutine to run
174             description string that decribes the constraint
175              
176             Example:
177              
178             Data::Constraint->add_constraint(
179             $name_of_constraint,
180             'run' => sub {...},
181             description => 'This is what I do",
182             );
183              
184             =item get_all_names
185              
186             Return a list of all the defined constraints.
187              
188             =item get_by_name( CONSTRAINT_NAME )
189              
190             Return the constraint with name CONSTRAINT_NAME. This is
191              
192             =item delete_by_name( CONSTRAINT_NAME )
193              
194             Delete the constraint with name CONSTRAINT_NAME. It's no longer available.
195              
196             =item delete_all()
197              
198             Delete all the constraints, even the default ones.
199              
200             =item description
201              
202             Return the description. The default description is the empty string. You
203             should supply your own description with C.
204              
205             =item run
206              
207             Return the description. The default description is the empty string. You
208             should supply your own description with C.
209              
210             =back
211              
212             =cut
213              
214             __PACKAGE__->reflect->addSlots(
215             check => sub {
216 26 100   26   10702 $_[0]->run( $_[1] ) ? 1 : 0;
217             },
218              
219             # the list of added constraints
220             constraints => {},
221              
222             add_constraint => sub {
223 16     16   6215 my $class = shift;
224 16         36 my $name = shift;
225              
226 16         67 my $constraint = $class->new(
227             'name' => $name,
228             'class*' => $class,
229             @_,
230             );
231              
232 16         7281 $class->constraints->{$name} = $constraint;
233             },
234              
235             get_all_names => sub {
236 4     4   2240 return sort keys %{ $_[0]->constraints };
  4         17  
237             },
238              
239             get_by_name => sub {
240 10     10   22415 $_[0]->constraints->{ $_[1] };
241             },
242              
243             delete_by_name => sub {
244 2     2   3215 delete $_[0]->constraints->{ $_[1] };
245             },
246              
247             delete_all => sub {
248 1     1   2522 $_[0]->constraints( {} );
249             },
250              
251 1     1   848 description => sub { "" },
252 0     0     run => sub { 1 },
253             );
254              
255             =head1 SOURCE AVAILABILITY
256              
257             This source is in Github:
258              
259             https://github.com/briandfoy/data-constraint
260              
261             =head1 AUTHOR
262              
263             brian d foy, C<< >>
264              
265             =head1 COPYRIGHT AND LICENSE
266              
267             Copyright © 2004-2022, brian d foy . All rights reserved.
268              
269             This program is free software; you can redistribute it and/or modify
270             it under the terms of the Artistic License 2.0.
271              
272             =cut
273              
274             1;
275