Hello everyone...

I've posted here the relevant bits of a discussion that Jesse and I had in private email re: run modes, form validation and object composition. He asked me to pass it along to the list.

Cheers,
Richard

/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-

Subject: playing around with CGI::Application for the first time...
Date: Fri, 01 Aug 2003 15:32:17 -0400
From: Richard Dice <[EMAIL PROTECTED]>
To: [EMAIL PROTECTED]

Jesse:

Hello again... I hope all has been well with you...

So, I'm web programming again. *sigh* :-)  It's a temporary gig until
the 2nd year of my MBA starts up again in about 5 weeks, though it could
continue into the school year.  It gives me a chance to finally give
CGI::Application a good once-over.  I'm having a lot of fun with it so
far, but there are things I'm wondering about.  The end-result of the
wondering leaves me with 3 options:

  * either I "don't get it" with regards to idiomatic CGI::Application
    programming, or

  * some features I think would be cool are missing, and therefore I'll
    have to put them into my stock subclass, or

* I could have some significant patches for you in a week or so :-)

What this is regarding is form validation -- but not just the
"mechanics" of validating a form against a regex or coderef or something
like that.  There are plenty of modules on CPAN I can use to do that.
Rather, my interest is at a higher level of abstraction.  That is, the
automatic management of run-modes given form validation as an integral
step in a web application.

Basically, my thought is that a "web application" split over several
pages usually entails a "form" split over several pages as well.  And
that you can't do Step N of the form before you successfully submit Step
(N-1) of the form.  Each of these corresponds to a run mode in
CGI::Application.

I'll make this a bid more concrete now...

I'm imagining a "setup" method in a CGI::Application-derived class that
might look like this:

sub setup {
    my $self = shift;
    $self->start_mode('mode1');
    $self->mode_param('rm');
    $self->run_modes(
                     .
                     .
                     .
                     'mode2' => {
                         defineform => 'defineform_mode2',
                         validate => 'validate_mode2',
                         run      => 'run_mode2'
                         },
                     'mode3' => {
                         defineform => 'defineform_mode3',
                         validate => 'validate_mode3',
                         run      => 'run_mode3'
                         },
                     'mode4' => {
                         defineform => 'defineform_mode4',
                         validate => 'validate_mode4',
                         run      => 'run_mode4'
                         },
                     .
                     .
                     .
                     );
}

.
.
.



sub defineform_mode3 {

#
# create some kind of data definition of the segment of the form
# that would be seen in mode 3.  This data definition should have a 1:1
# correspondance between definition statements and form input elements.
# The definition would include
#
#   * parametric name of form element
#   * form element type (i.e. appropriate HTML tag)
#   * HTML attributes for form type (e.g. LENGTH, MAXSIZE, etc.)
#   * validation rule for each... basically, a hook into one of the
#     respectable form validation modules on CPAN
#
# When run, the definition would be jammed somewhere into the $self
#

}

sub validate_mode3 {

#
# Inspect the form element definitions in $self, compare against
# actual form submission, collect any errors in self
#

}

sub run_mode3 {

#
# do what you'd normally do in "do_mode3" (per existing nomenclature)
# *but* also provide some utility methods so that you can generate
# form input tags automatically using the appropriate CGI.pm methods,
# given the data definition file
#
# Note that you are now responsible for putting TWO runmode tags
# into your form:
#
#    * one corresponding to the run mode you want to go to upon
#      form submission
#    * another corresponding to the current run mode
#

    return $tmpl->output;  # probably how you'd finish things off here,
                           # just like you'd do right now

}

Okay... I hope that wasn't too scary. :-)

So now, the idea is this...

Upon invocation of a CGI::Application-derived program, two run modes are
figured out:  the run-mode that this coming from, and the run mode that
is supposed to go to ASSUMING THE STUFF THAT WAS SUBMITTED IS ALL OKAY!

Assume that we are coming from run mode 3, and we want to go to run mode
4.  The sequence of subroutine dispatches will be:

 * call defineform_mode3 (the definition of the form that we just came
   from)
 * call validate_mode3 (now, we validate what's in $self->query against
   the definition of what it should match)
 * if everything is okay
      * call defineform_mode4
      * call run_mode4, where two hidden input tags will be put into
        the form:  current mode = 4, next mode =5
 * else there are errors from validate_mode3
      * call run_mode3, with error information sufficient
        to allow it to populate error parameters in the template
        that should flag the user as to what they did wrong, and they
        can try it again
      * note that current mode hidden form tag = 3, next mode hidden
        form tag = 4

Note that I envision a .tmpl file accompanying this that might look like...

<form name=formname3 method=post action=%SELF_URL%>

%THIS_RUN_MODE%      <!-- a hidden form field with value = 3 -->
%SUBMIT_TO_RUN_MODE% <!-- a hidden form field with value = 4 -->

<pre>
What is your name? %NAME_INPUT%  <!-- CGI.pm will generate this tag
given the form input definition found in defineform_mode3 -->

What is your favourite colour? %COLOUR_INPUT% <!-- CGI.pm, from
formdefinition_mode3 -->

What is the air speed velocity of an unladden swallow?
%TOUGHQUESTION_INPUT% <!-- CGI.pm, from formdefinition_mode3 -->

%SUBMIT_INPUT% <!-- CGI.pm, from formdefinition_mode3 -->

</pre>

</form>

I want CGI.pm to generate all this stuff so that HTML escaping of form
data can be handled by someone who is not I.  Other reasons too.  (Maybe
I should start the form definition with a CGI.pm call... hmm... not sure.)

Make any sense?  Perhaps this kind of approach is either unneeded with
CGI::Application for reasons I don't yet perceive, or it is done using
methods I don't yet perceive.

Another (more or less unrelated) thought... regarding $self->load_tmpl()
method:

   A) why isn't the created $tmpl_obj "sucked" into $self as well, akin
      to how the CGI query object is embedded into self in the form of
      $self->query?  I'd like to have a $self->template to do my
      object composition off of, rather than having a freefloating
      $tmplobj
   B) why does this template object necessarily mean an HTML::Template
      object?  I mean, I like HTML::Template fine, but it seems to me
      like this should be a merely a default template type.  Others
      could/should be supported. (e.g. Template Toolkit)

Note that *everything* I mentioned in here, I'm very willing to code up
and submit to you in short order.  (within a week or so)

In other news...

Toronto.pm has submit a bid for YAPC::NA 2004. Check it out...

http://to.pm.org/cgi-bin/kwiki/index.pl?BidProposal

I'm the prime mover behind the bid, so we'll see whether or not I've
been successful in "winning" myself a vast amount of uncompensated-for
work and responsibility soon. :-)  If we get it, it would be very nice
to see you around here.  Regardless, hopefully we can bump into each
other again soon.

Cheers,
Richard

/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-

From: "Jesse Erlbaum" <[EMAIL PROTECTED]>
To: "'Richard Dice'" <[EMAIL PROTECTED]>
Subject: RE: playing around with CGI::Application for the first time...
Date: Sat, 2 Aug 2003 00:19:04 -0400

Hi Richard!

It's great to hear from you. I'm glad to see you're doing well.

Last things first:  Good luck on the YAPC::NA 2004 bid!  If you get it
I'll definitely go.  I've never been to one, and this is close enough
that I couldn't avoid it.

On to the CGI-App questions....

First off, I highly recommend you subscribe to the (low volume) mailing
list and re-post your message.  You will get many excellent replies and
suggestions.  To subscribe, send email to:

[EMAIL PROTECTED]

In lieu of many excellent replies from list members, here is my merely
adequate response.  ;-)


> What this is regarding is form validation -- but not just the > "mechanics" of validating a form against a regex or coderef > or something > like that. There are plenty of modules on CPAN I can use to do that. > Rather, my interest is at a higher level of abstraction. > That is, the > automatic management of run-modes given form validation as an > integral > step in a web application.

This is not built into CGI::Application.  I also believe that it may not
be in the spirit of CGI-App for reasons I will try to describe.  Before
I launch into "how I do it", one of the members of the CGI-App mailing
list is Mark Stosberg, maintainer of the Data::FormValidator perl
module.  These two modules, used together, is a very popular solution.
I am sure that he (and the lists members) will have many interesting
suggestions for your problem as a result.

As far as what I usually do, CGI-App is a very simple module.  It
doesn't include any additional functionality (excepting HTML::Template,
which I'll talk about later).  Instead, it stays out of the way and
allows you to implement things as you see fit.

You envision three "sub modes" per run-mode:  "define", "validate", and
"run".  It is true that these are general functions of a web app, but
not specifically functions of a CGI-App run-mode.  Generally, I
implement these in-line of each run-mode method.  For example, imagine
the following mode handles the submission from an edit form:

  sub setup {
    my $self = shift;
    $self->run_modes(
      list_thingies => 'list_thingies',
      edit_thingy   => 'edit_thingy',
      update_thingy => 'update_thingy',
    );
  }


sub update_thingy { my $self = shift;

    # Return to edit run-mode if input is bad
    return $self->edit_thingy("Bad input")
      unless ($self->input_is_valid);

    # Update thingy in database (private method)
    $self->_update_thingy();

    # Return to the list view
    return $self->list_thingies("Thingy saved!");
  }


As you can see from this example, there is one run-mode which is called when you want to update a "thingy" in the database. This one run-mode is responsible for validation and directing traffic forwards and backwards.

You will also notice a rather odd construct which is common for
"save"-type modes -- this mode does not really generate its own HTML.
Instead, if successful, it merely redirects the user to the list view
(mode: "list_thingies").  This is not the usual behavior.  Most
run-modes will create and return their own HTML (usually via a
templating system, but that's another story), and not return the output
of another run-mode.  However, I wanted to give you an interesting case,
so there you go.

> <form name=formname3 method=post action=%SELF_URL%>
>
> %THIS_RUN_MODE%      <!-- a hidden form field with value = 3 -->
> %SUBMIT_TO_RUN_MODE% <!-- a hidden form field with value = 4 -->

Two quick comments:  First, you should probably use the more standard
HTML::Template syntax -- <tmpl_var THIS_RUN_MODE>, instead of the
'%BLEH%' format.  The latter was for backwards compatibility to a
templating system which hasn't been used in many years.

Second:  It is not usually necessary to dynamically populate the
run-mode in the template.  Typically, clicking on a button will always
send you to the same mode.  As a result, you can statically define your
run-modes in the template:

  <form name="update_thingy_form" method="POST">
  <input type="hidden" name="rm" value="update_thingy">
  <!-- form fields here -->
  <input type="submit">
  </form>


> Another (more or less unrelated) thought... regarding > $self->load_tmpl() > method: > > A) why isn't the created $tmpl_obj "sucked" into $self as > well, akin > to how the CGI query object is embedded into self in > the form of > $self->query? I'd like to have a $self->template to do my > object composition off of, rather than having a freefloating > $tmplobj

Not everybody uses templates.  I didn't think it appropriate to assume.
Also, there are many run-modes which might not have a template.  For
instance, an HTTP redirection mode.


> B) why does this template object necessarily mean an > HTML::Template > object? I mean, I like HTML::Template fine, but it seems to me > like this should be a merely a default template type. Others > could/should be supported. (e.g. Template Toolkit)

It is supported!  You should ask this question on the list.  There are
many TT users of CGI-App.  In a nutshell, the solution is to override
load_tmpl() to create an return a TT object instead of H::T object.


Do post your message to the list! It will get everybody's creative juices flowing.


TTYL,


-Jesse-

/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-

Date: Mon, 04 Aug 2003 11:59:39 -0400
From: Richard Dice <[EMAIL PROTECTED]>
To: Jesse Erlbaum <[EMAIL PROTECTED]>
Subject: Re: playing around with CGI::Application for the first time...

> On to the CGI-App questions....
>
> First off, I highly recommend you subscribe to the (low volume) mailing
> list and re-post your message.  You will get many excellent replies and
> suggestions.  To subscribe, send email to:
>
>   [EMAIL PROTECTED]

I shall do so!

> This is not built into CGI::Application.  I also believe that it may not
> be in the spirit of CGI-App for reasons I will try to describe.  Before
> I launch into "how I do it", one of the members of the CGI-App mailing
> list is Mark Stosberg, maintainer of the Data::FormValidator perl
> module.  These two modules, used together, is a very popular solution.
> I am sure that he (and the lists members) will have many interesting
> suggestions for your problem as a result.
>
> As far as what I usually do, CGI-App is a very simple module.  It
> doesn't include any additional functionality (excepting HTML::Template,
> which I'll talk about later).  Instead, it stays out of the way and
> allows you to implement things as you see fit.
>
> You envision three "sub modes" per run-mode:  "define", "validate", and
> "run".  It is true that these are general functions of a web app, but
> not specifically functions of a CGI-App run-mode.  Generally, I
> implement these in-line of each run-mode method.  For example, imagine
> the following mode handles the submission from an edit form:
>
>   sub setup {
>     my $self = shift;
>     $self->run_modes(
>       list_thingies => 'list_thingies',
>       edit_thingy   => 'edit_thingy',
>       update_thingy => 'update_thingy',
>     );
>   }

I can see how ->update_thingy can send-back to ->edit_thingy, but how
should ->edit_thingy send-forward to ->update_thingy?  Explicity
faux-redispatch via a "return $self->update_thingy()" call typed
(hard-coded?) directly into ->edit_thingy?  If the hidden form input rm
== 'edit_thingy' within ->edit_thingy's output HTML, then I can't see
another way that it would happen.  Of course, this could be how you
meant for it to happen.   In which case, you would either have to
hard-code in the redispatch to ->update_thingy directly within
->edit_thing, or you could do so parametrically, with some kind of
parameter (e.g. <input type=hidden name=next_run_mode
value=update_thingy>)) and a generalized redispatch technique, e.g.
$self->redispatch($self->next_run_mode)), or maybe even
$self->next_run_mode->redispatch().  Of course, this call doesn't exist
yet, but it could via a CGI::Application subclassing.

In either case (hardcode or parametrical ->redispatch() ) there would
need to be an explicit if-then test within ->edit_thingy to see if this
invocation of ->edit_thingy came without a form submission (i.e. the
"first time" the user has come to this run mode) or with a form
submission (i.e. submission of an earlier invocation of the form
displayed by ->edit_thingy).

If there was a form submission, then ->edit_thingy sends-forward to
->update_thingy.  And within ->update_thingy, do a check on form
validation -- if validation successful, proceed to main body of
->update_thingy; else, return to ->edit_thingy.

Isn't this the same as I proposed with my "sub run mode" approach?
Except here, instead of my explicit identification of 3 sub-run-modes
with dispatch logic handled implicitly with CGI::Application, there is
just a lot of if-then-else checking spread across the 2 run modes
involved, and explicit redispatching?

[
Note that I don't feel as though the existing ->run_modes approach and
syntax should *completely* be replaced with my 3-sub-run-mode approach.
  I think this should be an if/else situation... if "classic" style
syntax, then classic style dispatch.  If "Richard" style syntax, then
"Richard" style dispatch.
]

My main concern was that I wanted to keep all information regarding a
form (including validation information) within the method responsible
for generating the form.  This way, I could edit one thing in one place
without concern for screwing things up anywhere else.  In your example
(if I understand/guess correctly) validation info regarding the fields
of the form displayed by ->edit_thingy would have to be placed within
->update_thingy.  That causes me to worry about maintainability.

Though, I can see that this could still be addressed with an
if-statement within ->edit_thingy and a forced dispatch to
->update_thingy.  But if this is the case, why "register" run modes at
all?  Or at least, why register ->update_thingy?  Wouldn't it then be
entirely an auxillary method to ->update_thingy then?  The run mode
dispatcher of CGI::Application will *never* call this method!

>   sub update_thingy {
>     my $self = shift;
>
>     # Return to edit run-mode if input is bad
>     return $self->edit_thingy("Bad input")
>       unless ($self->input_is_valid);
>
>     # Update thingy in database (private method)
>     $self->_update_thingy();

Private method... as compared to what?  The registered run mode
handlers?  Aren't they all in fact  "private"? (E.g.  none are called
from the .pl application -- only the ->new and ->run methods should be
called from the program itself.)

>     # Return to the list view
>     return $self->list_thingies("Thingy saved!");
>   }
>
>
> As you can see from this example, there is one run-mode which is called
> when you want to update a "thingy" in the database.  This one run-mode
> is responsible for validation and directing traffic forwards and
> backwards.
>
> You will also notice a rather odd construct which is common for
> "save"-type modes -- this mode does not really generate its own HTML.
> Instead, if successful, it merely redirects the user to the list view
> (mode: "list_thingies").  This is not the usual behavior.  Most
> run-modes will create and return their own HTML (usually via a
> templating system, but that's another story), and not return the output
> of another run-mode.  However, I wanted to give you an interesting case,
> so there you go.

Noted.  I think I have a much better idea of how this stuff is
"supposed" to happen than I did before.

>><form name=formname3 method=post action=%SELF_URL%>
>>
>>%THIS_RUN_MODE%      <!-- a hidden form field with value = 3 -->
>>%SUBMIT_TO_RUN_MODE% <!-- a hidden form field with value = 4 -->
>
>
> Two quick comments:  First, you should probably use the more standard
> HTML::Template syntax -- <tmpl_var THIS_RUN_MODE>, instead of the
> '%BLEH%' format.  The latter was for backwards compatibility to a
> templating system which hasn't been used in many years.

Yeah, I know, but I hate all that typing. *sigh*  That's the one thing
that bugs me about HTML::Template... the verbosity of the parameters.
But I can get over it.

> Second:  It is not usually necessary to dynamically populate the
> run-mode in the template.  Typically, clicking on a button will always
> send you to the same mode.  As a result, you can statically define  your
> run-modes in the template:
>
>   <form name="update_thingy_form" method="POST">
>   <input type="hidden" name="rm" value="update_thingy">
>   <!-- form fields here -->
>   <input type="submit">
>   </form>

This comment makes more sense to me now that I understand that (as
intended) run modes submit to themselves.  I thought I saw an example in
the search.cpan.org CGI::Application documentation that suggested that a
hidden rm input generated by run mode "n-1" had value "n", which seems
different from what you've described here.  But now I'm having a tough
time finding that again.  Hmm.

>>Another (more or less unrelated) thought... regarding
>>$self->load_tmpl()
>>method:
>>
>>    A) why isn't the created $tmpl_obj "sucked" into $self as
>>well, akin
>>       to how the CGI query object is embedded into self in
>>the form of
>>       $self->query?  I'd like to have a $self->template to do my
>>       object composition off of, rather than having a freefloating
>>       $tmplobj
>
>
> Not everybody uses templates.  I didn't think it appropriate to assume.
> Also, there are many run-modes which might not have a template.  For
> instance, an HTTP redirection mode.

Fair enough, but even in your own examples the HTML::Template object
doesn't *necessarily* have to be created -- it only is in the event that
the ->load_tmpl method is called.

Perhaps this suggests a more general functionally...

Right now, you automatically get a $self->query CGI object composited
into the $self CGI::Application-derived class object.  And you get an
HTML::Template object generated if asked for, but not composited into
the $self.  Well, what about having a generic ->composite method, so
that you can stick whatever object you want into your $self?  E.g.
HTML::Template, Template toolkit, Apache::SessionX, DBI, Class::DBI,
etc.  That is, you:

    * create an object of some other class,
      $tmpl_obj => HTML::Template->new(...);

    * $self->composite(object => $tmpl_obj, name => "templateFOO");
      # or whatever syntax works out best

    * now, you have $self->templateFOO available to you which exposes
      the entire API of the composited object, e.g.
      $self->templateFOO->output();

Note that the $self->query stuff that CGI::Application currently
implements would then be just an internal invocation of
$self->composite(object => CGI->new(), name => 'query');

>>    B) why does this template object necessarily mean an
>>HTML::Template
>>       object?  I mean, I like HTML::Template fine, but it seems to me
>>       like this should be a merely a default template type.  Others
>>       could/should be supported. (e.g. Template Toolkit)
>
>
> It is supported!  You should ask this question on the list.  There are
> many TT users of CGI-App.  In a nutshell, the solution is to override
> load_tmpl() to create an return a TT object instead of H::T object.

Again, maybe a generic "composite" method (along with some auxillaries,
like something to list all composited objects, remove a composited
object, etc.) would simplfy this.  Subclassing these specific existing
methods seems like too much work, if CGI::Application could just
composite stuff automatically.

> Do post your message to the list!  It will get everybody's creative
> juices flowing.

I'll be compiling these emails into a thread (with minor edits) and
posting them there soon.

Cheers,
Richard


--------------------------------------------------------------------- Web Archive: http://www.mail-archive.com/[EMAIL PROTECTED]/ http://marc.theaimsgroup.com/?l=cgiapp&r=1&w=2 To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]



Reply via email to