cvsuser 01/11/16 15:21:39
Added: P5EEx/Blue CHANGES MANIFEST Makefile.PL README TODO
P5EEx/Blue/P5EEx/Blue Config.pm Config.pod Context.pm
P5EE.pm datetime.pod podstyle.pod styleguide.pod
Log:
initial stuff
Revision Changes Path
1.1 p5ee/P5EEx/Blue/CHANGES
Index: CHANGES
===================================================================
#########################################
# CHANGE LOG
#########################################
1.1 p5ee/P5EEx/Blue/MANIFEST
Index: MANIFEST
===================================================================
CHANGES
MANIFEST
Makefile.PL
README
TODO
P5EEx/Blue/styleguide.pod
P5EEx/Blue/design.pod
P5EEx/Blue.pm
P5EEx/Blue/Config.pm
P5EEx/Blue/Context.pm
P5EEx/Blue/Context/CGI.pm
P5EEx/Blue/Config/XML.pm
P5EEx/Blue/Utils/Date.pm
P5EEx/Blue/Utils/HTML.pm
1.1 p5ee/P5EEx/Blue/Makefile.PL
Index: Makefile.PL
===================================================================
######################################################################
## File: $Id: Makefile.PL,v 1.1 2001/11/16 23:21:38 spadkins Exp $
######################################################################
use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
%opts = (
'INSTALLDIRS' => 'perl',
'NAME' => 'P5EEx-Blue',
'DISTNAME' => 'P5EEx-Blue',
'VERSION' => '0.01',
'PMLIBDIRS' => [ 'P5EEx', ],
'linkext' => { LINKTYPE=>'' }, # no link needed
'dist' => {'COMPRESS'=>'gzip -9f', 'SUFFIX' => 'gz',
'ZIP'=>'/usr/bin/zip','ZIPFLAGS'=>'-rl'}
);
######################################################################
# PREFIX
######################################################################
# I like setting the PREFIX variable in the environment,
# so I write my Makefile.PL to use it as a valid alternative
# to specifying it on the command line with
# "perl Makefile.PL PREFIX=/usr/foo".
if ($ENV{PREFIX}) {
$PREFIX = $ENV{PREFIX};
$opts{PREFIX} = $PREFIX;
}
else {
$PREFIX = "/usr/local";
}
######################################################################
# HTML FILES
######################################################################
if ($ENV{HTMLDIR}) {
$HTMLDIR = $ENV{HTMLDIR};
}
else {
$HTMLDIR = "$PREFIX/htdocs/api";
}
$opts{'INST_HTMLLIBDIR'} = 'htdocs'; # local build area
$opts{'INSTALLHTMLPRIVLIBDIR'} = "$HTMLDIR/$opts{NAME}"; # install area
######################################################################
# CGI SCRIPTS
######################################################################
if ($ENV{CGIDIR}) {
$CGIDIR = $ENV{CGIDIR};
}
else {
$CGIDIR = "$PREFIX/cgi-bin";
}
$opts{'INST_HTMLSCRIPTDIR'} = 'cgi-bin'; # local build area
$opts{'INSTALLHTMLSCRIPTDIR'} = "$CGIDIR/$opts{NAME}"; # install area
######################################################################
# TEMPLATES
######################################################################
if ($ENV{TEMPLATEDIR}) {
$TEMPLATEDIR = $ENV{TEMPLATEDIR};
}
else {
$TEMPLATEDIR = "$PREFIX/templates";
}
$opts{'INST_SCRIPT'} = 'templates'; # local build area
$opts{'INSTALLSCRIPT'} = "$TEMPLATEDIR/$opts{NAME}"; # install area
######################################################################
# MAKE THE MAKEFILE
######################################################################
WriteMakefile(%opts);
sub MY::postamble {
return <<'EOF';
all ::
@#rm -f $(INSTALLHTMLSCRIPTDIR)/cgi.conf
@#mkdir -p $(INSTALLHTMLSCRIPTDIR)
@#echo "perlinc = $(INSTALLPRIVLIB)" > $(INSTALLHTMLSCRIPTDIR)/cgi.conf
@#echo "templatedir = $(INSTALLSCRIPT)" >> $(INSTALLHTMLSCRIPTDIR)/cgi.conf
@#[ -f $(PREFIX)/.prefixvars ] && cat $(PREFIX)/.prefixvars >>
$(INSTALLHTMLSCRIPTDIR)/cgi.conf
EOF
}
1.1 p5ee/P5EEx/Blue/README
Index: README
===================================================================
######################################################################
## File: $Id: README,v 1.1 2001/11/16 23:21:38 spadkins Exp $
######################################################################
WHAT IS THIS?
This is the Perl 5 Enterprise Extensions (P5EE) distribution.
For more information, see the web pages and join the mailing list at
[EMAIL PROTECTED]
HOW DO I INSTALL IT?
To install this module, cd to the directory that contains this README
file and type the following:
perl Makefile.PL
make
make test
make install
1.1 p5ee/P5EEx/Blue/TODO
Index: TODO
===================================================================
######################################################################
## File: $Id: TODO,v 1.1 2001/11/16 23:21:38 spadkins Exp $
######################################################################
====================================
= TODO
====================================
Envisioned P5EE Components
o P5EE::Standard - Mainly documentation about modules that P5EE relies on.
o P5EE::Config - Configuration for all the other modules
o P5EE::Authen - Authentication
o P5EE::Authz - Authorization
Features
o none yet
1.1 p5ee/P5EEx/Blue/P5EEx/Blue/Config.pm
Index: Config.pm
===================================================================
######################################################################
## $Id: Config.pm,v 1.1 2001/11/16 23:21:38 spadkins Exp $
######################################################################
package P5EEx::Blue::Config;
$VERSION = do { my @r=(q$Revision: 1.1 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r};
use strict;
# NOTE: The pod material has been separated out to Config.pod because
# MakeMaker ignores files matching /(config|setup).*.pm/
######################################################################
# CONSTANTS
######################################################################
######################################################################
# ATTRIBUTES
######################################################################
# INPUTS FROM THE ENVIRONMENT
######################################################################
# CONSTRUCTOR
######################################################################
sub new {
my ($this) = @_;
my ($class, $self);
$class = ref($this) || $this;
# bootstrap phase: bless an empty hash
$self = {};
bless $self, $class;
# load phase: replace empty hash with loaded hash
$self = $self->load();
bless $self, $class;
$self->init(); # allows a subclass to override this portion
return $self;
}
# available for subclasses to override
sub init {
my $self = shift;
}
sub load {
local(*FILE);
my ($file, @perl, $perl, $conf, $open);
$file = $0;
$file =~ s!\.[^/]*$!!;
$file .= ".pl";
$conf = {};
$open = open(main::FILE,"< $file");
$open = open(main::FILE,"< widget.pl") if (!$open);
if ($open) {
@perl = <main::FILE>;
close(main::FILE);
$perl = join("",@perl);
eval($perl);
print STDERR "CONFIGURATION SYNTAX ERROR: $@\n" if ($@);
}
$conf;
}
######################################################################
# METHODS
######################################################################
use Data::Dumper;
sub dump {
my $self = shift;
my $d = Data::Dumper->new([ $self ], [ "data" ]);
$d->Indent(1);
return $d->Dump();
}
sub print {
my $self = shift;
print $self->dump();
}
1;
1.1 p5ee/P5EEx/Blue/P5EEx/Blue/Config.pod
Index: Config.pod
===================================================================
######################################################################
## $Id: Config.pod,v 1.1 2001/11/16 23:21:38 spadkins Exp $
######################################################################
=head1 NAME
P5EEx::Blue::Config - Static site configuration data (used internally to the library)
=head1 SYNOPSIS
use P5EEx::Blue::Config;
$config = P5EEx::Blue::Config->new();
$config = P5EEx::Blue::Config->new("file" => $file);
$config = P5EEx::Blue::Config->new("file" => $file, "configClass" =>
"P5EEx::Blue::Config::XML");
print $config->dump(), "\n"; # use Data::Dumper to spit out the Perl
representation
$config->print($fh); # print out a config file
$config->overlay($config2); # merge the two config structures using
overlay rules
$config->overlay($config1, $config2); # merge $config2 onto $config1
# The configuration for each component within P5EEx::Blue will be located
# in the first level under the hash ref.
$config->{Standard} # config settings for the Standard component
$config->{Config} # config settings for the Config component
$config->{Auth} # config settings for the Auth component
# The default driver (if "configClass" not supplied) reads in a Perl
# data structure from the file. Alternate drivers can read a Storable,
# unvalidated XML, DTD-validated XML, RDF-validated XML, or any other
# file format or data source anyone cares to write a driver for.
$conf = { # must be named $conf
'Standard' => {
'Log-Dispatch' => {
'logdir' => '/var/p5ee',
}
},
'Authen' => {
'passwd' => '/etc/passwd',
'seed' => '303292',
},
};
# A comparable XML file would look like this.
<conf>
<Standard>
<Log-Dispatch logdir="/var/p5ee"/>
</Standard>
<Authen passwd="/etc/passwd" seed="303292"/>
</conf>
# A comparable properties file (.ini) would look like this.
[Standard.Log-Dispatch]
logdir = /var/p5ee
[Authen]
passwd = /etc/passwd
seed = 303292
=head1 DESCRIPTION
P5EEx::Blue::Config is the class which represents data which is configured
at application deployment time.
The P5EEx::Blue::Config class is a very thin code wrapper around a perl
data structure. The other P5EEx::Blue::* classes are "friends" (in a C++ sense)
in that they can (and usually do) access the configuration data directly
from the perl structure (without using special methods to do so).
The main function of the class is to load the data into the right structure
from an appropriately formatted configuration file.
There are also some helper functions.
A P5EEx::Blue::Config object is essentially read-only.
However, there may be some cases where data which is entirely derivable
from other data in the P5EEx::Blue::Config object may be stored there so
that the next time it is requested, it does not need to be derived
again.
=cut
1;
1.1 p5ee/P5EEx/Blue/P5EEx/Blue/Context.pm
Index: Context.pm
===================================================================
######################################################################
## $Id: Context.pm,v 1.1 2001/11/16 23:21:38 spadkins Exp $
######################################################################
package P5EEx::Blue::Context;
use strict;
use Widget;
use CGI;
=head1 NAME
P5EEx::Blue::Context - Controller for sets of widgets in a CGI environment
=head1 SYNOPSIS
my ($wc, $widget, @widgets);
# ... official way to get a Controller object ...
use Widget;
$wc = Widget->controller(); # make factory which knows the
config info
$wc->dispatch_events();
$wc = Widget->controller("P5EEx::Blue::Context"); # use other than the
autodetected controller
print "wc is of type: ", ref($wc), "\n"; # should be the Controller class
instantiated
# ... alternative way (used internally) ...
use P5EEx::Blue::Context;
$wc = P5EEx::Blue::Context->new();
print "wc is of type: ", ref($wc), "\n"; # should be the Controller class
instantiated
# the rest represents what might be done with the resulting widget controller
$widget = $wc->widget("birth_dt"); # gets a widget
print $widget->html(), "\n"; # this works for an HTML widget
# or more generally... (maybe one day we will have Widget::Tk, Widget::Curses,
etc.?)
push(@widgets, $widget); # add widget to a list of widgets
$wc->display(@widgets); # controller renders list of widgets
on display
=cut
######################################################################
# CONSTANTS
######################################################################
######################################################################
# ATTRIBUTES
######################################################################
# $self->{widget} # {} cache of widgets (by widgetName)
# $self->{widgetconfig} # {} cache of widget configuration information (by
widgetName)
# $self->{messages} # "" HTML text (?) derived from the event handling
#
# MAJOR SECTIONS OF THE config STRUCTURE
# $self->{config} # {} global, read-only, config information
# $self->{config}{repository} # {} accessible repositories for data retrieval
# $self->{config}{domain} # {} domains of value/label pairs
# $self->{config}{widgetType} # {} widget configs ready for use in a widget
# $self->{config}{widget} # {} widget config info
#
# MAJOR SECTIONS OF THE state STRUCTURE
# $self->{state} # {} dynamic state
# $self->{state}{org} # {} dynamic state (for the user's current org)
# $self->{state}{user} # {} dynamic state (for the current user)
# $self->{state}{widget} # {} dynamic state (for the current state for each
widget)
# INPUTS FROM THE ENVIRONMENT
=head1 DESCRIPTION
This is a controller class for widgets running in a CGI environment.
=cut
######################################################################
# CONSTRUCTOR
######################################################################
sub new {
my ($this, $args) = @_;
my $class = ref($this) || $this;
my $self = {};
bless $self, $class;
Widget->dbgprint("$class->new(" . join(", ",%$args) . ")")
if ($Widget::DEBUG && Widget->dbg(ref($self),"new"));
$self->{widget} = {}; # initialize empty cache of widgets
$self->{widgetconfig} = {}; # initialize empty cache of their effective config
my ($config_class, $state_class);
$config_class = $args->{config};
$config_class = "Widget::Config" if (!defined $config_class || $config_class
eq "");
$state_class = $args->{state};
$state_class = "Widget::State" if (!defined $state_class || $state_class eq
"");
$self->{config} = Widget->create($config_class, $args);
$self->{state} = Widget->create($state_class, $args);
# create the pseudo-widget "global" only as a widgetconfig
$self->{widgetconfig}{global}{lang} = "en_us";
# create the pseudo-widget "session"
$self->widget(
-name => "session",
-widgetClass => "Widget::Base",
-container => "global",
);
$self->init($args);
return $self;
}
# override this method to get custom constructor functionality
sub init {
my ($self, $args) = @_;
}
######################################################################
# METHODS
######################################################################
sub add_message {
my ($self, $msg) = @_;
if (defined $self->{messages}) {
$self->{messages} .= "<br>" . $msg;
}
else {
$self->{messages} = $msg;
}
}
# NOTE: This is a key method to override in the driver classes
sub dispatch_events {
my ($self, $eventarray) = @_;
# TODO: implement a default handler here
# $self->widget($name)->handle_event($ename, $event, @args);
}
# NOTE: This is a key method to override in the driver classes
sub display {
my $self = shift;
my ($widget);
foreach $widget (@_) {
$widget->display();
}
}
sub user {
my $self = shift;
"guest"; # improve this over time
}
sub state {
$_[0]->{state};
}
sub repository {
my ($self, $repname) = @_;
my ($config, $repdef, $instance, $key, $global, $var);
my ($class, $method, $args, @args, $argidx);
$repname = "db" if (!defined $repname || $repname eq "");
Widget->dbgprint("P5EEx::Blue::Context->repository($repname)")
if ($Widget::DEBUG && Widget->dbg(ref($self),"repository"));
$config = $self->{config};
$repdef = $config->{repository}{$repname};
return undef if (!defined $repdef);
$instance = $self->{repository}{$repname};
if (! defined $instance) {
if ($repdef->{import}) {
$global = $self->{widgetconfig}{global};
foreach $key (keys %$repdef) {
$repdef->{$key} = $global->{$key} if (defined $global->{$key});
}
}
$class = $repdef->{class};
if (defined $class) {
$method = $repdef->{newmethod};
$method = "new" if (!defined $method);
$args = $repdef->{newmethod_args};
$args = [] if (!defined $args || ref($args) ne "ARRAY");
@args = @$args;
for ($argidx = 0; $argidx <= $#$args; $argidx++) {
if ($args->[$argidx] =~ /^\{(.+)\}$/) {
$var = $1;
if ($var eq "config") {
$args[$argidx] = $config;
}
elsif ($var eq "repconfig") {
$args[$argidx] = $repdef;
}
elsif (defined $repdef->{$var}) {
$args[$argidx] = $repdef->{$var};
}
}
}
Widget->dbgprint("P5EEx::Blue::Context->repository($repname):
$class->$method(@$args)")
if ($Widget::DEBUG && Widget->dbg(ref($self),"repository"));
Widget->use($class);
$instance = $class->$method(@args);
$self->{repository}{$repname} = $instance;
}
}
$instance;
}
sub shutdown {
my $self = shift;
my ($config, $repdef, $repname, $instance);
my ($class, $method, $args, $argidx);
Widget->dbgprint("P5EEx::Blue::Context->shutdown()")
if ($Widget::DEBUG && Widget->dbg(ref($self),"shutdown"));
$config = $self->{config};
if (defined $self->{repository} && ref($self->{repository}) eq "HASH") {
foreach $repname (keys %{$self->{repository}}) {
$instance = $self->{repository}{$repname};
$repdef = $config->{repository}{$repname};
$method = $repdef->{finishmethod};
$method = "disconnect" if (!defined $method);
Widget->dbgprint("P5EEx::Blue::Context->shutdown():
$instance->$method()")
if ($Widget::DEBUG && Widget->dbg(ref($self),"shutdown"));
$instance->$method();
delete $self->{repository}{$repname};
}
}
}
sub domain {
my ($self, $domain) = @_;
my ($config, $domainref, $repository, $rep);
my ($values, $labels, $needs_loading, $time_to_live, $time);
my ($class, $method, $args, $rows, $row);
Widget->dbgprint("P5EEx::Blue::Context->domain($domain)")
if ($Widget::DEBUG && Widget->dbg(ref($self),"domain"));
$config = $self->{config};
$domainref = $config->{domain}{$domain};
if (defined $domainref) {
$values = $domainref->{values};
$labels = $domainref->{labels};
$needs_loading = 0;
$repository = $domainref->{repository};
if (defined $repository && $repository ne "") {
if (!defined $values || !defined $labels) {
$needs_loading = 1;
}
else {
$time_to_live = $domainref->{time_to_live};
if (defined $time_to_live && $time_to_live ne "" && $time_to_live >=
0) {
if ($time_to_live == 0) {
$needs_loading = 1;
}
else {
if (time() >= $domainref->{time} + $time_to_live) {
$needs_loading = 1;
}
}
}
}
}
Widget->dbgprint("P5EEx::Blue::Context->domain($domain):
needs_loading=$needs_loading")
if ($Widget::DEBUG && Widget->dbg(ref($self),"domain"));
if ($needs_loading) {
$rep = $self->repository($repository);
if (defined $rep) {
$method = $domainref->{getmethod};
$method = "get" if (!defined $method);
$args = $domainref->{getmethod_args};
$args = [ $domain ] if (!defined $args);
Widget->dbgprint("P5EEx::Blue::Context->domain($domain):
$rep->$method(@$args)")
if ($Widget::DEBUG && Widget->dbg(ref($self),"domain"));
$rows = ${rep}->${method}(@$args);
$values = [];
$labels = {};
foreach $row (@$rows) {
push(@$values, $row->[0]);
$labels->{$row->[0]} = $row->[1];
}
$domainref->{values} = $values;
$domainref->{labels} = $labels;
$time = time();
$domainref->{time} = $time;
}
}
$values = $domainref->{values};
$labels = $domainref->{labels};
}
$values = [] if (! defined $values);
$labels = {} if (! defined $labels);
return ($values, $labels);
}
# The widget config is a simple hash which is the result of several
# complementary sources:
# 1. config of the widget (in Config) (which override widgetType)
# 2. optional config of the widget's widgetType (in Config)
# 3. config of container widget
# 4. args to the widget_config() call, usually coming from the widget constructor
# some of these are defaults, other are overrides
# NOTES;
# * we don't want to copy *all* attributes of container config, so
# we need a list of "absorbable" attributes
# * all widget configuration happens before instantiation
# this allows you to override the widgetClass in time for instantiation
# * widget_config's are cached in the Controller because the controller
# needs to know the configuration before it can instantiate the widget.
# * A reference to this config is kept by the widget for its own convenience.
# Usage: $config = $wc->widget_config("last_name");
sub widget_config {
my ($self, $name, $args) = @_;
my ($confroot, $config, $readonly_config, $state, $widgetType, $attrib,
$override);
my ($new_config);
Widget->dbgprint("P5EEx::Blue::Context->widget_config($name, " . join(",
",(defined $args)?%$args:"") . ")")
if ($Widget::DEBUG && Widget->dbg(ref($self),"widget_config",3));
if (defined $self->{widgetconfig}{$name}) {
$config = $self->{widgetconfig}{$name};
$new_config = 0;
$state = $self->{state}{widget}{$name};
$state = {} if (!defined $state);
}
else {
################################################################
# set up the {config} and {state} hashrefs
################################################################
$config = {};
$self->{widgetconfig}{$name} = $config;
$new_config = 1;
$state = $self->{state}{widget}{$name};
$state = {} if (!defined $state);
################################################################
# start with runtime state for the widget from the state data
################################################################
%$config = %$state;
$config->{name} = $name;
################################################################
# overlay with attributes from the (read-only) config file
################################################################
$confroot = $self->{config};
$readonly_config = $confroot->{widget}{$name};
foreach $attrib (keys %$readonly_config) {
# include config attributes only if not provided in the state
if (!defined $config->{$attrib}) {
$config->{$attrib} = $readonly_config->{$attrib};
}
}
################################################################
# overlay with attributes from the "widgetType"
################################################################
$widgetType = $config->{widgetType};
if ($widgetType) {
$readonly_config = $confroot->{widgetType}{$widgetType};
foreach $attrib (keys %$readonly_config) {
# include widgetType configs only if not provided in the widget
configs
if (!defined $config->{$attrib}) {
$config->{$attrib} = $readonly_config->{$attrib};
}
}
}
}
my ($lightweight);
$lightweight = ($name =~ /[\[\]\{\}]/);
####################################################################
# include defaults/overrides from args to $wc->widget($name, $args);
####################################################################
if (defined $args && ref($args) eq "HASH") { # ... args were provided
$override = $args->{override}; # don't override by default
################################################################
# take care of attributes associated with widgetType first
################################################################
if ($args->{widgetType} &&
($override || !defined $config->{widgetType})) {
$widgetType = $args->{widgetType};
if (!$config->{widgetType} || $widgetType ne $config->{widgetType}) {
$readonly_config = $confroot->{widgetType}{$widgetType};
foreach $attrib (keys %$readonly_config) {
if (!defined $config->{$attrib} || $override) {
$config->{$attrib} = $readonly_config->{$attrib};
}
}
}
}
################################################################
# take care of all %$args attributes next
################################################################
foreach $attrib (keys %$args) {
# don't include the entry which says whether we are overriding or not
next if ($attrib eq "override");
# include attrib if overriding OR attrib not provided in the widget
configs already
if (!defined $config->{$attrib} ||
($override && $config->{$attrib} ne $args->{$attrib})) {
$config->{$attrib} = $args->{$attrib};
# don't store state for lightweight widgets
$state->{$attrib} = $args->{$attrib} if (! $lightweight);
}
Widget->dbgprint("Controller->widget_config() [arg=$attrib] name=$name
lw=$lightweight ovr=$override",
" config=", $config->{$attrib},
" state=", $state->{$attrib},
" args=", $args->{$attrib})
if ($Widget::DEBUG && Widget->dbg(ref($self),"widget_config",6));
}
}
####################################################################
# infer a container (if none supplied, by the dots in the "name")
####################################################################
if (! defined $config->{container}) {
if ($name =~ /^(.+)\./) {
$config->{container} = $1;
}
elsif ($name eq "session") {
$config->{container} = "global";
}
elsif ($name ne "global") {
$config->{container} = "session";
}
if (defined $config->{widgetClass}) {
Widget->use($config->{widgetClass}); # load the perl code
$config->{absorbable_attribs} =
$config->{widgetClass}->absorbable_attribs();
}
}
# absorb attributes of the container config if ...
# TODO: sort out whether we need to absorb attributes more often
# (i.e. push model rather than a pull model)
if ($new_config && # ... it is the first time through
$config->{container} && # ... a container exists
$config->{absorbable_attribs}) { # ... there are known attributes to
absorb
my ($container, $container_config, $absorbable_attribs);
$container = $config->{container};
$container_config = $self->widget_config($container); # notice a recursion
here on containers
$absorbable_attribs = $config->{absorbable_attribs};
if (ref($absorbable_attribs) eq "") {
$absorbable_attribs = [ split(/ *, */,$absorbable_attribs) ];
}
if (ref($absorbable_attribs) eq "ARRAY") {
foreach $attrib (@$absorbable_attribs) {
if (!defined $config->{$attrib}) { # incorporate only if from no
other source
$config->{$attrib} = $container_config->{$attrib};
}
}
}
}
if (!defined $self->{state}{widget}{$name} && !$lightweight && (%$state)) {
$self->{state}{widget}{$name} = $state;
}
Widget->dbgprint("P5EEx::Blue::Context->widget_config($name) = [" . join(",
",(defined $config)?%$config:"") . "]")
if ($Widget::DEBUG && Widget->dbg(ref($self),"widget_config",3));
return $config;
}
# Usage: $widget = $wc->widget("first_name");
sub widget {
my $self = shift;
Widget->dbgprint("P5EEx::Blue::Context->widget(" . join(", ",@_) . ")")
if ($Widget::DEBUG && Widget->dbg(ref($self),"widget",3));
my ($args);
my ($name, $widget, $config, $class, $oldclass);
if ($#_ == -1) {
return undef; # we need at least a name
}
elsif (ref($_[0]) eq "HASH") {
$args = shift;
$name = $args->{name};
}
elsif ($_[0] =~ /^-/) {
$args = {};
my ($i, $var);
for ($i = 0; $i < $#_; $i += 2) {
$var = $_[$i];
$var =~ s/^-//;
$args->{$var} = $_[$i+1];
}
$name = $args->{name};
}
else {
$name = $_[0];
$args = $_[1];
# no other positional args for this class
}
Widget->dbgprint("P5EEx::Blue::Context->widget: name=[$name]")
if ($Widget::DEBUG && Widget->dbg(ref($self),"widget",3));
return undef if (! defined $name); # we need a name!
$config = $self->widget_config($name, $args);
Widget->dbgprint("P5EEx::Blue::Context->widget: returned from widget_config()")
if ($Widget::DEBUG && Widget->dbg(ref($self),"widget",3));
$class = $config->{"widgetClass"};
if (!defined $class || $class eq "") {
if ($name ne "global") {
$class = "Widget::HTML::TextField";
$config->{"widgetClass"} = $class;
$config->{backgroundColor} = "#ffcccc";
}
}
# NOTE: This does not allow widgets to change their class during execution
# i.e. once the widget is instantiated as a particular class, it is stored
# in the cache and never instantiated again. I think this is what we want.
# I could conceivably allow a widget to change its class. Then it would
# need to get reinstantiated as the new class, but I don't think this is
# what we want.
$widget = $self->{widget}{$name}; # check the cache
if (!defined $widget) {
Widget->dbgprint("P5EEx::Blue::Context->widget(): creating new widget (not
in cache)")
if ($Widget::DEBUG && Widget->dbg(ref($self),"widget",3));
if (defined $class && $class ne "") {
Widget->use($class);
$widget = $class->new($name,$self,1);
}
else {
$widget = {};
$widget->{name} = $name;
$widget->{controller} = $self;
}
$self->{widget}{$name} = $widget;
}
Widget->dbgprint("P5EEx::Blue::Context->widget() = $widget")
if ($Widget::DEBUG && Widget->dbg(ref($self),"widget",3));
return $widget;
}
sub get_table_cell {
my ($self, $table, $rowkey, $colname) = @_;
my ($tablehash, $rowidx, $colidx);
$tablehash = $self->{config}{table}{$table};
return undef if (!defined $tablehash);
$rowidx = $tablehash->{rowindex}{$rowkey};
$colidx = $tablehash->{colindex}{$colname};
return "" if (!defined $rowidx || !defined $colidx);
return $tablehash->{data}[$rowidx][$colidx];
}
# $self->set("{arr}[1][2]", 14);
# $self->set("{arr.totals}[1][2]", 14);
#sub set {
# my ($self, $var, $value) = @_;
# my ($perl);
#
# if ($var !~ /[\[\]\{\}]/) { # no special chars, "foo.bar"
# $self->{$var} = $value;
# return;
# } # match {
# elsif ($var =~ /^\{([^\}]+)\}$/) { # a simple "{foo.bar}"
# $var = $1;
# $self->{$var} = $value;
# return;
# } # match {
#
# $var =~ s/\{([^\}]+)\}/\{"$1"\}/g;
# $perl = "\$self->$var = \$value;";
# eval $perl;
# print STDERR "ERROR: P5EEx::Blue::Context->set($var,$value): eval ($perl):
$@\n" if ($@);
# return $value;
#}
sub wget_value {
my ($self, $name, $default, $setdefault) = @_;
my ($wname, $var, $value);
if ($name =~ /^(.+)\.([^\.]+)$/) {
$wname = $1;
$var = $2;
$value = $self->{widgetconfig}{$wname}{$var};
if (!defined $value && defined $default) {
$value = $default;
if ($setdefault) {
$self->{state}{widget}{$wname}{$var} = $value;
$self->widget_config($wname) if (!defined
$self->{widgetconfig}{$wname});
$self->{widgetconfig}{$wname}{$var} = $value;
}
}
}
else {
$value = $self->{widgetconfig}{session}{$name};
if (!defined $value && defined $default) {
$value = $default;
if ($setdefault) {
$self->{state}{widget}{session}{$name} = $value;
$self->{widgetconfig}{session}{$name} = $value;
}
}
}
return $value;
}
sub wset_value {
my ($self, $name, $value) = @_;
my ($wname, $var);
if ($name =~ /^(.+)\.([^\.]+)$/) {
$wname = $1;
$var = $2;
$self->{state}{widget}{$wname}{$var} = $value;
$self->widget_config($wname) if (!defined $self->{widgetconfig}{$wname});
$self->{widgetconfig}{$wname}{$var} = $value;
}
else {
$self->{state}{widget}{session}{$name} = $value;
$self->{widgetconfig}{session}{$name} = $value;
}
}
# $self->set("{arr}[1][2]", 14);
# $self->set("{arr.totals}[1][2]", 14);
sub wset {
my ($self, $name, $var, $value) = @_;
my ($perl, $multiple);
Widget->dbgprint("P5EEx::Blue::Context->wget($name,$var,$value)")
if ($Widget::DEBUG && Widget->dbg(ref($self),"wset",3));
if ($var !~ /[\[\]\{\}]/) { # no special chars, "foo.bar"
$self->{state}{widget}{$name}{$var} = $value;
$self->widget_config($name) if (!defined $self->{widgetconfig}{$name});
$self->{widgetconfig}{$name}{$var} = $value;
return;
} # match {
elsif ($var =~ /^\{([^\}]+)\}$/) { # a simple "{foo.bar}"
$var = $1;
$self->{state}{widget}{$name}{$var} = $value;
$self->widget_config($name) if (!defined $self->{widgetconfig}{$name});
$self->{widgetconfig}{$name}{$var} = $value;
return;
} # match {
$self->widget_config($name) if (!defined $self->{widgetconfig}{$name});
$var =~ s/\{([^\}]+)\}/\{"$1"\}/g; # put quotes around hash keys
$perl = "\$self->{state}{widget}{\$name}$var = \$value;
\$self->{widgetconfig}{\$name}$var = \$value;";
eval $perl;
print STDERR "ERROR: P5EEx::Blue::Context->wset($name,$var,$value): eval
($perl): $@\n" if ($@);
return $value;
}
sub wget {
my ($self, $name, $var, $default, $setdefault) = @_;
my ($perl, $value);
if ($var !~ /[\[\]\{\}]/) { # no special chars, "foo.bar"
$value = $self->{widgetconfig}{$name}{$var};
if (!defined $value && defined $default) {
$value = $default;
if ($setdefault) {
$self->{state}{widget}{$name}{$var} = $value;
$self->widget_config($name) if (!defined
$self->{widgetconfig}{$name});
$self->{widgetconfig}{$name}{$var} = $value;
}
}
Widget->dbgprint("P5EEx::Blue::Context->wget($name,$var) (value) = [$value]")
if ($Widget::DEBUG && Widget->dbg(ref($self),"wget",3));
return $value;
} # match {
elsif ($var =~ /^\{([^\}]+)\}$/) { # a simple "{foo.bar}"
$var = $1;
$value = $self->{widgetconfig}{$name}{$var};
if (!defined $value && defined $default) {
$value = $default;
if ($setdefault) {
$self->{state}{widget}{$name}{$var} = $value;
$self->widget_config($name) if (!defined
$self->{widgetconfig}{$name});
$self->{widgetconfig}{$name}{$var} = $value;
}
}
Widget->dbgprint("P5EEx::Blue::Context->wget($name,$var) (attrib) =
[$value]")
if ($Widget::DEBUG && Widget->dbg(ref($self),"wget",3));
return $value;
} # match {
$self->widget_config($name) if (!defined $self->{widgetconfig}{$name});
$var =~ s/\{([^\}]+)\}/\{"$1"\}/g;
$perl = "\$value = \$self->{widgetconfig}{\$name}$var;";
eval $perl;
print STDERR "ERROR: Widget::Base->get($var): eval ($perl): $@\n" if ($@);
Widget->dbgprint("P5EEx::Blue::Context->wget($name,$var) (indexed) = [$value]")
if ($Widget::DEBUG && Widget->dbg(ref($self),"wget",3));
return $value;
}
1;
1.1 p5ee/P5EEx/Blue/P5EEx/Blue/P5EE.pm
Index: P5EE.pm
===================================================================
#############################################################################
## $Id: P5EE.pm,v 1.1 2001/11/16 23:21:38 spadkins Exp $
#############################################################################
package P5EEx::Blue::P5EE;
use strict;
# eliminate warnings about uninitialized values
$SIG{__WARN__} = sub { warn @_ unless $_[0] =~ /Use of uninitialized value/ };
use Date::Parse;
use Date::Format;
=head1 NAME
P5EEx::Blue::P5EE - Backplane for core P5EE services
=head1 SYNOPSIS
use P5EEx::Blue::P5EE;
# CONTEXT: SINGLETON ACCESS TO EXTERNAL ENVIRONMENT (cmd, CGI, mod_perl)
my ($c);
$c = P5EEx::Blue::P5EE->context();
$c = P5EEx::Blue::P5EE->context( # or you can specify any context class...
contextClass => "P5EEx::Blue::Context::CGI",
);
# CONVENIENCE ACCESS TO THE USER'S CONFIGURATION (via Context)
$config = P5EEx::Blue::P5EE->config(); # returns a hashref to config data
# FOUNDATION FOR A PLUGIN ARCHITECTURE
$obj = P5EEx::Blue::P5EE->object($type, $name, @args);
@retvals = P5EEx::Blue::P5EE->send($type, $name, $method, @args);
=head1 DESCRIPTION
The P5EEx::Blue::P5EE module is the module in from which core services are
called.
=head1 REQUIREMENTS
=head1 DESIGN
=cut
#############################################################################
# CONSTANTS
#############################################################################
#############################################################################
# ATTRIBUTES
#############################################################################
# INPUTS FROM THE ENVIRONMENT
=head1 DESCRIPTION
P5EEx::Blue::P5EE.pm is a convenience package for static methods for the
Perl P5EEx::Blue::P5EE Library. It also serves as a factory class,
producing objects whose class is driven by configuration
rather than being known at development time.
=cut
#############################################################################
# FACTORY
#############################################################################
$P5EEx::Blue::P5EE::DEBUG = 0 if (!defined $P5EEx::Blue::P5EE::DEBUG);
# The P5EEx::Blue::P5EE->new() method is *not* a constructor for a P5EEx::Blue::P5EE
class.
# It is simply a synonym for "P5EEx::Blue::P5EE->context()", which is a
# factory-style constructor for a class derived from P5EEx::Blue::Context.
# This is done for the sake of tools like the Template Toolkit, so
# that usage like [% USE wc = P5EEx::Blue::P5EE %] works.
sub new {
my $self = shift;
$self->context(@_);
}
my ($default_context); # singleton
sub context {
my $self = shift;
my ($args, $context);
if ($#_ == -1) {
$args = {};
}
elsif (ref($_[0]) eq "HASH") {
$args = shift;
}
elsif ($_[0] =~ /^-/) {
$args = {};
my ($i, $var);
for ($i = 0; $i < $#_; $i += 2) {
$var = $_[$i];
$var =~ s/^-//;
$args->{$var} = $_[$i+1];
}
}
else {
$args = {};
# no positional args for this method
}
if (! $args->{context}) {
if (defined $ENV{"P5EE_CONTEXT_CLASS"}) { # env variable set?
$args->{context} = $ENV{"P5EE_CONTEXT_CLASS"};
}
else { # try autodetection ...
my $gateway = $ENV{"GATEWAY_INTERFACE"};
if (defined $gateway && $gateway =~ /CGI-Perl/) { # running under
mod_perl?
$args->{context} = "P5EEx::Blue::Context::Modperl";
}
elsif ($ENV{"HTTP_USER_AGENT"}) { # running as CGI script?
$args->{context} = "P5EEx::Blue::Context::CGI";
$args->{state} = "P5EEx::Blue::State::CGI";
}
# let's be real... these next two are not really critical right now
#elsif ($ENV{"DISPLAY"}) { # running with an X DISPLAY var set?
# $args->{context} = "P5EEx::Blue::Context::Gtk";
#}
#elsif ($ENV{"TERM"}) { # running with a TERM var to support
Curses?
# $args->{context} = "P5EEx::Blue::Context::Curses";
#}
else { # fall back to CGI, because it works
OK in command mode
$args->{context} = "P5EEx::Blue::Context::CGI";
$args->{state} = "P5EEx::Blue::State::CGI";
}
}
}
if (!defined $args->{config} || $args->{config} eq "") {
if (defined $ENV{"P5EE_CONFIG_CLASS"}) {
$args->{config} = $ENV{"P5EE_CONFIG_CLASS"};
}
}
$context = $self->create($args->{context}, $args);
$default_context = $context if (!defined $default_context);
return $context;
}
sub default_context {
my $self = shift;
return ($default_context) if (defined $default_context);
return P5EEx::Blue->context(@_);
}
sub create {
my $self = shift;
my $class = shift;
eval("use $class;");
return undef if ($@);
return $class->new(@_);
}
#############################################################################
# METHODS
#############################################################################
sub current_directory {
eval("use Cwd;");
cwd();
}
sub command {
my $cmd = $0;
$cmd =~ s!^\.\/!!;
return $cmd;
}
#############################################################################
# DEBUG SUPPORT
#############################################################################
sub dbg {
my ($self, $class, $method, $level) = @_;
return 0 if (! $P5EEx::Blue::DEBUG); # if DEBUG is off, no debug
output should be created
return 0 if (defined $level && $P5EEx::Blue::DEBUG < $level); # if DEBUG level
is higher, no debug output
return 1 if (! defined $P5EEx::Blue::DEBUGSCOPE); # else, if no DEBUGSCOPE is
defined, we are debugging everything
return 1 if (! defined $class); # else, if no class was
specified, go ahead and debug it
return 1 if (defined $P5EEx::Blue::DEBUGSCOPE->{$class}); # else, if in the
scope specified, debug it
return 0 if (! defined $method); # else, if no method was
specified, suppress debug output
return 1 if (defined $method && defined
$P5EEx::Blue::DEBUGSCOPE->{"$class.$method"}); # else, debug if in scope
return 0; # else, suppress debug output
}
sub dbgprint {
my $self = shift;
print STDOUT @_, "\n";
#print "<!-- ", @_, " -->\n";
#print STDERR @_, "\n";
#local (*FILE);
#if (open(main::FILE, ">> debug.out")) {
# print main::FILE $self->now(), " ", @_, "\n";
# close(main::FILE);
#}
}
1;
1.1 p5ee/P5EEx/Blue/P5EEx/Blue/datetime.pod
Index: datetime.pod
===================================================================
#!perl -w
# run this document through perl to check its syntax
use Pod::Checker;
podchecker(\*DATA);
__END__
=head1 NAME
P5EE Date and Time Considerations
=head1 INTRODUCTION
Most Enterprise development includes processing of dates and times.
There are many date and time modules on CPAN, and choosing the right
one can be confusing.
API's for dates and times are not part of P5EE,
so some direction is needed.
This document represents the recommendations and collective wisdom
from the P5EE developers.
The short answer is that we recommend the following
for most common date and time operations.
Class::Date
Class::Date::Rel
However, other modules are appropriate in certain circumstances.
So for the longer answer, read on.
=head1 PERL 5 LANGUAGE SUPPORT
The native Perl 5 datetime type is an integer.
It is not different from other integers in any way other than
how it is used.
It represents the number of non-leap seconds since
January 1, 1970 UTC (the "Epoch" at GMT). The following internal
Perl function gets the current time.
$current_time = time;
$current_time = time();
Other Perl functions that return this "datetime" integer are
($dev, $ino, $mode, $nlink, $uid, $gid, $redev, $size,
$atime, $mtime, $ctime, $blksize, $blocks) = stat($filename);
($dev, $ino, $mode, $nlink, $uid, $gid, $redev, $size,
$atime, $mtime, $ctime, $blksize, $blocks) = lstat($filename);
where $atime, $mtime, and $ctime are the same kind of integers,
representing the access time, modification time, and change time
of a file.
These $time values may be converted to human-readable
form using the following internal perl functions.
(See the "perlfunc" man page for more information.)
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($time);
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time);
Furthermore, the current time zone needs to be accessed through
the environment variable, "TZ".
$timezone = $ENV{TZ};
This leaves the Perl developer with lots of work to do in order to
process dates.
=over
=item * Formatting dates for output
=item * Parsing dates on input
=item * Comparing dates
=item * Date math (addition, subtraction)
=item * Other calendar-specific functions (i.e. holidays, days of week, etc)
=back
Numerous modules have been posted to CPAN allowing the Perl developer
to accomplish these tasks.
However, they have pros and cons related to the
following features.
=over
=item * Internationalization
=item * Speed
=item * Portability
=item * Ranges of Dates Supported
=item * Compliance with Perl Styleguide (function naming)
=back
=head1 FUNCTIONAL SOLUTIONS
Modules exist to allow you to process integers like those
returned by the time() function. They do not create "date" objects
with methods. They simply provide functions that allow you do the
required tasks.
=head2 Date::Parse, Date::Format
http://search.cpan.org/search?module=Date::Parse
http://search.cpan.org/search?module=Date::Format
Very simple, clean functions for parsing text dates and formatting
them for output in a variety of ways. The fact that these modules
work with integers implies that you can do date comparisons and
some degree of date math simply ($tomorrow = $today + 24*60*60;).
* Parses many different formats of dates
* Flexible formatting using POSIX strftime() format specifiers.
* Limited internationalization support.
* Limited date math support.
* Unknown support for dates outside [1970-2038]
=head2 Date::Calc
http://search.cpan.org/search?module=Date::Calc
Powerful, fast manipulation of dates.
* No explicit support for parsing or formatting dates.
* Non-perlstyle function names (internal caps, as in Add_Delta_YMD())
* Powerful, fast support for date math
* Support for all A.D. dates [1-9999]
=head2 Date::Manip
http://search.cpan.org/search?module=Date::Manip
The most powerful and slowest (all perl, large)
of date manipulation packages.
Includes many obscure calendar-related functions.
* Powerful parsing many different formats of dates
* No explicit support for or formatting dates.
* Non-perlstyle function names (internal caps, as in ParseDate())
* Powerful support for date math (but slower than Date::Calc)
* Support for all A.D. dates [1-9999]
* Function support for holidays, business days, etc.
=head2 HTTP::Date
http://search.cpan.org/search?module=HTTP::Date
This module is part of the larger libwww-perl bundle.
It seems to parse a wider variety of dates than Date::Parse,
but it is focused on those date formats which occur in HTTP headers.
It only formats dates in the format preferred by HTTP headers.
=head2 Time::HiRes
http://search.cpan.org/search?module=Time::HiRes
Completely separate from the modules above, which deal with dates,
there is sometimes a need to deal with times at the sub-second
level. Time::HiRes works in seconds and milliseconds.
It is particularly useful in timing sections of code.
=head1 OBJECT-ORIENTED SOLUTIONS
An alternative to the functional solutions described above is an
object-oriented solution that involves creating and manipulating
true "datetime" objects.
=head2 Time::Piece [Time::Object, Time::Seconds]
http://search.cpan.org/search?module=Time::Object
On the perl5-porters mailing
Larry Wall described some thoughts on how dates and times might become part
of the Perl language and sketched out an object-oriented interface.
http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2000-01/msg00241.html
This interface was implemented in the Time::Piece package.
* OO interface
* Perl gmtime, localtime return objects of type Time::Object
* No explicit support for parsing dates.
* Flexible formatting using POSIX strftime() format specifiers.
* Limited internationalization support.
* Limited date math support.
* Unknown support for dates outside [1970-2038]
* Unsupported on Win32 platform.
=head2 Class::Date, Class::Date::Rel
http://search.cpan.org/search?module=Class::Date
This class started with Time::Object and was enhanced.
* Some native support for parsing dates.
* Uses Date::Parse internally for extended date parsing
* Better date math support.
* Supported on Win32 platform.
=head2 Date::Simple
http://search.cpan.org/search?module=Date::Simple
This is a simple, object-oriented class that deals with
dates only (not times at all).
=head2 Date::Calc::Object
http://search.cpan.org/search?module=Date::Calc::Object
Date::Calc has an object-oriented interface.
=cut
1.1 p5ee/P5EEx/Blue/P5EEx/Blue/podstyle.pod
Index: podstyle.pod
===================================================================
#############################################################################
## $Id: podstyle.pod,v 1.1 2001/11/16 23:21:38 spadkins Exp $
#############################################################################
=head1 NAME
P5EEx::Blue::podstyle - Style-guide for writing embedded POD documentation
=head1 TERMINOLOGY
In Perl, the term "Package" refers to a namespace in which subroutines
and variables can be defined. In P5EE, the developer usually uses Packages
to write object-oriented code, so that a Package becomes a Module
(i.e. a Class). Thus, in object-oriented perl, all three names mean
approximately the same thing.
Confusingly, in UML and Java, "packages" are groups of related classes.
In Perl, related classes are usually kept in the same directory, so this
concept is called a "Class Set" or "Set of Classes".
A Distribution is a set of all classes bundled up along with their support
files (test scripts, examples, doc, etc.) and versioned together.
A Distribution generally comes in a gzipped tar file
(i.e. P5EEx-Blue-0.25.tar.gz).
=cut
#############################################################################
## TAGS
#############################################################################
=head1 COMMENT TAGS
The documentation specified here takes the form of a "head1" section,
a "head2" section, or bullet list with specialized keywords.
=head1 NAME
=head1 Distribution: <Dist-name> <description>
=item * Version: $Id: podstyle.pod,v 1.1 2001/11/16 23:21:38 spadkins Exp $
=item * Class Set: <Class-Set-name> <description>
=head1 Class Set: <Class-Set-name> <description>
=item * Class: <Class-name> <description>
=head1 SYNOPSIS
=head1 Class: <Class-name> <description>
=item * Throws: <Exception-Name>
=item * Deprecated: <Since-Version> <Planned-Discontinue-Version>
=item * Since: <Version-Number>
=item * See: L<Text|Link-Name>
=head1 Attributes:
=head2 Public Attributes: [<functional-category>]
=head2 Protected Attributes: [<functional-category>]
=head2 Private Attributes: [<functional-category>]
=item * Attribute: <Attribute-Name> <Type> <description>
=head1 Public Methods: [<functional-category>]
=head1 Protected Methods: [<functional-category>]
=head1 Private Methods: [<functional-category>]
=head2 Method: <Method-signature>
=item * Param: <Param-Name> <Type> <In/Out> <Undef-OK>
=item * Return: <Return-Name> <Type> <Undef-OK>
=item * Throws: <Exception-Name>
=item * Deprecated: <Since-Version> <Planned-Discontinue-Version>
=item * Since: <Version-Number>
=head1 SYNOPSIS
=item * Author: <author> [< <email-address> >]
=item * License: <licence>
=head1 SYNOPSIS
=head1 SEE ALSO
=over
=cut
#############################################################################
## DISTRIBUTION
#############################################################################
=head1 DOC FOR THE DISTRIBUTION
In some pod file, there should exist the documentation for the entire
distribution. There may be a naturally top-level package to put this
in (i.e. P5EEx::Blue.pm), or you may need to create a separate pod file
for this (i.e. P5EEx::Blue::distribution.pod).
The distribution documentation is composed of those specifications that
apply to the entire set of classes and accompanying files.
The following documentation must be included in a the Distribution doc.
=over
=item * Perl version required to run the Distribution
=item * Hard Dependencies (other Distributions, and their versions,
required to be installed)
=item * Soft Dependencies (other Distributions, and their versions,
optionally used if installed)
=item * All Exceptions Thrown by any Classes in the Distribution
=item * List of Class Sets in the Distribution (every Class should belong
to exactly one Class Set for the purposes of documentation)
=item * A link to the Exceptions documentation
=item * Requirements
=item * Design
=back
Note: Every P5EE-compliant distribution should include a
single Exceptions.pm file (i.e. P5EEx::Blue::Exceptions.pm).
=cut
#############################################################################
## SET OF CLASSES
#############################################################################
=head1 DOC FOR A SET OF CLASSES (DIRECTORY)
For each subset of classes in the distribution (typically a directory),
there should be additional documentation for that Set of Classes.
=over
=item * Description
=item * Platform Dependencies (OS/Hardware/Configuration)
=item * References to any external specifications
=back
=cut
#############################################################################
## CLASS
#############################################################################
=head1 DOC FOR A CLASS (MODULE/PACKAGE)
Each Class file (*.pm) should have Class documentation.
=over
=item * Class Description
=item * State/Transition Information (high level)
=item * Platform Dependencies (OS/Hardware/Configuration)
=item * Allowed Implementation Variances
=item * Security Constraints
=item * References to any External Secifications
=back
=cut
#############################################################################
## ATTRIBUTE
#############################################################################
=head1 DOC FOR AN ATTRIBUTE
The documentation for the attributes of the Class should follow immediately
after the Class documentation. For each attribute, consider including the
following type of information.
Put the public attributes first (users of the class should access this
attribute directly, rare!), then protected attributes (only other classes
in this Class Set should access the attribute directly), then private
attributes (only this class and derived classes should access the attribute
directly).
=over
=item * Description
=item * Range of valid values
=item * Intended Visibility (Public, Protected, Private)
=item * Undef Value Behavior
=back
=cut
#############################################################################
## METHOD
#############################################################################
=head1 DOC FOR A METHOD
Each method should have a section which explains the following.
Multiple sections of doc should exist for each possible method signature.
=over
=item * Description and Behavior
=item * State Transitions
=item * Range of Valid Argument Values
=item * Undef Argument Behavior
=item * Range of Return Values
=item * Algorithms Defined
=item * Platform Dependencies (OS/Hardware/Configuration)
=item * Allowed Implementation Variances
=item * Type and Cause of Exceptions
=item * Security Constraints
=back
=cut
1;
1.1 p5ee/P5EEx/Blue/P5EEx/Blue/styleguide.pod
Index: styleguide.pod
===================================================================
#!perl -w
# run this document through perl to check its syntax
use Pod::Checker;
podchecker(\*DATA);
__END__
=head1 NAME
styleguide - P5EE Style Guide
=head1 INTRODUCTION
All code and documentation that is submitted to be included in the P5EE
distribution should follow the style in this document. This is not to
try to stifle your creativity, but to make life easier for everybody who
has to work with your code, and to aid those who are not quite sure how
to do something. It also serves to resolve disputes so that no one takes
it personally.
These conventions below apply to perl modules, web (CGI/mod_perl) programs,
command-line programs, specifically, but also might apply to some
degree to any Perl code written for use in P5EE.
Note that these are all guidelines, not unbreakable rules. If you have
a really good need to break one of the rules herein, however, then it is
best to ask the core P5EE team first.
Note that with much of this document, it is not so much the Right Way as
it is Our Way. We need to have conventions in order to make life easier
for everyone.
If you have any questions, please ask us on the P5EE-development
mailing list, [EMAIL PROTECTED]
http://lists.perl.org/showlist.cgi?name=p5ee
Unofficial, proposed documentation for the P5EE project is at the
following sites.
http://www.officevision.com/pub/p5ee
This document can and will be modified over time.
We hope to add any significant changes at the bottom of the document.
=head1 CODING PRINCIPLES
=head2 Perl Version
We code everything to perl 5.005_03. Some day we may switch to take
advantage of perl 5.6 features. Regardless, all code should run on perl
5.005_03 or any later version of perl 5. All of the core P5EE code has
been tested on perl 5.005_03 and perl 5.6.0, though it has probably
been used more on perl 5.6.0.
=head2 Documentation
All modules will be documented using the POD examples in the module
boilerplate. The function, purpose, use of the module will be
explained, and each public API will be documented with name,
description, inputs, outputs, side effects, etc.
If an array or hash reference is returned, document the size of the
array (including what each element is, as appropriate) and name each key
in the hash. For complex data structures, map out the structure as
appropriate.
Also document what kind of data returned values are. Is it an integer,
a block of HTML, a boolean?
All command-line program options will be documented using the
boilerplate code for command-line programs. Each available function,
switch, etc. should be documented, along with a statement of function,
purpose, use of the program. Try not to use the same options as another
program, for a different purpose.
All web programs should be documented with a statement of function,
purpose, and use in the comments of the program.
Any external documents, and documentation for command-line programs and
modules, should be written in POD, where appropriate. From there, they
can be translated to many formats with the various pod2* translators.
Read the perlpod manpage before writing any POD, because although POD is
not difficult, it is not what most people are used to. It is not a
regular markup language; it is just a way to make easy documentation
for translating to other formats. Read, and understand, the perlpod
manpage, and ask us or someone else who knows if you have any questions.
=head2 Version
Use the boilerplate code for versions of modules, web programs, and
command-line programs. The $VERSION of the module will then reflect the
CVS revision.
The Makefile.PL should contain the distribution version, independent
of any individual file version within the CVS repository.
Also, XS modules should probably have $VERSION
also reflect the distribution, or else you'll need to recompile the
shared library every time you make a change to the file, which is
really a pain to do during development.
Our distribution versions use tuples, where the first number is the
major revision, the second number is the version, and third
number is the subversion. Odd-numbered versions are development
versions. Examples:
1.0.0 First release of P5EE 1
1.0.1 Second release of P5EE 1.0
1.0.10 etc.
1.1.0 First development release of P5EE 1.2 (or 2.0)
2.0.0 First release of P5EE 2
Versions can be modified with a hyphen followed by some text, for
special versions, or to give extra information. Examples:
1.1.4-bender Notes that this is a bender release
2.0.0-pre1 Notes that this is not final, but preview
In perl 5.6.0, you can have versions like C<v2.0.0>, but this is not
allowed in previous versions of perl. So to convert a tuple version
string to a string to use with $VERSION, use a regular integer for
the revision, and three digits for version and subversion. Examples:
1.1.6 -> 1.001006
2.0.0 -> 2.000000
This way, perl can use the version strings in greater-than and
less-than comparisons.
=head2 Comments
All code should be self-documenting as much as possible. Only include
necessary comments. Use names like "$story_count", so you don't need to
do something like:
# story count
my $sc = 0;
Include any comments that are, or might be, necessary in order for
someone else to understand the code. Sometimes a simple one-line
comment is good to explain what the purpose of the following code is
for. Sometimes each line needs to be commented because of a complex
algorithm. A good reference is Kernighan & Pike's
I<Practice of Programming> about commenting.
=head2 Warnings and Strict
All code must compile and run cleanly with "use strict" enabled and the
perl "-w" (warnings) option on. If you must do something that -w or
strict complains about, there are workarounds, but the chances that you
really need to do it that way are remote.
The one exception is the "Use of uninitialized variable" warnings.
We have those disabled in P5EE.pm, so by including "use
P5EE" you are disabling that warning in your code, too, and you don't
need to worry about them.
=head2 Lexical Variables
Use only lexical variables, except for special global variables
($VERSION, %ENV, @ISA, $!, etc.) or very special circumstances.
Global variables
for regular use are never appropriate. When necessary, "declare"
globals with "use vars", not with our() (our() was introduced in perl
5.6).
A lexical variable is created with my(). A global variable is
pre-existing (if it is a special variable), or it pops into existence
when it is used. local() is used to tell perl to assign a temporary
value to a variable. This should only be used with special variables,
like $/, or in special circumstances. If you must assign to any global
variable, consider whether or not you should use local().
local() may also be used on elements of arrays and hashes, though there
is seldom a need to do it, and you shouldn't.
=head2 Exporting
Do not export anything from a module by default.
Feel free to put anything you
want to in @EXPORT_OK, so users of your modules can explicitly ask
for symbols (e.g., "use P5EE::Something qw(getFoo setFoo)"), but
do not export them by default.
=head2 Pass by Reference
Arrays and hashes should be passed to and from functions by reference
only. Note that a list and an array are NOT the same thing. This
is perfectly fine:
return($user, $form, $constants);
An exception might be a temporary array of discrete arguments:
my @return = ($user, $form);
push @return, $constants if $flag;
return @return;
Although, usually, this is better (faster, easier to read, etc.):
if ($flag) {
return($user, $form, $constants);
} else {
return($user, $form);
}
=head2 Garbage Collection
Perl does pretty good garbage collection for you. It will automatically
clean up lexical variables that have gone out of scope and objects whose
references have gone away. Normally you don't need to worry about
cleaning up after yourself, if using lexicals.
However, some glue code, code compiled in C and linked to Perl, might
not automatically clean up for you. In such cases, clean up for
yourself. If there is a method in that glue to dispose or destruct,
then use it as appropriate.
Also, if you have a long-running function that has a large data
structure in it, it is polite to free up the memory as soon as you are
done with it, if possible.
my $huge_data_structure = get_huge_data_structure();
do_something_with($huge_data_structure);
undef $huge_data_structure;
=head2 __END__ and __DATA__ and __PACKAGE__
Do not use __END__ or __DATA__ in web programs. They break mod_perl.
Also, __PACKAGE__ will likely not return the value you expect in web
programs. These are all fine for modules.
=head2 Tests
Modules should provide test code, with documentation on how to use
it.
=head2 STDIN/STDOUT
Always report errors using the (yet-to-be-defined) P5EE logging
facility. Never print directly to STDERR.
Do not print directly to STDOUT, unless you need to
print directly to the user's browser.
In command-line programs, feel free to print to STDERR and STDOUT as
needed.
=head2 Files and Globs
For constructing and parsing file paths, use File::Spec::Functions
and File::Basename. For creating or removing paths, use File::Path.
This increases portability to non-Un*x platforms.
my $path = "$dir/$file"; # wrong
my $path = catfile($dir, $file); # right
my $dir = "."; # wrong
my $dir = curdir(); # right
mkdir("/path"), mkdir("/path/to"), ... # wrong
`mkdir /path`; `mkdir /path/to`, ... # very wrong
mkpath("/path/to/my/dir", 0, 0775); # right
Do not use the glob operator (C<glob('*')> or C<E<lt>*E<gt>>). Use
opendir() with readdir() instead. Note that glob() is much more
portable in perl 5.6 than it was in previous versions of perl, but
its behavior is still unreliable, as each perl installation can
choose to implement perl using local conventions instead of the
default, which is via the File::Glob module.
Do not use symbol table globs (not the same kind of glob as above!) like
C<*foo> for anything, except for when direct symbol table manipulation
is necessary, which it almost never is.
=head2 System Calls
Always check return values from system calls, including open(),
close(), mkdir(), or anything else that talks directly to the system.
Perl built-in system calls return the error in $!; some functions in
modules might return an error in $@ or some other way, so read the module's
documentation if you don't know. Always do something, even if it is
just calling errorLog(), when the return value is not what you'd expect.
=head1 STYLE
Much of the style section is taken from the perlstyle manpage. We make
some changes to it here, but it wouldn't be a bad idea to read that
document, too.
=head2 Terminology
=over 4
=item P5EE
The name of the project is "P5EE".
There is no "P5EE1" or "P5EE2".
To specify a version, use "P5EE 2.0" or "P5EE 2.0.1".
=item function vs. sub(routine) vs. method
"Method" should be used only to refer to a subroutine that are object
methods or class methods; that is, these are functions that are used
with OOP that always take either an object or a class as the first
argument. Regular subroutines, ones that are not object or class
methods, are functions. Class methods that create and return an object
are optionally called constructors.
=back
=head2 Names
Don't use single-character variables, except as iterator variables.
Don't use two-character variables just to spite us over the above rule.
Constants are in all caps; these are variables whose value will I<never>
change during the course of the program.
$Minimum = 10; # wrong
$MAXIMUM = 50; # right
Other variables are lowercase, with underscores separating the words.
They words used should, in general, form a noun (usually singular),
unless the variable is a flag used to denote some action that should be
taken, in which case they should be verbs (or gerunds, as appropriate)
describing that action.
$thisVar = 'foo'; # wrong
$this_var = 'foo'; # right
$work_hard = 1; # right, verb, boolean flag
$running_fast = 0; # right, gerund, boolean flag
Arrays and hashes should be plural nouns, whether as regular arrays and
hashes or array and hash references. Do not name references with "ref"
or the data type in the name.
@stories = (1, 2, 3); # right
$comment_ref = [4, 5, 6]; # wrong
$comments = [4, 5, 6]; # right
$comment = $comments->[0]; # right
Make the name descriptive. Don't use variables like "$sc" when you
could call it "$story_count". See L<"Comments">.
Methods and Functions (except for special cases, like AUTOLOAD) begin
with a verb, with words following to complete the action.
Multi-word names should be all lower-case, separated by underscores,
in keeping with the "perlstyle" guide and most of the modules
already on CPAN. They
should as clearly as possible describe the activity to be peformed, and
the data to be returned.
$obj->getStory(); # wrong.
$obj->setStoryByName(); # wrong again.
$obj->getStoryByID(); # wrong again. This isn't Java!
$obj->get_story(); # right.
$obj->set_story_by_name(); # right.
$obj->get_story_by_id(); # right.
Methods and Functions beginning with C<_> are special:
they are not to be used
outside the current file (i.e. "private").
This is not enforced by the code itself,
but by programmer convention only.
For large for() loops, do not use $_, but name the variable.
Do not use $_ (or assume it) except for when it is absolutely
clear what is going on, or when it is required (such as with
map() and grep()).
for (@list) {
print; # OK; everyone knows this one
print uc; # wrong; few people know this
print uc $_; # better
}
Note that the special variable C<_> I<should> be used when possible.
It is a placeholder that can be passed to stat() and the file test
operators, that saves perl a trip to re-stat the file. In the
example below, using C<$file> over for each file test, instead of
C<_> for subsequent uses, is a performance hit. You should be
careful that the last-tested file is what you think it is, though.
if (-d $file) { # $file is a directory
# ...
} elsif (-l _) { # $file is a symlink
# ...
}
Package names begin with a capital letter in each word, followed by
lower case letters.
P5EE::Standard # good
P5EE::Authz # good
P5EE::MainCode # good
Use all lower case for POD files which are documentation only.
P5EE::styleguide # good for doc only
Naming for modules should be according to the following general rules.
All P5EE components which have *broad* support from the
[EMAIL PROTECTED] list would go into the "P5EE" package
Naming style is similar to other modules on CPAN
Naming choice draws from precedent of other modules on CPAN
Naming choice draws from precedent of J2EE
Packages which aren't intended to be instantiated as objects may
have an "adjective" or "concept" for a name
(i.e. P5EE::Standard). Packages which are
Modules/Classes and are intended to be instantiated as objects
should be nouns, potentially accompanied by modifying adjectives
(i.e. P5EE::Authen::Principal).
=head2 Indents, Line Lengths, and Blank Space
Code checked into CVS must never contain tabs.
Patches of code with tabs do not email well, and different people
have their tabstops set different ways.
If you want to set tab stops on your editor, just make sure it
converts tabs to spaces when it saves the file.
Indentation for normal block-style coding should be 4 spaces.
The settings for Emacs and vim are as follows.
=over
=item * x?emacs: cperl-mode
.xemacs/custom.el:
------------------
(custom-set-variables
'(cperl-indent-level 4)
'(cperl-continued-statement-offset 4)
'(cperl-tab-always-indent t)
'(indent-tabs-mode nil)
)
=item * vim
.vimrc:
-------
set expandtab " replaces any tab keypress with the appropriate number of spaces
set tabstop=4 " sets tabs to 4 spaces
=back
Maximum line lengths should be 77 columns (or 75 columns for
an unbroken line of characters).
This is for maximum portability to different people's
development environments and for decent transmission
through e-mail to a wide array of e-mail clients
(i.e. for patches).
Example: Eudora 3.0.6 wraps a solid, single line of 80 non-whitespace
characters (i.e. ######...#####) at character 76. If there are
spaces in the line, it allows lines up to character 78 before wrapping
the last words down to the next line. If sources have no more than
77 characters in a line, a "diff -u" patch will add a column, and the
lines will escape being folded.
No space before a semicolon that closes a statement.
foo(@bar) ; # wrong
foo(@bar); # right
Line up corresponding items vertically.
my $foo = 1;
my $bar = 2;
my $xyzzy = 3;
open(FILE, $fh) or die $!;
open(FILE2, $fh2) or die $!;
$rot13 =~ tr[abcedfghijklmnopqrstuvwxyz]
[nopqrstuvwxyzabcdefghijklm];
# note we use a-mn-z instead of a-z,
# for readability
$rot13 =~ tr[a-mn-z]
[n-za-m];
Put blank lines where they make sense for readability, such as
the following.
Put blank lines between groups of code that do different things. Put
blank lines after your variable declarations. Put a blank line before a
final return() statement. Put a blank line following a block (and
before, with the exception of comment lines).
An example:
# this is my function!
sub foo {
my (@data) = @_;
my $obj = new Constructor;
my ($var1, $var2);
$obj->setFoo($data[1]);
$var1 = $obj->getFoo(1);
$var2 = $obj->getFoo($var1);
display($var1, $var2);
return($data[0]);
}
print 1;
=head2 Parentheses
For control structures, there is a space between the keyword and opening
parenthesis. For functions, there is not.
for(@list) # wrong
for (@list) # right
my ($ref) # OK
my ($ref) # preferred
localtime ($time); # wrong
localtime($time); # right
Be careful about list vs. scalar context with parentheses!
my @array = ('a', 'b', 'c');
my ($first_element) = @array; # a
my ($first_element) = ('a', 'b', 'c'); # a
my $element_count = @array; # 3
my $last_element = ('a', 'b', 'c'); # c
Always include parentheses after functions, even if there are no arguments.
There are some exceptions, such as list operators (like print) and unary
operators (like undef, delete, uc).
There is no space inside the parentheses, unless it is needed for
readability.
for ( map { [ $_, 1 ] } @list ) # OK
for ( @list ) # not really OK, not horrible
On multi-line expressions, match up the closing parenthesis with either
the opening statement, or the opening parenthesis, whichever works best.
Examples:
@list = qw(
bar
baz
); # right
if ($foo && $bar && $baz
&& $buz && $xyzzy
) {
print $foo;
}
Whether or not there is space following a closing parenthesis is
dependent on what it is that follows.
print foo(@bar), baz(@buz) if $xyzzy;
Note also that parentheses around single-statement control expressions,
as in C<if $xyzzy>, are optional (and discouraged) C<if> it is I<absolutely>
clear -- to a programmer -- what is going on. There is absolutely no
need for parentheses around C<$xyzzy> above, so leaving them out enhances
readability. Use your best discretion. Better to include them, if
there is any question.
The same essentially goes for perl's built-in functions, when there is
nothing confusing about what is going on (for example, there is only one
function call in the statement, or the function call is separated by a
flow control operator). User-supplied functions must always include
parentheses.
print 1, 2, 3; # good
delete $hash{key} if isAnon($uid); # good
However, if there is any possible confusion at all, then include the
parentheses. Remember the words of Larry Wall in the perlstyle manpage:
When in doubt, parenthesize. At the very least it will
let some poor schmuck bounce on the % key in vi.
Even if you aren't in doubt, consider the mental welfare
of the person who has to maintain the code after you, and
who will probably put parens in the wrong place.
So leave them out when it is absoutely clear to a programmer, but if
there is any question, leave them in.
=head2 Braces
(This is about control braces, not hash/data structure braces.)
There is always a space befor the opening brace.
while (<$fh>){ # wrong
while (<$fh>) { # right
A one-line block may be put on one line, and the semicolon may be
omitted.
for (@list) { print }
Otherwise, finish each statement with a semicolon, put the keyword and
opening curly on the first line, and the ending curly lined up with the
keyword at the end.
for (@list) {
print;
smell();
}
perlstyle likes to have "uncuddled elses":
# right
if ($foo) {
print;
}
else {
die;
}
# wrong
if ($foo) {
print;
} else {
die;
}
=head2 Operators
Put space around most operators. The primary exception is the for
aesthetics; e.g., sometimes the space around "**" is ommitted,
and there is never a space before a ",", but always after.
print $x , $y; # wrong
print $x, $y; # right
$x = 2 >> 1; # good
$y = 2**2; # ok
Note that "&&" and "||" have a higher precedence than "and" and "or".
Other than that, they are exactly the same. It is best to use the lower
precedence version for control, and the higher for testing/returning
values. Examples:
$bool = $flag1 or $flag2; # WRONG (doesn't work)
$value = $foo || $bar; # right
open(FILE, $file) or die $!;
$true = foo($bar) && baz($buz);
foo($bar) and baz($buz);
Note that "and" is seldom ever used, because the statement above is
better written using "if":
baz($buz) if foo($bar);
Most of the time, the confusion between and/&&, or/|| can be alleviated
by using parentheses. If you want to leave off the parentheses then you
I<must> use the proper operator. But if you use parentheses -- and
normally, you should, if there is any question at all -- then it doesn't
matter which you use. Use whichever is most readable and aesthetically
pleasing to you at the time, and be consistent within your block of code.
Break long lines AFTER operators, except for "and", "or", "&&", "||".
Try to keep the two parts to a binary operator (an operator that
has two operands) together when possible.
print "foo" . "bar" . "baz"
. "buz"; # wrong
print "foo" . "bar" . "baz" .
"buz"; # right
print $foo unless $x == 3 && $y ==
4 && $z == 5; # wrong
print $foo unless $x == 3 && $y == 4
&& $z == 5; # right
=head2 Other
Put space around a complex subscript inside the brackets or braces.
$foo{$bar{baz}{buz}}; # OK
$foo{ $bar{baz}{buz} }; # better
In general, use single-quotes around literals, and double-quotes
when the text needs to be interpolated.
It is OK to omit quotes around names in braces and when using
the => operator, but be careful not to use a name that doubles as
a function; in that case, quote.
$what{'time'}{it}{is} = time();
When making compound statements, put the primary action first.
open(FILE, $fh) or die $!; # right
die $! unless open(FILE, $fh); # wrong
print "Starting\n" if $verbose; # right
$verbose && print "Starting\n"; # wrong
Use here-docs instead of repeated print statements.
print <<EOT;
This is a whole bunch of text.
I like it. I don't need to worry about messing
with lots of print statements and lining them up.
EOT
Just remember that unless you put single quotes around your here-doc
token (<<'EOT'), the text will be interpolated, so escape any "$" or "@"
as needed.
=head1 REQUIREMENTS RFC AND CODING PROCEDURE
This is for new programs, modules, specific APIs, or anything else.
Contact for core team is the P5EE-development mailing list.
Discuss all ideas there.
The basic process for a new P5EE component is:
get the blessing from the P5EE list for a top-level package name
(i.e. "P5EE::NewModule")
begin a CPAN-able source directory skeleton
write the spec (no code) as POD inside the target module(s)
publish HTML to the web
announce whenever progress is made so that comments can be sought
code is added after there is broad support for the API spec
and supporting doc
=head1 BUG REPORTS, PATCHES, CVS
We don't have bug tracking set up yet.
Use C<diff -u> for patches.
Do not add anything to the main branches in CVS without approval from
a member of the core team.
=head1 TO DO
lots
=head1 ACKNOWLEDGEMENTS
This style guide was based on the slashcode style guide.
It is also in conformance with the mod_perl style guide
and is in the spirit of the C-language Apache style guide.
http://slashcode.com/docs/slashstyle.html
http://cvs.apache.org/viewcvs.cgi/modperl-docs/src/devel/modperl_style/modperl_style.pod?rev=1.5
http://dev.apache.org/styleguide.html
=head1 CHANGES
$Log: styleguide.pod,v $
Revision 1.1 2001/11/16 23:21:38 spadkins
initial stuff
=head1 VERSION
$Id: styleguide.pod,v 1.1 2001/11/16 23:21:38 spadkins Exp $