@@ -1,17 +1,18 @@
-# $Id: Build.PL,v 1.2 2004/02/14 02:09:17 mark Exp $
use Module::Build;
my $build = Module::Build->new
(
module_name => 'CGI::Application',
license => 'perl',
requires => {
- # For Moved vs Found change
'CGI' => 0,
'HTML::Template' => 0,
'Test::More' => 0.47,
'Carp' => 0,
'Class::ISA' => 0,
},
+ recommends => {
+ CGI::PSGI => 0.09, # If you want to use run_as_psgi()
+ },
'dist_author' => [
'Jesse Erlbaum <jesse@erlbaum.net>',
'Mark Stosberg <mark@summersault.com>',
@@ -1,5 +1,26 @@
Revision history for CGI::Application.
+4.50 Thu Jun 16, 2011
+
+ [FEATURES]
+ Better PSGI support in the core
+ - run_as_psgi() works like run, but directly returns the expected PSGI response structure
+ - psgi_app() wraps up all the PSGI bits to call and run the application and return a PSGI
+ code ref:
+
+ $psgi_coderef = WebApp->psgi_app({ ... args to new() ... });
+
+ [DOCUMENTATION]
+ - docs for header_props() were improved
+
+ [INTERNAL]
+ - source control management was moved from darcs to git. Those who prefer
+ darcs are are more familiar with it are advised to try the darcs-git.py
+ wrapper for git.
+
+ - The test file psgi_app.t is missing from this release and will be added later.
+
+
4.31 Wed Jul 29, 2009
[FEATURES]
@@ -28,6 +28,7 @@ t/mode_param_path_info.t
t/mode_param_overwritten.t
t/load_tmpl_hook.t
t/query.t
+t/run_as_psgi.t
t/lib/TestApp.pm
t/lib/TestApp2.pm
t/lib/TestApp3.pm
@@ -44,3 +45,4 @@ t/lib/TestApp13.pm
t/lib/TestApp14.pm
t/lib/TestCGI.pm
t/lib/templates/test.tmpl
+META.json
@@ -0,0 +1,57 @@
+{
+ "abstract" : "Framework for building reusable web-applications",
+ "author" : [
+ "Jesse Erlbaum <jesse@erlbaum.net>",
+ "Mark Stosberg <mark@summersault.com>",
+ "with the help of many others!"
+ ],
+ "dynamic_config" : 1,
+ "generated_by" : "Module::Build version 0.38, CPAN::Meta::Converter version 2.110930",
+ "license" : [
+ "perl_5"
+ ],
+ "meta-spec" : {
+ "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
+ "version" : "2"
+ },
+ "name" : "CGI-Application",
+ "no_index" : {
+ "file" : []
+ },
+ "prereqs" : {
+ "configure" : {
+ "requires" : {
+ "Module::Build" : "0.38"
+ }
+ },
+ "runtime" : {
+ "recommends" : {
+ "CGI::PSGI" : "0.09"
+ },
+ "requires" : {
+ "CGI" : 0,
+ "Carp" : 0,
+ "Class::ISA" : 0,
+ "HTML::Template" : 0,
+ "Test::More" : "0.47"
+ }
+ }
+ },
+ "provides" : {
+ "CGI::Application" : {
+ "file" : "lib/CGI/Application.pm",
+ "version" : "4.50"
+ },
+ "CGI::Application::Mailform" : {
+ "file" : "lib/CGI/Application/Mailform.pm",
+ "version" : 0
+ }
+ },
+ "release_status" : "stable",
+ "resources" : {
+ "license" : [
+ "http://dev.perl.org/licenses/"
+ ]
+ },
+ "version" : "4.50"
+}
@@ -1,29 +1,36 @@
---
-name: CGI-Application
-version: 4.31
+abstract: 'Framework for building reusable web-applications'
author:
- 'Jesse Erlbaum <jesse@erlbaum.net>'
- 'Mark Stosberg <mark@summersault.com>'
- - "with the help of many others!"
-abstract: Framework for building reusable web-applications
+ - 'with the help of many others!'
+build_requires: {}
+configure_requires:
+ Module::Build: 0.38
+dynamic_config: 1
+generated_by: 'Module::Build version 0.38, CPAN::Meta::Converter version 2.110930'
license: perl
-requires:
- CGI: 0
- Carp: 0
- Class::ISA: 0
- HTML::Template: 0
- Test::More: 0.47
-generated_by: Module::Build version 0.32
meta-spec:
- url: http://module-build.sourceforge.net/META-spec-v1.2.html
- version: 1.2
+ url: http://module-build.sourceforge.net/META-spec-v1.4.html
+ version: 1.4
+name: CGI-Application
no_index:
- file:
+ file: []
provides:
CGI::Application:
file: lib/CGI/Application.pm
- version: 4.31
+ version: 4.50
CGI::Application::Mailform:
file: lib/CGI/Application/Mailform.pm
+ version: 0
+recommends:
+ CGI::PSGI: 0.09
+requires:
+ CGI: 0
+ Carp: 0
+ Class::ISA: 0
+ HTML::Template: 0
+ Test::More: 0.47
resources:
- license: ~
+ license: http://dev.perl.org/licenses/
+version: 4.50
@@ -1,7 +1,8 @@
-# Note: this file was auto-generated by Module::Build::Compat version 0.32
+# Note: this file was auto-generated by Module::Build::Compat version 0.3800
use ExtUtils::MakeMaker;
WriteMakefile
(
+ 'PL_FILES' => {},
'INSTALLDIRS' => 'site',
'NAME' => 'CGI::Application',
'EXE_FILES' => [],
@@ -3,7 +3,7 @@ use Carp;
use strict;
use Class::ISA;
-$CGI::Application::VERSION = '4.31';
+$CGI::Application::VERSION = '4.50';
my %INSTALLED_CALLBACKS = (
# hook name package sub
@@ -196,21 +196,50 @@ sub run {
# Call cgiapp_postrun() hook
$self->call_hook('postrun', \$body);
- # Set up HTTP headers
- my $headers = $self->_send_headers();
-
- # Build up total output
- my $output = $headers.$body;
+ my $return_value;
+ if ($self->{__IS_PSGI}) {
+ my ($status, $headers) = $self->_send_psgi_headers();
+ $return_value = [ $status, $headers, [ $body ]];
+ }
+ else {
+ # Set up HTTP headers non-PSGI responses
+ my $headers = $self->_send_headers();
- # Send output to browser (unless we're in serious debug mode!)
- unless ($ENV{CGI_APP_RETURN_ONLY}) {
- print $output;
- }
+ # Build up total output
+ $return_value = $headers.$body;
+ print $return_value unless $ENV{CGI_APP_RETURN_ONLY};
+ }
# clean up operations
$self->call_hook('teardown');
- return $output;
+ return $return_value;
+}
+
+
+sub psgi_app {
+ my $class = shift;
+ my $args_to_new = shift;
+
+ return sub {
+ my $env = shift;
+
+ if (not defined $args_to_new->{QUERY}) {
+ require CGI::PSGI;
+ $args_to_new->{QUERY} = CGI::PSGI->new($env);
+ }
+
+ my $webapp = $class->new($args_to_new);
+ return $webapp->run_as_psgi;
+ }
+}
+
+sub run_as_psgi {
+ my $self = shift;
+ $self->{__IS_PSGI} = 1;
+
+ # Run doesn't officially support any args, but pass them through in case some sub-class uses them.
+ return $self->run(@_);
}
@@ -601,6 +630,7 @@ sub get_current_runmode {
###########################
+# return headers as a string
sub _send_headers {
my $self = shift;
my $q = $self->query;
@@ -613,6 +643,20 @@ sub _send_headers {
: croak "Invalid header_type '$type'"
}
+# return a 2 element array modeling the first PSGI redirect values: status code and arrayref of header pairs
+sub _send_psgi_headers {
+ my $self = shift;
+ my $q = $self->query;
+ my $type = $self->header_type;
+
+ return
+ $type eq 'redirect' ? $q->psgi_redirect( $self->header_props )
+ : $type eq 'header' ? $q->psgi_header ( $self->header_props )
+ : $type eq 'none' ? ''
+ : croak "Invalid header_type '$type'"
+
+}
+
# Make all hash keys CAPITAL
# although this method is internal, some other extensions
@@ -671,6 +715,10 @@ CGI::Application - Framework for building reusable web-applications
my $webapp = WebApp->new();
$webapp->run();
+ ### Or, in a PSGI file, webapp.psgi
+ use WebApp;
+ WebApp->psgi_app();
+
=head1 INTRODUCTION
CGI::Application makes it easier to create sophisticated, high-performance,
@@ -971,7 +1019,69 @@ data returned is print()'ed to STDOUT and to the browser. If
the specified mode is not found in the run_modes() table, run() will
croak().
-=head2 Methods to possibly override
+=head2 PSGI support
+
+CGI::Application offers native L<PSGI> support. The default query object
+for this is L<CGI::PSGI>, which simply wrappers CGI.pm to provide PSGI
+support to it.
+
+=head3 psgi_app()
+
+ $psgi_coderef = WebApp->psgi_app({ ... args to new() ... });
+
+The simplest way to create and return a PSGI-compatible coderef. Pass in
+arguments to a hashref just as would to new. This returns a PSGI-compatible
+coderef, using L<CGI:::PSGI> as the query object. To use a different query
+object, construct your own object using C<< run_as_psgi() >>, as shown below.
+
+It's possible that we'll change from CGI::PSGI to a different-but-compatible
+query object for PSGI support in the future, perhaps if CGI.pm adds native
+PSGI support.
+
+=head3 run_as_psgi()
+
+ my $psgi_aref = $webapp->run_as_psgi;
+
+Just like C<< run >>, but prints no output and returns the data structure
+required by the L<PSGI> specification. Use this if you want to run the
+application on top of a PSGI-compatible handler, such as L<Plack> provides.
+
+If you are just getting started, just use C<< run() >>. It's easy to switch to using
+C<< run_as_psgi >> later.
+
+Why use C<< run_as_psgi() >>? There are already solutions to run
+CGI::Application-based projects on several web servers with dozens of plugins.
+Running as a PSGI-compatible application provides the ability to run on
+additional PSGI-compatible servers, as well as providing access to all of the
+"Middleware" solutions available through the L<Plack> project.
+
+The structure returned is an arrayref, containing the status code, an arrayref
+of header key/values and an arrayref containing the body.
+
+ [ 200, [ 'Content-Type' => 'text/html' ], [ $body ] ]
+
+By default the body is a single scalar, but plugins may modify this to return
+other value PSGI values. See L<PSGI/"The Response"> for details about the
+response format.
+
+Note that calling C<< run_as_psgi >> only handles the I<output> portion of the
+PSGI spec. to handle the input, you need to use a CGI.pm-like query object that
+is PSGI-compliant, such as L<CGI::PSGI>. This query object must provide L<psgi_header>
+and L<psgi_redirect> methods.
+
+The final result might look like this:
+
+ use WebApp;
+ use CGI::PSGI;
+
+ my $handler = sub {
+ my $env = shift;
+ my $webapp = WebApp->new({ QUERY => CGI::PSGI->new($env) });
+ $webapp->run_as_psgi;
+ };
+
+
+=head2 Methods to possibly override
CGI::Application implements some methods which are expected to be overridden
by implementing them in your sub-class module. These methods are as follows:
@@ -1596,17 +1706,24 @@ been set.
=head3 header_props()
- $webapp->header_props(-type=>'image/gif',-expires=>'+3d');
+ # Set a complete set of headers
+ %set_headers = $webapp->header_props(-type=>'image/gif',-expires=>'+3d');
+
+ # clobber / reset all headers
+ %set_headers = $webapp->header_props({});
+
+ # Just retrieve the headers
+ %set_headers = $webapp->header_props();
The C<header_props()> method expects a hash of CGI.pm-compatible
HTTP header properties. These properties will be passed directly
-to CGI.pm's C<header()> or C<redirect()> methods. Refer to L<CGI>
-for exact usage details.
+to the C<header()> or C<redirect()> methods of the query() object. Refer
+to the docs of your query object for details. (Be default, it's L<CGI>.pm).
-Calling header_props any arguments will clobber any existing headers that have
+Calling header_props with an empty hashref clobber any existing headers that have
previously set.
-C<header_props()> return a hash of all the headers that have currently been
+C<header_props()> returns a hash of all the headers that have currently been
set. It can be called with no arguments just to get the hash current headers
back.
@@ -2349,10 +2466,9 @@ some people involved with the project there.
B<Source Code>
-This project is managed using the darcs source control system (
-http://www.darcs.net/ ). The darcs archive is here:
-http://mark.stosberg.com/darcs_hive/cgi-app
+This project is managed using git and is available on Github:
+ https://github.com/markstos/CGI--Application
=head1 SEE ALSO
@@ -1,6 +1,6 @@
use strict;
-use Test::More tests => 6;
+use Test::More tests => 9;
BEGIN{use_ok('CGI::Application');}
@@ -63,3 +63,32 @@ $ENV{CGI_APP_RETURN_ONLY} = 1;
);
}
+{
+ my $app = CGI::Application->new;
+
+ $app->header_props({ -type => 'banana/ripe' });
+
+ like(
+ $app->run,
+ qr{Content-type: banana/ripe}i,
+ "headed added via hashref arg to header_props",
+ );
+
+ $app->header_props();
+
+ like(
+ $app->run,
+ qr{Content-type: banana/ripe}i,
+ "Calling with no args is safe",
+ );
+
+ $app->header_props({});
+
+ unlike(
+ $app->run,
+ qr{Content-type: banana/ripe}i,
+ "Calling with an empty hashref clobbers existing data",
+ );
+
+}
+
@@ -0,0 +1,48 @@
+
+use Test::More;
+use CGI::Application;
+
+eval { require CGI::PSGI; };
+# XXX, really, we need CGI::PSGI 0.09 or later.
+if ($@) {
+ plan 'skip_all' => 'CGI::PSGI is not available';
+}
+else {
+ plan 'no_plan';
+}
+
+
+# Set up a CGI environment
+my $env;
+$env->{REQUEST_METHOD} = 'GET';
+$env->{QUERY_STRING} = 'game=chess&game=checkers&weather=dull';
+$env->{PATH_INFO} = '/somewhere/else';
+$env->{PATH_TRANSLATED} = '/usr/local/somewhere/else';
+$env->{SCRIPT_NAME} = '/cgi-bin/foo.cgi';
+$env->{SERVER_PROTOCOL} = 'HTTP/1.0';
+$env->{SERVER_PORT} = 8080;
+$env->{SERVER_NAME} = 'the.good.ship.lollypop.com';
+$env->{REQUEST_URI} = "$env->{SCRIPT_NAME}$env->{PATH_INFO}?$env->{QUERY_STRING}";
+$env->{HTTP_LOVE} = 'true';
+
+package TestApp;
+use base 'CGI::Application';
+sub setup {
+ my $self = shift;
+ $self->run_modes(
+ start => sub { 'Hello World' },
+ );
+}
+
+package main;
+
+my $app = TestApp->new( QUERY => CGI::PSGI->new($env) );
+
+my $response = $app->run_as_psgi;
+
+is_deeply $response, [
+ '200',
+ [ 'Content-Type' => 'text/html; charset=ISO-8859-1' ],
+ [ 'Hello World' ],
+],
+"run_as_psgi: reality check basic response";