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 !~ '@') {
$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 $email->error ;
# And if we insist on proceeding, the following line will fail.
my $personBad = Person->new(email => $emailBad) ;
As a note, I tried setting this up without the role, and just having two
classes, Email and EmailError.
The technique fails because in Person->new, the object passed, namely
$emailBad, passes the 'isa Email' test
and is accepted. The technique used above ensures that the two classes
are completely independent, and so the 'isa Email' fails.
What I would like is some feedback
a) what are the pitfalls of this design.
b) are there any better designs?
_______________________________________________
Perl mailing list
[email protected]
http://mail.perl.org.il/mailman/listinfo/perl