Declar...Simple(3pm)

πŸ“¦
version 0.03 Β· 5 versions
, in debian bookworm, debian trixie, ubuntu jammy, ubuntu noble, ubuntu resolute, ubuntu stonking

πŸ’– Declar...Simple(3pm) β€” like, the whole vibe πŸ’–

okay bestie so this is a page from the User Contributed Perl Documentation collection, and it's about Declare::Constraints::Simple. Buckle up, it's giving data-structure-gatekeeping in the best way possible. πŸ¦‹


✨ NAME ✨

Declare::Constraints::Simple β€” Declarative Validation of Data Structures. yes queen, you declare what you want and it checks it for you. no cap. πŸ‘‘


✨ SYNOPSIS ✨

here's the little taste test, literally copy-paste core:

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'

see how it just… tells you exactly where your data structure is being a disappointment? iconic behaviour honestly. πŸ’…


✨ DESCRIPTION ✨

so the main character energy here is this: the whole point of the module is to give you an easy way to build a profile that validates a data structure. and how does it do that, you ask? it hands you a lil set of declarative keywords right in whatever namespace is doing the importing. that's it, that's the trick. ✨


✨ USAGE ✨

okay so this is just a quick lil intro moment β€” for the actual deep lore you gotta read the docs mentioned down in "SEE ALSO". 🫢

🌸 Constraint Import 🌸

use Declare::Constraints::Simple-All;

that line right there? it imports every single constraint generator in the whole library into your current namespace. all of them. the entire roster. πŸ’–

but maybe you're not a maximalist (valid). if you only want a selection, use only:

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

and hey, you can find all the constraints β€” plus the constraint-like generators, aka operators (fun fact: And up there is an operator, and ngl they're both implemented exactly the same way, so the distinction between "constraint" and "operator" is a merely philosophical one, no cap 🧚) β€” documented over in the Declare::Constraints::Simple::Library pod. that document also has the exact parameters for using them, because this page right here is just a brief intro and absolutely not a full coverage of all the possibilities. periodt.

🌸 Building a Profile 🌸

you use these constraints by building a tree that describes what data structure you expect. every constraint can be used as a sub-constraint, as a parent (if it accepts other constraints), or just stand-alone. like, if you literally just said:

my $check = IsInt;
print "yes!\n" if $check->(23);

it works. yes, on its own. it's that girl. πŸ’…

this also means you can predefine tree segments and nest them:

my $id_to_objects = IsArrayRef(IsObject);

here $id_to_objects would give its OK on an array reference containing a list of objects. πŸŽ€

but what if we now decide we actually want a hashref containing two lists of objects? bestie, behold:

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

see what happened there? constraints like IsArrayRef and IsHashRef let you apply constraints to their keys and values, so you can just keep stepping down and down into the data structure. it's giving spelunking but make it validation. πŸ•³οΈβœ¨

🌸 Applying a Profile to a Data Structure 🌸

constraints return just code references that can be applied to one value (and only one value, don't get greedy) like this:

my $result = $object_lists->($value);

after that call, $result contains a Declare::Constraints::Simple::Result object. the first thing you're gonna wanna know is obviously did the validation succeed:

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

pretty straight forward, right? and to shorten things, the result object also overloads its "bool"ean context, which means you can alternatively just say:

if ($result) { ... }

but okay, plot twist β€” if the result indicates an invalid data structure, you've got a few options for figuring out what went wrong. there's a human parsable message in the message accessor. you can override those by forcing it to a message in a subtree with the Message declaration. and the stack contains the name of the chain of constraints up to the point of failure. πŸ”

you can also use the path accessor for a joined string path representing the stack. chef's kiss. πŸ‘‘

🌸 Creating your own Libraries 🌸

you can declare a package as a library with:

use Declare::Constraints::Simple-Library;

which installs the base class and the helper methods you need to define constraints. for the complete list, read the documentation in Declare::Constraints::Simple::Library::Base. πŸ“š

and get this β€” you can use other libraries as base classes to include their constraints in your export possibilities. so 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 boom, you've got all the constraints β€” from the default library and yours from above β€” installed into your requesting namespace. and you can override a constraint just by redeclaring it in a subclass. simple as. πŸ’œ

🌸 Scoping 🌸

sometimes you wanna 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's a complete example, no crumbs:

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 )) )));

so this profile would accept a hash reference 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. conditional validation, we love to see it. πŸ¦‹


✨ SEE ALSO ✨

go read these, bestie, they're the whole syllabus:

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

✨ REQUIRES ✨

the friend group this module rolls with: Carp::Clan, aliased, Class::Inspector, Scalar::Util, overload and Test::More (for build). πŸ’•


✨ TODO ✨

the wishlist, aka things that are not done yet, and we're being honest about it:

  • 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.
not me cackling at "???" followed by "Profit." β€” the classic. πŸ’€

✨ INSTALLATION ✨

the sacred four-step ritual:

perl Makefile.PL
make
make test
make install

for details read Module::Install. πŸŽ€


✨ AUTHOR ✨

Robert 'phaylon' Sedlacek "<phaylon@dunkelheit.at>" β€” thank you for your service, king. 🫢


This module is free software, you can redistribute it and/or modify it under the same terms as perl itself. 🌈


and that's the whole page, bestie!! this was Declar...Simple(3pm) from the User Contributed Perl Documentation set, and the footer says it's perl v5.32.0, dated 2021-01-09. mwah, go validate something. πŸ’‹πŸ’–βœ¨

πŸ“¦ where this page comes from