Declare::Constraints::Simple.3pm

Langue: en

Autres versions - même langue

Version: 2006-09-14 (fedora - 01/12/10)

Section: 3 (Bibliothèques de fonctions)

NAME

Declare::Constraints::Simple - Declarative Validation of Data Structures

SYNOPSIS

   use Declare::Constraints::Simple-All;
 
   my $profile = IsHashRef(
                     -keys   => HasLength,
                     -values => IsArrayRef( IsObject ));
 
   my $result1 = $profile->(undef);
   print $result1->message, "\n";    # 'Not a HashRef'
 
   my $result2 = $profile->({foo => [23]});
 
   print $result2->message, "\n";    # 'Not an Object'
 
   print $result2->path, "\n";       
                     # 'IsHashRef[val foo].IsArrayRef[0].IsObject'
 
 

DESCRIPTION

The main purpose of this module is to provide an easy way to build a profile to validate a data structure. It does this by giving you a set of declarative keywords in the importing namespace.

USAGE

This is just a brief intro. For details read the documents mentioned in ``SEE ALSO''.

Constraint Import

   use Declare::Constraints::Simple-All;
 
 

The above command imports all constraint generators in the library into the current namespace. If you want only a selection, use "only":

   use Declare::Constraints::Simple
       Only => qw(IsInt Matches And);
 
 

You can find all constraints (and constraint-like generators, like operators. In fact, "And" above is an operator. They're both implemented equally, so the distinction is a merely philosophical one) documented in the Declare::Constraints::Simple::Library pod. In that document you will also find the exact parameters for their usage, so this here is just a brief Intro and not a coverage of all possibilities.

Building a Profile

You can use these constraints by building a tree that describes what data structure you expect. Every constraint can be used as sub-constraint, as parent, if it accepts other constraints, or stand-alone. If you'd just say
   my $check = IsInt;
   print "yes!\n" if $check->(23);
 
 

it will work too. This also allows predefining tree segments, and nesting them:

   my $id_to_objects = IsArrayRef(IsObject);
 
 

Here $id_to_objects would give it's OK on an array reference containing a list of objects. But what if we now decide that we actually want a hashref containing two lists of objects? Behold:

   my $object_lists = 
     IsHashRef( HasAllKeys( qw(good bad) ),
                OnHashKeys( good => $id_to_objects,
                            bad  => $id_to_objects ));
 
 

As you can see, constraints like "IsArrayRef" and "IsHashRef" allow you to apply constraints to their keys and values. With this, you can step down in the data structure.

Applying a Profile to a Data Structure

Constraints return just code references that can be applied to one value (and only one value) like this:
   my $result = $object_lists->($value);
 
 

After this call $result contains a Declare::Constraints::Simple::Result object. The first think one wants to know is if the validation succeeded:

   if ($result->is_valid) { ... }
 
 

This is pretty straight forward. To shorten things the result object also overloads it's "bool"ean context. This means you can alternatively just say

   if ($result) { ... }
 
 

However, if the result indicates a invalid data structure, we have a few options to find out what went wrong. There's a human parsable message in the "message" accessor. You can override these by forcing it to a message in a subtree with the "Message" declaration. The "stack" contains the name of the chain of constraints up to the point of failure.

You can use the "path" accessor for a joined string path representing the stack.

Creating your own Libraries

You can declare a package as a library with
   use Declare::Constraints::Simple-Library;
 
 

which will install the base class and helper methods to define constraints. For a complete list read the documentation in Declare::Constraints::Simple::Library::Base. You can use other libraries as base classes to include their constraints in your export possibilities. This means that with a package setup like

   package MyLibrary;
   use warnings;
   use strict;
 
   use Declare::Constraints::Simple-Library;
   use base 'Declare::Constraints::Simple::Library';
 
   constraint 'MyConstraint',
     sub { return _result(($_[0] >= 12), 'Value too small') };
 
   1;
 
 

you can do

   use MyLibrary-All;
 
 

and have all constraints, from the default library and yours from above, installed into your requesting namespace. You can override a constraint just by redeclaring it in a subclass.

Scoping

Sometimes you want to validate parts of a data structure depending on another part of it. As of version 2.0 you can declare scopes and store results in them. Here is a complete example:
   my $constraint =
     Scope('foo',
       And(
         HasAllKeys( qw(cmd data) ),
         OnHashKeys( 
           cmd => Or( SetResult('foo', 'cmd_a',
                        IsEq('FOO_A')),
                      SetResult('foo', 'cmd_b',
                        IsEq('FOO_B')) ),
           data => Or( And( IsValid('foo', 'cmd_a'),
                            IsArrayRef( IsInt )),
                       And( IsValid('foo', 'cmd_b'),
                            IsRegex )) )));
 
 

This profile would accept a hash references with the keys "cmd" and "data". If "cmd" is set to "FOO_A", then "data" has to be an array ref of integers. But if "cmd" is set to "FOO_B", a regular expression is expected.

SEE ALSO

Declare::Constraints::Simple::Library, Declare::Constraints::Simple::Result, Declare::Constraints::Simple::Base, Module::Install

REQUIRES

Carp::Clan, aliased, Class::Inspector, Scalar::Util, overload and Test::More (for build).

TODO

*
Examples.
*
A list of questions that might come up, together with their answers.
*
A "Custom" constraint that takes a code reference.
*
Create stack objects that stringify to the current form, but can hold more data.
*
Give the "Message" constraint the ability to get the generated constraint inserted in the message. A possibility would be to replace __Value__ and __Message__. It might also accept code references, which return strings.
*
Allow the "IsCodeRef" constraint to accept further constraints. One might like to check, for example, the refaddr of a closure.
*
A "Captures" constraint that takes a regex and can apply other constraints to the matches.
*
???
*
Profit.

INSTALLATION

   perl Makefile.PL
   make
   make test
   make install
 
 

For details read Module::Install.

AUTHOR

Robert 'phaylon' Sedlacek "<phaylon@dunkelheit.at>" This module is free software, you can redistribute it and/or modify it under the same terms as perl itself.