cvsuser     02/06/18 14:05:52

  Added:       P5EEx/Blue/t Context.t P5EE.t config.ini config.pl
                        config.properties config.xml
  Log:
  new file
  
  Revision  Changes    Path
  1.1                  p5ee/P5EEx/Blue/t/Context.t
  
  Index: Context.t
  ===================================================================
  #!/usr/local/bin/perl -wT
  
  use Test::More qw(no_plan);
  use lib ".";
  use lib "..";
  
  BEGIN {
     use_ok("P5EEx::Blue::P5EE");
     use_ok("P5EEx::Blue::Config::File");
  }
  
  my ($conf, $config, $file, $dir);
  #$P5EEx::Blue::DEBUG = 1;
  
  $dir = ".";
  $dir = "t" if (! -f "config.pl");
  $conf = do "$dir/config.pl";
  $config = P5EEx::Blue::P5EE->config();
  
  ok(defined $config, "constructor ok");
  isa_ok($config, "P5EEx::Blue::Config", "right class");
  is_deeply($conf, { %$config }, "config to depth");
  
  exit 0;
  
  __END__
  
  
  ## $Id: Context.t,v 1.1 2002/06/18 21:05:52 spadkins Exp $
  #############################################################################
  
  package P5EEx::Blue::Context;
  
  use strict;
  
  use P5EEx::Blue::P5EE;
  use P5EEx::Blue::UserAgent;
  
  =head1 NAME
  
  P5EEx::Blue::Context - context in which we are currently running
  
  =head1 SYNOPSIS
  
     # ... official way to get a Context object ...
     use P5EEx::Blue::P5EE;
     $context = P5EEx::Blue::P5EE->context();
     $config = $context->config();   # get the configuration
     $config->dispatch_events();     # dispatch events
  
     # any of the following named parameters may be specified
     $context = P5EEx::Blue::P5EE->context(
         contextClass => "P5EEx::Blue::Context::CGI",
         configClass => "P5EEx::Blue::Config::File",   # or any Config args
     );
  
     # ... alternative way (used internally) ...
     use P5EEx::Blue::Context;
     $context = P5EEx::Blue::Context->new();
  
  =cut
  
  #############################################################################
  # CONSTANTS
  #############################################################################
  
  =head1 DESCRIPTION
  
  A Context class models the environment (aka "context")
  in which the current process is running.
  
  The role of the Context class is to abstract the details of the
  various runtime environments (or Platforms) (including their event loops)
  so that the basic programming model for the developer is uniform.
  
  Since the Context objects are the objects that initiate events in the
  P5EE universe, they must be sure to wrap those event handlers with
  try/catch blocks (i.e. "eval{};if($@){}" blocks).
  
  The main functions of the Context class are to
  
      * load the Config data,
      * dispatch events from the Context event loop, and
      * manage Session data.
  
  The Context object is always a singleton per process (except in rare cases
  like debugging during development). 
  
  Conceptually, the Context may be associated with many
  Config's (one per authenticated user) and
  Sessions (one per unique session_id)
  in a single process (ModPerl).
  However, in practice, it is often
  associated with only one Config or Session throughout the lifetime of
  the process (CGI, Cmd).
  
  =cut
  
  #############################################################################
  # CLASS GROUP
  #############################################################################
  
  =head1 Class Group: Context
  
  The following classes might be a part of the Context Class Group.
  
  =over
  
  =item * Class: P5EEx::Blue::Context
  
  =item * Class: P5EEx::Blue::Context::CGI
  
  =item * Class: P5EEx::Blue::Context::FCGI
  
  =item * Class: P5EEx::Blue::Context::ModPerl
  
  =item * Class: P5EEx::Blue::Context::ModPerlRegistry
  
  =item * Class: P5EEx::Blue::Context::PPerl
  
  =item * Class: P5EEx::Blue::Context::Cmd
  
  =item * Class: P5EEx::Blue::Context::Daemon
  
  =item * Class: P5EEx::Blue::Context::POE
  
  =item * Class: P5EEx::Blue::Context::SOAP (when acting as a SOAP server)
  
  =item * Class: P5EEx::Blue::Context::Gtk
  
  =item * Class: P5EEx::Blue::Context::WxPerl
  
  =back
  
  =cut
  
  #############################################################################
  # ATTRIBUTES/CONSTANTS/CLASS VARIABLES/GLOBAL VARIABLES
  #############################################################################
  
  =head1 Attributes, Constants, Global Variables, Class Variables
  
  =head2 Master Data Structure Map
  
   $context
   $context->{debugscope}{$class}          Debugging all methods in class
   $context->{debugscope}{$class.$method}  Debugging a single method
   $context->{initconfig}    Args that Context was created with
   $context->{used}{$class}  Similar to %INC, keeps track of what classes used
   $context->{cgi}           (Context::CGI only) the CGI object
   $context->{Config}{$user} Info from config file
   [$context->{config}]
      $config->{$type}{$name}              Read-only service config
   $context->{Session}{$session_id}
   [$context->{session}]
      $session->{store}{$type}{$name}      Runtime state which is stored
      $session->{cache}{$type}{$name}      Instances of services
  
  =cut
  
  #############################################################################
  # CONSTRUCTOR METHODS
  #############################################################################
  
  =head1 Constructor Methods:
  
  =cut
  
  #############################################################################
  # new()
  #############################################################################
  
  =head2 new()
  
  The P5EEx::Blue::Context->new() method is rarely called directly.
  That is because a $context should always be instantiated by calling
  P5EEx::Blue::P5EE->context().  This allows for caching of the $context
  as a singleton and the autodetection of what type of Context subclass
  should in fact be instantiated.
  
      * Signature: $context = P5EEx::Blue::P5EE->new(%named);
      * Param:  contextClass class  [in]
      * Param:  configClass  class  [in]
      * Param:  configFile   string [in]
      * Return: $context     P5EEx::Blue::Context
      * Throws: Exception::Class::Context
      * Since:  0.01
  
      Sample Usage: 
  
      $context = P5EEx::Blue::Context->new();
      $context = P5EEx::Blue::Context->new(
          contextClass => 'P5EEx::Blue::Context::CGI',
          configClass  => 'P5EEx::Blue::Config::File',
          configFile   => 'config.xml',
      );
  
  =cut
  
  sub new {
      my $this = shift;
      my $class = ref($this) || $this;
      my $self = {};
      bless $self, $class;
  
      my ($args, %args, $i);
      if ($#_ > -1) {
          if (ref($_[0]) eq "HASH") {
              $args = shift;
              pop if ($#_ % 2 == 0);  # throw away odd arg (probably should throw 
exception)
              for ($i = 0; $i < $#_; $i++) {
                  $args->{$_[$i]} = $_[$i+1];
              }
          }
          else {
              $args = ($#_ > -1) ? { @_ } : {};
          }
      }
  
      my ($config_class, $session_class);
      %args = %$args;
      $self->{initconfig} = \%args;
      $args{context} = $self;
  
      $config_class   = $args{configClass};
      $config_class   = $ENV{P5EE_CONFIG_CLASS} if (! $config_class);
      $config_class   = "P5EEx::Blue::Config::File" if (! $config_class);
  
      $session_class   = $args{sessionClass};
      $session_class   = "P5EEx::Blue::Session::HTMLHidden" if (! $session_class);
  
      eval {
          $self->{config} = P5EEx::Blue::P5EE->new($config_class, "new", \%args);
      };
      $self->add_message($@) if ($@);
  
      $self->init(\%args);
  
      eval {
          $self->dbgprint("Context->new(): configClass=$config_class 
sessionClass=$session_class")
              if ($P5EEx::Blue::DEBUG && $self->dbg(1));
  
          $self->{session} = P5EEx::Blue::P5EE->new($session_class, "new", \%args);
      };
      $self->add_message($@) if ($@);
  
      eval {
          $self->{user_agent} = P5EEx::Blue::UserAgent->new($self);
      };
      $self->add_message($@) if ($@);
  
      return $self;
  }
  
  #############################################################################
  # PROTECTED METHODS
  #############################################################################
  
  =head1 Protected Methods:
  
  The following methods are intended to be called by subclasses of the
  current class (or environmental, "main" code).
  
  =cut
  
  #############################################################################
  # init()
  #############################################################################
  
  =head2 init()
  
  The init() method is called from within the standard Context constructor.
  The init() method in this class does nothing.
  It allows subclasses of the Context to customize the behavior of the
  constructor by overriding the init() method. 
  
      * Signature: $context->init($args)
      * Param:     $args            {}    [in]
      * Return:    void
      * Throws:    P5EEx::Blue::Exception
      * Since:     0.01
  
      Sample Usage: 
  
      $context->init($args);
  
  =cut
  
  sub init {
      my ($self, $args) = @_;
  }
  
  #############################################################################
  # PUBLIC METHODS
  #############################################################################
  
  =head1 Public Methods: Services
  
  =cut
  
  #############################################################################
  # service()
  #############################################################################
  
  =head2 service()
  
  The service() method returns a named object of a certain service type.
  
      * Signature: $service = $context->service($type);
      * Signature: $service = $context->service($type,$name);
      * Signature: $service = $context->service($type,$name,%named);
      * Param:  $type        string  [in]
      * Param:  $name        string  [in]
      * Return: $service     P5EEx::Blue::Service
      * Throws: P5EE::Blue::Exception
      * Since:  0.01
  
      Sample Usage: 
  
      $user = $context->service("Widget","db.user.spadkins");
      $gobutton = $context->service("Widget","gobutton");
  
  There are many services available within a P5EE application.
  Each service is identified by two pieces of information:
  it's type and its name.
  
  The following service types are standard in P5EE.
  Others can be developed by deriving a class from the
  P5EEx::Blue::Service class.
  All service types must start with a capital letter.
  
      * Repository
      * Security
      * Widget
      * TemplateEngine
      * Messaging
      * Procedure
      * LogChannel
  
  Within each service type, each individual service is
  identified by its name.
  The name of a service, if not
  specified, is assumed to be "default".
  
  Whenever a service is requested from the Context via this
  service() method, the service cache in the Session is checked
  first.  If it exists, it is generally returned immediately
  without modification by the named parameters.
  (Parameters *are* taken into account if the "override"
  parameter is supplied.)
  
  If it does not exist, it must be created and stored in the 
  cache.
  
  The name of a service, if not specified, is assumed to be "default".
  
  The named parameters (%named or $named),
  if supplied, are considered defaults.
  They are ignored if the values already exist in the service config.
  However, the additional named parameter, "override", may be supplied.
  In that case, all of the values in the named parameters will accepted
  into the service config.
  
  Every service (i.e. $config->{Repository}{default}) starts as
  a simple hash which is populated with attributes from several
  complementary sources.  If we imagine that a service is requested
  with type $type and name $name, we can envision the following
  additional derived variables.
  
    $type           = "Repository";
    $name           = "sysdb";
    $lcf_type       = "repository";  # lower-case first letter
    $config         = $context->config();
    $repositoryType = $config->{Repository}{sysdb}{repositoryType};
  
  The following sources are consulted to populate the service
  attributes.
  
    1. config of the service (in Config)
       i.e. $config->{Repository}{sysdb}
  
    2. optional config of the service's service_type (in Config)
       i.e. $config->{RepositoryType}{$repositoryType}
  
    3. named parameters to the service() call
  
  All service configuration happens before instantiation
  this allows you to override the "serviceClass" in the configuration
  in time for instantiation
  
  =cut
  
  sub service {
      my ($self, $type, $name, %named) = @_;
      $self->dbgprint("Context->service(" . join(", ",@_) . ")")
          if ($P5EEx::Blue::DEBUG && $self->dbg(3));
  
      my ($args, $lcf_type, $new_service, $override, $lightweight, $attrib);
      my ($service, $config, $class, $session);
      my ($service_store, $service_config, $service_type, $service_type_config);
      my ($default);
  
      if (!defined $type) {
          P5EEx::Blue::Exception->throw(
              error => "cannot create a service of unknown type\n",
          );
      }
  
      if (%named) {
          $args = \%named;
      }
      else {
          $args = {};
      }
  
      if (! defined $name || $name eq "") {    # we need a name!
          $name = "default";
      }
  
      $lcf_type = lcfirst($type);
  
      $session = $self->{session};
      $service = $session->{cache}{$type}{$name};  # check the cache
  
      $new_service = 0;
  
      if (!defined $service || ref($service) eq "HASH") {
          $service = {} if (!defined $service);  # start with new hash ref
          $service->{name} = $name;
          $service->{context} = $self;
  
          $config         = $self->{config};
          $service_config = $config->{$type}{$name};
          $service_store  = $session->{store}{$type}{$name};
  
          $self->dbgprint("Context->service(): new service. config=$config 
sconf=$service_config sstore=$service_store")
              if ($P5EEx::Blue::DEBUG && $self->dbg(6));
      
          $new_service = 1;
  
          ################################################################
          # start with runtime store for the service from the session
          ################################################################
          if ($service_store) {
              foreach $attrib (keys %$service_store) {
                  if (!defined $service->{$attrib}) {
                      $service->{$attrib} = $service_store->{$attrib};
                  }
              }
          }
  
          ################################################################
          # overlay with attributes from the config file
          ################################################################
          if ($service_config) {
              foreach $attrib (keys %$service_config) {
                  # include config attributes only if not set already
                  if (!defined $service->{$attrib}) {
                      $service->{$attrib} = $service_config->{$attrib};
                  }
              }
          }
  
          ################################################################
          # overlay with attributes from the "service_type"
          ################################################################
          $service_type = $service->{"${lcf_type}Type"}; # i.e. "widgetType"
          if ($service_type) {
              $service_type_config = $config->{"${type}Type"}{$service_type};
              if ($service_type_config) {
                  foreach $attrib (keys %$service_type_config) {
                      # include service_type configs only if not set already
                      if (!defined $service->{$attrib}) {
                          $service->{$attrib} = $service_type_config->{$attrib};
                      }
                  }
              }
          }
      }
  
      ################################################################
      # take care of all %$args attributes next
      ################################################################
  
      # A "lightweight" service is one which never has to handle events.
      #   1. its attributes are only ever required when they are all supplied
      #   2. its attributes will be OK by combining the %$args with the %$config
      #      and %$store.
      # This all saves space in the Session store, as the attribute values can
      # be relied upon to be supplied by the config file and the code (and
      # minimal reliance on the Session store).
      # This is really handy when you have something like a huge spreadsheet
      # of text entry cells (usually an indexed variable).
  
      if (defined $args->{lightweight}) {          # may be specified explicitly
          $lightweight = $args->{lightweight};
      }
      else {
          $lightweight = ($name =~ /[\{\}\[\]]/);  # or implicitly for indexed 
variables
      }
      $override = $args->{override};
  
      if ($new_service || $override) {
          foreach $attrib (keys %$args) {
              # don't include the entry which says whether we are overriding or not
              next if ($attrib eq "override");
  
              if ($attrib eq "default") {
                  if ($name =~ /^(.+)\.([^.]+)$/) {
                      $self->wget($1, $2, $args->{default}, 1);
                  }
                  else {
                      $self->wget("session", $name, $args->{default}, 1);
                  }
                  next;
              }
    
              # include attrib if overriding OR attrib not provided in the widget 
configs already
              if (!defined $service->{$attrib} ||
                  ($override && $service->{$attrib} ne $args->{$attrib})) {
                  $service->{$attrib} = $args->{$attrib};
                  $session->{store}{$type}{$name}{$attrib} = $args->{$attrib} if 
(!$lightweight);
              }
              $self->dbgprint("Context->service() [arg=$attrib] name=$name 
lw=$lightweight ovr=$override",
                  " service=", $service->{$attrib},
                  " service_store=", $service_store->{$attrib},
                  " args=", $args->{$attrib})
                  if ($P5EEx::Blue::DEBUG && $self->dbg(6));
          }
      }
   
      if ($new_service) {
  
          $class = $service->{"${lcf_type}Class"};      # find class of service
  
          if (!defined $class || $class eq "") {      # error if no class given
              if ($name eq "session") {
                  $class = "P5EEx::Blue::$type";   # assume the "generic" class
              }
              else {
                  P5EEx::Blue::Exception->throw(
                      error => "no class was configured for the \"$type\" named 
\"$name\"\n",
                  );
              }
          }
  
          if (! $self->{used}{$class}) {                        # load the code
              P5EEx::Blue::P5EE->use($class);
              $self->{used}{$class} = 1;
          }
  
          bless $service, $class;            # bless the service into the class
          $session->{cache}{$type}{$name} = $service;       # save in the cache
          $service->init();                # perform additional initializations
      }
  
      $self->dbgprint("Context->service() = $service")
          if ($P5EEx::Blue::DEBUG && $self->dbg(3));
  
      return $service;
  }
  
  #############################################################################
  # service convenience methods
  #############################################################################
  
  =head2 session()
  
  =head2 repository()
  
  =head2 security()
  
  =head2 widget()
  
  =head2 template_engine()
  
  =head2 messaging()
  
  =head2 procedure()
  
  =head2 log_channel()
  
  =head2 shared_datastore()
  
  =head2 shared_resource_set()
  
  These are all convenience methods, which simply turn around
  and call the service() method with the service type as the
  first argument.
  
      * Signature: $session = $context->session();
      * Signature: $session = $context->session($name);
      * Signature: $session = $context->session($name,%named);
      * Param:  $name        string  [in]
      * Return: $service     P5EEx::Blue::Service
      * Throws: P5EE::Blue::Exception
      * Since:  0.01
  
      Sample Usage: 
  
      $session             = $context->session();
      $repository          = $context->repository();
      $security            = $context->security();
      $template_engine     = $context->template_engine();
      $log_channel         = $context->log_channel();
      $shared_datastore    = $context->shared_datastore();
      $shared_resource_set = $context->shared_resource_set();
  
  =cut
  
  sub repository          { my $self = shift; return $self->service("Repository",@_); }
  sub security            { my $self = shift; return $self->service("Security",@_); }
  sub widget              { my $self = shift; return $self->service("Widget",@_); }
  sub template_engine     { my $self = shift; return 
$self->service("TemplateEngine",@_); }
  sub messaging           { my $self = shift; return $self->service("Messaging",@_); }
  sub procedure           { my $self = shift; return $self->service("Procedure",@_); }
  sub log_channel         { my $self = shift; return $self->service("LogChannel",@_); }
  sub shared_datastore    { my $self = shift; return 
$self->service("SharedDatastore",@_); }
  sub shared_resource_set { my $self = shift; return 
$self->service("SharedResourceSet",@_); }
  
  #############################################################################
  # widget_exists()
  #############################################################################
  
  =head2 widget_exists()
  
      * Signature: $exists = $context->widget_exists($widget_name);
      * Param:  $widget_name     string
      * Return: $exists          boolean
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      if ($context->widget_exists($widget_name)) {
          # do something
      }
  
  The widget_exists() returns whether or not a widget is already known to the
  Context.  This is true if 
  
   * it exists in the Session's widget cache, or
     (i.e. it has already been referenced and instantiated in the cache),
   * it exists in the Session's store, or
     (i.e. it was referenced in an earlier request in this session)
   * it exists in the Config
  
  If this method returns FALSE (undef), then any call to the widget() method
  must specify the widgetClass (at a minimum) and may not simply call it
  with the $widget_name.
  
  This is useful particularly for lightweight widgets which generate events
  (such as image buttons).  The $context->dispatch_events() method can check
  that the widget has not yet been defined and automatically passes the
  event to the widget's container (implied by the name) for handling.
  
  =cut
  
  sub widget_exists {
      my ($self, $widget_name) = @_;
      my ($exists, $widget_type, $widget_class);
  
      $widget_class =
          $self->{session}{cache}{Widget}{$widget_name}{widgetClass} ||
          $self->{session}{store}{Widget}{$widget_name}{widgetClass} ||
          $self->{config}{Widget}{$widget_name}{widgetClass};
  
      if (!$widget_class) {
  
          $widget_type =
              $self->{session}{cache}{Widget}{$widget_name}{widgetType} ||
              $self->{session}{store}{Widget}{$widget_name}{widgetType} ||
              $self->{config}{Widget}{$widget_name}{widgetType};
  
          if ($widget_type) {
              $widget_class = $self->{config}{WidgetType}{$widget_type}{widgetClass};
          }
      }
  
      $exists = $widget_class ? 1 : 0;
  
      $self->dbgprint("Context->widget_exists($widget_name) = $exists")
          if ($P5EEx::Blue::DEBUG && $self->dbg(2));
  
      return $exists;
  }
  
  #############################################################################
  # PUBLIC METHODS
  #############################################################################
  
  =head1 Public Methods: Accessors
  
  =cut
  
  #############################################################################
  # iget()
  #############################################################################
  
  =head2 iget()
  
      * Signature: $value = $context->iget($var, $default);
      * Param:  $var             string
      * Param:  $attribute       string
      * Return: $value           string
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      $script_url_dir = $context->iget("scriptUrlDir", "/cgi-bin");
  
  The iget() returns the value of an Initialization Config variable
  (or the "default" value if not set).
  
  This is an alternative to 
  getting the reference of the entire hash of Initialization Config
  variables with $self->initconfig().
  
  =cut
  
  sub iget {
      my ($self, $var, $default) = @_;
      my $value = $self->{initconfig}{$var};
      return (defined $value) ? $value : $default;
  }
  
  #############################################################################
  # wget()
  #############################################################################
  
  =head2 wget()
  
  The wget() returns the attribute of a widget.
  
      * Signature: $value = $context->wget($widgetname, $attribute);
      * Param:  $widgetname      string
      * Param:  $attribute       string
      * Return: $value           string,ref
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      $wname = $context->wget("session", "wname");
      $width = $context->wget("main.app.toolbar.calc", "width");
  
  =cut
  
  sub wget {
      my ($self, $name, $var, $default, $setdefault) = @_;
      my ($perl, $value);
  
      if ($var !~ /[\[\]\{\}]/) {         # no special chars, "foo.bar"
          $value = $self->{session}{cache}{Widget}{$name}{$var};
          if (!defined $value && defined $default) {
              $value = $default;
              if ($setdefault) {
                  $self->{session}{store}{Widget}{$name}{$var} = $value;
                  $self->widget($name) if (!defined 
$self->{session}{cache}{Widget}{$name});
                  $self->{session}{cache}{Widget}{$name}{$var} = $value;
              }
          }
          $self->dbgprint("Context->wget($name,$var) (value) = [$value]")
              if ($P5EEx::Blue::DEBUG && $self->dbg(3));
          return $value;
      } # match {
      elsif ($var =~ /^\{([^\}]+)\}$/) {  # a simple "{foo.bar}"
          $var = $1;
          $value = $self->{session}{cache}{Widget}{$name}{$var};
          if (!defined $value && defined $default) {
              $value = $default;
              if ($setdefault) {
                  $self->{session}{store}{Widget}{$name}{$var} = $value;
                  $self->widget($name) if (!defined 
$self->{session}{cache}{Widget}{$name});
                  $self->{session}{cache}{Widget}{$name}{$var} = $value;
              }
          }
          $self->dbgprint("Context->wget($name,$var) (attrib) = [$value]")
              if ($P5EEx::Blue::DEBUG && $self->dbg(3));
          return $value;
      } # match {
      elsif ($var =~ /^([a-zA-Z0-9_\.-]+)([\{\}\[\]].*)$/) {
  
          $name = $1;
          $var = $2;
  
          $self->widget($name) if (!defined $self->{session}{cache}{Widget}{$name});
  
          $var =~ s/\{([^\}]+)\}/\{"$1"\}/g;
          $perl = "\$value = \$self->{session}{cache}{Widget}{\$name}$var;";
          eval $perl;
          print STDERR "ERROR: Context->get($var): eval ($perl): $@\n" if ($@);
  
          $self->dbgprint("Context->wget($name,$var) (indexed) = [$value]")
              if ($P5EEx::Blue::DEBUG && $self->dbg(3));
      }
  
      return $value;
  }
  
  #############################################################################
  # wset()
  #############################################################################
  
  =head2 wset()
  
  The wset() sets an attribute of a widget in the Session.
  
      * Signature: $context->wset($widgetname, $attribute, $value);
      * Param:  $widgetname      string
      * Param:  $attribute       string
      * Param:  $value           string,ref
      * Return: void
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      $context->wset("session", "wname", "main_screen");
      $context->wset("main.app.toolbar.calc", "width", 50);
      $context->wset("xyz", "{arr}[1][2]",  14);
      $context->wset("xyz", "{arr.totals}", 14);
  
  =cut
  
  sub wset {
      my ($self, $name, $var, $value) = @_;
      my ($perl);
  
      if ($value eq "{:delete:}") {
          return $self->wdelete($name,$var);
      }
  
      $self->dbgprint("Context->wset($name,$var,$value)")
          if ($P5EEx::Blue::DEBUG && $self->dbg(3));
  
      if ($var !~ /[\[\]\{\}]/) {         # no special chars, "foo.bar"
          $self->{session}{store}{Widget}{$name}{$var} = $value;
          $self->{session}{cache}{Widget}{$name}{$var} = $value
              if (defined $self->{session}{cache}{Widget}{$name});
          return;
      } # match {
      elsif ($var =~ /^\{([^\}]+)\}$/) {  # a simple "{foo.bar}"
          $var = $1;
          $self->{session}{store}{Widget}{$name}{$var} = $value;
          $self->{session}{cache}{Widget}{$name}{$var} = $value
              if (defined $self->{session}{cache}{Widget}{$name});
          return;
      }
      elsif ($var =~ /^\{/) {  # { i.e. "{columnSelected}{first_name}"
  
          $var =~ s/\{([^\}]+)\}/\{"$1"\}/g;  # put quotes around hash keys
  
          #$self->widget($name) if (!defined $self->{session}{cache}{Widget}{$name});
  
          $perl  = "\$self->{session}{store}{Widget}{\$name}$var = \$value;";
          $perl .= "\$self->{session}{cache}{Widget}{\$name}$var = \$value;"
              if (defined $self->{session}{cache}{Widget}{$name});
  
          eval $perl;
          die "ERROR: Context->wset($name,$var,$value): eval ($perl): $@" if ($@);
      }
      # } else we do nothing with it!
  
      return $value;
  }
  
  #############################################################################
  # wdelete()
  #############################################################################
  
  =head2 wdelete()
  
  The wdelete() deletes an attribute of a widget in the Session.
  
      * Signature: $context->wdelete($widgetname, $attribute);
      * Param:  $widgetname      string
      * Param:  $attribute       string
      * Return: void
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      $context->wdelete("session", "wname");
      $context->wdelete("main.app.toolbar.calc", "width");
      $context->wdelete("xyz", "{arr}[1][2]");
      $context->wdelete("xyz", "{arr.totals}");
  
  =cut
  
  sub wdelete {
      my ($self, $name, $var) = @_;
      my ($perl);
  
      $self->dbgprint("Context->wdelete($name,$var)")
          if ($P5EEx::Blue::DEBUG && $self->dbg(3));
  
      if ($var !~ /[\[\]\{\}]/) {         # no special chars, "foo.bar"
          delete $self->{session}{store}{Widget}{$name}{$var};
          delete $self->{session}{cache}{Widget}{$name}{$var}
              if (defined $self->{session}{cache}{Widget}{$name});
          return;
      } # match {
      elsif ($var =~ /^\{([^\}]+)\}$/) {  # a simple "{foo.bar}"
          $var = $1;
          delete $self->{session}{store}{Widget}{$name}{$var};
          delete $self->{session}{cache}{Widget}{$name}{$var}
              if (defined $self->{session}{cache}{Widget}{$name});
          return;
      }
      elsif ($var =~ /^\{/) {  # { i.e. "{columnSelected}{first_name}"
  
          $var =~ s/\{([^\}]+)\}/\{"$1"\}/g;  # put quotes around hash keys
  
          #$self->widget($name) if (!defined $self->{session}{cache}{Widget}{$name});
  
          $perl  = "delete \$self->{session}{store}{Widget}{\$name}$var;";
          $perl .= "delete \$self->{session}{cache}{Widget}{\$name}$var;"
              if (defined $self->{session}{cache}{Widget}{$name});
  
          eval $perl;
          die "ERROR: Context->wdelete($name,$var): eval ($perl): $@" if ($@);
      }
      # } else we do nothing with it!
  }
  
  #############################################################################
  # PUBLIC METHODS
  #############################################################################
  
  =head1 Public Methods: Miscellaneous
  
  =cut
  
  #############################################################################
  # add_message()
  #############################################################################
  
  =head2 add_message()
  
  The add_message() method stores a string (the concatenated list of @args) in
  the Context until it can be viewed by and acted upon by the user.
  
      * Signature: $context->add_message($msg);
      * Param:  $msg         string  [in]
      * Return: void
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      $context->add_message("Data was not saved. Try again.");
  
  =cut
  
  sub add_message {
      my ($self, $msg) = @_;
      if (defined $self->{messages}) {
          $self->{messages} .= "<br>\n" . $msg;
      }
      else {
          $self->{messages} = $msg;
      }
  }
  
  #############################################################################
  # log()
  #############################################################################
  
  =head2 log()
  
  The log() method writes a string (the concatenated list of @args) to
  the default log channel.
  
      * Signature: $context->log(@args);
      * Param:  @args        string  [in]
      * Return: void
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      $context->log("oops, a bug happened");
  
  =cut
  
  sub log {
      my $self = shift;
      print STDERR "Log: ", @_, "\n";
  }
  
  #############################################################################
  # user()
  #############################################################################
  
  =head2 user()
  
  The user() method returns the username of the authenticated user.
  The special name, "guest", refers to the unauthenticated (anonymous) user.
  
      * Signature: $username = $context->user();
      * Param:  void
      * Return: string
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      $username = $context->user();
  
  =cut
  
  sub user {
      my $self = shift;
      "guest";
  }
  
  #############################################################################
  # initconfig()
  #############################################################################
  
  =head2 initconfig()
  
      * Signature: $initconfig = $context->initconfig();
      * Param:  void
      * Return: $initconfig    {}
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      $initconfig = $context->initconfig();
  
  The initconfig() method returns a hashreference to all of the variable/value
  pairs used in the initialization of the Context.
  
  =cut
  
  sub initconfig {
      my $self = shift;
      $self->{initconfig};
  }
  
  #############################################################################
  # config()
  #############################################################################
  
  =head2 config()
  
      * Signature: $config = $context->config();
      * Param:  void
      * Return: $config    P5EEx::Blue::Config
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      $config = $context->config();
  
  The config() method returns the user's config data structure.
  
  =cut
  
  sub config {
      my $self = shift;
      $self->{config};
  }
  
  #############################################################################
  # session()
  #############################################################################
  
  =head2 session()
  
  The session() method returns the session
  
      * Signature: $session = $context->session();
      * Param:  void
      * Return: $session    P5EEx::Blue::Session
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      $session = $context->session();
  
  =cut
  
  sub session {
      my $self = shift;
      $self->{session};
  }
  
  #############################################################################
  # user_agent()
  #############################################################################
  
  =head2 user_agent()
  
  The user_agent() method returns a UserAgent objects which is primarily
  useful to see what capabilities the user agent (browser) supports.
  
      * Signature: $user_agent = $context->user_agent();
      * Param:  void
      * Return: $user_agent    P5EEx::Blue::UserAgent
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      $user_agent = $context->user_agent();
  
  =cut
  
  sub user_agent {
      my $self = shift;
      $self->{user_agent};
  }
  
  #############################################################################
  # domain()
  #############################################################################
  
  =head2 domain()
  
  The domain() method is called to get the list of valid values in a data
  domain and the labels that should be used to represent these values to
  a user.
  
      * Signature: ($values, $labels) = $self->domain($domain_name)
      * Param:     $domain_name      string
      * Return:    $values           []
      * Return:    $labels           {}
      * Throws:    P5EEx::Blue::Exception
      * Since:     0.01
  
      Sample Usage: 
  
      ($values, $labels) = $self->domain("gender");
      foreach (@$values) {
          print "$_ => $labels->{$_}\n";
      }
  
  =cut
  
  sub domain {
      my ($self, $domain) = @_;
      my ($domain_config, $domain_session, $repository, $rep);
      my ($values, $labels, $needs_loading, $time_to_live, $time);
      my ($class, $method, $args, $rows, $row);
  
      $self->dbgprint("Context->domain($domain)")
          if ($P5EEx::Blue::DEBUG && $self->dbg(1));
  
      $domain_config  = $self->{config}{Domain}{$domain};
      $domain_session = $self->{session}{Domain}{$domain};
      $domain_config  = {} if (!defined $domain_config);
      $domain_session = {} if (!defined $domain_session);
  
      $values = $domain_session->{values};
      $values = $domain_config->{values} if (!$values);
  
      $labels = $domain_session->{labels};
      $labels = $domain_config->{labels} if (!$labels);
  
      $needs_loading = 0;
      $repository = $domain_session->{repository};
      $repository = $domain_config->{repository} if (!$repository);
  
      if (defined $repository && $repository ne "") {
          if (!defined $values || !defined $labels) {
              $needs_loading = 1;
          }
          else {
              $time_to_live = $domain_config->{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() >= $domain_session->{time} + $time_to_live) {
                          $needs_loading = 1;
                      }
                  }
              }
          }
      }
  
      $self->dbgprint("Context->domain($domain): needs_loading=$needs_loading")
          if ($P5EEx::Blue::DEBUG && $self->dbg(1));
  
      if ($needs_loading) {
          $rep = $self->repository($repository);
          if (defined $rep) {
              #$method = $domain_session->{getmethod};
              #$method = "get" if (!defined $method);
              #$args   = $domain_session->{getmethod_args};
              #$args   = [ $domain ] if (!defined $args);
  
              #$self->dbgprint("Context->domain($domain): $rep->$method(@$args)")
              #    if ($P5EEx::Blue::DEBUG && $self->dbg(1));
  
              #$rows   = ${rep}->${method}(@$args);
              #$values = [];
              #$labels = {};
              #foreach $row (@$rows) {
              #    push(@$values, $row->[0]);
              #    $labels->{$row->[0]} = $row->[1];
              #}
              #$domain_session->{values} = $values;
              #$domain_session->{labels} = $labels;
              #$time = time();
              #$domain_session->{time} = $time;
          }
  
          $values = $domain_session->{values};
          $labels = $domain_session->{labels};
      }
  
      $values = [] if (! defined $values);
      $labels = {} if (! defined $labels);
      return ($values, $labels);
  }
  
  #############################################################################
  # PUBLIC METHODS
  #############################################################################
  
  =head1 Public Methods: Debugging
  
  =cut
  
  #############################################################################
  # dbg()
  #############################################################################
  
  =head2 dbg()
  
  The dbg() method is used to check whether a given line of debug output
  should be generated.  
  It returns true or false (1 or 0).
  
  If all three parameters are specified, this function
  returns true only when the global debug level ($P5EEX::Blue::Context::DEBUG)
  is at least equal to $level and when the debug scope
  is set to debug this class and method.
  
      * Signature: $flag = $context->dbg($class,$method,$level);
      * Param:     $class       class   [in]
      * Param:     $method      string  [in]
      * Param:     $level       integer [in]
      * Return:    void
      * Throws:    P5EEx::Blue::Exception::Context
      * Since:     0.01
  
      Sample Usage: 
  
      $context->dbgprint("this is debug output")
          if ($P5EEx::Blue::DEBUG && $context->dbg(3));
  
      $context->dbgprint("this is debug output")
          if ($context->dbg(3));
  
  The first usage is functionally identical to the second, but the check
  of the global debug level explicitly reduces the runtime overhead to
  eliminate any method calls when debugging is not turned on.
  
  =cut
  
  my %debugscope;
  
  sub dbg {
      my ($self, $level) = @_;
      return 0 if (! $P5EEx::Blue::DEBUG);
      $level = 1 if (!defined $level);
      return 0 if (defined $level && $P5EEx::Blue::DEBUG < $level);
      my ($debugscope, $stacklevel);
      my ($package, $file, $line, $subroutine, $hasargs, $wantarray);
      $debugscope = (ref($self) eq "") ? \%debugscope : $self->{debugscope};
      $stacklevel = 1;
      ($package, $file, $line, $subroutine, $hasargs, $wantarray) = 
caller($stacklevel);
      while (defined $subroutine && $subroutine eq "(eval)") {
          $stacklevel++;
          ($package, $file, $line, $subroutine, $hasargs, $wantarray) = 
caller($stacklevel);
      }
      return 1 if (! defined $debugscope);
      return 1 if (! %$debugscope);
      return 1 if (defined $debugscope->{$package});
      return 1 if (defined $debugscope->{$subroutine});
      return 0;
  }
  
  #############################################################################
  # dbgprint()
  #############################################################################
  
  =head2 dbgprint()
  
  The dbgprint() method is used to produce debug output.
  The output goes to an output stream which is appropriate for
  the runtime context in which it is called.
  
      * Signature: $flag = $context->dbgprint(@args);
      * Param:     @args        string  [in]
      * Return:    void
      * Throws:    P5EEx::Blue::Exception::Context
      * Since:     0.01
  
      Sample Usage: 
  
      $context->dbgprint("this is debug output")
          if ($P5EEx::Blue::DEBUG && $context->dbg(3));
  
  =cut
  
  sub dbgprint {
      my $self = shift;
      my ($file);
      $file = $self->{initconfig}{debugfile};
      if (! $file) {
          print STDOUT "Debug: ", @_, "\n";
      }
      else {
          local(*FILE);
          if (open(main::FILE, ">> $file")) {
              print main::FILE $$, ": ", @_, "\n";
              close(main::FILE);
          }
      }
  }
  
  #############################################################################
  # dbglevel()
  #############################################################################
  
  =head2 dbglevel()
  
  The dbglevel() method is used to set the debug level.
  Setting the dbglevel to 0 turns off debugging output and is suitable
  for production use.  Setting the dbglevel to 1 or higher turns on
  increasingly verbose debug output.
  
      * Signature: $context->dbglevel($dbglevel);
      * Signature: $dbglevel = $context->dbglevel();
      * Param:     $dbglevel   integer
      * Return:    $dbglevel   integer
      * Throws:    P5EEx::Blue::Exception::Context
      * Since:     0.01
  
      Sample Usage: 
  
      $context->dbglevel(1);             # turn it on
      $context->dbglevel(0);             # turn it off
      $dbglevel = $context->dbglevel();  # get the debug level
  
  =cut
  
  sub dbglevel {
      my ($self, $dbglevel) = @_;
      $P5EEx::Blue::DEBUG = $dbglevel if (defined $dbglevel);
      return $P5EEx::Blue::DEBUG;
  }
  
  #############################################################################
  # dbgscope()
  #############################################################################
  
  =head2 dbgscope()
  
  The dbgscope() method is used to get the hash which determines which
  debug statements are to be printed out when the debug level is set to a
  positive number.  It returns a hash reference.  If class names or
  "class.method" names are defined in the hash, it will cause the
  debug statements from those classes or methods to be printed.
  
      * Signature: $dbgscope = $context->dbgscope();
      * Param:     void
      * Return:    $dbgscope   {}
      * Throws:    P5EEx::Blue::Exception::Context
      * Since:     0.01
  
      Sample Usage: 
  
      $dbgscope = $context->dbgscope();
      $dbgscope->{"P5EEx::Blue::Context::CGI"} = 1;
      $dbgscope->{"P5EEx::Blue::Context::CGI.process_request"} = 1;
  
  =cut
  
  sub dbgscope {
      my $self = shift;
      my $dbgscope = $self->{dbgscope};
      if (!defined $dbgscope) {
          $dbgscope = {};
          $self->{dbgscope} = $dbgscope;
      }
      $dbgscope;
  }
  
  #############################################################################
  # dump()
  #############################################################################
  
  =head2 dump()
  
      * Signature: $perl = $context->dump();
      * Param:     void
      * Return:    $perl      text
      * Throws:    P5EEx::Blue::Exception
      * Since:     0.01
  
      Sample Usage: 
  
      print $self->dump(), "\n";
  
  =cut
  
  use Data::Dumper;
  
  sub dump {
      my ($self) = @_;
      my $d = Data::Dumper->new([ $self ], [ "context" ]);
      $d->Indent(1);
      return $d->Dump();
  }
  
  #############################################################################
  # PROTECTED METHODS
  #############################################################################
  
  =head1 Protected Methods
  
  These methods are considered protected because no class is ever supposed
  to call them.  They may however be called by the context-specific drivers.
  
  =cut
  
  #############################################################################
  # dispatch_events()
  #############################################################################
  
  =head2 dispatch_events()
  
  The dispatch_events() method is called by the bootstrap environmental code
  in order to get the Context object rolling.  It causes the program to block
  (wait on I/O), loop, or poll, in order to find events from the environment
  and dispatch them to the appropriate places within the P5EE framework.
  
  It is considered "protected" because no classes should be calling it.
  
      * Signature: $context->dispatch_events()
      * Param:     void
      * Return:    void
      * Throws:    P5EEx::Blue::Exception
      * Since:     0.01
  
      Sample Usage: 
  
      $context->dispatch_events();
  
  =cut
  
  sub dispatch_events {
      my ($self) = @_;
      P5EEx::Blue::Exception->throw (
          error => "dispatch_events(): unimplemented\n",
      );
  }
  
  #############################################################################
  # shutdown()
  #############################################################################
  
  =head2 shutdown()
  
  The shutdown() method is called when the Context is preparing to exit.
  This allows for connections to databases, etc. to be closed gracefully.
  
      * Signature: $self->shutdown()
      * Param:     void
      * Return:    void
      * Throws:    P5EEx::Blue::Exception
      * Since:     0.01
  
      Sample Usage: 
  
      $self->shutdown();
  
  =cut
  
  sub shutdown {
      my $self = shift;
      my ($config, $repdef, $repname, $instance);
      my ($class, $method, $args, $argidx, $repcache);
  
      $self->dbgprint("Context->shutdown()")
          if ($P5EEx::Blue::DEBUG && $self->dbg(1));
  
      $repcache = $self->{session}{cache}{Repository};
      if (defined $repcache && ref($repcache) eq "HASH") {
          foreach $repname (keys %$repcache) {
              $instance = $repcache->{$repname};
         
              $self->dbgprint("Context->shutdown(): $instance->disconnect()")
                  if ($P5EEx::Blue::DEBUG && $self->dbg(1));
       
              $instance->disconnect();
              delete $repcache->{$repname};
          }
      }
  }
  
  1;
  
  
  
  
  1.1                  p5ee/P5EEx/Blue/t/P5EE.t
  
  Index: P5EE.t
  ===================================================================
  #!/usr/local/bin/perl -wT
  
  use Test::More qw(no_plan);
  use lib ".";
  use lib "..";
  
  BEGIN {
     use_ok("P5EEx::Blue::P5EE");
  }
  
  use strict;
  
  my ($conf, $config, $file, $dir, $w);
  #$P5EEx::Blue::DEBUG = 1;
  
  $dir = ".";
  $dir = "t" if (! -f "config.pl");
  
  ########################################################
  # config()
  ########################################################
  $conf = do "$dir/config.pl";
  $config = P5EEx::Blue::P5EE->config("configFile" => "$dir/config.pl");
  ok(defined $config, "constructor ok");
  isa_ok($config, "P5EEx::Blue::Config", "right class");
  is_deeply($conf, { %$config }, "config to depth");
  
  ########################################################
  # use()
  ########################################################
  eval {
     P5EEx::Blue::P5EE->use("P5EEx::Blue::Nonexistent");
  };
  ok($@, "use(001) class does not exist");
  
  eval {
     $w = P5EEx::Blue::Widget->new("w");
  };
  ok($@, "use(002) known class not used before");
  
  P5EEx::Blue::P5EE->use("P5EEx::Blue::Widget");
  ok(1, "use(002) class never used before");
  P5EEx::Blue::P5EE->use("P5EEx::Blue::Widget");
  ok(1, "use(003) class used before");
  $w = P5EEx::Blue::Widget->new("w");
  ok(1, "use(004) can use class after");
  ok(defined $w, "constructor ok");
  isa_ok($w, "P5EEx::Blue::Widget", "right class");
  
  
  exit 0;
  
  __END__
  
  # eliminate warnings about uninitialized values
  $SIG{__WARN__} = sub { warn @_ unless $_[0] =~ /Use of uninitialized value/};
  
      my ($context, $config, $object);
      $context = P5EEx::Blue::P5EE->context();  # singleton per process
      $config  = P5EEx::Blue::P5EE->config();   # returns a hashref to config
      $context = P5EEx::Blue::P5EE->new();
      $object  = P5EEx::Blue::P5EE->new($class);
      $object  = P5EEx::Blue::P5EE->new($class, $method);
      $object  = P5EEx::Blue::P5EE->new($class, $method, @args);
  
  =head1 Class: P5EEx::Blue::P5EE
  
  P5EE is the main class through which all of the features
  of the Perl 5 Enterprise Environment may be accessed.
  
   * Throws: Exception::Class::Base
   * Throws: P5EEx::Blue::Exception
   * Throws: P5EEx::Blue::Exception::Config
   * Throws: P5EEx::Blue::Exception::Context
   * Since:  0.01
  
  =head2 use()
  
  The use() method loads additional perl code and enables aspect-oriented
  programming (AOP) features if they are appropriate.
  
      * Signature: P5EEx::Blue->use($class);
      * Param:  $class      string  [in]
      * Return: void
      * Throws: <none>
      * Since:  0.01
  
      Sample Usage: 
  
      P5EEx::Blue->use("P5EEx::Blue::Widget::Entity");
  
  =cut
  
  my (%used);
  my (%class_aop_enabled, %class_aop_instrumented);
  my ($aop_entry, $aop_exit, @advice);
  
  sub use {
      my ($self, $class) = @_;
      return if (defined $used{$class});
      eval "use $class;";
      if ($@) {
          P5EEx::Blue::Exception->throw(
              error => "class $class failed to load: $@\n",
          );
      }
      $used{$class} = 1;
      @advice = ();
      #P5EEx::Blue->instrument_aop();
  }
  
  #############################################################################
  # printargs()
  #############################################################################
  
  =head2 printargs()
  
      * Signature: P5EEx::Blue::P5EE->printargs($depth, $skipatend, @args);
      * Param:     $depth       integer  [in]
      * Param:     $skipatend   integer  [in]
      * Param:     @args        any      [in]
      * Return:    void
      * Throws:    none
      * Since:     0.01
  
  =cut
  
  sub printargs {
      my $depth = shift;
      my $skipatend = shift;
      my ($narg);
      for ($narg = 0; $narg <= $#_ - $skipatend; $narg++) {
          print "," if ($narg);
          if (ref($_[$narg]) eq "") {
              print $_[$narg];
          }
          elsif (ref($_[$narg]) eq "ARRAY") {
              print "[";
              if ($depth <= 1) {
                  print join(",", @{$_[$narg]});
              }
              else {
                  &printdepth($depth-1, 0, @{$_[$narg]});
              }
              print "]";
          }
          elsif (ref($_[$narg]) eq "HASH") {
              print "{";
              if ($depth <= 1) {
                  print join(",", %{$_[$narg]});
              }
              else {
                  &printdepth($depth-1, 0, %{$_[$narg]});
              }
              print "}";
          }
          else {
              print $_[$narg];
          }
      }
  }
  
  #############################################################################
  # CONSTRUCTOR METHODS
  #############################################################################
  
  =head1 Constructor Methods:
  
  =cut
  
  #############################################################################
  # new()
  #############################################################################
  
  =head2 new()
  
  The P5EEx::Blue::P5EE->new() method is not a constructor for
  a P5EEx::Blue::P5EE class.  However, it is a constructor, returning
  an object of the class given as the first parameter.
  
  If no parameters are given,
  it is simply a synonym for "P5EEx::Blue::P5EE->context()".
  
      * Signature: $context = P5EEx::Blue::P5EE->new()
      * Signature: $object = P5EEx::Blue::P5EE->new($class)
      * Signature: $object = P5EEx::Blue::P5EE->new($class,$method)
      * Signature: $object = P5EEx::Blue::P5EE->new($class,$method,@args)
      * Param:  $class       class  [in]
      * Param:  $method      string [in]
      * Return: $context     P5EEx::Blue::Context
      * Return: $object      ref
      * Throws: Exception::Class::Base
      * Since:  0.01
  
      Sample Usage: 
  
      $context = P5EEx::Blue::P5EE->new();
      $dbh = P5EEx::Blue::P5EE->new("DBI", "new", "dbi:mysql:db", "dbuser", 
"dbpasswd2");
      $cgi = P5EEx::Blue::P5EE->new("CGI", "new");
  
  =cut
  
  sub new {
      my $self = shift;
      return $self->context(@_) if ($#_ == -1);
      my $class = shift;
      if ($class =~ /^([A-Za-z0-9:_]+)$/) {
          $class = $1;  # untaint the $class
          if (! $used{$class}) {
              $self->use($class);
          }
          my $method = ($#_ > -1) ? shift : "new";
          return $class->$method(@_);
      }
      print STDERR "Illegal Class Name: [$class]\n";
      return undef;
  }
  
  #############################################################################
  # context()
  #############################################################################
  
  =head2 context()
  
      * Signature: $context = P5EEx::Blue::P5EE->context()
      * Param:     contextClass class  [in]
      * Param:     configFile   string [in]
      * Return:    $context     P5EEx::Blue::Context
      * Throws:    P5EEx::Blue::Exception::Context
      * Since:     0.01
  
      Sample Usage: 
  
      $context = P5EEx::Blue::P5EE->context();
      $context = P5EEx::Blue::P5EE->context(
          contextClass => "P5EEx::Blue::Context::CGI",
          configFile => "config.xml",
      );
  
  This static (class) method returns the $context object
  of the context in which you are running.
  It tries to use some intelligence in determining which
  context is the right one to instantiate, although you
  can override it explicitly.
  
  It implements a "Factory" design pattern.  Instead of using the
  constructor of a class itself to get an instance of that
  class, the context() method of P5EE is used.  The former
  technique would require us to know at development time
  which class was to be instantiated.  Using the factory
  style constructor, the developer need not ever know what physical class
  is implementing the "Context" interface.  Rather, it is
  configured at deployment-time, and the proper physical class
  is instantiated at run-time.
  
  =cut
  
  my (%context);  # usually a singleton per process (under "default" name)
                  # multiple named contexts are allowed for debugging purposes
  
  sub context {
      my $self = shift;
  
      my ($name, $args, $i);
      if ($#_ == -1) {
          $args = {};
          $name = "default";
      }
      else {
          if (ref($_[0]) eq "HASH") {
              $args = shift;
              $name = shift if ($#_ % 2 == 0);
              for ($i = 0; $i < $#_; $i++) {
                  $args->{$_[$i]} = $_[$i+1];
              }
          }
          else {
              $name = shift if ($#_ % 2 == 0);
              $args = ($#_ > -1) ? { @_ } : {};
          }
          $name = $args->{name} if (!$name);
          $name = "default" if (!$name);
      }
      return ($context{$name}) if (defined $context{$name});
      
      if (! $args->{contextClass}) {
          if (defined $ENV{P5EE_CONTEXT_CLASS}) {     # env variable set?
              $args->{contextClass} = $ENV{P5EE_CONTEXT_CLASS};
          }
          else {   # try autodetection ...
              my $gateway = $ENV{GATEWAY_INTERFACE};
              if (defined $gateway && $gateway =~ /CGI-Perl/) {  # mod_perl?
                  $args->{contextClass} = "P5EEx::Blue::Context::Modperl";
              }
              elsif ($ENV{HTTP_USER_AGENT}) {  # running as CGI script?
                  $args->{contextClass} = "P5EEx::Blue::Context::CGI";
              }
              # let's be real... these next two are not critical right now
              #elsif ($ENV{DISPLAY}) { # running with an X DISPLAY var set?
              #    $args->{contextClass} = "P5EEx::Blue::Context::Gtk";
              #}
              #elsif ($ENV{TERM}) { # running with a TERM var for Curses?
              #    $args->{contextClass} = "P5EEx::Blue::Context::Curses";
              #}
              else {   # fall back to CGI, because it works OK in command mode
                  $args->{contextClass} = "P5EEx::Blue::Context::CGI";
              }
          }
      }
  
      $context{$name} = $self->new($args->{contextClass}, "new", $args);
      return $context{$name};
  }
  
  #############################################################################
  # config()
  #############################################################################
  
  =head2 config()
  
      * Signature: $config = P5EEx::Blue::P5EE->config(%named);
      * Param:     configClass  class  [in]
      * Param:     configFile   string [in]
      * Return:    $config      P5EEx::Blue::Config
      * Throws:    P5EEx::Blue::Exception::Config
      * Since:     0.01
  
  =cut
  
  sub config {
      my $self = shift;
  
      my ($name, $args, $i);
      if ($#_ == -1) {
          $args = {};
          $name = "default";
      }
      else {
          if (ref($_[0]) eq "HASH") {
              $args = shift;
              $name = shift if ($#_ % 2 == 0);
              for ($i = 0; $i < $#_; $i += 2) {
                  $args->{$_[$i]} = $_[$i+1];
              }
          }
          else {
              $name = shift if ($#_ % 2 == 0);
              $args = ($#_ > -1) ? { @_ } : {};
          }
          $name = $args->{name} if (!$name);
          $name = "default" if (!$name);
      }
  
      $self->context($args)->config();
  }
  
  =head1 ACKNOWLEDGEMENTS
  
   * Author:  Stephen Adkins <[EMAIL PROTECTED]>
   * License: This is free software. It is licensed under the same terms as Perl 
itself.
  
  =head1 SEE ALSO
  
  =cut
  
  1;
  
  
  
  
  1.1                  p5ee/P5EEx/Blue/t/config.ini
  
  Index: config.ini
  ===================================================================
  
  [Standard.Log-Dispatch]
  logdir = /var/p5ee
  
  [Authen]
  passwd = /etc/passwd
  seed = 303292
  
  [Session.default]
  sessionClass = P5EE::Blue::Session::CGI
  
  
  
  
  1.1                  p5ee/P5EEx/Blue/t/config.pl
  
  Index: config.pl
  ===================================================================
  $conf = {
    Session => {
      default => {
        sessionClass => "P5EE::Blue::Session::CGI",
      },
    },
    Standard => {
      'Log-Dispatch' => {
        logdir => '/var/p5ee',
      }
    },
    Authen => {
      passwd => '/etc/passwd',
      seed => 303292,
    },
    Repository => {
      default => {
        repositoryClass => "P5EEx::Blue::Repository::DBI",
        dbidriver => "mysql",
        dbname => "test",
        dbuser => "dbuser",
        dbpass => "dbuser7",
      },
      test => {
        repositoryClass => "P5EEx::Blue::Repository::DBI",
        dbidriver => "mysql",
        dbname => "test",
        dbuser => "dbuser",
        dbpass => "dbuser7",
      },
    },
    SharedResourceSet => {
      default => {
        sharedResourceSetClass => "P5EEx::Blue::SharedResourceSet::IPCLocker",
      },
    },
  };
  
  
  
  
  1.1                  p5ee/P5EEx/Blue/t/config.properties
  
  Index: config.properties
  ===================================================================
  
  # this is a comment
  Standard.Log-Dispatch.logdir = /var/p5ee
  
  # regarding Authen ..
  Authen.passwd = /etc/passwd
  Authen.seed = 303292
  
  # stuff about Session.default
  Session.default.sessionClass = P5EE::Blue::Session::CGI
  
  
  
  
  1.1                  p5ee/P5EEx/Blue/t/config.xml
  
  Index: config.xml
  ===================================================================
  
  <conf>
    <Standard>
      <Log-Dispatch logdir="/var/p5ee"/>
    </Standard>
    <Authen passwd="/etc/passwd" seed="303292"/>
    <Session>
      <default>
        <sessionClass>P5EE::Blue::Session::CGI</sessionClass>
      </default>
    </Session>
  </conf>
  
  
  
  


Reply via email to