DESCRIPTION

A constraint is some sort of condition on a datum. This module checks one condition against one value at a time, and I call the thing that checks that condition the "constraint". A constraint returns true or false, and that's it. It should have no side effects, it should not change program flow, and it should mind its own business. Let the thing that calls the constraint figure out what to do with it. I want something that says "yes" or "no" (and I discuss why this needs a fancy module later).

For instance, the constraint may state that the value has to be a number. The condition may be something that ensures the value does not have non-digits.

$value =~ /^\d+\z/

The value may have additional constraints, such as a lower limit.

$value > $minimum

Although I designed constraints to be a single condition, you may want to create contraints that check more than one thing.

$value > $minimum and $value < $maximum

In the previous examples, we could tell what was wrong with the value if the return value was false: the value didn't satisfy it's single condition. If it was supposed to be all digits and wasn't, then it had non-digits. If it was supposed to be greater than the minimum value, but wasn't, it was less than (or equal to) the minimal value. With more than one condition, like the last example, I cannot tell which one failed. I might be able to say that a value of out of range, but I think it is nicer to know if the value should have been larger or smaller so I can pass that on to the user. Having said that, I give you enough rope to do what you wish.

Why I need a fancy, high-falutin' module

This module is a sub-class of Class::Prototyped. In brief, that means constraints are class-objects even if they don't look like they are. Each constraint is a self-contained class, and I can modify a constraint by adding data and behaviour without affecting any of the other constraints. I can also make a list of constraints that I store for later use (also known as "delayed" execution).

Several data may need the same conditions, so they can share the same constraint. Other data that need different constraints can get their own, or modify copies of ones that exist.

I can also associate several constraints with some data, and each one has its own constraint. In the compelling case for this module, I needed to generate different warnings for different failures.

Interacting with a constraint

I can get a constraint object by asking for it.

my $constraint = Data::Constraints->get_by_name( $name );

If no constraint has that name, I get back the default constraint which always returns true. Or should it be false? I guess that depends on what you are doing.

If I don't know which constraints exist, I can get all the names. The names are just simple strings, so they have no magic. Maybe this should be a hash so you can immediately use the value of the key you want.

my @names = Data::Constraints->get_all_names;

Once I have the constraint, I give it a value to check if

$constraint->check( $value );

I can do this all in one step.

Data::Constraints->get_by_name( $name )->check( $value );

Predefined constraints

defined

True if the value is defined.

ordinal

True if the value is an ordinal number, also known as a strictly positive integer, which means it only has digit characters [0-9].

true

True if the value is true. That's a lot of work to find out just that since I could just use the value itself. This trivial constraints sticks with the metaphor though. It still returns only true or false, so the value I get back will be true if the value is true, but it won't be the value I started with, necessarily.

Adding a new constraint

Add a new constraint with the class method add_constraint. The first argument is the name you want to give the constraint. The rest of the arguments are optional, although I need to add a run key if I want the constraint to do anything useful: its value should be something that returns true when the value satisfies the condition (so a constant is probably not what you want). An anonymous subroutine is probably what you want.

Once I create the constraint, it exists forever (for now). I get back the constraint object:

my $constraint = Data::Constraint->add_constraint( ... );

The object sticks around after $constraint goes out of scope. The $constraint is just a reference to the object. I can get another reference to it through get_by_name(). See "Deleting a constraint" if you want to get rid of them.