package Person;
use Moose;
use MooseX::Types::Moose qw(Str Int HashRef);
use MooseX::Types::Structured qw(Dict Tuple Optional);
## A name has a first and last part, but middle names are not required
has name => (
isa=>Dict[
first => Str,
last => Str,
middle => Optional[Str],
],
);
## description is a string field followed by a HashRef of tagged data.
has description => (
isa=>Tuple[
Str,
Optional[HashRef],
],
);
## Remainder of your class attributes and methods

A structured type constraint is a standard container Moose type constraint, such as an ArrayRef or HashRef, which has been enhanced to allow you to explicitly name all the allowed type constraints inside the structure. The generalized form is:

Parameterized constraints are built into core Moose and you are probably already familiar with the type constraints HashRef and ArrayRef. Structured types have similar functionality, so their syntax is likewise similar. For example, you could define a parameterized constraint like:

subtype ArrayOfInts,
as ArrayRef[Int];

which would constrain a value to something like [1,2,3,...] and so on. On the other hand, a structured type constraint explicitly names all it's allowed 'internal' type parameter constraints. For the example:

subtype StringFollowedByInt,
as Tuple[Str,Int];

would constrain its value to things like ['hello', 111] but ['hello', 'world'] would fail, as well as ['hello', 111, 'world'] and so on. Here's another example:

Notice that the last type constraint in the structure is optional. This is enabled via the helper Optional type constraint, which is a variation of the core Moose type constraint Maybe. The main difference is that Optional type constraints are required to validate if they exist, while Maybe permits undefined values. So the following example would not validate:

StringIntOptionalHashRef->validate(['Hello Undefined', 1000, undef]);

Please note the subtle difference between undefined and null. If you wish to allow both null and undefined, you should use the core Moose Maybe type constraint instead:

Please notice how the type parameters can be visually arranged to your liking and to improve the clarity of your meaning. You don't need to run then altogether onto a single line. Additionally, since the Dict type constraint defines a hash constraint, the key order is not meaningful. For example:

This method may take some additional time to set up but will give you more flexibility. However, structured constraints are highly compatible with this method, granting some interesting possibilities for coercion. Try:

This will actually work BUT you have to take care that the subtype has a structure that does not contradict the structure of it's parent. For now the above works, but I will clarify the syntax for this at a future point, so it's recommended to avoid (should not really be needed so much anyway). For now this is supported in an EXPERIMENTAL way. Your thoughts, test cases and patches are welcomed for discussion. If you find a good use for this, please let me know.

Please take care to make sure the recursion node is either Optional, or declare a union with an non-recursive option such as:

subtype Value
as Tuple[
Str,
Str|Tuple,
];

Which validates:

[
'Hello', [
'World', [
'Is', [
'Getting',
'Old',
],
],
],
];

Otherwise you will define a subtype that is impossible to validate since it is infinitely recursive. For more information about defining recursive types, please see the documentation in MooseX::Types and the test cases.

The Values of @constraints should ideally be MooseX::Types declared type constraints. We do support 'old style' Moose string based constraints to a limited degree but these string type constraints are considered deprecated. There will be limited support for bugs resulting from mixing string and MooseX::Types in your structures. If you encounter such a bug and really need it fixed, we will required a detailed test case at the minimum.

This is primarily a helper constraint for Dict and Tuple type constraints. What this allows is for you to assert that a given type constraint is allowed to be null (but NOT undefined). If the value is null, then the type constraint passes but if the value is defined it must validate against the type constraint. This makes it easy to make a Dict where one or more of the keys doesn't have to exist or a tuple where some of the values are not required. For example:

...creates a constraint that validates against a hashref with the keys 'first' and 'last' being strings and required while an optional key 'middle' is must be a string if it appears but doesn't have to appear. So in this case both the following are valid:

Structured type constraints by their nature are closed; that is validation will depend on an exact match between your structure definition and the arguments to be checked. Sometimes you might wish for a slightly looser amount of validation. For example, you may wish to validate the first 3 elements of an array reference and allow for an arbitrary number of additional elements. At first thought you might think you could do it this way:

This will now work as expected, validating ArrayRef structures such as:

[1,"hello", $obj, 2,3,4,5,6,...]

A few caveats apply. First, the slurpy type constraint must be the last one in the list of type constraint parameters. Second, the parent type of the slurpy type constraint must match that of the containing type constraint. That means that a Tuple can allow a slurpy ArrayRef (or children of ArrayRefs, including another Tuple) and a Dict can allow a slurpy HashRef (or children/subtypes of HashRef, also including other Dict constraints).

Please note the technical way this works 'under the hood' is that the slurpy keyword transforms the target type constraint into a coderef. Please do not try to create your own custom coderefs; always use the slurpy method. The underlying technology may change in the future but the slurpy keyword will be supported.

Error reporting has been improved to return more useful debugging messages. Now I will stringify the incoming check value with Devel::PartialDump so that you can see the actual structure that is tripping up validation. Also, I report the 'internal' validation error, so that if a particular element inside the Structured Type is failing validation, you will see that. There's a limit to how deep this internal reporting goes, but you shouldn't see any of the "failed with ARRAY(XXXXXX)" that we got with earlier versions of this module.

This support is continuing to expand, so it's best to use these messages for debugging purposes and not for creating messages that 'escape into the wild' such as error messages sent to the user.

Please see the test '12-error.t' for a more lengthy example. Your thoughts and preferable tests or code patches very welcome!

You need a hashref to conform to a canonical structure but are required accept a bunch of different incoming structures. You can normalize using the Dict type constraint and coercions. This example also shows structured types mixed which other MooseX::Types libraries.