File Coverage

blib/lib/Data/Constraint.pm
Criterion Covered Total %
statement 23 24 95.8
branch 2 2 100.0
condition n/a
subroutine 11 12 91.6
pod n/a
total 36 38 94.7


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