Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

"Hello world" for CGI::Application

by wfsp (Abbot)
on Jul 23, 2006 at 11:24 UTC ( #563095=perlmeditation: print w/ replies, xml ) Need Help??

I'm preparing to move my CGI scripts over to CGI::Application.

The constraints are that any modules I need that don't come with Perl need to be installed locally and therefore need to be pure Perl. Also, the webhoster doesn't provide a cgi-bin dir so the cgi scripts are under docroot.

Please bear with me while I explain how the dir tree is arranged as it will have some bearing on how it all holds together.

I've created a 'project' dir (sw) outside of docroot.

This has 'app' dirs (e.g. test) and the local 'lib' dir.

Each app dir has a 'cnf' dir (config files), tmpl dir (template files), upld (uploaded files) etc..

The lib dir has the extra CPAN modules required plus a project dir (SW) for project specific modules and 'app' dirs (like Test).

my_root # top dir of web space docroot # html files and the cgi instance scripts sw lib CGI HTML # etc. SW Test OtherApps test cnf tmpl upld otherapps cnf tmpl upld

The first thing the instance script needs to do is set use lib;.

Rather that hard code the path to the local lib it knocks the last dir off the docroot env var and constructs the path given the scheme above. Due to the way CGI::App works the instance script is very short which makes keeping them in docroot less of a worry.

#!/usr/bin/perl -T # instance script # somewhere under docroot use strict; use warnings; #use CGI::Carp qw(fatalsToBrowser); use File::Spec; my ($my_root, $project, $lib); BEGIN { $ENV{DOCUMENT_ROOT} =~ m|([\w/-]+)/|; $my_root = $1; $project = 'sw'; $lib = File::Spec->catdir($my_root, $project, 'lib'); } use lib $lib; use SW::Test::TestApp; $ENV{PATH} = ''; my $c = SW::Test::TestApp->new( PARAMS => { 'root' => $my_root, 'project' => $project, 'app' => 'test', }, ); $c->run;

SW::Test::TestApp; is a sandbox that exercises all the plugins. A CGI::App 'hello world' script! Like nurse, something to hang on to when the going gets rough!

package SW::Test::TestApp; use strict; use warnings; use base 'SW::ProjectClass'; sub setup { my ($self) = @_; $self->error_mode( 'error_runmode' ); $self->start_mode( 'm1' ); $self->mode_param( path_info=> 1, param =>'rm' ); $self->run_modes( 'm1' => 'start', 'm2' => 'show_log_records', 'm5' => 'show_session', 'm7' => 'get_cnf_value', 'm8' => 'force_error', 'm9' => 'send_email_test', 'm10' => 'path_info', 'm11' => 'upload_get', 'm12' => 'upload_process', ); } # and then all the run modes # to exercise each plugin sub start { # }

CGI::Apps cgiapp_init method is in and will be the base class for all the apps.

The project super class loads all the required CGI::App::Plugins, configures them, sets a common error_runmode and sets cgiapp_get_query to CGI::Simple. As the instance script sets the root, project and app params the correct path for template files, config files, upload dirs, mysql cnfig file etc. are easily obtained.

package SW::ProjectClass; use strict; use warnings; use base 'CGI::Application'; use CGI::Simple; use CGI::Application::Plugin::Config::MyNewt; use CGI::Application::Plugin::DBH (qw/dbh_config dbh/); use CGI::Application::Plugin::LogDispatch; use CGI::Application::Plugin::Session; use CGI::Application::Plugin::SQL::Library; use MIME::Lite; sub cgiapp_init { my $self = shift; my $app_path = File::Spec->catdir( $self->param('root'), $self->param('project'), $self->param('app') ); $self->tmpl_path( File::Spec->catdir($app_path, 'tmpl'), ); my $cnf_path = File::Spec->catdir($app_path, 'cnf'); my $cnf_file = join('.', $self->param('app'), 'cnf'); $self->config_init( File::Spec->catfile($cnf_path, $cnf_file), ); my $dsn = join ('', "DBI:mysql:;mysql_read_default_file=", File::Spec->catfile($cnf_path, 'db.cnf'), ); $self->dbh_config( $dsn, undef, undef, {RaiseError => 1} ); $self->sql_lib_config( File::Spec->catfile($cnf_path, 'sql.lib'), ); $self->session_config( CGI_SESSION_OPTIONS => [ "driver:MySQL;serializer:Storable", $self->query, {Handle=>$self->dbh} ], DEFAULT_EXPIRY => '+1w', COOKIE_PARAMS => { -expires => '+24h', -path => '/' }, SEND_COOKIE => 1, ); $self->log_config( LOG_DISPATCH_MODULES => [ { module => 'Log::Dispatch::DBI', name => 'dbi', dbh => $self->dbh, table => 'log', min_level => 'debug', }, { module => 'Log::Dispatch::Email::MailSend', name => 'email', to => [qw(], subject => 'Log message from', min_level => 'emerg' } ] ); } sub cgiapp_get_query { my $self = shift; $CGI::Simple::DISABLE_UPLOADS = 0; # enable uploads $CGI::Simple::POST_MAX = 1_048_576; # allow 1MB uploads return CGI::Simple->new(); } sub mime_email { my ($self, $over_ride) = @_; my %default = ( From => '', To => '', Subject => 'website enquiry', Type => 'text/html', Data => undef, ); @default{keys %{$over_ride}} = values %{$over_ride}; my $msg = MIME::Lite->new(%default,) or die "Error creating MIME body: $!\n"; $msg->send or die "Error sending MIME body: $!\n"; return \%default; } sub error_runmode{ my ($self, $msg) = @_; $self->log->emerg("Error: $msg"); my $tmpl_obj = $self->load_tmpl('error.html'); $tmpl_obj->param(msg => $msg,); return $tmpl_obj->output; } 1;

The apps I'll be converting use mysql as a backend which is why I've used it for session tracking and logging.

I still have a few wrinkles to sort out.

I've tried, as yet unsuccessfully, to install Template::Toolkit to the local lib. As explained before it would need to be the pure Perl version. I have to confess that tracking down the dependancy trail and installing those on the webhost has defeated me but it's still on the list. As it happens I'm a great fan of HTML::Template so this isn't a major draw back.

I had a similar snag with CGI::Application::Plugin::ValidateRM. The dependancies seemed to go on for ever! Also on the list.

I've yet to investigate CGI::Application::MailPage (I've rolled my own sub) and, for my sins, I've also rolled my own config module. At the moment I'm leaning toward CGI::Application::Plugin::Config::Simple.

I'm quite pleased with my very own CGI::Application::Plugin::SQL::Library, a simple wrapper around SQL::Library. ("Mum! Look! I've written a plugin!" :-))

For any monks in a similar situation to myself I would strongly recommend having a serious look at CGI::Application.

There is some work up front straightening out your structure and your base class. But after that things like query handling, sessions, db connection, logging, error handling, templating, configuration etc. all just work.

Removed email/url details

Comment on "Hello world" for CGI::Application
Select or Download Code

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://563095]
Approved by g0n
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (12)
As of 2014-08-20 12:48 GMT
Find Nodes?
    Voting Booth?

    The best computer themed movie is:

    Results (113 votes), past polls