views:

249

answers:

5

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?

A: 

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).

Zenham
You'll also need to set various environment variables to simulate the stuff the web server would set up. CGI.pm looks for those so it knows what to do.
brian d foy
@brian d foy: not unless your code for some reason depends on those; CGI.pm does fine without them.
ysth
@ysth: if this run mode has side effects, it should be using POST since its not an idempotent request. In that case, you have to add -debug to CGI.pm's import to allow this stuff to work. That's a change to the code which has side effects too. It's much easier to do it as George says.
brian d foy
A: 

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.

brian d foy
I completely agree, there is a library that does the actual work generating the result and i've tried to limit the amount of code in the 'Controller' portion of the CGI::A to the point where its just formatting. Its just the 'Laziness' part of me that thinks I could use the CGI::App to send the email rather than write another script to call the library.
Todd Hunter
Well, Laziness with a capital L allows you to easily do other tasks without future work: that Laziness is not the avoidance of work but the upfront work to save time later.
brian d foy
(obligatory reference to YAGNI, although I tend to operate in capital L mode most of the time)
Zenham
It's ironic to mention YAGNI in a case where he's actually asking for it.
brian d foy
+3  A: 

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).

George Phillips
When I do that, I normally use an exec. No big whoop though.
brian d foy
+4  A: 

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.

Todd Hunter
It's even easier than that; see http://search.cpan.org/perldoc/CGI#DEBUGGING
ysth
True, the 'your_script.pl name1=value1 name2=value2' form works for running the basic CGI::App .cgi file, however I would lose any ability to control input, set defaults etc.
Todd Hunter
A: 

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..

sidler