Fellow Monasterians,
I've been working with CGI::Application for a few years now and thought it would be good to submit a tutorial for a simple, but practical application. So here's my draft for your review.
Why another C::A tutorial? A few reasons: 1) I learn by example, so I thought more code examples would be helpful, 2) I felt showing a more practical example might spur on beginners, 3) show more integration with HTML::Template, and 4) show a more structured example showing scalability.
I've heard it said, "If you want to learn something, teach it." I've relearned some of the fundamentals I've forgotten in rote day-to-day coding I do. So, this has been good for me.
Having said that, I plan to learn even more as the good monks weigh in on this. Thanks in advance for your help and comments.
Update: Copied tutorial draft from scatchpad to this node per Moritz. Making edits per comments.
CGI::Application falls under the category of a framework. Frameworks enable developers to use shorter, more readable coding conventions, at least on the surface, that can be performing more complicated, and possibly combined tasks behind it. The result is faster development, more consistent style, and fewer errors, to name but a few. CGI::Application provides an addition model of mapping your Web application to "screens" (forms, etc.) and creating the functions to process by pages.
There are more robust frameworks (with more robust learning curves), like Catalyst and Maypole, and more recently, Jifty, but CGI::Application provides a very approachable, easy-to-use, low overhead framework for Web developers who like working in a less abstracted environment and a little closer to Perl itself.
If you are convinced that CGI::Application is the framework for you, you're in the right place. If you are still unsure, check out some additional rationale. Otherwise, let's dive in.
This tutorial does not cover the installation of CGI::Application, or any of it's growing number of plug-ins, but will show how you can use CGI::Application and HTML::Template to build one of the most common ancillary interactive applications on the Web: a contact form. Also, this tutorial is not meant as a placement for CGI::Application's well-written POD, which should be required reading.
While this is not a tutorial for HTML::Template or HTML::FillInForm, the novice Web developer will see an example of how they can all work together with CGI::Application to create more useful, dynamic, and user-friendly applications.
The locations of your files will vary depending on your server, e.g., you could be on a shared host and using something like /usr/home/foobar and /foobar/public_html/. The salient point is that the CGI::Application applications (server-side) are placed "out-of-reach" of the public Web directory (client-side).
Notes:
/opt/foobar/myapps/---+ | | | | | Foobar_Super.pm | | | Common.pm | | | /Acmecorp/---+ | | | Contact.pm | | | /conf/---+ | | | acmecorp.conf | | /var/www/acmecorp/--+ | home.html | /contact/----+ | | | index.cgi | /templates/---+ | | | contact.tmpl | thankyou.html
index.cgi (our instance script)
Notes:
#!/usr/local/bin/perl -T use lib "/opt/foobar/myapps/"; use warnings; use strict; use Acmecorp::Contact; my $app = Acmecorp::Contact->new( PARAM => 'client' ); $app->run();
contact.tmpl ('rm' is our run mode and will be passed upon submission to our instance script '/contact/index.cgi')
<form action="/contact/index.cgi" method="post"> <input type="hidden" name="rm" value="s" /> <tmpl_if errors> <tmpl_loop errors> <p style="color: red"><tmpl_var error></p> </tmpl_loop> </tmpl_if> <p>Today's date: <tmpl_var today></p> <p>Name: <input name="name" type="text" value="" /></p> <p>Address: <input name="address" type="text" value="" /></p> <p>City: <input name="city" type="text" value="" /></p> <p>More info: <input name="more_info" type="checkbox" value="yes +" /></p> <p><input name="Submit" type="submit" value="Submit" /></p> </form>
Foobar_Super (a super class for CGI::Application applications)
Notes:
package Foobar_Super; use strict; use warnings; use base 'CGI::Application'; use CGI::Application::Plugin::FillInForm(qw/fill_form/); use CGI::Application::Plugin::Config::Simple; use CGI::Application::Plugin::Redirect; use CGI::Application::Plugin::Session; use CGI::Application::Plugin::DBH (qw/dbh_config dbh/); use HTML::Template; #--- Start CGI::APP sub cgiapp_init { my $self = shift; #--- Set Paths $self->config_file( '/opt/foobar/myapps/' . ucfirst $self->param('c +lient') .'/conf/'. $self->param('client').'.conf');; $self->tmpl_path( '/var/www/' . $self->param('client') . '/template +s ); #--- Session $self->session_config( DEFAULT_EXPIRY => '+8h'); #--- Contact to DB $self->dbh_config( $self->config_param('db.host'), $self->config_param('db.user'), $self->config_param('db.pass'), {RaiseError => 1} ); } 1;
acmecorp.conf (a configuration file)
Note: read by CA_Super's cgiapp_init
#--- MySQL Server --- [db] host = DBI:mysql:foobar:localhost user = acmecorp pass = AKCgKYxc
Contact.pm (the actual application called by the instance script
Notes:
package Acmecorp::Contact; use base qw(Foobar_Super Common); use strict; use warnings; use MIME::Lite; #load any extra modules needed use Date::Calc qw(Today); #--- SETUP Run modes sub setup { my $self = shift; $self->start_mode('d'); #if no run mode, use 'd' $self->mode_param('rm'); $self->run_modes( 'd' => 'display', 's' => 'save_form' ); } #--- Display sub display { my $self = shift; my $template = $self->load_tmpl( 'contact.tmpl', die_on_bad_params => 0 ); $template->param( today => sprintf( '%4d-%02d-%02d', Today() ) ); + return $template->output(); } #--- Process sub save_form { my $self = shift; my ( %sql, @errors, $error, $fifvalues ); ($sql{'name'}, $error ) = $self->validate( $self->query->param('nam +e') ); if ( $error ) { push @errors, ( { 'error' => 'Name'.$error } ); +} ($sql{'address'}, $error ) = $self->validate( $self->query->param(' +address') ); if ( $error ) { push @errors, ( { 'error' => 'Address'.$error } +); } ($sql{'city'}, $error ) = $self->validate( $self->query->param('cit +y') ); if ( $error ) { push @errors, ( { 'error' => 'City'.$error } ); +} $sql{'more_info'} = $self->query->param('more_info'); #if there are errors, return the form with original input and error + messages if ( @errors ) { my $template = $self->load_tmpl( 'contact.tmpl', die_on_bad_params => 0, ); $template->param( errors => \@errors, today => sprintf( '%4d-%02d-%02d', Today() ), ); for my $key ( keys %sql ) { $fifvalues->{$key} = $sql{$key}; #assign fill-in-form values } return $self->fill_form( \$template->output, $fifvalues ); } else { $self->record(\%sql); #record the input return $self->redirect('/thankyou.html'); } } #--- Record sub record { my $self = shift; my $sql = shift; my %sql = %{ $sql }; #we use CAP::DBH to connect to the DB and execute our SQL statemen +t my $stmt = 'INSERT INTO contacts (' . join(',', keys %sql) . ') VALUES (' . join(',', ('?') x keys %sql) . ')'; $self->dbh->do($stmt, undef, values %sql); } 1;
Common.pm (a module with common methods)
package Common;sub validate { my $self = shift; my $to_check = shift; if ( $to_check !~ /^([\w ]+)$/ ) { return ( $to_check, " has invalid characters or is blank" ); } else { return $1; } } 1;
This tutorial has shown use of the basis tenents of using CGI::Application as an application framework:
As always, you are encouraged to read the POD for CGI::Application and then take a look at the growing number of Plugins to see if CGI::Application can further streamline your coding process.
CGI::Application Wiki
Mailing List
HTML::Template Tutorial
Red Antiqua Tutorial
Using CGI::Application
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: Draft of CGI::Application Tutorial for review
by dragonchild (Archbishop) on Jul 16, 2008 at 17:07 UTC | |
by bradcathey (Prior) on Jul 16, 2008 at 19:45 UTC | |
by dragonchild (Archbishop) on Jul 17, 2008 at 00:54 UTC | |
Re: Draft of CGI::Application Tutorial for review
by moritz (Cardinal) on Jul 16, 2008 at 19:56 UTC | |
by bradcathey (Prior) on Jul 16, 2008 at 21:23 UTC | |
by moritz (Cardinal) on Jul 16, 2008 at 21:25 UTC | |
Re: Draft of CGI::Application Tutorial -- RFC
by wfsp (Abbot) on Jul 17, 2008 at 13:33 UTC | |
by bradcathey (Prior) on Jul 17, 2008 at 14:36 UTC | |
Re: Draft of CGI::Application Tutorial -- RFC
by scorpio17 (Canon) on Jul 30, 2008 at 21:27 UTC |