Table of Contents

REVISION LOG *

2. introduction *

3. pre-pop processing ("Before you Pop") *

3.1 Familiarizing yourself with pox and poxen *

3.2 Using POX to describe a class. *

3.2.1 *

A sample written description of a hypothetical class: *

3.2.2 A sample pox description of a hypothetical class ("Us_company.pox"): *

3.3 Setting Environment Variables prior to processing poxen. *

3.4 *

Understanding how the pox utilities parse their parameters *

3.5 Creating documentation using "poxhtml" *

3.6 Creating Perl modules using "poxperl" *

3.7 Creating database schema using "poxdb" *

3.8 Using "schemadrop" *

3.9 *

Using "schemaload" *

3.10 Merging pox changes with an updated perl module *

3.11 Perl class code required when you add an attribute to your class *

3.11.1 Scalar non-object *

3.11.2 Scalar object *

3.11.3 List of non-objects *

3.11.4 List of objects *

3.11.5 Hash of non-objects *

3.11.6 Hash of objects *

4. POP processing *

REVISION LOG

Version

Date

Who

Description of change

1.0

1/25/99

K. Briggs

Created

1.0

1/29/99

K. Briggs

Modified to reflect release 1.0 of POP and include suggested examples.

1.1

2/24/99

T. Burzesi

  • Added section on Perl code required when you add attributes to a class as per email from B. Holzman.
  • Removed 3.10 on updating using diff: a practice no longer used.
  • Changed title to æDeveloperÆ Guide from User Guide.

2. introduction

Perl Object Persistence (POP) will be implemented by the PPS team using implicit persistence and with all information regarding persistent objects stored in a Sybase database. This user guide has been created to help the EEIT developer understand and utilize POP as implemented here at Matthew Bender on the PPS team. This user guide also describes many POP utilities and includes many explicit examples on their use for further clarification.

3. pre-pop processing ("Before you Pop")

This section describes the steps a developer will follow prior to utilizing POP.

3.1 Familiarizing yourself with pox and poxen

POX* is a markup language conforming to the XML** specification which describes persistent class structure. WeÆll refer a single file containing pox as "pox" and a collection of pox files as "poxen" from this point forward in the documentation.

* POX is an acronym for Persistent Object Xml. ** XML stands for eXtensible Markup Language.

3.2 Using POX to describe a class.

Create a separate pox file for each new class you are attempting to describe/define. When creating a new pox file, the file name should be the name of the class youÆre defining and the file extension will always be "pox". Our naming convention is:

Employee_site.pox

where the class ("Employee_site" here) is the file name which always begins with a capital letter and may contain underscores for readability and with the aforementioned ".pox" file extension. A pox file should always contain information describing the class (such as name and abbreviated name if necessary), as well as information on attributes, methods, class-methods, method-parameters, and text format, if pertinent. The first line of a pox file should always contain a "version" tag used for project source control. A sample version tag would be:

<?version '$Revision: 1.1.1.1 $'?>

This will keep the revision number up-to-date each and every time the pox is checked into CVS (Concurrent Versions System; our source control software used on the PPS team). It is not necessary to create a "constructor" as constructors will be inherited from the persistent base class. Any text found in the pox elements will be considered comments regarding the element and will appear in the documentation created from the pox files. (The procedure on how to generate html documentation from any text found in the pox appears later in this document in the section entitled: "Create documentation using æpoxhtmlÆ").

 

 

In addition to the aforementioned version tag, the pox file will also contain one or more pox elements which will each help to further define the class. Each pox element, as well as the attributes for each, should be as outlined in the following table:

Element

Parents

Attributes

Description

class

<none>

  • name
  • isa
  • abbr

  • Should be identical to Class_name
  • Comma-separated list of (fully-qualified) class names from which this class is derived.
  • Short version of name for use in database
  • attribute

    class

    • name
    • abbr
    • type

     

    • key_type

    • val_type

    • list
    • hash
    • default

  • The name of the attribute, all lower-case
  • Short version of name for use in database
  • Either the database type or the (fully-qualified) class name if this is an embedded object attribute, unless hash is defined
  • type of the key if hash is defined (may _not_ be a class name)
  • type of the value if hash is defined (may be a class name)
  • 1 if the attribute is a list
  • 1 if the attribute is a hash
  • initial value of the attribute, if any (only if list and hash are not defined)
  • method

    class

    • name

  • The name of the method, all lower-case
  • class-method

    class

    • name

  • The name of the class method, all lower-case
  • constructor

    class

    • name

  • The name of the constructor, all lower-case
  • param

    method, class-method, constructor

    • name
    • type
    • pos

  • The name of the parameter, all lower-case
  • Either "scalar" or "array"
  • Number indicating which parameter this is on the stack
  • em

    <any>

    • <none>

  • Emphasizes the enclosed text (format)
  •  

     

    3.2.1

    A sample written description of a hypothetical class:

    LetÆs say we have a new upcoming project (codenamed "Djakarta") and we determine that we have a need to define a class to represent companies with headquarters in the United States. Suppose we have a base-class which we may inherit from (LetÆs say the æDjakarta::corporationÆ class which describes international corporations). From our analysis, we realize that we have specific characteristics which we wish to keep regarding each of these US companies that is not necessarily already found in our base class such as:

    The type of each of these variable attributes must be decided when creating the pox to define a class. The five major types recognized in the pox file are: numbers, strings, bits, dates and objects.

    Our analysis also leads us to believe that Djakarta will need a way to perform the following operations:

    All of the aforementioned class attributes and class methods can easily be represented in a pox file which weÆll create to describe our class. In order to continue with our example, letÆs call our new class "Us_company". If we were to represent this new class using pox, what do you suppose our pox file would look like? WeÆll show you a sample pox for the "Us_company" class on the next page.

     

     

    3.2.2 A sample pox description of a hypothetical class ("Us_company.pox"):

    <?version '$Revision: 1.1.1.1 $'?>

    <class name=æUs_companyÆ isa=æDjakarta::CorporationÆ abbr=æuscoÆ>

    This United States Company class inherits from the Corporation class. This

    class represents American companies that the Djakarta project will monitor.

    <attribute name='Company_name' type=ævarchar(255)Æ>

    This is the name of the Company headquartered in the United States.

    </attribute>

    <attribute name='Company_tin' type=æchar(11)Æ abbr=æemplr_idÆ>

    Unique Tax identification number (assigned by US govÆt/IRS). (e.g. æ123-45-0987Æ)

    </attribute>

    <attribute name=æUsa_Street_addressÆ type=ævarchar(255)Æ>

    This is the street address of company headquarters. (e.g. æ125 Main StreetÆ).

    </attribute>

    <attribute name=æUsa_CityÆ type=ævarchar(80)Æ>

    This is the City where the companyÆs headquarters is located.

    </attribute>

    <attribute name=æUsa_StateÆ type=æchar(2)Æ abbr=æSTÆ>

    This is the State (abbreviated; e.g. "NY") where the companyÆs headquarters is located.

    </attribute>

    <attribute name=æUsa_ZipcodeÆ type=æchar(9)Æ abbr=æZipÆ>

    This is the zipcode for the companyÆs headquarters.

    </attribute>

    <attribute name=æUs_ExporterÆ type=æbitÆ default=æ0Æ>

    Boolean bit-flag to distinguish exporters (ship to outside US) from non-exporters.

    </attribute>

    <attribute name=æDate_incorporated type=ædatetimeÆ>

    This is the date the company was incorporated.

    </attribute>

    <attribute name=æProduct_listÆ list=æ1Æ type='Djakarta::Product'>

    List of products the Company produces. (Empty list for "no products".)

    </attribute>

    <attribute name=æProduct_id_and_detailÆ key_type=æchar(10)Æ val_type=æDjakarta::ProductÆ hash=æ1Æ>

    Hash [keyed on product-id] of Product objects. Useful in retrieving specific product information

    such as description, price, in-stock inventory, etc.

    </attribute>

    <class-method name='Current_Employees'>

    <param name='Company_tin' type='char(10)' pos='1'>

    A Company_tin (tax id number) is passed as a parameter to this method.

    </param>

    Method returns list of current employees for the Company-id passed in parm.

    </method>

    <class-method name='Subsidiaries'>

    <param name='Company_tin' type='char(10)' pos='1'>

    A Company_tin (tax id number) is passed as a parameter to this method.

    </param>

    Method returns list of subsidiary companies for the Company-id passed in parm.

    </method>

    <method name=æNYSE_Trading_InformationÆ>

    <param name='Company_tin' type='char(10)' pos='1'>

    An Company_tin (tax id number) is passed as a parameter to this method.

    </param>

    Returns the New York Stock Exchange acronym for US Company with TIN passed.

    </method>

    </class>

     

    Once a developer has created a pox file to describe a class (such as the "Us_company" class on the previous page) several utilities can be invoked which use pox/poxen as input. These utilities are described in detail later in this document. Prior to invoking these utilities, other preparations must be made such as the setting of several environment variables.

    3.3 Setting Environment Variables prior to processing poxen.

    Several environment variables need to be set for each individual system before that system is able to use POP. These variables will use an environment.template file and the create_environment program to create an environment shell script. This script must be sourced prior to using the system. Please refer to the

    PPS DeveloperÆs Guide for more information regarding the setting and use of the required POP environment variables. The following table outlines the names of the various POP environment variables and their purpose.

    Environment Variable

    Purpose

    POXLIB

    Specifies the directory where the poxen reside (especially those to be inherited from.)

    SYBASE

    Specifies the location of the sybase client library (e.g. "/sybase/server11p" on albsun4 or "/opt/sybase" on albsun9.)

    DBI_DRIVER

    Specifies which DBI driver to use. (Should be "Sybase" for current POP processing.)

    DB_SERVER

    Specifies which database server to use. (Currently

    "albsun2_sql11_dev" for the development server and "albsun8_sql11_prd" for the production server.)

    DB_DB

    Specifies which database to use in the DB_SERVER server. (For fez, "fez_dbdata_dev" in development, "fez_dbdata_prd" in production.)

    DB_USER

    Specifies user name to connect to database as.

    DB_PASSWD

    Specifies password for DB_USER

    POX_SYSTEM

    Specifies system name with first letter always capitalized. (For fez, would be "Fez".)

     

    3.4

    Understanding how the pox utilities parse their parameters

    The major pox utilities (poxhtml, poxperl and poxdb) all use the same code to parse the parameters they are passed. This section will take advantage of that fact and explain the way arguments are handled for these utilities all at once. The major pox utilities accept parameters primarily in the following ways:

    In the case of arguments passed in a matching set of files, consider the following example:

    poxhtml Us_company.pox -out Us_company.html

    When we dissect the example we find the utility name ("poxhtml"), followed by the name of a single pox file ("Us_company.pox"), which is followed by the switch ("-out"), which is in turn followed by the name of the requested output file ("Us_company.html", in this case). In this example, the utility poxhtml will take the Us_company.pox file found in the current directory and from the pox and comments found inside it will generate a documentation file in html format which will be placed in the Us_company.html output file.

    In the case of arguments passed in a matching set of directories, consider the following example:

    poxperl Djakarta/pox/ -out Djakarta/lib

    When we dissect the example we find the utility name ("poxperl"), followed by the name of a poxen directory ("Djakarta/pox") which is followed by the switch ("-out"), which is in turn followed by the name of the requested output directory ("Djakarta/lib", in this case). In this example, each individual pox file that the poxperl utility finds in the input directory will be processed and will have a corresponding like-named output file placed in the output directory.

    In the case of arguments passed as multiple matching sets of files, consider the following example:

    poxdb Employee.pox -out Employee.schema Car.pox -out Car.schema

    When we dissect the example we find the utility name ("poxdb"), followed by the name of the first pox input file ("Employee.pox") which is followed by the switch ("-out"), which is in turn followed by the name of the first requested output file ("Employee.schema", in this case). This matched set of files is then followed by another pox input file ("Car.pox") which is followed by another switch ("-out") and the corresponding output file ("Car.schema" in this case.) In this example, each individual pox file that the poxdb utility finds as an input file parameter (i.e. not preceded by an "-out" switch) will be processed and will have an output file placed in the corresponding output file (i.e. the parameter which follows the previous input file and "-out" switch.) Note that any number of multiple matching sets of files can be passed as parameters as long as the arguments are passed in the scheme illustrated by the example above.

     

    3.5 Creating documentation using "poxhtml"

    A utility named "poxhtml" is used by PPS developers to create documentation in html format. This utility should be invoked from the root of the developerÆs working directory. "Poxhtml" takes one or more pox files as input and creates a corresponding html output file for each pox file found as input. (Please see the section on "Understanding how the pox utilities parse their parameters" for examples on how to invoke this utility with parameters.) Each html output file will contain the name of the class encountered in the pox file at the highest heading level. Any classes from which this class is derived will also be found in the html output file. The name of each base class will be a hyperlink to the page for that class. Sections will be created for attributes, methods, class-methods and constructors. Underneath each of the aforementioned sections will be the corresponding elements of that type (inherited or otherwise). Also, any types which are actually class names will be hyperlinks to the page for that class.

     

    3.6 Creating Perl modules using "poxperl"

    A utility named "poxperl" takes pox file(s) as input and converts each class definition encountered into a perl module which begins the implementation of that class. The developer is responsible for making sure that POP environment variables are set correctly for the current project prior to running "poxperl". (Please see the section on "Setting Environment Variables prior to processing poxen".) The output perl module is created but could be considered more of a "skeleton" in that it is up to the developer to modify the module in such a way as to fully implement the class.. (i.e. methods found in pox files will contain parameters for a method but would not necessarily contain the perl code for the method.) One perl module will be created for each pox file found as an input parameter. (Please see the section on "Understanding how the pox utilities parse their parameters" for examples on how to invoke this utility with parameters.)

     

     

    3.7 Creating database schema using "poxdb"

    A utility named "poxdb" takes pox file(s) as input and converts each class definition encountered into the schemata necessary to provide object persistence for each class. One schema file will be created for each pox input file passed as a parameter. (Please see the section on "Understanding how the pox utilities parse their parameters" for examples on how to invoke this utility with parameters.)

     

    3.8 Using "schemadrop"

    A utility named "schemadrop" is used when a developer wishes to drop a particular class or group of classes. Please note that before schema is "dropped" or tables are dropped, responsible developers will consider what data in the database, if any, must be retained. Regardless of the environment (test, certification, or, most notably production) the This utility will drop all tables, indices and stored procedures associated with the schema passed as input. If an error occurs during "schemadrop", a message will be displayed for each error and processing will continue. The "schemadrop" utility can be passed a single schema file or a directory full of schema files (where the asterisk [*] is used as a wildcard symbol meaning "all æ*.schemaÆ files found in the specified directory.) When "Schemadrop" is passed an entire directory as a parameter, it will drop all the schema associated with each of the files with "schema" file-extensions. A sample invocation with a single schema file passed as a parameter might be:

    schemadrop db/Car.schema

    whereas a sample invocation with a directory parameter might be:

    schemadrop db/*

     

    3.9

    Using "schemaload"

    A utility named "schemaload" is used when a developer wishes to incorporate the schema output from the most recent "poxdb" run into the database in the developerÆs environment of choice. If an error occurs during "schemaload", a message will be displayed and processing is halted. The developer is then responsible for making corrections to the schema or pox files and re-attempting a "schemaload". The "schemaload" utility can be passed a single schema file or a directory full of schema files (where the asterisk [*] is used as a wildcard symbol meaning "all æ*.schemaÆ files found in the specified directory.) A sample invocation with a single schema file passed as a parameter might be:

    schemaload db/Car.schema

    which will load the schema (Car.schema, in this case) into the database specified by the current values in the environment variables whereas a sample invocation with a directory parameter might be:

    schemaload db/*

    which will load the schema (all schema files found in the db directory, in this case) into the database specified by the current values in the environment variables.

     

    3.10 Merging pox changes with an updated perl module

    In the event that developers find the need to change pox/poxen after they have already modified the perl modules output by "poxperl" they must be careful not to lose the perl code theyÆve developed. We recommend that the developer perform the following steps to ensure that no code is lost:

    1. Create original pox file to describe and define a class.
    2. Perform a "check-in" of the pox file into the CVS source code repository. (cvs "add", then "commit".)
    3. Use the poxperl utility to generate a "skeleton" perl module. (e.g. "Us_company.pm").
    4. Modify the perl module expanding methods to attempt to fully implement the class.
    5. Perform a "check-in" of the perl module into the CVS source code repository. (cvs "add", then "commit")
    6. If changes are deemed necessary at this point, then change the pox file (e.g."Us_company.pox") as you have all of your work saved in CVS.
    7. Perform a "check-in" of the new pox file into the CVS source code repository. (cvs "add", then "commit" and the use of a version tag is recommended here.)
    8. Run poxperl again (this time on the new pox file.)
    9. Check the old-version of the perl module out of CVS into a temporary file (e.g."Temp_Us_co.pm").
    10. Run a UNIX "diff" to obtain the difference between the temporary file and the new perl module and pipe it to a "patch" file.
    11. Run a UNIX "patch" command to patch the patch-file changes into the new perl module code.

    This process should save you from losing valuable perl code.

    3.11 Perl class code required when you add an attribute to your class

    When adding an attribute, you must create an accessor for it and add

    it to the initialize method. These are the rules you must follow,

    based on the type of the attribute.

    In the examples which follow, the

    attribute name will be 'attr', the system name will be 'System' and

    the class for which we are adding the 'attr' attribute will be

    'Class'.

    3.11.1 Scalar non-object

    ACCESSOR:

    =head2 ACCESSOR

    Title: System::Class::attr

    Desc: Description of 'attr' attribute

    =cut

    sub attr {

    my $this = shift;

    if (@_) {

    my $obj = shift;

    $this->{'attr'} = $obj;

    }

    $this->{'attr'};

    }

    INITIALIZE:

    $this->{'attr'} = '';

    ---

    3.11.2 Scalar object

    This example uses a scalar embedded (has-a) object of class æSystem::FooÆ

    ACCESSOR:

    =head2 ACCESSOR

    Title: System::Class::attr

    Desc: Description of 'attr' attribute

    =cut

    sub attr {

    my $this = shift;

    if (@_) {

    my $obj = shift;

    unless (ref($obj) && UNIVERSAL::isa($obj, 'System::Foo')) {

    croak "[$obj] is not a System::Foo object";

    }

    $this->{'attr'} = \$obj;

    }

    ${$this->{'attr'}};

    }

    INITIALIZE:

    $this->{'attr'} = \do{my $a};

    ---

    3.11.3 List of non-objects

    This example assumes a list of type æintÆ

    ACCESSOR:

    =head2 ACCESSOR

    Title: System::Class::attr

    Desc: Description of 'attr' attribute

    =cut

    sub attr {

    my $this = shift;

    if (@_) {

    $this->{'attr'} = [@_];

    }

    wantarray ? @{$this->{'attr'}} : $this->{'attr'};

    }

    INITIALIZE:

    $this->{'attr'} =

    $this->_POP__Persistent_list_from_db('int', 'attr');

    3.11.4 List of objects

    This example uses a list of embedded (has-a) objects of class æSystem::FooÆ

    ACCESSOR:

    =head2 ACCESSOR

    Title: System::Class::attr

    Desc: Description of 'attr' attribute

    =cut

    sub attr {

    my $this = shift;

    if (@_) {

    foreach (@_) {

    unless (ref($_) && UNIVERSAL::isa($_, 'System::Foo')) {

    croak "[$_] is not a System::Foo object";

    }

    }

    $this->{'attr'} = [@_];

    }

    wantarray ? @{$this->{'attr'}} : $this->{'attr'};

    }

    INITIALIZE:

    $this->{'attr'} =

    $this->_POP__Persistent_list_from_db('System::Foo', 'attr');

    3.11.5 Hash of non-objects

    This example uses a hash of strings (char 30)

    ACCESSOR:

    =head2 ACCESSOR

    Title: System::Class::attr

    Desc: Description of 'attr' attribute

    =cut

    sub attr {

    my $this = shift;

    if (@_) {

    my %hash = @_;

    $this->{'attr'} = \%hash;

    }

    wantarray ? %{$this->{'attr'}} : $this->{'attr'};

    }

    INITIALIZE:

    $this->{'attr'} =

    $this->_POP__Persistent_hash_from_db('char(30)', 'attr', {});

    ---

    3.11.6 Hash of objects

    This example uses a hash of embedded objects, of type 'System::Foo'

    ACCESSOR:

    =head2 ACCESSOR

    Title: System::Class::attr

    Desc: Description of 'attr' attribute

    =cut

    sub attr {

    my $this = shift;

    if (@_) {

    my %hash = @_;

    while (my($k,$v) = each %hash) {

    unless (ref($v) && UNIVERSAL::isa($v, 'System::Foo') {

    croak "[$v] is not a System::Foo object";

    }

    }

    $this->{'attr'} = \%hash;

    }

    wantarray ? %{$this->{'attr'}} : $this->{'attr'};

    }

    INITIALIZE:

    $this->{'attr'} =

    $this->_POP__Persistent_hash_from_db('System::Foo', 'attr', {});

     

    4. POP processing