Table of Contents
*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
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 |
|
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> |
|
|
attribute |
class |
|
|
method |
class |
|
|
class-method |
class |
|
|
constructor |
class |
|
|
param |
method, class-method, constructor |
|
|
em |
<any> |
|
|
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".) |
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.)
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/*
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.
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:
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'.
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'} = '';
---
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};
---
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');
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');
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', {});
---
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', {});