Discussion:
Data::FormValidator status
Dave Howorth
2005-02-02 15:43:38 UTC
Permalink
I thought I'd let anybody who's interested know how far I've got.
Basically, it runs!

I used Maypole code from Jesse Sheidlower, and from Cees Hek and Perrin
Harkins for the error handling. Thanks to them all.

I've patched most of it into Maypole::Model::CDBI for now. I have this
at the top:

use Data::FormValidator;
Maypole::Config->mk_accessors qw(dfv_profiles);

Then here's my do_edit action:


sub do_edit : Exported {
my ( $self, $r ) = @_;
my $profile = $r->config->dfv_profiles->{$r->model_class}
|| $self->dfv_profile;
my $params = $r->params;
my $results = Data::FormValidator->check($params, $profile);
# could warn about unknown fields here if debugging
if ($results->has_invalid or $results->has_missing)
{
my $dfv_msgs = $results->msgs({
format => '%s',
invalid_seperator => "\cZ",
# Yes, you have to misspell separator!!
});
my $errors; # Template variable for error hash
foreach my $missing_field ($results->missing) {
$errors->{$missing_field}->{missing} = 'Missing';
}
my $invalid = $results->invalid;
foreach my $invalid_field (keys %$invalid) {
my @msg_text = split "\cZ", $dfv_msgs->{$invalid_field};
foreach my $failed_constraint
(@{$invalid->{$invalid_field}}) {
$errors->{$invalid_field}->{$failed_constraint} =
shift @msg_text ;
}
}

# Set up to redisplay the completed form with the errors
$r->template_args->{cgi_params} = $params;
$r->template_args->{errors} = $errors;
$r->template('edit');
return;
}

# If we got this far, the results are OK
#
my $valid_params = $results->valid;
my ($obj) = @{ $r->objects || [] }; # get first object
if ($obj) { # editing an existing object
$obj->set(%$valid_params);
$obj->update;
}
else { # creating a new object
$obj = $self->create($valid_params);
}
$r->template('view');
$r->objects( $obj ? [$obj] : []);
}


The point of all the messing with errors is to allow all the error text
to be kept in the templates and not in the code, whilst still allowing
plausible defaults. Here's an extract from addnew.tt to give a flavour.
This is not a finished template, it just shows both possibilities.
coldata.singular is my replacement for colnames.$col.

[%- IF errors.$col %]
<span class="error">
[%- FOREACH err = errors.$col ;
IF err.key == 'missing' -%]
Please provide a value for [% coldata.singular %]<br />
[%- ELSE ;
err.key ; '&nbsp;' ; err.value ; '<br />' ;
END ;
END -%]
</span>
[%- END ;


I've played with two methods of setting profiles. I'm still undecided
about their merits so the code supports both. Either the profiles can be
stored in the config like this:

# Data::FormValidator configuration
use Regexp::Common qw/RE_num_int RE_URI_HTTP/;
my $printable = qr/^[\040-\377\r\n\t]+$/;
# surely there must be a d-fv 'printable' constraint!!?
my $profiles = BeerDB->config->dfv_profiles({
'BeerDB::Pub' => {
required => [qw/name/],
optional => [qw/notes url/],
constraints => {
name => $printable,
notes => $printable,
url => [
{ name => 'format', constraint => 'RE_URI_HTTP' },
]
},
},

'BeerDB::Style' => {
required => [qw/name/],
optional => [qw/notes/],
constraints => {
name => $printable,
notes => $printable,
},
},

... etc ...
});

My other method of loading profiles is via a method in each model
sublass. This has the merit that everything is in the model, which some
people see as a 'good thing'. For example:

package QD1::Coord; # for coords table

sub dfv_profile {
return {
required => [qw/name bio_database_id/],
missing_optional_valid => 1,
constraints => {
id => 'RE_num_int',
name => qr/^[\040-\377\r\n\t]+$/,
bio_database_id => 'RE_num_int',
}
}
}


I've only tested D-FV a very little but it seems to work so far. Its
power is definitely in the profiles. But so is the complexity and I'm
still at the bottom of the learning curve. I haven't worked out how to
make them most convenient and I haven't yet found any kind of dictionary
or cookbook of practical constraints and programming idioms to achieve
particular results. But it's early days.

Cheers, Dave
--
Dave Howorth
MRC Centre for Protein Engineering
Hills Road, Cambridge, CB2 2QH
01223 252960
Tony Bowden
2005-02-02 17:49:33 UTC
Permalink
Post by Dave Howorth
I used Maypole code from Jesse Sheidlower, and from Cees Hek and Perrin
Harkins for the error handling. Thanks to them all.
Looks good.

Are you planning on taking this out of Maypole into a generic "update
Class::DBI objects using Data::FormValidator" that can go to CPAN just
like Class::DBI::FromCGI?

Tony
Sebastian Riedel
2005-02-02 17:51:22 UTC
Permalink
Post by Tony Bowden
Are you planning on taking this out of Maypole into a generic "update
Class::DBI objects using Data::FormValidator" that can go to CPAN just
like Class::DBI::FromCGI?
This could also become a patch for Class::DBI::FromForm. ;)
--
sebastian
Dave Howorth
2005-02-03 11:47:07 UTC
Permalink
Post by Tony Bowden
Are you planning on taking this out of Maypole into a generic "update
Class::DBI objects using Data::FormValidator" that can go to CPAN just
like Class::DBI::FromCGI?
This could also become a patch for Class::DBI::FromForm. ;)
At the moment, I don't see anything worth extracting. It looks to me
like glue between Maypole's request and its view. There are several
equivalent modules that do a similar job for other frameworks , I think.
It's early days but if this hack does ever mutate into a module, I
suspect it belongs under the Maypole top-level.

Cheers, Dave
Tony Bowden
2005-02-03 12:13:23 UTC
Permalink
Post by Dave Howorth
At the moment, I don't see anything worth extracting. It looks to me
like glue between Maypole's request and its view. There are several
equivalent modules that do a similar job for other frameworks , I think.
It's early days but if this hack does ever mutate into a module, I
suspect it belongs under the Maypole top-level.
I disagree.

Having a module that integrates Class::DBI with Data::FormValidator is
of much wider use than Maypole.

Tony
David Naughton
2005-02-04 05:08:17 UTC
Permalink
For a wiki framework I'm developing, I'm considering a port to either
Maypole or Catalyst. I'd greatly appreciate any help in making up my
mind.

One big reason I'm asking has to do with URIs. My wiki supports
hierarchical namespaces, i.e. /path/to/page. I'm planning to use Maypole
or Catalyst to do things like /path/to/page/edit, /path/to/page/history,
etc. where 'edit', 'history', etc. are methods. All pages will be
handled by a default "wiki page" plugin, but I also want to easily
override the default with other plugins for arbitrary URIs.

Maypole?

Maypole seems to be built around the assumption that URIs will be
/path/to/some_class/some_method. Is it easy to override that behavior?
Even if it is, I'm a little uncomfortable with the idea of constantly
overriding a core feature. Am I right to be concerned about this?

Catalyst?

It seems that Catalyst may have the flexibility to support my goals
OOTB. However, the documentation is so sparse that it's hard to tell.
Sebastian, can you provide some more complex examples? I'd also love to
know what features you've stolen from Ruby on Rails, Springs, etc. along
with some examples of their use.

If either of these projects would meet my needs, I'd be happy to
contribute whatever I can.

Thank you,
David Naughton
Sebastian Riedel
2005-02-04 07:16:59 UTC
Permalink
Post by David Naughton
One big reason I'm asking has to do with URIs. My wiki supports
hierarchical namespaces, i.e. /path/to/page. I'm planning to use Maypole
or Catalyst to do things like /path/to/page/edit, /path/to/page/history,
etc. where 'edit', 'history', etc. are methods. All pages will be
handled by a default "wiki page" plugin, but I also want to easily
override the default with other plugins for arbitrary URIs.
It's a bit unusual scheme, but i guess you've got your reasons...
Post by David Naughton
Maypole?
Maypole seems to be built around the assumption that URIs will be
/path/to/some_class/some_method. Is it easy to override that behavior?
Even if it is, I'm a little uncomfortable with the idea of constantly
overriding a core feature. Am I right to be concerned about this?
Yes Maypole is bound to it's uri scheme and expects you to rewrite them
in a parse_path() method or to use mod_rewrite...
The nature of Maypole is to expose tables to the web and let the user
call methods on them (whis mostly just works for most kind of webapps).
Post by David Naughton
Catalyst?
It seems that Catalyst may have the flexibility to support my goals
OOTB.
Definately.
Post by David Naughton
However, the documentation is so sparse that it's hard to tell.
Sebastian, can you provide some more complex examples? I'd also love to
know what features you've stolen from Ruby on Rails, Springs, etc. along
with some examples of their use.
I've stolen lots of features, and am still stealing every day. ;)
Post by David Naughton
From RoR i've mostly stolen the overall structure with multiple
controllers, optional scaffolding and automatically loading of
components.

For example a controller class.

# lib/MyApp/C/Hunter.pm
package Kobai::C::Hunter;

use strict;
use base 'Catalyst::Base';

Kobai->action(
'?create' => sub {
my ( $self, $c ) = @_;
$c->stash->{template} = 'hunter/create.tt';
# do stuff
}
'?delete' => sub {
my ( $self, $c ) = @_;
$c->stash->{template} = 'hunter/delete.tt';
# do stuff
}
'sign-in' => sub {
my ( $self, $c ) = @_;
$c->stash->{template} = 'sign-in.tt';
# do stuff
}

);

You just create this file and Catalyst will automatically find and load
it without any configuration.

This class would magically expose /hunter/create, /hunter/delete
and /sign-in. (? is a placeholder for the actual class's moniker)
Post by David Naughton
From Spring i've mostly just taken ideas on how to apply cool design
pattern like interceptors, decorators and filters.
Which was very simple, thanks to Catalysts easy flow control with
$c->forward().

Kobai->action(
'?create' => sub {
my ( $self, $c ) = @_;
# This is the entry point /prefix/create
return unless $c->forward('!check-roles');
# do stuff
$c->forward('prefix/send-new-password');
}
'!check-roles' => sub {
my ( $self, $c ) = @_;
# check the permissions,
# which are role based in Catalyst
return 1 if world_is_fine();
return 0;
}
'?send-new-password' => sub {
my ( $self, $c ) = @_;
# This action can be used internally and externally!
$c->stash->{template} ||= 'index.tt';
# do stuff
}
'!end' => sub {
my ( $self, $c ) = @_;
# This is a built in private action
# used to render templates at end of request
# Automatically called by Catalyst.

# Set a template if noone else did
$c->stash->{template} ||= 'index.tt';

# This will render the template
$c->forward('Kobai::V::TT');
}
);

Not a perfect example but you get the idea. ;)
! indicates private actions, some are built in...
Post by David Naughton
If either of these projects would meet my needs, I'd be happy to
contribute whatever I can.
Most of the time i'm hanging around in #maypole on irc.perl.org and am
happy to answer all kind of questions.

I also tend to extend the Catalyst Manual on request. ;)

A Catalyst powered wiki would be a great contribution.
Recipes for the manual would be cool too.

Sad thing is that currently no big examples exist, but note that
Catalyst is just a few weeks old. ;)

I'm actually working on something really big, you'll see soon. =)

You should get in contact with Marcus "draven" Ramberg (also on
#maypole), he recently started porting his Mitiki to Catalyst.
--
sebastian
Simon Cozens
2005-02-04 07:58:21 UTC
Permalink
Post by Sebastian Riedel
Yes Maypole is bound to it's uri scheme and expects you to rewrite them
in a parse_path() method or to use mod_rewrite...
Another way to put this is to say that, since you can override parse_path,
Maypole is not bound to an URI scheme at all. ;)
--
She said that she was working for the ABC News,
It was as much of the alphabet as she knew how to use
-- Elvis Costello
Aaron Trevena
2005-02-05 09:40:29 UTC
Permalink
Post by Tony Bowden
Post by Dave Howorth
At the moment, I don't see anything worth extracting. It looks to me
like glue between Maypole's request and its view. There are several
equivalent modules that do a similar job for other frameworks , I think.
It's early days but if this hack does ever mutate into a module, I
suspect it belongs under the Maypole top-level.
I disagree.
Having a module that integrates Class::DBI with Data::FormValidator is
of much wider use than Maypole.
Quite true.

I'd like to use something like this in Class::PINT.

Cheers,

A.

Loading...