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, $v +alidator ); # 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}||"c +vs"); # 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 => $validatio +n_spec ); # will die if validation fails eval { $validator->validate( config => $c->request->{parameter +s} ) }; 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 va +lidation 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 Formbuilde +r). For the "heavy lifting" it uses L<http://search.cpan.org/~cmo/Config-V +alidate-0.2.6/lib/Config/Validate.pm>; All validation options from that module are supported, as they get sim +ply 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 $looku +p->{$_[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-Au +toValidate 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-AutoValidat +e> =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 i +t under the same terms as Perl itself. =cut


holli, /regexed monk/
  • Comment on [RFC: Catalyst::Plugin::AutoValidate] Easy request parameter validation with Catalyst
  • Download Code

Replies are listed 'Best First'.
Re: [RFC: Catalyst::Plugin::AutoValidate] Easy request parameter validation with Catalyst
by Your Mother (Archbishop) on Apr 10, 2008 at 06:58 UTC

    One off the bat. Same one expressed on the Catalyst list. If it's specific only to controllers it should be a controller base, not a plugin. Plugins should only be plugins if they cannot be generalized to work in any other fashion.

    Use c3 if you can. NEXT is deprecated for new development.

    Defaulting to "index" makes me a bit queasy. Index is peer-pressure deprecated and too much voodoo (automatic action) makes me uncomfortable with a package.

    I hate seeing $_ in loops that call subroutines. Declared temp vars are cheaper than debugging.

    If you do stick with a plugin, "Catalyst::Plugin::AutoValidate" in the config should just be "Plugin::AutoValidate."

    You're using "/" for paths. Many devs are running Cat on Windows. You just broke their applications. :) Path::Class is guaranteed to be there for Cat stuff.

    Setting hash keys against the request object strikes me as really bad... Just gut feeling.

    So, how's that for encouragement? In fact, I hope you do continue with it. One thing I have never been satisfied with is validation stuff and the more competing entries the better as far as I'm concerned. Keep working on it!

      You're using "/" for paths. Many devs are running Cat on Windows. You just broke their applications. :) Path::Class is guaranteed to be there for Cat stuff.

      What about Catalyst causes it to break on Windows from using a solidus for paths? Most applications have no problem using it on Windows. IIRC the system internally even supports it, although the command interpreter does not. In any case, I've been using '/' as a path separator on Windows for years.

      perl -e "print $_ . qq{\n} for glob q{'c:/documents and settings/*'}"
      perl -e "opendir my $d, 'c:/documents and settings'; print $_ . qq{\n} + for readdir $d;"
      perl -e "open my $f, '<', $ENV{systemdrive} . '/windows/system32/drive +rs/etc/hosts'; print $_ while <$f>;"
      perl -e "$ENV{PATH} = 'c:/windows/'; system( 1, 'notepad.exe', 'c:/win +dows/system32/drivers/etc/hosts' );"

      You might notice that last one is an example of the system using a path with '/' to find an application and launch it and that program using its argument with '/' as the separator to open the file. It's not subject to any special reworking by the Perl runtime.

      Having a portable path-handling library helps with platforms like MacOS Classic, VMS, and others. Any version of Windows that's still getting security updates from MS shouldn't need any special treatment for the path separator, though. That volume enumeration by letter in the paths is still oddball these days, but that's supposed to go away in the near future.

      If an application on Windows is broken by a path with '/' as the separator, there's a problem, IMO, in that application. The Windows CLI itself gets excused for mistakes of the past. Even it does the right thing with proper convincing:

      dir "c:/windows/system"

        I actually knew that. What I don't know is what happens when you mix the two? "c:\windows\system32" . "/drivers/etc/hosts" for example. I can't believe that would work(?). Better to abstract them out and let the underlying stuff do what it really wants. Portable by design instead of chance.

      I gently disagree :) At least with your first points. AutoValidate isn't controller specific. All it does is looking at the request and leaving it's opinion in the request object by setting "validated" and "validation_error". Granted, validattion is a task which is traditionally done (if you can talk about traditions anyway here), but this module is an attemt to abstract that away from the controllers as much as possible. Also, being it a plugin i can enable it in a single place and have it active everywhere.

      I wasn't sure about C3, the Catalyst::Plugin::C3 says it's still experimental. But as it seems it's a simple replacement, when NEXT is ever abandoned.

      Defaulting to index is a sensible thing to do, I think. I need a file to put the spec in (here index.vcs). Without index that would be ".vcs" which is much worse.

      I agree with the rest. I'll make the paths compatible, turn the keys into accessors and replace $_.

      Thanks for your input mum :)

      Update: new version as per YourMothers suggestion:


      holli, /regexed monk/