Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

CGI::Application design strategy

by BUU (Prior)
on Mar 02, 2004 at 09:34 UTC ( [id://333201]=perlquestion: print w/replies, xml ) Need Help??

BUU has asked for the wisdom of the Perl Monks concerning the following question:

This isn't a perl question, it's more of a design question. Anyways, for example, I have two simple run modes, one called "login" and the other called "login_submit". The "login" mode simply presents the login screen while the "login_submit" mode does the actual processing of the submitted data.

My problem is thus: if I submit 'bad data' to the login_submit form, how do I go back to the 'login' mode AND tell the what he did wrong? If cgi::app had proper mode switching my 'login_submit' mode could just set a param or something in my object and then switch to the login mode, but nope, not allowed to switch run modes.

The only way I see to change modes (aside from the prerun_mode which can only be called from one specific function) is a header-redirect, which is just ugly and inefficient. So whats the easist way of changing modes in mid run?

Replies are listed 'Best First'.
Re: CGI::Application design strategy
by Ctrl-z (Friar) on Mar 02, 2004 at 10:39 UTC
    Run_modes are like the public interface to your App - they dont need to account for everything your app does. Id be more likely to have just 1 runmode - login - that calls private methods depending on whether the appropriate username/password combo was coming in on the CGI params.
    sub login
    {
        my $self = shift;
        my $q    = $self->query(); # ?
        if($q->param("username") && $q->param("password"))
        {
            $self->_verify_login() ;
        }
        else
        {
            $self->_generate_form();
        }
    
        # ... do template stuff here
    }
    
    For login_retry - id probably just put a <TMPL_IF> in my original form template, and reuse it for the retry.
    <tmpl_if name="BAD_LOGIN"> 
         Sorry please try again...  
    </tmpl_if>
        <form>...</form>
    
    Hope that helps



    time was, I could move my arms like a bird and...
Re: CGI::Application design strategy
by dragonchild (Archbishop) on Mar 02, 2004 at 12:44 UTC
    I use three runmodes for this purpose.
    • login() presents the login form. It also optionally accepts an error message which it passes off to HTML::Template.
    • validate() is the runmode executed by the login form. If the login is unsuccessful, it returns a call to login() with the appropriate error message. If it is successful, it calls home(). validate() doesn't display anything of its own.
    • home() displays the home page. It's a passthru to HTML::Template, with the appropriate values for the specific user set. (It doesn't do any processing of its own - all the user processing is done in a base class.)

    I hope that helps.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: CGI::Application design strategy
by Corion (Patriarch) on Mar 02, 2004 at 09:38 UTC

    I didn't find anything wrong with the run mode switching in CGI::Application, but I didn't do authentication through it.

    You say in your intro, that you have two run modes, "login" and "login_submit", but later on, you mention a third run mode, which I would calll "login_retry". I would redirect the user from a failed login to the login_retry run mode, either internally (by changing the used template) or externally, by sending the correct location: redirect.

Re: CGI::Application design strategy
by larsen (Parson) on Mar 02, 2004 at 15:29 UTC
    I'm used to write two runmodes for every panel in my application. In your case, they would be login and login_update. Given a method redirect to set the appropriate headers, the code could be the following (not-so-pseudo-code):
    # Does nothing except printing the form. # Using sessions you can pass to it an error_message, # for example. # sub login + { my $self = shift; + my $tmpl = $self->load_tmpl( 'login' ); + return $tmpl->output(); + } sub login_update + { my $self = shift; + my $q = $self->query; # Fetch username and password from the form # if ( PASSWORD CORRECT ) { $self->redirect( { rm => 'first_panel' } ); } else { # It could store some useful info in a session, # for example the error message. # $self->redirect( { rm => 'login' } ); } return; }
    I use this technique a lot, and it has proven its flexibility and efficiency.
      I realise you say the above is psuedo code but I have to ask. How do you normally represent:
      $self->redirect( { rm => 'first_panel' } );
      In your normal applications? Do you have a template method like the following (this is naturally untested):
      sub redirect { my $self = shift; my $mode = shift; my $new_url = $self->param('app_url') . $mode->{'rm'}; $self->header_type('redirect'); $self->header_props(-url=>$new_url); return "Redirecting to $new_url"; }
      I ask as I have wanted to switch between run modes based on data and have had to stick with these kind of methods. Either that or I call the runmode method directly.

      Just a quick question - I'm sure the answer is yes but I thought I would ask anyhoo.
        The answer is yes. My actual code is pretty similar to yours.
        sub build_redirect { my ($self, $form, $script) = @_; $form ||= {}; + my $q = $self->query; my $uri = URI->new( $script || $q->script_name ); + $uri->query_form(%$form); + return $uri->as_string; } sub redirect { my $self = shift; + $self->header_add( -uri => $self->build_redirect( @_ ) ); + $self->header_type('redirect'); + }

        If you want to switch run modes in mid-stream your current run mode just has to call the runmode that you want.

        There is never any need for a normal CGI::Application based app to use redirect headers to access another runmode unless the resource has actually moved (e.g. has a new URL)

        Here is larsen's example modified updated to remove the redirect.

        # Does nothing except printing the form. # Using sessions you can pass to it an error_message, # for example. # sub login + { my $self = shift; + my $tmpl = $self->load_tmpl( 'login' ); + return $tmpl->output(); + } sub login_update + { my $self = shift; + my $q = $self->query; # Fetch username and password from the form # if ( PASSWORD CORRECT ) { return $self->first_panel; } else { # It could store some useful info in a session, # for example the error message. # return $self->login; } # You have a problem if this return ever actually executed #return; }
        --
        Clayton
Re: CGI::Application design strategy
by zby (Vicar) on Mar 02, 2004 at 12:21 UTC
    In my app I just call the other mode from inside of my analog of login_submit:
    sub login_submit{ my $self = shift; . . . if(badlogin()){ return $self->login(); } }
    Update: Added 'return' (following rchiav).
      Since your login_submit sub has to return what's to be displayed to the browser, don't you have to do
      if(badlogin()){ return $self->login(); }
      ?
        No, not necessarilly, if $self->login returns to a place where at some later point the output is returned to CGI::A then the return is not strictly necessary.

        Believe me, I know, I got bitten by that one yesterday.

        jdtoronto

        Yeah - you are right. That's how I did that. I can't comment on what jdtoronto wrote.
Re: CGI::Application design strategy
by esskar (Deacon) on Mar 02, 2004 at 10:30 UTC
    i would definitly go with location-redirect using the "Status: 302 Moved" and the "Location" header-field. There is nothing wrong with that and inefficienty can not be your reason of not doing it!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://333201]
Approved by Enlil
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (7)
As of 2025-03-18 13:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    When you first encountered Perl, which feature amazed you the most?










    Results (57 votes). Check out past polls.