How can I run a CGI::Application run mode from the command line?

4.6k Views Asked by At

I have a run mode in my CGI::Application web-app that I would like to be able to trigger from the command line so i can automate it. From the web-app's perspective it does some processing then sends the results in an email.

When called from the web interface it passes in a set of parameters (email address, which query to run, date, etc) so these need to be passed in.

How can I construct a call to the CGI::Application app that will be the same as if I ran it from the web?

6

There are 6 best solutions below

2
On BEST ANSWER

Upon further digging through the CGI::App and the CGI documentation, it appeared to be more straightforward than I thought. The simplest case (no real argument handling or dealing with the output from the webapp run call) is:

#!/usr/bin/perl

use strict;
use warnings;

use CGI;
use WebApp;

my $cgi = new CGI( \%{@ARGV} );

my $webapp = WebApp->new( QUERY => $cgi );
$webapp->run();

It just takes a series of space separated name value pairs to create the CGI. You need to pass in the run mode and all the arguments.

0
On

I'm the maintainer of CGI::Application, and I do this all the time-- I have dozen of cron scripts built with CGI::Application because it's convenient to share the infrastructure with the application.

The simplest approach is this:

# There is no browser to return results to.
$ENV{CGI_APP_RETURN_ONLY} = 1;

my $app = WebApp->new;
$app->direct_run_mode_method;

In that example, you bypass the normal flow and call a method directly. Be sure you don't need any of the "setup" or "teardown" actions to happen in that case.

If you just have one run mode you are calling, you can also just set the "start_mode", and call run(), so then the default run mode is called by default.

Another idea: you can use a module like Getopt::Long and pass in values through the PARAM hash to new(), or completely replace the run-mode selection process. Here's an example where command line flags are used to determine the run mode:

sub setup {
    my $self = shift;

    $self->start_mode('send_error_digests');
    $self->run_modes([qw/
        send_error_digests
        help
    /]);

    my ($dry_run, $help);
    GetOptions(
        'dry-run' => \$dry_run,
        'help'    => \$help
    );

    $self->param('dry_run' => $dry_run);

    $self->mode_param(sub {
        return 'help' if $help;
        return $self->start_mode();
    });
}
3
On

Thusly:

$ perl yourscript.pl  field1=value1 field2=value2

Perl's CGI library takes care of the magic for you, and it appears that CGI::Application relies on CGI (judging from their example code).

4
On

Instead of having to go through CGI::Application every time you want to get something done, enforce a proper separation of concerns, perhaps using an MVC setup. All of the functionality should exist outside of the CGI::Application stuff since that should only work as a controller. Once you separate out those bits, you can easily write other controllers for other input methods.

Don't write a web application; write an an application that happens to have a web interface. When you have that, you can easily give your application other sorts of interfaces.

2
On

The original CGI specification makes it easy to run things from the command line and was fully intended not as a specific HTTP-only interface but something that could handle FTP and gopher as well as new top-level URL schemes. I know what I wanted when I helped specify it.

The spec I referenced should give you all you need, but for the most part it is just a collection of environment variables. If you see a request for:

http://some.server.com/some/path?a=b&c=d

The environment variables come out looking like this:

SERVER_PROTOCOL=http
REQUEST_METHOD=GET
HTTP_HOST=some.server.com
SERVER_PORT=80
PATH_INFO=/some/path
QUERY_INFO=a=b&c=d

To reverse the polarity of that in Perl would go something like this:

$ENV{'SERVER_PROTOCOL'} = 'http';
$ENV{'REQUEST_METHOD'} = 'GET';
$ENV{'SERVER_PORT'} = 80;
$ENV{'PATH_INFO'} = '/some/path';
$ENV{'QUERY_INFO'} = 'a=b&c=d';
system("perl your-CGI-script.pl");

Things get a bit more complicated in handling POST queries and there are more possible environment variables that may be required. Worst case you can enumerate them all with a quick CGI script something like:

print "Content-Type: text/plain\r\n\r\n";
foreach (keys(%ENV))
{
    print "$_=$ENV{$_}\r\n";
}

Now put that on the web server in place of your CGI script and you'll see all the environment that gets passed in (and the original environment so you'll need to make a few judgement calls).

1
On

You could automate by calling the web app using curl, wget, or an LWP GET-script with the appropriate parameters. I've used a similar system for cron-driven tasks with a Catalyst application.

That deals with all the environment variables for you..