develooper Front page | perl.moose | Postings from October 2011

Design Pattern for Validation

Thread Next
From:
Peter Gordon
Date:
October 11, 2011 07:30
Subject:
Design Pattern for Validation
Message ID:
1318343425.12206.3.camel@qed
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 => 'xxx@yyy.com') ;
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?







Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About