[Catalyst] RFC: Catalyst::Plugin::AutoValidate

Markus Holzer holli.holzer at googlemail.com
Wed Apr 9 10:06:04 BST 2008


Hello fellows, please bear with me, as this is my first contribution to 
Catalyst.

 From the pod: This Plugin validates request parameters against a 
specification in a file that directly relates to the requests path (like 
the .fb files when using Formbuilder).

I'm curiuos of your opinions.

Thank you,
Holli

_______________________________________________________


package Catalyst::Plugin::AutoValidate;

use warnings;
use strict;

use YAML qw( LoadFile );

use Catalyst::Request;
use Config::Validate;
use Data::Dumper;
use NEXT;

our $VERSION = 0.01;

sub prepare {
    my $class = shift;
    my $c     = $class->NEXT::prepare( @_ );
   
    my ( $config, $rpath, $spec_root, $spec_file, $validation_spec, 
$validator );
   
    # config for this package
    $config    = $c->config->{'Catalyst::Plugin::AutoValidate'};
    # the request path
    $rpath     = $c->request->path || $config->{index_spec} || 'index';
    # root dir to look for specifications
    $spec_root = $config->{spec_root} || "$c->{home}/cvspec";
    # spec file, deduced from path
    $spec_file = "$spec_root/${rpath}.".($config->{spec_extension}||"cvs");
   
    # check if there are custom types configured
    if ( $config->{types} )
    {
        # add to the validation module
        if ( ref( $config->{types} ) eq "ARRAY" )
        {
            for ( @{$config->{types}} )
            {
                Config::Validate::add_default_type(%$_);
            }
        }
        else
        {
            Config::Validate->add_default_type(%{$config->{types}});
        }
       
        # move the types, so they won't get reprocessed another time
        $config->{xtypes} = delete $config->{types};
    }
   
    # there is no matching spec file
    unless ( -e $spec_file )
    {
        # that's ok or not, depending if we are paranoid
        $c->request->{validated} = $config->{paranoid} ? 0 : 1;
    }
    else
    {
        # load spec and validate the request against it
        $validation_spec = LoadFile( $spec_file );
        $validator       = Config::Validate->new( schema => 
$validation_spec );
       
        # will die if validation fails
        eval { $validator->validate( config => $c->request->{parameters} 
) };

        unless ( $@ )
        {
            $c->request->{validated} = 1;
        }
        else
        {
            $_ = "$@"; s/^.+?validate\(\): //; s/instead.+//;
            $c->request->{validation_error} = "$_";
            $c->request->{validated} = 0;
        }
    }
   
    return $c;
}
1;

__DATA__
=head1 NAME

Catalyst::Plugin::AutoValidate - Catalyst-Plugin for easy automatic 
validation of Request-Parameters

=head1 VERSION

Version 0.01

=head1 SYNOPSIS

This Plugin validates request parameters against a specification in a 
file that directly
relates to the requests path (like the .fb files when using Formbuilder).

For the "heavy lifting" it uses 
L<http://search.cpan.org/~cmo/Config-Validate-0.2.6/lib/Config/Validate.pm>;
All validation options from that module are supported, as they get 
simply passed through.

 # application code
 package MyApp;
 use strict;
 use warnings;

 use Catalyst qw(AutoValidate);

 # MyApp::Controller::Root.pm
 # check parameters and display error message when
 # appropriate. Does NOT RUN THE CONTROLLER in that case,
 # otherwise proceed with controller
 sub begin : Private
 {
     my ($self, $c) = @_;

     $c->response->body( $c->request->{validation_error} ),
     $c->detach
         unless $c->request->{validated};
 }
 
 # $c->{home}/cvspec/math/multiply.cvs (YAML)
 a:
     type: integer
 b:
     type: integer
 
 # MyApp::Controller::Math.pm
 sub multiply : Local
 {
     my ($self, $c) = @_;
     # this is safe because Autovalidate ensures both are integers
     $s->stash->{result} = $c->req->param('a') / $c->req->param('a')
 }


=head1 CONFIGURATION

 __PACKAGE__->config(
 'Catalyst::Plugin::AutoValidate' =>
 {
     # root dir for looking up specifications
     spec_root => '',
    
     # when set, all requests without specification die
     paranoid  => 0,
    
     # the name of the index spec (for empty paths)
     index_spec => 'index',

     # extension for spec-files
     spec_extension => 'cvs',
    
     # custom types
     types =>
     [
         # generator sub that generates a closure, could come in handy 
for lookups
         {
             name => 'custom',
             validate => sub {
                 my $lookup = { exists => 1 };
                 return sub { die "Not in lookup table!" unless 
$lookup->{$_[1]} };
             }->()
         },
         # or easier
         sub { die "Doesnt match!" unless $_[1] =~ /[abc]/ };
     ]
}
);

=cut


=head1 AUTHOR

Markus Holzer, C<< <holli.holzer at googlemmail.com> >>

=head1 BUGS

Please report any bugs or feature requests to 
C<bug-catalyst-plugin-AutoValidate at rt.cpan.org>, or through
the web interface at 
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Catalyst-Plugin-AutoValidate>.  
I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.




=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Catalyst::Plugin::AutoValidate


You can also look for information at:

=over 4

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Catalyst-Plugin-AutoValidate>

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/Catalyst-Plugin-AutoValidate>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/Catalyst-Plugin-AutoValidate>

=item * Search CPAN

L<http://search.cpan.org/dist/Catalyst-Plugin-AutoValidate>

=back


=head1 ACKNOWLEDGEMENTS


=head1 COPYRIGHT & LICENSE

Copyright 2008 Markus Holzer, all rights reserved.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.


=cut




More information about the Catalyst mailing list