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

Esteemed Monks,

I have been working on a rather large WebApp for several weeks now. So far I have been working in the same way I used to develop straight CGI applications. Each CGI::Application run_mode is self contained. For example if you want to register for the site you go to mode_001 and it renders the empty form. When you submit it takes you back to mode_001 and the form is validated and the data untainted or taint checked and written to the database. Error handling (duplicate username, invalid password and all that) get taken care of in the same run_mode. So you may in fact call that run_mode a number of times whilst you complete that phase of your interaction.

When you are registered I was going to produce an appropriate navigation panel that said, e.g. "You are now registered, the following options are available to you:" and the nav section would provide the next run_mode.

To my mind this gives the user a degree of flexibility about what he does. But last weekend a friend and sometime colleague was looking at the code and said: "nah, you gotta control the users path. Lead them by the nose or they will get it all wrong!". This would mean a rewrite to allow each run_mode to pass the user directly to one and only one run mode for the next phase.

How do other monks feel? I have spent much of the day agonising over the issue and felt that some other opinions would give me the clues as to how to go about this application. It will be very large, I expect some 200+ run modes by the time I am finished.

One of my questions is this: should run-modes be kept should and simple perhaps handling only a part of a sungle site function? For example, for an administrator to select and view user profiles, should the saearch, select, view and edit each be a separate run_mode or is it, in your opinion, okay to combine them into one run_mode?

jdtoronto

  • Comment on CGI::Application, have I made a big mistake.

Replies are listed 'Best First'.
Re: CGI::Application, have I made a big mistake.
by PodMaster (Abbot) on Nov 24, 2003 at 22:31 UTC
    Here goes :)(hopefully you'll see what i'm trying to say)
    "nah, you gotta control the users path. Lead them by the nose or they will get it all wrong!". This would mean a rewrite to allow each run_mode to pass the user directly to one and only one run mode for the next phase.
    I personally don't know what your friend is talking about (control the path?). As for rewriting each run_more to apss the user ... how are you storing the session (form) data? If it's a single form submit/validation cycle, one run mode is fine (present form, if validate, do whatever, redirect to success page, else re-present form with some error messages).

    You could have a separate run mode just for validation

    present_form -> validate_form -> success -> present_form
    but having a single run mode (present_validate) instaed of two would be just fine. If it gets more complext than present_validate (2 in 1), then you need to split it up.

    If it's a set of forms (multiple pages to submit, you have to store the session on the server), then a group of run modes is reccomended (each successfull submit leading to the next form/run_mode).

    One of my questions is this: should run-modes be kept should and simple perhaps handling only a part of a sungle site function? For example, for an administrator to select and view user profiles, should the saearch, select, view and edit each be a separate run_mode or is it, in your opinion, okay to combine them into one run_mode?
    I would say that's not ok. You're basically going against the design methodology behind CGI::Applicaton (read the "USAGE EXAMPLE" in the docs). Stick to one operation per run mode (it easier to think about it if you use templates. You basically have 1 template for every run mode. If you ever find yourself using more than 1 template, and keep in mind i'm not talking about includes, but two distinct templates for one run mode, you need to split that run mode into two).

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

      PodMaster

      What my friend is saying is that the user should be taken along a siungle path through each major functional area of the site - that they should not be allowed the flexibility of registering at, say a tour level, but they need to provide more data into their user profile before they can use the full functionality (my scheme!) Thats one issue out of the way.

      My reason for grouping actions together is this: Each of my current run modes uses a single template (well, really a page template for the overall look, a navigation template that gives the relevant navigation for the current action and a form template that actually contains the form). I am using CGI::FormBuilder to render the forms - becuase it gives me an easy way of loading defaults, requiring a field and validating field values. The same form is used for 'new entry', 'display record' and 'update record' functions purely by changing the buttons below the form (part of what FormBuilder does very easily). So by the PodMaster rule of ONE TEMPLATE ONE RUN-MODE I am still on the correct path.

      I have been thinking about this for a coupole of hours over dinner now. One of the things I like about some websites is they give me the option of being able to browse without logging in. Just like PM does! So I want to be able to pop up my login form, the user can log in, and then they can go on from where they left off.

      Session management is important as has been stated in another reply, I am using CGI::Session and storing the users entire state in the session. So I start a session before there is even a log-in attempt. My reasoning for this is that I can have a 'BACK' button that works within the application. I can read the session, find out where 'BACK' would take them, and then even query the database if necessary to fill out a form with valid values rather than using the browser back button and the difficulties attendant upon that. It also means I can be much more logical, if a 'BACK' move would take an already registered client to the form where he built his registration profile - well, he doesn't want to register again does he! We could take him to an 'edit profile' version of the same form.

      jdtoronto

Re: CGI::Application, have I made a big mistake.
by freddo411 (Chaplain) on Nov 25, 2003 at 00:27 UTC
    I'm working on a medium sized CGI:A based web app here at work. It has about 70 or so rms. Each rm corresponds to one page, with a couple of exceptions. (more about these in a moment.) Unlike your setup, I also dedicate a run mode to processing each page that has inputs. These are imtimately related so that share a naming convention like:
    • edit
    • edit_proc
    edit_proc validates input, and does something with the valid results. It would call edit if there were any user input errors and return that form to the user with errors indicated.

    I've got a situation where two input forms are very similar, (creating a user, and modifying a user) They are just different enough that I have a unique run mode for each to set them up, but I use the same _proc to handle the inputs.

    You posit:

    you gotta control the users path. Lead them by the nose or they will get it all wrong!
    This is a UI decision, which depends closely upon what you are trying to accomplish. Sometimes it is appropriate, othertimes no. In terms of CGI:A and how you code, it doesn't really matter, as you could support either. Supporting a lot choices is easy, just have them encoded in hrefs in the returned html. If you want to force the user on a path, just give them html that contains only a submit button with a hidden rm field indicating the next runmode.

    Should "search, select, view and edit each be a separate run_mode"? Yes, in my opinion they should be seperated. Keep in mind that you can always factor out common code into an outside module for reuse across these runmodes.

    Cheers

    -------------------------------------
    Nothing is too wonderful to be true
    -- Michael Faraday

      You said:
      This is a UI decision, which depends closely upon what you are trying to accomplish. Sometimes it is appropriate, othertimes no. In terms of CGI:A and how you code, it doesn't really matter, as you could support either. Supporting a lot choices is easy, just have them encoded in hrefs in the returned html. If you want to force the user on a path, just give them html that contains only a submit button with a hidden rm field indicating the next runmode.
      This is one of the things I thought about this evening. You are right. On the basis of what the client paying for the job wants I can modify the navigation system very easily without having to modify the code more than trivially.
      Should "search, select, view and edit each be a separate run_mode"? Yes, in my opinion they should be seperated. Keep in mind that you can always factor out common code into an outside module for reuse across these runmodes.
      I keep thinking about this and I am becoming more and more of the view that they can and perhaps in some cases should be grouped. This keeps all of the code associated witha single template inside a single run_mode which could itself ultimately be made into a module of its own.

      A few weeks ago at the Toronto Perl Mongers, Richard Dice presented a talk on an application framework he built using CGI::Application, Template Toolkit and Class::DBI. He spoke of doing something that at the time I did not understand. His idea was that you subclass CGI::A and then create a module which has all the common code in it - encryption, decryptio, authentication and such. Then you subclass that to give you the final application module. He postualted that this way you could in fact subclass CGI::A once to provide an App module and an instantiation CGI for user access and a separate module and instantiation for admins. In my case, as I have users, service providers and admins I can do it three times and have three totally controllable environments for each class of access to the site.

      jdtoronto

        I am eager to try out the various ideas concerning subclassing an entire CGI:App. I'm doing a "lite" version of this on my project. On our web server we have a common piece of infrastructure that limits user access depending on dir location. There is a piece of my application that I want two different sets of users to have access to. So I set up this scheme:

        MyApp.pm (off somewhere outside of the document root) doc-root--- -------------- | | foo_dir bar_dir | | foo.cgi foo.cgi Code for foo.cgi: use MyApp; my $webapp = MyApp->new(); $webapp->run();

        Each use the same MyApp.pm, but access to the foo_dir is limited to different users than bar_dir.

        This approach has the advantage of using the existing login/ authentication scheme while allowing me to reuse my code across different locations.

        One thing worth noting, is that I haven't taken advantage of CGI:Apps ability to pass in different parameters so that the same MyApp code could behave somewhat differently in these two instances.

        good luck with your project.

        -------------------------------------
        Nothing is too wonderful to be true
        -- Michael Faraday

Re: CGI::Application, have I made a big mistake.
by hardburn (Abbot) on Nov 24, 2003 at 21:34 UTC

    Some would say that CGI::Application is for when you have multiple forms that are related in a linear fasion. For instance, a user registration form might have an initial form where they fill out basic information, a second form where the information is more in-depth, a third where they agree to the site's user agreement, and finally one where the complete data is submitted.

    Personally, I think this can be extended into pages where the run modes share a lot of information (like database connections and templates) but the user wouldn't navigate the pages linearly. This is CGI::Application after all, and there are very few non-web applications that are so linear (outside of examples for beginning GUI programmers).

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    : () { :|:& };:

    Note: All code is untested, unless otherwise stated

Re: CGI::Application, have I made a big mistake.
by simon.proctor (Vicar) on Nov 24, 2003 at 22:38 UTC
    I've only just started playing with CGI::Application so my view may be a little immature in that sense.

    I see it as providing a means of presenting views to the user in a nice encapsulated way but allowing pre and post processing on each view (or run mode).

    With that in mind, I don't see each run mode as needing to be linear as that requires state management. If you want it to be linear - use state management and encode your path into your run modes. You would want to encode the previous run mode into the session so when you hit the new run mode (or view in my terminology) you can check if you have come from the right place.

    Whether you do that in the prerun method or inside the run mode method is upto you. How you encode the session is also upto you. If its just tracking where you have been then something in the URL could be a nice way of doing it as it avoids cookies (but thats a topic for a different node?).

    In answer to your direct question, each page view on the screen is a view into your application (or run mode). One view, one run mode, one function. The only place I would stay within a run mode is if the user didn't fill something in properly. I'd then redisplay with an error message. But thats me :).

    I wouldn't worry about having a large number of run modes. Frankly it should be proportional (roughly) to the number of facilities you offer to your user. So if they register, login and logout then that would be 2+2+1. Register form mode, thanks for registering mode, login form mode, thanks for logging in mode redirect page (you could make this generic) and a logout mode (you don't need a confirmation screen for this!). Of course that's just off the top of my head.

    HTH

    SP
Re: CGI::Application, have I made a big mistake.
by Purdy (Hermit) on Nov 25, 2003 at 14:55 UTC
    I haven't seen anyone point you in the direction of CGI::Application::ValidateRM yet - that's what I've been using recently and it works great!

    The way I look at it, I have separate run_mode's for every "page" or step in the application. Then (following your example), when mode_001 is processed/submitted, it goes to mode_002 (instead of back to mode_001) and in mode_002's run_mode method, I use ValidateRM's check_rm method, which will take care of your error handling for you and you can tell it which run_mode to return to, if there was an error.

    sub mode_001 { my ( $self, $errs ) = @_; # ... $template->param( $errs ) if $errs; $template->output; } sub mode_002 { my ( $self, $results, $err_page ); $self = shift; ( $results, $err_page ) = $self->check_rm( 'mode_001', '_mode001_profile' ); return $err_page if $err_page; # If you've gotten this far, all the data is ok per # your FormValidator object } sub _mode001_profile { return { 'required' => [ qw( email password1 password2 ) ], 'constraints' => { 'email' => sub { return Email::Valid->address( shift ) +; } }, 'msgs' => { 'any_errors' => 'some_errors', 'prefix' => 'err_', }, }; }

    Hope that helps!

    Peace,

    Jason

    Update: It will also prefill out the previous form with the user's data when/if it returns to a previous run_mode. So if they provided their first e-mail address, but not any other stuff, then it will represent the original form with the data that the user did specify along with those 'msgs' (i.e. err_password1, err_password2) that you can incorporate into your template to color things red or whatever.

      Great post, ++ to parent.

      I to use ValidateRM with great success. The use use of VRM 'encouraged' me to keep each rm to just a single presentation screen, because when using VRM to process a form submission you call the orginal form presentation run mode to (re)produce the screen with errors message and data overlaid. This works best if the orginal run mode simply does presentation.

      I'd like to add one useful tip. VRM requires HTML::FillInForm to repopulate the info into the presenation form. This is a very handy module for prefilling out a form in your run mode, if, for example you are doing a edit operation on something you already have in the database. A code example of filling out into a template file:

      ... code to setup my $t template, and get info from the database into %fdat would be here ... my $html = $t->output; if ( %fdat ) { my $fif = new HTML::FillInForm; $html = $fif->fill(scalarref => \$html, fdat => \%fdat ); } return $html; # this is the filled-out HTML file

      -------------------------------------
      Nothing is too wonderful to be true
      -- Michael Faraday

      Thank you,

      I had printed out the docs and have them in front of me but hadn't really looked at them. TIme to do some more reading I think, before the project gets too far along and I can't change things.

      [jdtoronto}

Re: CGI::Application, have I made a big mistake.
by jerlbaum (Sexton) on Nov 26, 2003 at 04:37 UTC
    Hey jdtoronto --

    First off, I would encourage you to join the CGI-App mailing list. Send email to:

    cgiapp-subscribe@lists.erlbaum.net

    We've frequently discussed the philosophy of what should be a run-mode.

    To the heart of the matter: I think you have to adjust your architecture a bit. Based only on the fact that you're looking at a 200 run-mode app, I feel you have got way too much going on. As a point of reference, my typical CGI-App has about 5-10 run-modes.

    Two changes I would recommend:

    1. Simplify your run-mode configuration -- roughly one mode per screen.
    2. Separate functionality into multiple applications -- e.g., what you're now calling "run-modes" ("mode_001") becomes its own CGI::Application module ("UserRegistration.pm").

    TTYL,

    -Jesse-

Re: CGI::Application, have I made a big mistake.
by Theo (Priest) on Nov 30, 2003 at 23:43 UTC
    I'm reading a book named Code Generation in Action by Jack Herrington. With 200+ run modes, (does that need 200+ code modules?) your application may benefit greatly from code generation techniques. It takes a bit of overhead to set up the code generators, but when you're asked to make changes you modify the generator and regenerate all of the pages consistanly and in only minutes. I thought it seemed worth mentioning.

    -theo-
    (so many nodes and so little time ... )