package Bigtop::Backend::CGI::Gantry;
use strict;
use Bigtop;
use Bigtop::Backend::CGI;
use Inline;
sub what_do_you_make {
return [
[ 'app.cgi' => 'CGI or FastCGI dispatching script' ],
[ 'app.server' => 'Stand alone Gantry::Server [optional]' ],
];
}
sub backend_block_keywords {
return [
{ keyword => 'no_gen',
label => 'No Gen',
descr => 'Skip everything for this backend',
type => 'boolean' },
{ keyword => 'fast_cgi',
label => 'FastCGI',
descr => 'Make the script for use with FastCGI',
type => 'boolean' },
{ keyword => 'gantry_conf',
label => 'Use Gantry::Conf',
descr => 'check here if you use the Conf Gantry backend',
type => 'boolean', },
{ keyword => 'with_server',
label => 'Build Server',
descr => 'Turns on stand alone Gantry::Server generation',
type => 'boolean' },
{ keyword => 'server_port',
label => 'Server Port',
descr => 'Specifies the port for stand alone server '
. '[ignored unless Build Server is checked]',
type => 'text' },
{ keyword => 'gen_root',
label => 'Generate Root Path',
descr => q!used to make a default root on request, !
. q!now you get defaults by defaul!,
type => 'deprecated' },
{ keyword => 'flex_db',
label => 'Database Flexibility',
descr => 'Adds command line args to stand alone server to '
. 'allow easy DBD switching',
type => 'boolean',
default => 'false', },
{ keyword => 'template',
label => 'Alternate Template',
descr => 'A custom TT template.',
type => 'text' },
];
}
sub gen_CGI {
my $class = shift;
my $base_dir = shift;
my $tree = shift;
my $configs = $tree->get_app_configs();
my $fast_cgi = $tree->get_config->{CGI}{fast_cgi} || 0;
my $gantry_conf = $tree->get_config->{CGI}{gantry_conf} || 0;
my %cgi_conf_types;
CGI_ONLY_CHECK:
foreach my $conf_type ( keys %{ $configs } ) {
$cgi_conf_types{ $conf_type } = 1 if ( $conf_type =~ /^CGI|CGI$/i );
}
my $there_is_a_cgi = keys %cgi_conf_types;
CONF_TYPE:
foreach my $conf_type ( keys %{ $configs } ) {
my $content = $class->output_cgi(
{
tree => $tree,
configs => $configs,
conf_type => $conf_type,
fast_cgi => $fast_cgi,
base_dir => $base_dir,
}
);
my $write_cgi = 1;
my $file_type = ( $conf_type eq 'base' ) ? '' : "$conf_type.";
my $server_type = $file_type;
if ( $there_is_a_cgi ) {
$file_type = $conf_type;
$write_cgi = 0 if ( $file_type !~ s/^CGI|CGI$// );
}
my $cgi_file = File::Spec->catfile(
$base_dir, "app.${file_type}cgi"
);
Bigtop::write_file( $cgi_file, $content->{ cgi } ) if $write_cgi;
chmod 0755, $cgi_file;
if ( $tree->get_config->{CGI}{with_server} ) {
next CONF_TYPE if ( $gantry_conf and $conf_type ne 'base' );
my $server_file = File::Spec->catfile(
$base_dir,
"app.${server_type}server"
);
Bigtop::write_file( $server_file, $content->{ server } );
chmod 0755, $server_file;
}
}
}
our $template_is_setup = 0;
our $default_template_text = <<'EO_TT_BLOCKS';
[% BLOCK cgi_script %]
#![% perl_path +%]
use strict;
[% literal %]
use CGI::Carp qw( fatalsToBrowser );
use [% app_name %] qw{
-Engine=CGI
-TemplateEngine=[% template_engine +%]
[% IF plugins %] -PluginNamespace=[% app_name +%]
[% plugins +%]
[% END %]
};
use Gantry::Engine::CGI;
my $cgi = Gantry::Engine::CGI->new( {
[% config %]
[% locs %]
} );
$cgi->dispatch();
if ( $cgi->{config}{debug} ) {
foreach ( sort { $a cmp $b } keys %ENV ) {
print "$_ $ENV{$_}
\n";
}
}
[% END %][%# end of block cgi_script %]
[% BLOCK stand_alone_server %]
#![% perl_path +%]
use strict;
[% literal %]
use lib qw( lib );
use [% app_name %] qw{
-Engine=CGI
-TemplateEngine=[% template_engine +%]
Static
[% IF plugins %] -PluginNamespace=[% app_name +%]
[% plugins +%]
[% END %]
};
[% IF flex_db %]
use Getopt::Long;
[% END %]
use Gantry::Server;
use Gantry::Engine::CGI;
[% IF flex_db %]
use Gantry::Conf;
my $dbd;
my $dbuser;
my $dbpass;
my $dbname;
my $conf_instance = '[% instance %]';
my $conf_type;
my $conf_file = 'docs/app.gantry.conf';
GetOptions(
'dbd|d=s' => \$dbd,
'dbuser|u=s' => \$dbuser,
'dbpass|p=s' => \$dbpass,
'dbname|n=s' => \$dbname,
'instance|i=s' => \$conf_instance,
'type|t=s' => \$conf_type,
'file|f=s' => \$conf_file,
'help|h' => \&usage,
);
if ( $conf_type and $conf_type ne 'base' ) {
$conf_instance = "[% instance %]_$conf_type";
}
my $config = Gantry::Conf->retrieve(
{
instance => $conf_instance,
config_file => $conf_file,
}
);
if ( $dbd or $dbname ) {
$dbd ||= 'SQLite';
$config->{ dbconn } = "dbi:$dbd:dbname=$dbname";
}
$config->{ dbuser } = $dbuser if $dbuser;
$config->{ dbpass } = $dbpass if $dbpass;
my $cgi = Gantry::Engine::CGI->new( {
config => $config,
[% locs %]
} );
[% ELSE %]
my $cgi = Gantry::Engine::CGI->new( {
[% config %]
[% locs %]
} );
[% END %]
my $port = shift || [% port || 8080 %];
my $server = Gantry::Server->new( $port );
$server->set_engine_object( $cgi );
print STDERR "Available urls:\n";
foreach my $url ( sort keys %{ $cgi->{ locations } } ) {
print STDERR " http://localhost:${port}$url\n";
}
print STDERR "\n";
$server->run();
[% IF flex_db %]
sub usage {
print << 'EO_HELP';
usage: app.server [options] [port]
port defaults to [% port || 8080 +%]
options:
-h --help prints this message and quits
-i --instance name of a Gantry::Conf instance
defaults to [% instance +%]
-t --type type of one Bigtop config block
defaults to the unnamed block
-f --file master Gantry::Conf file
defaults to docs/app.gantry.conf
options which override Gantry::Conf values:
-d --dbd DBD module name (e.g. Pg, mysql, etc)
-n --dbname name of database
-u --dbuser database user name
-p --dbpass dbuser's database password
Note that -i and -t are incompatible. The former fully specifies an
instance name for Gantry::Conf. The later specifies the config type
suffix of an instance name. If you use both, -t takes precedence.
-d defaults to SQLite.
EO_HELP
exit 0;
}
=head1 NAME
app.server - A generated server for the [% app_name %] app
=head1 SYNOPSIS
usage: app.server [options] [port]
port defaults to 8080
=head1 DESCRIPTION
This is a Gantry::Server based stand alone server for the [% app_name +%]
app. It was built to use the [% instance %] Gantry::Conf instance in the
docs directory.
To override the database connection information in your conf file,
see L below.
To change instances or master conf files, use these
flags (they all require values):
=over 4
=item --instance (or -i)
(Incompatible with --type)
The full name of your conf instance, defaults to [% instance %].
=item --type (or -t)
(Incompatible with --instance)
Use this if you use named config blocks in your Bigtop file. Use the
name of the config block as the value for --type. This will build the
corresponding instance name as [% instance %]_TYPE, where TYPE is the value
of this flag.
If you don't neither --instance nor --type, the instance you get will
be [% instance %].
=item --file (or -f)
The name of your master Gantry::Conf file, defaults to docs/app.gantry.conf.
=back
=head1 Changing Databases without Changing Conf
You may use the following flags to control database connections. If you
supply these flags, they will take precedence over your Gantry::Conf instance.
All of them require values.
=over 4
=item --dbd (or -d)
The name of your DBD module (like SQLite, Pg, or mysql). If you use
dbname, this defaults to SQLite.
=item --dbname (or -n)
The name of your database.
=item --dbuser (or -u)
Your database user name.
=item --dbpass (or -p)
Your database password.
=back
=cut
[% END %][%# end of if flex_db %]
[% END %][%# end of stand_alone_server %]
[% BLOCK fast_cgi_script %]
#![% perl_path +%]
use strict;
use FCGI;
use CGI::Carp qw( fatalsToBrowser );
use [% app_name %] qw{
-Engine=CGI
-TemplateEngine=[% template_engine +%]
[% IF plugins %] -PluginNamespace=[% app_name +%]
[% plugins +%]
[% END %]
};
use Gantry::Engine::CGI;
my $cgi = Gantry::Engine::CGI->new( {
[% config %]
[% locs %]
} );
my $request = FCGI::Request();
while ( $request->Accept() >= 0 ) {
$cgi->dispatch();
if ( $cgi->{config}{debug} ) {
foreach ( sort { $a cmp $b } keys %ENV ) {
print "$_ $ENV{$_}
\n";
}
}
}
[% END %][%# end of block fast_cgi_script %]
[% BLOCK application_loc %]
locations => {
'[% location %]' => '[% name %]',
[% body %]
},
[% END %][%# end of block application_loc %]
[% BLOCK application_config %]
config => {
[% body +%]
},
[% END %][%# end of block application_config %]
[% BLOCK controller_block_loc %]
[% IF rel_loc %]
'[% app_location %]/[% rel_loc %]' => '[% full_name %]',
[% ELSE %]
'[% abs_loc %]' => '[% full_name %]',
[% END %][%# end of if rel_loc %]
[% END %]
[% BLOCK config_body %]
[% FOREACH config IN configs %]
[% IF config.value.match( '^\d+$' ) %]
[% config.name %] => [% config.value %],
[% ELSE %]
[% config.name %] => '[% config.value %]',
[% END %][%# end of if %]
[% END %][%# end of foreach %]
[% END %][%# end of block config %]
EO_TT_BLOCKS
sub setup_template {
my $class = shift;
my $template_text = shift || $default_template_text;
return if ( $template_is_setup );
Inline->bind(
TT => $template_text,
POST_CHOMP => 1,
TRIM_LEADING_SPACE => 0,
TRIM_TRAILING_SPACE => 0,
);
$template_is_setup = 1;
}
sub output_cgi {
my $class = shift;
my $opts = shift;
my $tree = $opts->{ tree };
my $fast_cgi = $opts->{ fast_cgi };
my $conf_type = $opts->{ conf_type };
my $configs = $opts->{ configs };
# first find the base location
my $location_output = $tree->walk_postorder( 'output_location' );
my $location = $location_output->[0] || ''; # default to host root
$location =~ s{/+$}{};
# now build the config and locations hashes
my $config;
my $stand_alone_config;
my $locations = $tree->walk_postorder( 'output_cgi_locations', $location );
my $literals = $tree->walk_postorder( 'output_literal' );
my $app_name = $tree->get_appname();
my $literal = join "\n", @{ $literals };
my $backend_block = $tree->get_config->{CGI};
my $gconf = $backend_block->{ gantry_conf };
my $instance;
my $conffile;
if ( $gconf ) {
my $gantry_conf_block = $tree->get_config->{ Conf };
$instance = $gantry_conf_block->{ instance };
$conffile = $gantry_conf_block->{ conffile };
}
$instance ||= $backend_block->{ instance };
$conffile ||= $backend_block->{ conffile };
if ( $instance ) {
$instance .= "_$conf_type" unless $conf_type eq 'base';
my $conffile_text = '';
if ( $conffile ) {
$conffile_text = ' ' x 8
. "GantryConfFile => '$conffile',";
}
$config =
" config => {
GantryConfInstance => '$instance',
$conffile_text
},
";
if ( $backend_block->{ flex_db } ) {
$stand_alone_config =
' config => {
GantryConfInstance => $conf_instance,
GantryConfFile => $conf_file,
},' . "\n";
}
else {
$stand_alone_config = $config;
}
}
else {
my $config_output = $tree->walk_postorder(
'output_config',
{
backend_block => $backend_block,
conf_type => $conf_type,
configs => $configs,
base_dir => $opts->{ base_dir },
}
);
my %configs = @{ $config_output };
$config = $configs{ cgi_config };
$stand_alone_config = $configs{ stand_along_config };
}
if ( $backend_block->{ flex_db } and not $instance ) {
die "Use of flex_db now requires Conf Gantry backend and "
. "gantry_conf statement.\n";
}
my $port;
$port = $backend_block->{server_port}
if ( defined $backend_block->{server_port} );
my $cgi_output;
my $perl_path = $^X;
if ( $fast_cgi ) {
$cgi_output = Bigtop::Backend::CGI::Gantry::fast_cgi_script(
{
config => $config,
locs => join( '', @{ $locations } ),
app_name => $app_name,
literal => $literal,
%{ $tree->get_config() }, # Go Fish! (think template_engine)
perl_path => $perl_path,
}
);
}
else {
$cgi_output = Bigtop::Backend::CGI::Gantry::cgi_script(
{
config => $config,
locs => join( '', @{ $locations } ),
app_name => $app_name,
literal => $literal,
%{ $tree->get_config() }, # Go Fish! (think template_engine)
perl_path => $perl_path,
}
);
}
my $server_output = Bigtop::Backend::CGI::Gantry::stand_alone_server(
{
config => $stand_alone_config,
locs => join( '', @{ $locations } ),
app_name => $app_name,
literal => $literal,
port => $port,
flex_db => $backend_block->{ flex_db },
%{ $tree->get_config() }, # Go Fish! (think template_engine)
perl_path => $perl_path,
instance => $instance,
}
);
return { cgi => $cgi_output, server => $server_output };
}
package # application
application;
use strict; use warnings;
use Cwd;
sub output_config {
my $self = shift;
my $child_output = shift;
my $data = shift;
my $backend_block = $data->{ backend_block };
# see if there is already a root variable
my $gen_root = 1;
CONFIG_VAR:
foreach my $var ( @{ $child_output } ) {
$var =~ /^\s+(\S+)/;
my $var_name = $1;
if ( $var_name eq 'root' ) {
$gen_root = 0;
last CONFIG_VAR;
}
}
# if no root, make one no questions asked
if ( $gen_root ) {
my $templates = File::Spec->catdir( qw( html templates ) );
if ( $data->{ conf_type } =~ /^CGI|CGI$/ ) {
my $cwd = getcwd();
my $html = File::Spec->catdir( $cwd, $data->{ base_dir }, 'html' );
$templates = File::Spec->catdir( $html, 'templates' );
push @{ $child_output }, " root => '$html:$templates',";
}
else {
push @{ $child_output }, " root => 'html:$templates',";
}
}
my $output = Bigtop::Backend::CGI::Gantry::application_config(
{
body => join "\n", @{ $child_output },
}
);
my @stand_alone_output = @{ $child_output };
if ( $backend_block->{ flex_db } ) {
@stand_alone_output = grep !
/^\s*GantryConfInstance|^\s*GantryConfFile|/,
@{ $child_output };
unshift @stand_alone_output,
' ' x 8 . q!GantryConfInstance => $conf_instance,!,
' ' x 8 . q!GantryConfFile => $conf_file,!;
}
my $extra_output = Bigtop::Backend::CGI::Gantry::application_config(
{
body => join "\n", @stand_alone_output,
}
);
return [ cgi_config => $output, stand_along_config => $extra_output ];
}
sub output_cgi_locations {
my $self = shift;
my $child_output = shift;
my $location = shift || '/';
my $output = Bigtop::Backend::CGI::Gantry::application_loc(
{
location => $location,
name => $self->get_name(),
body => join '', @{ $child_output },
}
);
return [ $output ];
}
package # app_statement
app_statement;
use strict; use warnings;
package # app_config_block
app_config_block;
use strict; use warnings;
sub output_config {
my $self = shift;
my $child_output = shift;
my $data = shift;
my $conf_type = $data->{ conf_type };
my $configs = $data->{ configs };
return unless $child_output;
my $my_type = $self->{__TYPE__} || 'base';
return unless $my_type eq $conf_type;
if ( $my_type ne 'base' ) {
my %config_set_for;
# see what conf was in the named block
foreach my $conf_item ( @{ $child_output } ) {
my $var = $conf_item->{ name };
$config_set_for{ $var }++;
}
# fill in omitted keys from the base block
BASE_KEY:
foreach my $base_key ( keys %{ $configs->{ base } } ) {
next BASE_KEY if $config_set_for{ $base_key };
push @{ $child_output }, {
name => $base_key,
value => $configs->{ base }{ $base_key }
};
}
}
my $output = Bigtop::Backend::CGI::Gantry::config_body(
{
configs => $child_output,
}
);
my @output = split /\n/, $output;
return \@output;
}
package # app_config_statement
app_config_statement;
use strict; use warnings;
sub output_config {
my $self = shift;
my $output_vals = $self->{__ARGS__}->get_args();
return [ { name => $self->{__KEYWORD__}, value => $output_vals } ];
}
package # controller_block
controller_block;
use strict; use warnings;
sub output_cgi_locations {
my $self = shift;
my $child_output = shift;
my $location = shift;
return if $self->is_base_controller();
my %child_loc = @{ $child_output };
if ( keys %child_loc != 1 ) {
die "Error: controller '" . $self->get_name()
. "' must have one location or rel_location statement.\n";
}
my $app = $self->{__PARENT__}{__PARENT__}{__PARENT__};
my $full_name = $app->get_name() . '::' . $self->get_name();
my $output = Bigtop::Backend::CGI::Gantry::controller_block_loc(
{
full_name => $full_name,
rel_loc => $child_loc{rel_location},
abs_loc => $child_loc{location},
app_location => $location,
}
);
return [ $output ];
}
# controller_statement
package # controller_statement
controller_statement;
use strict; use warnings;
sub output_cgi_locations {
my $self = shift;
if ( $self->{__KEYWORD__} eq 'rel_location' ) {
return [ rel_location => $self->{__ARGS__}->get_first_arg() ];
}
elsif ( $self->{__KEYWORD__} eq 'location' ) {
return [ location => $self->{__ARGS__}->get_first_arg() ];
}
else {
return;
}
}
package # literal_block
literal_block;
use strict; use warnings;
sub output_literal {
my $self = shift;
return $self->make_output( 'PerlTop' );
}
1;
=head1 NAME
Bigtop::CGI::Backend::Gantry - CGI dispatch script generator for the Gantry framework
=head1 SYNOPSIS
If your bigtop file includes:
config {
CGI Gantry {
# optional statements:
# to get a stand alone server:
with_server 1;
# to use FastCGI instead of regular CGI:
fast_cgi 1;
}
}
and there are controllers in your app section, this module will generate
app.cgi when you type:
bigtop app.bigtop CGI
or
bigtop app.bigtop all
You can then directly point your httpd.conf directly to the generated
app.cgi.
=head1 DESCRIPTION
This is a Bigtop backend which generates cgi dispatching scripts for Gantry
supported apps.
=head1 KEYWORDS
This module does not register any keywords. See Bigtop::CGI
for a list of allowed keywords (think app and controller level 'location'
and controller level 'rel_location' statements).
=head1 METHODS
To keep podcoverage tests happy.
=over 4
=item backend_block_keywords
Tells tentmaker that I understand these config section backend block keywords:
no_gen
fast_cgi
with_server
server_port
flex_db
gantry_conf
template
instance
conffile
Note that instance and conffile are now deprecated in favor of setting
gantry_conf to true, which draws the values from the Conf Gantry backend.
You may still use them if you like, but that may change in the future.
=item what_do_you_make
Tells tentmaker what this module makes. Summary: app.server and app.cgi.
=item gen_CGI
Called by Bigtop::Parser to get me to do my thing.
=item output_cgi
What I call on the various AST packages to do my thing.
=item setup_template
Called by Bigtop::Parser so the user can substitute an alternate template
for the hard coded one here.
=back
=head1 AUTHOR
Phil Crow
=head1 COPYRIGHT and LICENSE
Copyright (C) 2005 by Phil Crow
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.6 or,
at your option, any later version of Perl 5 you may have available.
=cut