Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl Moose - check if variable is a Moose data type

Tags:

perl

moose

I'm converting a legacy application to use Moose (and Catalyst) and have the following question.

How do I determine Moose Type of data input by a user?

In the following, crude, example I 'submit' multiple queries and validate the data against expected form fields 'id', 'name' and 'email' using the very basic 'validate' method below.

use MooseX::Declare;
class CheckFields
{
    #has '_field' => ( is => 'rw', isa => 'Any' );

    # Fields on form and type to which they must match.
    method fields()
    {
        return [ { name => 'id',    type => 'Int' },
                 { name => 'name',  type => 'Str' },
                 { name => 'email', type => 'Email' }
               ];
    }

    # Dummy form posted requests.
    method queries()
    {
        return [ { 'id'    => 1,
                   'name'  => 'John Doe',
                   'email' => '[email protected]'
                 },
                 { 'id'    => 'John Doe',
                   'name'  => 1,
                   'email' => 'john.at.doe.net'
                 }
               ];
    }

    method validate_fields()
    {
        my $fields = $self->fields();

        # Loop through dummy form post requests
        foreach my $query ( @{ $self->queries } )
        {
            # Validate each field in posted form.
            foreach my $field ( @{ $fields } )
            {
                my $type = $field->{'type'};
                my $name = $field->{'name'};

                my $res = $self->validate->{ $type }( $query->{ $name} );
                print "$name is $res\n";
            }
            print "\n";
        }
    }

    # Very basic, slightly crude field validation.
    method validate()
    {
        return { 'Int'   => sub { my $val = shift; return $val =~ /^\d+$/ ? "ok" : "not ok" },
                 'Str'   => sub { my $val = shift; return $val =~ /^[a-zA-Z\s]+$/ ?"ok" : "not ok"  },
                 'Email' => sub { my $val = shift; return $val =~ /^(.+)\@(.+)$/ ?"ok" : "not ok"  }
               };
    }
}

To test this code simply run...

#!/usr/bin/perl
use Moose;
use CheckFields;

CheckFields->new()->validate_fields();

1;

Is it possible to do something like this where you setup a variable with isa set to 'Any' ...

has '_validate_field' => ( is => 'rw', isa => 'Any' );

...then test for specific types as follows?

$self->validate_field(1);
print $self->validate_field->meta->isa('Int') ? 'Int found' : 'Int not found';

$self->validate_field('ABC');
print $self->validate_field->meta->isa('Int') ? 'Int found' : 'Int not found';

Thank you in advance

EDIT : @bvr - thank you for taking the time to reply, I'm relatively new to Moose so your use of MooseX::Params::Validate might well be the end solution though not quite what I'm looking for. My intention is to be able to report each specific field that is in error rather than reporting a validation failure as a whole. To that end I thought I could define a default, Moose friendly, input holder with isa set to 'Any'. Then 'eval' (or some such) to see if the data conformed to a particular type (Int, Str or some customised type defined by me).

What I was trying to get at with the "$self->validate_field->meta->isa('Int')..." reference was something along the lines of a union in C/C++ where a variable can be of different types - in this instance I'm just trying to test if the data conforms to a certain data type.

like image 798
user647248 Avatar asked Nov 05 '22 01:11

user647248


1 Answers

I am not sure I quite follow last part of your question, but your initial example could benefit from use of MooseX::Params::Validate.

Edit: I made some code to evaluate my suggestion.

use MooseX::Declare;
class CheckFields {

    use Moose::Util::TypeConstraints;
    use MooseX::Params::Validate;
    use Try::Tiny;
    use Data::Dump qw(pp);

    subtype 'Email' 
        => as 'Str' 
        => where {/^(.+)\@(.+)$/};

    method fields() {
        return [
            id    => {isa => 'Int'},
            name  => {isa => 'Str'},
            email => {isa => 'Email'},
        ];
    }

    method queries() {
        return [
            {   'id'    => 1,
                'name'  => 'John Doe',
                'email' => '[email protected]'
            },
            {   'id'    => 'John Doe',
                'name'  => 1,
                'email' => 'john.at.doe.net'
            }
        ];
    }

    method validate_fields() {
        my $fields = $self->fields();

        foreach my $query (@{$self->queries}) {
            try {
                my (%params) = validated_hash([%$query], @{$fields});
                warn pp($query) . " - OK\n";
            }
            catch {
                warn pp($query) . " - Failed\n";
            }
        }
    }
}

package main;

CheckFields->new()->validate_fields();

Other approach I can see is to make a Moose class for data (validation included this way) and check if instance can be created without validation error. Something like this:

use MooseX::Declare;
class Person {
    use Moose::Util::TypeConstraints;

    subtype 'Email'
        => as 'Str'
        => where {/^(.+)\@(.+)$/};

    has id    => (is => 'ro', isa => 'Int');
    has name  => (is => 'ro', isa => 'Str');
    has email => (is => 'ro', isa => 'Email');
}

package main;

use Try::Tiny;
use Data::Dump qw(pp);

my @tests = (
    { id => 1,          name => 'John Doe', email => '[email protected]'},
    { id => 'John Doe', name => 1,          email => 'john.at.doe.net'},
);

for my $query (@tests) {
    try {
        my $person = Person->new(%$query);
        warn pp($query) . " - OK\n";
    }
    catch {
        warn pp($query) . " - Failed\n";
    };
}
like image 166
bvr Avatar answered Nov 09 '22 13:11

bvr