Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris

Flow control / case structure

by George_Sherston (Vicar)
on Sep 08, 2001 at 01:58 UTC ( #111042=perlquestion: print w/replies, xml ) Need Help??

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

I humbly solicit the advice and opinions of brother and sister monks:

My current task is a big CGI script that takes input from two dozen different forms, each of which gives it the information it needs to do a different task. The way I have it set up at the moment is that each form returns a name / value pair in which the name is always "Action" and the value is something memorable and descriptive like "Register", "Book Course" etc (in fact these are taken from the value field of the submit button, which is always named Action).

So my question is, what are the pros and cons of different ways to have my script do the correct task depending on what "Action" is?

I can think of a number of different ways. A tree of if / elsif / elsif... else; a tree of ifs; either of these with code in the loops or else with the loops simply calling the appropriate subroutine; giving my subroutines the same names as the Actions themselves and calling them with, e.g., &$Register... And then I came across this which suggested there must be something called a switch statement that it would be sinful not to use - and I looked it up in Ch 4 of the Camel but I can't see why that would be better than if / elsif. But the fact of the matter is, I don't know.

In any event there seem to a be a shed load of different options for doing this, and I bet there's a lot of accumulated wisdom, and I'd like summa that before I commit. As well as anything else you care to mention I would value views on how these different choices have an effect on:
- readability / maintainability
- speed of execution
- ease of avoiding stupid coding errors.

Also, I should mention that the jury's still out whether this will end up being one file or several.

I'm grateful for any wise words you can offer.

George Sherston

Replies are listed 'Best First'.
Re: Flow control / case structure
by dws (Chancellor) on Sep 08, 2001 at 02:03 UTC
    So my question is, what are the pros and cons of different ways to have my script do the correct task depending on what "Action" is?

    CGI::Application provides a framework for tackling this very problem.

Re: Flow control / case structure
by wog (Curate) on Sep 08, 2001 at 02:07 UTC
    One way to do this is with dispatch table:

    my %actions = ( Register => \&handle_register, "Book Course"=> \&handle_book_course, # ... ); # ... if (defined $actions{$action}) { $actions{$action}->(); } else { print "No such action $action..."; }

    Basically you have a hash giving a subref for each action. You can even write an anonymous subroutine in the dispatch table using the sub { ... code here ... } syntax. This should be pretty fast and allow you to easily add or remove actions, and have one action be callable by multiple names.

    Note that calling a subroutine named by the action argument is a major security risk if you don't at least run checks to see if it's valid first.

      An interesting twist on this idea is to put the 'dispatcher' part of the dispatch table concept into AUTOLOAD. You execute your 'actions' as method calls against an object. When perl doesnt find it it calls the dispatcher from AUTOLOAD and installs the subroutine from the dispatch table. This method may actually be a little insecure, depending on how you built your objects. You might want to use an special class, with no additional methods (even new) so someone can't call your personal routines for instance.

      Another idea, more secure, is to use Perls class heirarchy as the dispatch function. Each action gets translated into a class name and then in an eval used and new()ed. The returned object is then manipulated as so desired. If you prepend a 'safe' root name to the class and the appropriate directory is secure then overall security shouldn't too much of an issue. Also a bit of s///g ing of $action first might be in order.

      #untested sub handle_action { my ($self,$action,$value)=@_; my $obj=eval " use Some::Class::$action; Some::Class::$action->new() "; #eval returns the last value evaluated return $self->raiserror($action,$value,$@) unless $obj->is("Some::Class::Proto"); $obj->exec($value); }
      well anyways those are some of the more unusual ways. :-) I like the class heirarchy the best in some ways cause it neatly modularizes the whole process.

      You are not ready to use symrefs unless you already know why they are bad. -- tadmc (CLPM)

(Ovid) Re: Flow control / case structure
by Ovid (Cardinal) on Sep 08, 2001 at 02:46 UTC

    For one of my applications, I have all forms return an action (add, edit, delete, etc) and a type (Press Release, Press Cuttings, etc.). Then, I control the page and the function as follows:

    # $action $type my %page_control = ( add => { PressRelease => { page => 'pm-mai +n-pr-add.tmpl', function => \&add_p +ress_release }, PressCutting => { page => 'pm-mai +n-pc-add.tmpl', function => \&add_p +ress_cutting } }, edit => { PressRelease => { page => 'pm-mai +n-pr-edit-del.tmpl', function => \&edit_ +press_release }, PressCutting => { page => 'pm-mai +n-pc-view.tmpl', function => \&view_ +press_cuttings } }, delete => { PressRelease => { page => 'pm-mai +n-pr-edit-del.tmpl', function => \&delet +e_press_release }, PressCutting => { page => '', function => '' } }, display=> { tabs => { page => 'pm-tab +s.tmpl', function => '' }, menu => { page => 'pm-men +u.tmpl', function => '' } }, default =>{ page => 'pm-main.tmpl', function => \&show_main_console } ); my $page = $page_control{ $action }{ $type }{ page }; my $function = $page_control{ $action }{ $type }{ function }; $page ||= $page_control{ default }{ page };

    With that one data structure, it's relatively easy to track what's going on. However, I do want to look a bit more closely at CGI::Application as I lately hear its name getting bandied about quite a bit.


    Vote for paco!

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: Flow control / case structure
by Maclir (Curate) on Sep 08, 2001 at 06:49 UTC
    One structure you may want to consider is a State Machine. Now, I am sure CS and algorithm purists will have a far better definition that what I have here, but I will make an attempt.
    1. Your program consists of a single loop, consisting of what is really a big CASE statement (or SELECT, depending on your religion). You start with one of several initial states, based on your initial task.
    2. Each CASE / SELECT entry (just use an if test like if (&mystate eq 'SomeValue') will do some processing or validation that is required at that step, and then makes a decision on what the next state should be.
    3. The loop continues until you reach a "completed" state.
    So, you initial state could well be set by the value of your submit button, and you can then detrermine what the correct next state shuld be. For example, assume the initial state is "REGISTER". The fragment of code may be:
    if (&mystate eq "REGISTER") { if (&UserName eq "") { &mystate = "ERROR"; &error_code = "Missing user name"; } else { &mystate = "CHECKADDRESS"; } }
    and so on. Now, several of your forms may want to perform the same processing - for example, checking a form value that could be on several forms. So the decision for the new value of &mystate could depend on what the initial request was.

    Percentive monks will see that by using a sequence of IF blocks, within one pass of the loop, the program may pass through several states in sequence - that is not a problem. That advantage of this approach is that you have broken up your processing logic into a number of discrete chunks, each doing a particular function (validating an address, doing a data base search for a list of courses, whatever), and hence becomes easier to maintain.

    As well, your business logic - what is required to process a course booking, for example, is listed in the logic of chosing the state progressions. So if you need ot add some additional business logic at a later stage (oops - we forgot that we need an email address when accepting course bookings), then you can add it reasonable simply.

    I apologise if the explanation is a trifle obscure - one should not try to undertake algorithm design late on a friday evening!

Re: Flow control / case structure
by George_Sherston (Vicar) on Oct 10, 2001 at 16:54 UTC

Log In?

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

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (9)
As of 2021-10-22 15:47 GMT
Find Nodes?
    Voting Booth?
    My first memorable Perl project was:

    Results (85 votes). Check out past polls.