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 => 'x...@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? >
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