Re: Design Pattern for Validation

[email protected] (Stevan Little)
Newsgroups perl.moose
Message-ID <[email protected]>
On Oct 11, 2011, at 10:30 AM, Peter Gordon wrote:
> I am trying to find a decent design pattern for Moose validation of
> user input. All the Moose examples I have found either assume that 
> the data is correct or else dies.
> 
> Example:
> package Address ;
> use Moose;
> use Moose::Util::TypeConstraints;
> 
> subtype 'Email',
>      as 'Str',
>      where { $_ =~ m!@! },
>      message { "The email you provided, $_, was not a valid email" };
> 
> package Person ; 
> use Moose ; 
> has 'email' => (is => 'rw', isa => 'Email', required => 1) ;
> 
> package main ;
> 
> my $f = Person->new(email => 'xa.com') ; 
> 
> The above pattern is much like the examples in the manual. 
> 
>> From an OOP perspective, it seems to me that the validation performed
> by the subtype should be part of the class and not something that is
> just floating around. There is also no reasonable way to trap the
> error, and report back to the user that there was an error. And I am
> not a fan of try/catch.
> 
> I have come up with the following design pattern to allow validation.
> The idea is to create a class, Email, which accepts any data. If the
> data is good, error is set to 0.  If the error is set to 1, it also
> changes the package name so that if it is passed onwards the program
> dies.
> 
> package EmailRole ; 
> use Moose::Role ; 
> 
> has 'email' => ( is => 'rw', isa => 'Str',required => 1) ;   
> has 'error' => ( is => 'rw', isa => 'Str', default => 0) ;   
> 
> sub BUILD { 
>    my $my = shift ;
>    if ($my->email !~ m!@!) { 
>        $my->error(1) ; 
>        bless $my, 'EmailError'; 
>    } 
>    return $my ;
> } 
> no Moose ; 
> 
> package Email ;
> use Moose ; 
> with 'EmailRole' ; 
> 
> package EmailError ;
> use Moose ; 
> with 'EmailRole'; 
> 
> no Moose ; 
> 
> package Person ; 
> use Moose ; 
> has 'email' => ( is => 'ro', isa => 'Email' ) ; 
> 
> package main ; 
> my $emailGood = Email->new(email => '[email protected]') ;
> my $personGood = Person->new(email => $emailGood) ;
> 
> my $emailBad = Email->new(email => 'xxx') ; 
> print "Bad Person" if $emailBad->error ; 
> # And if we insist on proceeding, the following line will fail.
> my $personBad = Person->new(email => $emailBad) ;
> 
> What I would like is some feedback 
> a) what are the pitfalls of this design.
> b) are there any better designs?
> 

Peter,

Validation is different then type checking, which is why you are running into this issue. Putting the validation in the type allows a certain kind of re-use that goes beyond just using it in a class. Remember that Moose types are themselves objects which you can call methods on, etc.

I recommend looking at something like Data::Manager (https://www.metacpan.org/module/Data::Manager) or the lower level Data::Verifier (https://www.metacpan.org/module/Data::Verifier) which will allow you to use Moose types to test things and capture errors effectively. It also is a great illustration of using Moose types in a totally different context.

- Stevan
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.