Hello all- Some context: I have a web application with a fair bit of code for validating data (mostly from form fields). Most of that data eventually ends up in attributes of some Moose class, which also have type constraints. Functionality-wise, there seems to be a lot of overlap between the validation code and the type constraints. Basically, they both make assertions about the state of some data.
I’m using Input::Validator, which is a nice framework for composing validation rules. But instead, I’d like to just use Moose-compatible type constraints so I only have to define the rules in one place. The problem is, the validation rules seem to be more complex and dynamic than type constraints typically allow (at least, with MooseX::Types). I feel that I want to construct type constraints that can take arbitrary arguments. And I want to create type constraints by combining several other constraints that must all be satisfied (maybe this is a type intersection instead of a union). I have looked at MooseX::Types::Structured and Types::Tiny, but from my initial read of the documentation they didn’t seem to be what I want. Consider a “Password” constraint that must be a) defined, b) a string, c) more than $n characters in length, d) match some $regexp, and e) return true when passed to some callback. I could cook up a single Password type and validate all those characteristics with a single fat constraint. But I want to get specific messages about the exact nature of the constraint failure. Furthermore, I’d like to reuse those sub-constraints to create other constraints. Here’s a hypothetical example of what that might look like (misusing some of the existing sugar for Moose type constraints): subtype Defined as Any where { defined $_ }, message { 'Required' }; subtype String as Any where { not ref $_ }, message { 'Must be a string' }; subtype MinLength as Any where { my $min = shift; length $_ > $min }, message { 'Must be at least...' }; subtype Matches as Any where { my $rx = shift; $_ =~ $rx }, message { 'Must match pattern ...' }; subtype Callback as Any where { my $cb = shift; $cb->($_) }, message { 'Must not have been used before' }; subtype Password, as Defined & String & MinLength(16) & Matches(qr/.../) & Callback(sub{...}); subtype LongPassword, as Defined & String & MinLength(32) & Matches(qr/.../) & Callback(sub{...}); subtype LongNumericPassword, as Defined & String & MinLength(32) & Matches(qr/^\d$/) & Callback(sub{...}); Does that make any sense? Is this a misappropriate use of type constraints? Is this already supported and I’ve just overlooked it? -Jeff