The default respond behavior of the page that handled the response handling the rendering is usually just fine as the docs say. However, when you hit a page that does data validation, this page will return a redo page or a confirm page based on the results of data validation.

The enclosed code shows a Base class with a respond() subroutine that is useful for Data::FormValidator users. It also shows a subclass setting up the proper things in respond_enter so that respond() can "dispatch" appropriately.

sub respond { my $self = shift; warn Dumper $self; return $self unless $self =~ /Validate$/; warn 'checking results'; my $results = Data::FormValidator->check( $self->CGI, $self->dfv_pro +file ); warn Dumper $results; my $response_page; if ( $results->has_missing() || $results->has_invalid() ) { # There was something wrong w/ the data... $self->reflect->addSlots( results => $results ); $response_page = $self->redo_page } else { $response_page = $self->confirm_page } Class::Autouse->autouse($response_page); return $response_page; } ### and now the class that we subclass on the above... package Gimble::Page::Login::Validate; use base qw(Gimble::Page::Base); use Class::Autouse; use Data::Dumper; sub respond_enter { my $self = shift; $self->reflect->addSlots( redo_page => 'Gimble::Page::Login::Redo', confirm_page => 'Gimble::Page::Login::Confirm', dfv_profile => { msgs => { format => '%s' }, required => [ qw(login_name password) ], } ) }

Replies are listed 'Best First'.
Re: A CGI::Prototype respond() subroutine for Data::FormValidator users
by merlyn (Sage) on Feb 04, 2005 at 21:57 UTC
    Class::Autouse->autouse($response_page);
    If you're using CGI::Prototype::Hidden, this mechanism is already in place. No need to pull in the class.

    And for simplicity, I'd write that application class as:

    package Gimble::Page::Login::Validate; use base qw(Gimble::Page::Base); sub redo_page { 'Gimble::Page::Login::Redo' } sub confirm_page { 'Gimble::Page::Login::Confirm' } sub dfy_profile { +{ msgs => { format => '%s' }, required => [ qw(login_name password) ], } }
    No point in re-setting them as slots every time. Just compile a more-or-less constant subroutine to return them. If you would rather assemble the dfy_profile hash only once, use a closure:
    BEGIN { my $d = { msgs => { format => '%s' }, required => [ qw(login_name password) ], }; sub dfy_profile { return $d } }

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

Re: A CGI::Prototype respond() subroutine for Data::FormValidator users
by cbrandtbuffalo (Deacon) on Feb 07, 2005 at 17:11 UTC

    We do something similar. Our validate hook is in 'respond_per_app' like so:

    sub respond_per_app{ my $self = shift; # If we validate, go ahead with page-specific items. return 0 if $self->param($self->config_state_param) and $self->valid +ate(); # If we are here, something failed, so re-render ourselves. return $self; }

    Our assumption is that if something failed in validation, we want to re-render the page we came from with an error message that tells the user what they did wrong. This should also give them back the bad params so they can see what they did wrong.

    We then build the profile for Data::FormValidator on the fly based on the page we're on. The first part is set for the whole app like this:

    sub validator_profile{ # Set some app-wide values for the profile for Data::FormValidator. { validator_packages => [qw( My::Constraints )], msgs => { format => '%s', invalid_seperator => '!', constraints => \%My::constraint_msgs }, }; }

    Then in our config file, we have a hash with the constraint messages, and another that defines the required, optional, etc. fields. When the page loads, we have a 'load_attributes' method that builds out the profile. Then our validate method finalizes the profile and runs it through DFV. If anything fails, we load a data structure with errors to pass back to the template for display. These are displayed at the top of the page when we call ourselves again.

    Building the profile and data structure for DFV is a little more work, but we've found many cases where dependencies are complex or there are extra params, so we can't just pass all params to DFV.

      respond_per_app is not a standard call from CGI::Prototype's activate() method.

      What calls respond_per_app()?

        Whoops, sorry. That method is in CGI::Prototype::Hidden. The Hidden module inherits from CGI::Prototype and adds functionality because it has basic state information (i.e., where am I?). It gets that from a simple hidden field called _state which is the text version of the page name. You can call the hidden field whatever you like; you just need to write it to each page using wrapper.

        The idea is that there could eventually be different modules for different state mechanisms. So there is a simple one for Hidden fields. In the future there may be one for Cookies or MangledURLs, etc.

Re: A CGI::Prototype respond() subroutine for Data::FormValidator users
by metaperl (Curate) on Feb 04, 2005 at 19:13 UTC
    one thing. you can't call methods for slots which are not defined. this means the calls to $self->dfv_profile will not work in classes which do not (and should not) have this defined.

    so I cheat and check for the hash values in the hash which underly my object.

      so I cheat and check for the hash values in the hash which underly my object
      Perl leads you to bad habits. {grin}

      The clean way is to define a backstop method that returns an empty hash. I have lots of those kinds of examples in the source code for CGI::Prototype. No checking required, then.

      -- Randal L. Schwartz, Perl hacker
      Be sure to read my standard disclaimer if this is a reply.