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

I am using CGI::Application and 90% of my functions has the below syntax, except for the tmpl names.
Is there any SMART way of putting this code somewhere else and then include it in the function? Does anyone have any ideas on this matter or am I on the wrong track?
sub showStart { # application object my $self = shift; # database handle my $dbh = $self->param('dbh'); # get cgi query object my $q = $self->query(); my %post = $q->Vars; # load template files my $tmpl = $self->load_tmpl('main.phtml'); my $tmpl_sub = $self->load_tmpl('start.phtml'); # some function specific code... # output $tmpl->param( tmpl_sub => $tmpl_sub->output() ); return $tmpl->output; }

Replies are listed 'Best First'.
Re: CGI::Application same function code
by Tanktalus (Canon) on Feb 11, 2005 at 15:32 UTC
    $self->run_modes( start => sub { shift->_runmode('start'); }, middle => sub { shift->_runmode('middle'); }, end => sub { shift->_runmode('end'); }, ); # ... sub _runmode { my $self = shift; my $html = shift; my $dbh = $self->param('dbh'); #.... my $tmpl_sub = $self->load_tmpl($html . '.phtml'); #... }
    is one way. Another is to reverse it. The setup stays the same as you have now, but create a new function to do the actual output:
    sub showStart { my $self = shift; # function specific code ... return $self->display_tmpl( file => 'start.phtml', # other parms ); } sub display_tmpl { my $self = shift; my %opts = shift; my $tmpl = $self->load_tmpl('main.phtml'); my $tmpl_sub = $self->load_tmpl($opts{file}); #... use other parms... $tmpl->param(tmpl_sub => $tmpl_sub->output()); return $tmpl->output(); }
    The latter is close to how I do it.

    Update:Missed my open-code tag. Fixed.

    Update:Oh, and a couple other monks alerted me to the previous update ... just as I was noticing/fixing, but thanks to those who politely smacked me in private. :-)

      Thanks for your reply it contained some intresting new approaches some that I understood and some that I didn't.

      I think I read somewhere on the C:A mailing list that you could only use a function name as a runmode, what exactly are you doing here:

      start => sub { shift->_runmode('start'); },
      I understand that you call the _runmode function with the argument 'start' but besides that?? Could you send in other arguments, objects as well?

      However, In the examples I still need to add the cgi object-, dbh code in the showStart function to be able to get the function specific code to work.

      I somehow want my functions to inherit from one function that has all the basic code such as: self, cgi object, dbh, output etc.

        You can use either a function name, or a code reference. (Trust me - my name is in there for providing a patch to this area.) By giving an anonymous sub here, I'm creating a code reference which gets passed in. Then C::A will call it something like: $self->$function(). This means that the first (and only) parameter is the C::A object, which above I shift off to use to call _runmode.

        You can send in anything else that is visible at the point you're setting up run modes. There is not really a lot of point to doing so in this case, however, since you can just as easily put all of that information into $self->param() and everyone else would have access to those as well.

        As for getting the CGI, DBI, and other objects/data, I don't think there is a better way than what you're already doing. What you're looking for is a templating mechanism (on CPAN, Source Filters - I've never used source filters, but just a casual read around here for the last month has shown a number of concerns around using them - they can be quite dangerous). This is not part of standard programming languages - in C/C++, you would need to use the preprocessor. In Perl, a source filter. Maybe something along the lines of a macro that you put in your code, "RUNMODE_SETUP;", which your source filter could catch and insert the real setup code. It does seem a bit overkill to me, and you do have to be careful, but it may work.

Re: CGI::Application same function code
by dragonchild (Archbishop) on Feb 11, 2005 at 15:58 UTC
    I take a different approach. Since all CGI::Application runmodes are just object methods, I install the methods when the class is compiled. Then, I treat them as ordinary runmodes when I write my setup().
    sub install_method { my $package = shift; foreach my $method (@_) { no strict 'refs'; *{$package . '::' . $method} = sub { # Do whatever you want here. You can reference $method and + any other # lexical variables, just like any closure. }; } } __PACKAGE__->install_method qw( start middle end );

    Generally, I will put install_method() in my CGIApp baseclass. (You do have a CGIAPP baseclass, right?)

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

      I am actually just in the planning state, I've done some testcode just to be certain that I design this correctly from the beginning. I will use a baseclass but I havn't decided exactly how yet.

      Is there any chance that you could explain a little bit further how I could implement your suggestion?

        Re: Why CGI::Application? is a good start on the topic.

        Being right, does not endow the right to be rude; politeness costs nothing.
        Being unknowing, is not the same as being stupid.
        Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
        Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: CGI::Application same function code
by skx (Parson) on Feb 11, 2005 at 15:41 UTC

    I've written a few applications which just display a template file, and the simplest way I found was to use the AUTOLOAD function.

    That gets called for anything that hasn't got an explicit mapping setup. Inside that I will either show "Invalid node", or laod the existing template.

    Code could look like this:

    # # Called if the user submits an invalid run mode / node. # sub invalidnode { my ($self) = @_; # Get the node the user tried to use. my $q = $self->query(); my $node = $q->param("node"); if ( $node =~ /^([a-z]+)$/ ) { $node = $1; if ( -e "./templates/$node.tmpl" ) { my $html = $self->load_tmpl( "templates/$node.tmpl", global_vars => 1, ); return ( $html->output ); } } my $html = $self->load_tmpl( "templates/error.tmpl", global_vars => 1, ); return ( $html->output ); }

    With at the setup:

    $self->run_modes( 'home' => 'home', ..... 'AUTOLOAD' => 'invalidnode' );

    As you see I merely test for the existance of a template - and if one exists it's loaded, if not I show an error page.

    That means I can have ?node=FAQ, ?node=Contact, etc, which are just static templates and don't need any special handling.

    Steve
    ---
    steve.org.uk
CGI::Prototype example of the CGI::Application problem
by metaperl (Curate) on Feb 11, 2005 at 19:06 UTC
    Under CGI::Prototype, this would be done by each page having its own template method and inhertiting the engine method to apply to the template:
    package App::Base; sub engine { my($self, $template) = @_; # application object my $self = shift; # database handle my $dbh = $self->param('dbh'); # get cgi query object my $q = $self->query(); my %post = $q->Vars; # $template actions... # output $tmpl->param( tmpl_sub => $tmpl_sub->output() ); return $tmpl->output; } sub template { my $tmpl = $self->load_tmpl('main.phtml'); } package App::This; use base qw(App::Base); sub template { my $self = shift; $self->SUPER::template(); my $tmpl_sub = $self->load_tmpl('this.phtml'); } package App::That; use base qw(App::Base); sub template { my $self = shift; $self->SUPER::template(); my $tmpl_sub = $self->load_tmpl('that.phtml'); } 1;

    And then a render() method would come along and present the templates to the engine.