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]