File Coverage

lib/REST/Neo4p/Constrain.pm
Criterion Covered Total %
statement 77 129 59.6
branch 25 66 37.8
condition 5 29 17.2
subroutine 12 17 70.5
pod 4 4 100.0
total 123 245 50.2


line stmt bran cond sub pod time code
1             #$Id$
2             package REST::Neo4p::Constrain;
3 2     2   6054 use base 'Exporter';
  2         5  
  2         379  
4 2     2   1822 use REST::Neo4p::Constraint qw(:all);
  2         7  
  2         314  
5 2     2   1641 use REST::Neo4p::Constraint::Property;
  2         5  
  2         112  
6 2     2   1664 use REST::Neo4p::Constraint::Relationship;
  2         6  
  2         232  
7 2     2   1520 use REST::Neo4p::Constraint::RelationshipType;
  2         6  
  2         99  
8 2     2   13 use REST::Neo4p::Exceptions;
  2         4  
  2         38  
9 2     2   11 use strict;
  2         3  
  2         36  
10 2     2   10 use warnings;
  2         3  
  2         51  
11 2     2   10 no warnings qw(once redefine);
  2         4  
  2         77  
12              
13              
14             BEGIN {
15 2     2   3568 $REST::Neo4p::Constrain::VERSION = '0.4003';
16             }
17             our @EXPORT = qw(create_constraint drop_constraint constrain relax);
18             our @VALIDATE = qw(validate_properties validate_relationship validate_relationship_type);
19             our @SERIALIZE = qw(serialize_constraints load_constraints);
20             our @EXPORT_OK = (@VALIDATE,@SERIALIZE);
21             our %EXPORT_TAGS = (
22             validate => \@VALIDATE,
23             serialize => \@SERIALIZE,
24             auto => [@EXPORT],
25             all => [@EXPORT,@EXPORT_OK]
26             );
27              
28             our $entity_new_func = \&REST::Neo4p::Entity::new;
29             our $entity_set_prop_func = \&REST::Neo4p::Entity::set_property;
30             our $node_relate_to_func = \&REST::Neo4p::Node::relate_to;
31              
32             # this class is a factory for Constraint objects
33              
34             # how to constrain
35             # automatically constrain --
36             # prevent the constructors from creating invalid nodes
37             # prevent the constructors from creating invalid relationships
38             # prevent setting invalid properties
39             # - raise exceptions
40             # validate using Constraint class methods
41              
42             # building constraints
43             # - constructing Constraint subclass objects directly
44             # - factory function create_constraint()
45             # - load from a file (JSON, XML)
46              
47             require REST::Neo4p::Entity;
48             require REST::Neo4p::Node;
49              
50             sub create_constraint {
51 5     5 1 719 my %parms = @_;
52             # reqd: tag, type, constraints
53             # opt: condition, rtype, priority
54 5 50       18 if ( @_ % 2 ) {
55 0         0 REST::Neo4p::LocalException->throw("create_constraint requires a hash arg");
56             }
57 5 50       17 unless ($parms{tag}) {
58 0         0 REST::Neo4p::LocalException->throw("No constraint tag defined\n");
59             }
60 5 50 33     134 unless ($parms{type} && grep /^$parms{type}$/,@REST::Neo4p::Constraint::CONSTRAINT_TYPES) {
61 0         0 REST::Neo4p::LocalException->throw("Invalid constraint type '$parms{type}'\n");
62             }
63 5         11 my $ret;
64 5         9 for ($parms{type}) {
65 5 100       14 /^node_property$/ && do {
66 2 50       6 unless (ref $parms{constraints} eq 'HASH') {
67 0         0 REST::Neo4p::LocalException->throw("constraints parameter requires a hashref\n");
68             }
69 2   50     8 $parms{constraints}->{_priority} = $parms{priority} || 0;
70 2 50       6 $parms{constraints}->{_condition} = $parms{condition} if defined $parms{condition};
71 2         4 eval {
72             $ret = REST::Neo4p::Constraint::NodeProperty->new(
73             $parms{tag} => $parms{constraints}
74 2         10 );
75             };
76 2         4 my $e;
77 2 50       24 if ($e = REST::Neo4p::LocalException->caught()) {
78 0         0 REST::Neo4p::ConstraintSpecException->throw($e->message);
79             }
80 2 50       39 if ($e = Exception::Class->caught()) {
81 0 0 0     0 (ref $e && $e->can("rethrow")) ? $e->rethrow : die $e;
82             }
83 2         13 last;
84             };
85 3 100       9 /^relationship_property$/ && do {
86 1 50       4 unless (ref $parms{constraints} eq 'HASH') {
87 0         0 REST::Neo4p::LocalException->throw("constraints parameter requires a hashref\n");
88             }
89 1   50     7 $parms{constraints}->{_priority} = $parms{priority} || 0;
90 1 50       12 $parms{constraints}->{_condition} = $parms{condition} if defined $parms{condition};
91 1 50       4 $parms{constraints}->{_relationship_type} = $parms{rtype} if defined $parms{rtype};
92 1         2 eval {
93             $ret = REST::Neo4p::Constraint::RelationshipProperty->new(
94             $parms{tag} => $parms{constraints}
95 1         15 );
96             };
97 1         1 my $e;
98 1 50       5 if ($e = REST::Neo4p::LocalException->caught()) {
99 0         0 REST::Neo4p::ConstraintSpecException->throw($e->message);
100             }
101 1 50       22 if ($e = Exception::Class->caught()) {
102 0 0 0     0 (ref $e && $e->can("rethrow")) ? $e->rethrow : die $e;
103             }
104 1         9 last;
105             };
106 2 100       6 /^relationship$/ && do {
107 1 50       4 unless (ref $parms{constraints} eq 'ARRAY') {
108 0         0 REST::Neo4p::LocalException->throw("constraints parameter requires an arrayref for relationship constraint\n");
109             }
110 1         2 eval {
111             $ret = REST::Neo4p::Constraint::Relationship->new(
112             $parms{tag} => {
113             _condition => $parms{condition},
114             _priority => $parms{priority} || 0,
115             _relationship_type => $parms{rtype},
116             _descriptors => $parms{constraints}
117             }
118 1   50     11 );
119             };
120 1         1 my $e;
121 1 50       4 if ($e = REST::Neo4p::LocalException->caught()) {
122 0         0 REST::Neo4p::ConstraintSpecException->throw($e->message);
123             }
124 1 50       12 if ($e = Exception::Class->caught()) {
125 0 0 0     0 (ref $e && $e->can("rethrow")) ? $e->rethrow : die $e;
126             }
127 1         6 last;
128             };
129 1 50       5 /^relationship_type$/ && do {
130 1 50       4 unless (ref $parms{constraints} eq 'ARRAY') {
131 0         0 REST::Neo4p::LocalException->throw("constraints parameter requires an arrayref for relationship type constraint\n");
132             }
133 1         2 eval {
134             $ret = REST::Neo4p::Constraint::RelationshipType->new(
135             $parms{tag} => {
136             _condition => $parms{condition},
137             _priority => $parms{priority} || 0,
138             _type_list => $parms{constraints}
139             }
140 1   50     8 );
141             };
142 1         1 my $e;
143 1 50       4 if ($e = REST::Neo4p::LocalException->caught()) {
144 0         0 REST::Neo4p::ConstraintSpecException->throw($e->message);
145             }
146 1 50       11 if ($e = Exception::Class->caught()) {
147 0 0 0     0 (ref $e && $e->can("rethrow")) ? $e->rethrow : die $e;
148             }
149 1         7 last;
150             };
151 0         0 do { #fallthru
152 0         0 die "I shouldn't be here in create_constraint()";
153             };
154             }
155 5         45 return $ret; # the Constraint object created
156             }
157              
158             sub drop_constraint {
159 0     0 1 0 my ($tag) = @_;
160 0         0 REST::Neo4p::Constraint->drop_constraint($tag);
161             }
162              
163             # hooks into REST::Neo4p::Entity methods
164             sub constrain {
165 1     1 1 67 my %parms = @_;
166             *REST::Neo4p::Entity::new =
167             sub {
168 0     0   0 my ($class,$properties) = @_;
169 0         0 my ($entity_type) = $class =~ /.*::(.*)/;
170 0         0 $entity_type = lc $entity_type;
171 0 0       0 goto $entity_new_func if ($entity_type !~ /^node|relationship$/);
172              
173 0         0 my $addl_components = delete $properties->{_addl_components};
174 0         0 $properties->{__type} = $entity_type;
175 0 0       0 unless (validate_properties($properties)) {
176 0         0 REST::Neo4p::ConstraintException->throw(
177             "Specified properties violate active constraints\n"
178             );
179             }
180 0         0 delete $properties->{__type};
181 0         0 $properties->{_addl_components} = $addl_components;
182 0         0 goto $entity_new_func;
183 1         45 };
184              
185             *REST::Neo4p::Entity::set_property = sub {
186 0     0   0 my ($self, $props) = @_;
187 0 0 0     0 REST::Neo4p::LocalException->throw("Arg must be a hashref\n")
188             unless ref($props) && ref $props eq 'HASH';
189 0         0 my $entity_type = ref $self;
190 0         0 $entity_type =~ s/.*::(.*)/\L$1\E/;
191 0         0 my $orig_props = $self->get_properties;
192 0         0 for (keys %$props) {
193 0         0 $orig_props->{$_} = $props->{$_};
194             }
195 0 0       0 if ($entity_type eq 'relationship') {
196 0         0 $orig_props->{_relationship_type} = $self->type;
197             }
198 0 0       0 unless (validate_properties($orig_props)) {
199 0         0 REST::Neo4p::ConstraintException->throw(
200             message => "Specified properties would violate active constraints\n",
201             args => [@_]
202             );
203             }
204 0         0 goto $entity_set_prop_func;
205 1         21 };
206              
207             *REST::Neo4p::Node::relate_to = sub {
208 0     0   0 my ($n1, $n2, $reln_type, $reln_props) = @_;
209 0 0 0     0 unless (validate_relationship_type($reln_type) ||
210             !$REST::Neo4p::Constraint::STRICT_RELN_TYPES) {
211 0         0 REST::Neo4p::ConstraintException->throw(
212             message => "Relationship type '$reln_type' is not allowed by active constraints\n",
213             args => [@_]
214             );
215             }
216 0 0       0 unless (validate_relationship($n1,$n2,$reln_type,$reln_props)) {
217 0         0 REST::Neo4p::ConstraintException->throw(
218             message => "Relationship or its properties violate active relationship constraints\n",
219             args => [@_]
220             );
221             }
222 0         0 goto $node_relate_to_func;
223 1         17 };
224 1         7 return 1;
225             }
226              
227             sub relax {
228 0     0 1   *REST::Neo4p::Entity::new = $entity_new_func;
229 0           *REST::Neo4p::Entity::set_property = $entity_set_prop_func;
230 0           *REST::Neo4p::Node::relate_to = $node_relate_to_func;
231 0           return 1;
232             }
233              
234             =head1 NAME
235              
236             REST::Neo4p::Constrain - Create and apply Neo4j app-level constraints
237              
238             =head1 SYNOPSIS
239              
240             use REST::Neo4p;
241             use REST::Neo4p::Constrain qw(:all); # not included by REST::Neo4p
242            
243             # create some constraints
244            
245             create_constraint (
246             tag => 'owner',
247             type => 'node_property',
248             condition => 'only',
249             constraints => {
250             name => qr/[a-z]+/i,
251             species => 'human'
252             }
253             );
254            
255             create_constraint(
256             tag => 'pet',
257             type => 'node_property',
258             condition => 'all',
259             constraints => {
260             name => qr/[a-z]+/i,
261             species => qr/^dog|cat|ferret|mole rat|platypus$/
262             }
263             );
264            
265             create_constraint(
266             tag => 'OWNS_props',
267             type => 'relationship_property',
268             rtype => 'OWNS',
269             condition => 'all',
270             constraints => {
271             year_purchased => qr/^20[0-9]{2}$/
272             }
273             );
274              
275             create_constraint(
276             tag => 'owners_own_pets',
277             type => 'relationship',
278             rtype => 'OWNS',
279             constraints => [{ owner => 'pet' }] # note arrayref
280             );
281              
282             create_constraint(
283             tag => 'loves',
284             type => 'relationship',
285             rtype => 'LOVES',
286             constraints => [{ pet => 'owner' },
287             { owner => 'pet' }] # both directions ok
288             );
289              
290             create_constraint(
291             tag => 'ignore'
292             type => 'relationship',
293             rtype => 'IGNORES',
294             constraints => [{ pet => 'owner' },
295             { owner => 'pet' }] # both directions ok
296             );
297              
298             create_constraint(
299             tag => 'allowed_rtypes',
300             type => 'relationship_type',
301             constraints => [qw( OWNS FEEDS LOVES )]
302             # IGNORES is missing, see below
303             );
304              
305             # constrain by automatic exception-throwing
306              
307             constrain();
308              
309             $fred = REST::Neo4p::Node->new(
310             { name => 'fred', species => 'human' }
311             );
312             $fluffy = REST::Neo4p::Node->new(
313             { name => 'fluffy', species => 'mole rat' }
314             );
315              
316             $r1 = $fred->relate_to(
317             $fluffy, 'OWNS', {year_purchased => 2010}
318             ); # valid
319             eval {
320             $r2 = $fluffy->relate_to($fred, 'OWNS', {year_purchased => 2010});
321             };
322             if (my $e = REST::Neo4p::ConstraintException->caught()) {
323             print STDERR "Pet can't own an owner, ignored\n";
324             }
325              
326             eval {
327             $r3 = $fluffy->relate_to($fred, 'IGNORES');
328             };
329             if (my $e = REST::Neo4p::ConstraintException->caught()) {
330             print STDERR "Pet can't ignore an owner, ignored\n";
331             }
332              
333             # allow relationship types that are not explictly
334             # allowed -- a relationship constraint is still required
335              
336             $REST::Neo4p::Constraint::STRICT_RELN_TYPES = 0;
337              
338             $r3 = $fluffy->relate_to($fred, 'IGNORES'); # no throw now
339              
340             relax(); # stop automatic constraints
341              
342             # use validation
343              
344             $r2 = $fluffy->relate_to(
345             $fred, 'OWNS',
346             {year_purchased => 2010}
347             ); # not valid, but auto-constraint not in force
348              
349             if ( validate_properties($r2) ) {
350             print STDERR "Relationship properties are valid\n";
351             }
352             if ( !validate_relationship($r2) ) {
353             print STDERR
354             "Relationship does not meet constraints, ignoring...\n";
355             }
356              
357             # try a relationship
358              
359             if ( validate_relationship( $fred => $fluffy, 'LOVES' ) {
360             $fred->relate_to($fluffy, 'LOVES');
361             }
362             else {
363             print STDERR
364             "Prospective relationship fails constraints, ignoring...\n";
365             }
366              
367             # try a relationship type
368              
369             if ( validate_relationship( $fred => $fluffy, 'EATS' ) {
370             $fred->relate_to($fluffy, 'EATS');
371             }
372             else {
373             print STDERR
374             "Relationship type disallowed, ignoring...\n";
375             }
376              
377             # serialize all constraints
378              
379             open $f, ">my_constraints.json";
380             print $f serialize_constraints();
381             close $f;
382              
383             # remove current constraints
384              
385             while ( my ($tag, $constraint) =
386             each REST::Neo4p::Constraint->get_all_constraints ) {
387             $constraint->drop;
388             }
389              
390             # restore constraints
391              
392             open $f, "my_constraints.json";
393             local $/ = undef;
394             $json = <$f>;
395             load_constraints($json);
396              
397             =head1 DESCRIPTION
398              
399             L, as a NoSQL database, is intentionally
400             lenient. One of its only hardwired constraints is its refusal to
401             remove a Node that is involved in a Relationship. Other constraints to
402             database content (properties and their values, "kinds" of
403             relationships, and relationship types) must be applied at the
404             application level.
405              
406             L and L attempt to
407             provide a flexible framework for creating and enforcing Neo4j content
408             constraints for applications using L.
409              
410             The use case that inspired these modules is the following: You start
411             out with a set of well categorized things, that have some well defined
412             relationships. Each thing will be represented as a node, that's
413             fine. But you want to guarantee (to your client, for example) that
414              
415             =over
416              
417             =item 1. You can classify every node you add or read unambiguously into
418             a well-defined group;
419              
420             =item 2. You never relate two nodes belonging to particular groups in a
421             way that doesn't make sense according to your well-defined
422             relationships.
423              
424             =back
425              
426             These modules allow you to create a set of constraints on node
427             and relationship properties, relationships themselves, and
428             relationship types to meet this use case and others. It is flexible,
429             in that you can choose the level at which the validation is applied:
430              
431             =over
432              
433             =item * You can make L throw exceptions when registered
434             constraints are violated before object creation/database insertion or
435             updating;
436              
437             =item * You can validate properties and relationships using methods in
438             the code;
439              
440             =item * You can check the validity of L or
441             L objects as retrieved from
442             the database
443              
444             =back
445              
446             The L is a full example.
447              
448             =head2 Types of Constraints
449              
450             L handles four types of constraints.
451              
452             =over
453              
454             =item * Node property constraints
455              
456             A node property constraint specifies the presence/absence of
457             properties, and can specify the allowable values a property must (or
458             must not) take.
459              
460             =item * Relationship property constraints
461              
462             A relationship property constraint specifies the presence/absence of
463             properties, and can specify the allowable values a property must (or
464             must not) take. In addition, a relationship property constraint can be
465             linked to a given relationship type, so that, e.g., the creation of a
466             relationship of a given type can be forced to have specified
467             relationship properties.
468              
469             =item * Relationship constraints
470              
471             A relationship constraint specifies which "kinds" of nodes can
472             participate in a relationship of a given type. A node's "kind" is
473             determined by what node property constraint its properties satisfy.
474              
475             =item * Relationship type constraints
476              
477             A relationship type constraint simply enumerates the allowable (or
478             disallowed) relationship types.
479              
480             =back
481              
482             =head2 Specifying Constraints
483              
484             L exports C, which
485             creates and registers the different constraint types. (It also returns
486             the L object so created, which can be
487             useful.)
488              
489             C accepts a hash of parameters. The following are required:
490              
491             create_constraint(
492             tag => $tag, # a (preferably) simple and meaningful alias for this
493             # constraint
494             type => $type, # node_property|relationship_property|
495             # relationship|relationship_type
496             priority => $integer_priority, # to determine which constraints
497             # are evaluated first during validation
498             constraints => $constraints, # a reference that depends on the
499             # constraint type, see below
500             );
501              
502             Other parameters and the form of the constraint values depend on the
503             constraint type:
504              
505             =over
506              
507             =item * Node property
508              
509             The constraints are specified as a hashref whose keys are the property
510             names and values are the constraints on the property values.
511              
512             constraints => {
513             # property must be present, may have any value
514             prop_1 => '',
515             # property must be present, and value must eq 'value'
516             prop_2 => 'value',
517             # property must be present, and value must match qr/.alue/
518             prop_3 => qr/.alue/,
519             # property may be present, and may have any value
520             prop_4 => [],
521             # property may be present, if present
522             # value must match the given condition
523             prop_5 => [],
524             # (use regexps for enumerations)
525             prop_6 => qr/^value1|value2|value3$/
526             }
527              
528             A C parameter can be specified:
529              
530             condition => 'all' # all the specified constraints must be met, and
531             # other properties not in the constraint list may
532             # be added freely
533              
534             condition => 'only' # all the specified constraint must be met, and
535             # no other properties may be added
536              
537             condition => 'none' # reject if any of the specified constraints is
538             # satisfied ('blacklist')
539              
540             C defaults to 'all'.
541              
542             =item * Relationship property
543              
544             Constraints on properties are specified as for node properties above.
545              
546             A relationship type can be associated with the relationship property
547             constraint with the parameter C:
548              
549             rtype => $relationship_type # any relationship type name, or '*' for all types
550              
551             The C parameter works as for node properties above.
552              
553             =item * Relationship
554              
555             The basic constraint on a relationship is specified as a hashref that
556             maps a "kind" of from-node to a "kind" of to-node. The "kind" of node
557             is indicated by the tag of the node property constraint it satisfies.
558              
559             The C parameter takes an arrayref of these one-row hashrefs.
560              
561             The C parameter specifies the relationship type to which the
562             constraint applies.
563              
564             Here's an example. Create the following node property constraints:
565              
566             create_constraint(
567             tag => 'owner',
568             type => 'node_property',
569             constraints => {
570             name => qr/a-z/i,
571             species => 'human'
572             }
573             );
574              
575             create_constraint(
576             tag => 'pet',
577             type => 'node_property',
578             constraints => {
579             name => qr/a-z/i,
580             species => qr/^dog|cat|ferret|mole rat|platypus$/
581             }
582             );
583              
584             Then a relationship constraint that specifies owners can own pets is
585              
586             create_constraint(
587             tag => 'owners2pets',
588             type => 'relationship',
589             rtype => 'OWNS',
590             constraints => [{ owner => 'pet' }] # note arrayref
591             );
592            
593              
594             In L terms, if this constraint (and only this one) is registered,
595              
596             $fred = REST::Neo4p::Node->new( { name => 'fred', species => 'human' } );
597             $fluffy = REST::Neo4p::Node->new( { name => 'fluffy', species => 'mole rat' } );
598              
599             $r1 = $fred->relate_to($fluffy, 'OWNS'); # valid
600             $r2 = $fluffy->relate_to($fred, 'OWNS'); # NOT VALID, throws when
601             # constrain() is in force
602              
603             =item * Relationship type
604              
605             The relationship type constraint is just an arrayref of relationship types.
606              
607             constraints => [@rel_types]
608              
609             The C parameter can take the following values:
610              
611             condition => 'only' # new relationships must have one of the listed
612             # types (whitelist)
613              
614             condition => 'none' # no new relationship may have any of the listed
615             # types (blacklist)
616              
617             =back
618              
619             =head2 Using Constraints
620              
621             L|/create_constraint()> registers the created
622             constraint so that it is included in all relevant validations.
623              
624             L|/drop_constraint()> deregisters and removes the
625             constraint specified by its tag:
626              
627             drop_constraint('owner');
628             drop_constraint('pet');
629              
630             =head3 Automatic validation
631              
632             Execute L|/constrain()> to force L to raise a
633             L whenever
634             the construction or modification of a Node or Relationship would
635             violate the registered constraints.
636              
637             Executing L|/relax()> causes L to ignore all
638             constraint and create and modify entities as usual.
639              
640             C and C can be used anywhere at any time. The
641             effects are global.
642              
643             When C is in force, any new constraints created are
644             immediately available to the validation.
645              
646             =head3 "Manual" validation
647              
648             To control validation directly, use the C<:validate> export tag:
649              
650             use REST::Neo4p::Constrain qw(:validate);
651              
652             This provides three functions for checking properties, relationships,
653             and relationship types against registered constraints. They return
654             true if the object or spec satisfies the current constraints and false
655             if it violates the current constraints. No constraint exceptions are
656             raised.
657              
658             =head3 Controlling relationship validation strictness
659              
660             You can set whether relationship types or relationship properties are
661             strictly validated or not, even when constraints are in
662             force. Relaxing one or both of these can allow you to follow
663             constraints you have defined strictly, while enabling other kinds of
664             relationships to be created ad hoc outside of validation.
665              
666             See L for details.
667              
668             =head1 FUNCTIONS
669              
670             =head2 Exported by default
671              
672             =over
673              
674             =item create_constraint()
675              
676             create_constraint(
677             tag => $meaningful_tag,
678             type => $constraint_type, # [node_property|relationship_property|
679             # relationship|relationship_type]
680             condition => $condition # all|only|none, depends on type
681             rtype => $relationship_type # relationship type tag
682             constraints => $spec_ref # hashref or arrayref, depends on type
683             );
684              
685             Creates and registers a constraint. Returns the created L object.
686              
687             See L for details.
688              
689             =item drop_constraint()
690              
691             drop_constraint($constraint_tag);
692              
693             Deregisters a constraint identified by its tag. Returns the constraint object.
694              
695             =item constrain()
696              
697             =item relax()
698              
699             constrain();
700             eval {
701             $node = REST::Neo4p::Node->create({foo => bar, baz => 1});
702             };
703             if ($e = REST::Neo4p::ConstraintException->caught()) {
704             relax();
705             print "Got ".$e->msg.", but creating anyway\n";
706             $node = REST::Neo4p::Node->create({foo => bar, baz => 1});
707             }
708              
709             constrain() forces L constructors and property setters to
710             comply with the currently registered
711             constraints. Ls
712             are thrown if constraints are not met.
713              
714             relax() turns off the automatic validation of constrain().
715              
716             Effects are global.
717              
718             =back
719              
720             =head2 Serialization functions
721              
722             Use these functions to freeze and thaw the currently registered
723             constraints to and from a JSON representation.
724              
725             Import with
726              
727             use REST::Neo4p::Constrain qw(:serialize);
728              
729             =over
730              
731             =item serialize_constraints()
732              
733             open $f, ">constraints.json";
734             print $f serialize_constraints();
735              
736             Returns a JSON-formatted representation of all currently registered constraints.
737              
738             =item load_constraints()
739              
740             open $f, "constraints.json";
741             {
742             local $/ = undef;
743             load_constraints(<$f>);
744             }
745              
746             Creates and registers a list of constraints specified by a JSON string
747             as produced by L.
748              
749             =back
750              
751             =head2 Validation functions
752              
753             Functional interface. Returns the registered constraint object with
754             the highest priority that the argument satisfies, or false if no
755             constraint is satisfied.
756              
757             Import with
758              
759             use REST::Neo4p::Constrain qw(:validate);
760              
761             =over
762              
763             =item validate_properties()
764              
765             validate_properties( $node_object )
766             validate_properties( $relationship_object );
767             validate_properties( { name => 'Steve', instrument => 'banjo' } );
768              
769             =item validate_relationship()
770              
771             validate_relationship ( $relationship_object );
772             validate_relationship ( $node_object1 => $node_object2,
773             $reln_type );
774             validate_relationship ( { name => 'Steve', instrument => 'banjo' } =>
775             { name => 'Marcia', instrument => 'blunt' },
776             'avoids' );
777              
778             =item validate_relationship_type()
779              
780             validate_relationship_type( 'avoids' )
781              
782             These methods can also be exported from L.
783              
784             =back
785              
786             =head1 SEE ALSO
787              
788             L, L
789              
790             =head1 AUTHOR
791              
792             Mark A. Jensen
793             CPAN ID: MAJENSEN
794             majensen -at- cpan -dot- org
795              
796             =head1 LICENSE
797              
798             Copyright (c) 2012-2022 Mark A. Jensen. This program is free software; you
799             can redistribute it and/or modify it under the same terms as Perl
800             itself.
801              
802             =cut
803              
804             1;