This is Info file pm.info, produced by Makeinfo version 1.68 from the
input file bigpm.texi.


File: pm.info,  Node: Win32/ASP/DBRecord,  Next: Win32/ASP/DBRecordGroup,  Prev: Win32/ASP,  Up: Module List

an abstract parent class for representing database records
**********************************************************

NAME
====

   Win32::ASP::DBRecord - an abstract parent class for representing
database records

SYNOPSIS
========

DESCRIPTION
===========

   The main purpose of `Win32::ASP::DBRecord'is to be subclassed.  It
implements a generic set of default behavior for the purpose of reading a
record from a table, displaying that record in HTML, allowing edits to it,
and writing that record back to the table.  It relies heavily upon
Win32::ASP::Field objects, which are used to provide an object-oriented
interface to the most important class data for a `Win32::ASP::DBRecord'
subclass - the fields possessed by the record represented by the class.

Internal Data Structure
-----------------------

   The internal data structure of a instance of `Win32::ASP::DBRecord'
consists of the following elements, all optional:

orig
     This is a reference to a hash indexed on field names and storing data
     read from the database.

edit
     This is a reference to a hash indexed on field names and storing the
     currently modified data.

child_dbgroup
     There can be any number of child groups and these are stored at the
     root of the `Win32::ASP::DBRecord' object, not within orig or edit.
     See _CHILDREN for more information.

Class Methods
-------------

   Class methods were used to implement access to class properties.  Since
Perl doesn't enforce a distinction between class and instance methods,
these methods can be called on both class names and on instances of the
class, which ends up being incredibly useful.  I strongly recommend against
ever calling these methods using subroutine notation (i.e. `&amp;_DB' or
`&amp;_PRIMARY_KEY').  Perl methods execute in the namespace in which they
were defined, which means that if you further subclass and define a new
implementation of those methods, any methods you don't override that were
in the parent class will call the parent class's versions of those
methods.  That's bad.  Always call these methods with the arrow notation
and you'll be safe.

Mandatory Class Methods
.......................

   These class methods will be overridden in every child class.

_DB
     The _DB method should return the `Win32::ASP::DB' (or subclass
     there-of) object that is used for database access.  A frequent
     implementation looks like this:

          sub _DB {
            return $main::TheDB;
          }

_FRIENDLY
     The _FRIENDLY method should return a friendly name expressing what
     sorts of things these records are.  This friendly name may get used
     in certain error messages (in particular,
     `Win32::ASP::Error::Field::group_wrapper').  For instance, the
     _FRIENDLY method for line items on an invoice might return "Line
     Item".  An error message could then say, "There was an error in Line
     Item 4.  The error was . . ."

_READ_SRC
     The _READ_SRC method should return the name of the table or view that
     should be used to read records from the database.  Frequently a view
     will be defined on the SQL Server to include information from various
     lookup tables.

_WRITE_SRC
     The _WRITE_SRC method should return the name of the table that should
     be used to write records to the database.

_PRIMARY_KEY
     The _PRIMARY_KEY method should return a list of the field names in
     the Primary Key for the table.  Of note, this returns a list, not a
     *reference to an array*.  The order that the fields are in
     _PRIMARY_KEY is also the order in which the values will be specified
     for identifying records for reading them from the database.

_FIELDS
     The _FIELDS method should return a reference to a hash of
     `Win32::ASP::Field' objects, indexed on the field names.  Of note,
     for performance reasons the method is usually implemented like so:

          sub _FIELDS {
            return $MyStuff::MyRecord::fields;
          }

          $MyStuff::MyRecord::fields = {

          Win32::ASP::Field->new(
          			name => 'RecordID',
          			sec  => 'ro',
          			type => 'int',
          			desc => 'Record ID',
          		),

          Win32::ASP::Field->new(
            name => 'RecordRemarks',
            sec  => 'rw',
            type => 'text',
          ),

          };

Optional Class Methods
......................

   These class methods can be overriden in a child class.

_ACTIONS
     The _ACTIONS method should return a reference to a hash of
     `Win32::ASP::Action' objects, indexed on the action names.  Actions
     are used to implement things that users do to records.  For instance,
     a user might want to Edit a record.  Some users might not have
     permissions to edit some records though, and so it makes sense to
     implement an object that is responsible for determining whether a
     given user is able to execute a given action in a given circumstance.
     The action is also responsible for displaying the appropriate HTML
     for a link that implements the action, knowing how to warn a user
     before the action is carried out, etc.  For more information, see the
     `Win32::ASP::Action' class and its various sub-classes.

     Of note, for performance reasons the method is usually implemented
     like so:

          sub _ACTIONS {
            return $MyStuff::MyRecord::actions;
          }

          $MyStuff::MyRecord::actions = {

          Win32::ASP::Action::Edit->new,

          Win32::ASP::Action::Delete->new,

          Win32::ASP::Action->new(
            name   => 'cancel',
            label  => 'Cancel',
            . . .
          ),

          };

_CHILDREN
     Some records quite logically have child records.  For instance, a
     Purchase Order generally has a number of line-items on it, and this
     are usually implemented using a table that is 1:M linked to the
     Purchase Order table.  Within `Win32::ASP::DBRecord' class objects,
     this is implemented through a reference to a
     `Win32::ASP::DBRecordGroup' class object that contains the child
     records.

     The implementation normally looks something like this:

          sub _CHILDREN {
            return $MyStuff::MyRecord::children;
          }

          $MyStuff::MyRecord::children = {

          child_records => {
            type  => 'MyStuff::MyChildRecordGroup',
            pkext => 'ChildID',
          },

          };

     The implication of the above is that `MyStuff::MyRecord' objects have
     a group of associated `MyStuff::MyChildRecord' objects, which are
     accessed through a `MyStuff::MyChildRecordGroup' object.  The
     reference to that object will be stored in
     `$self-&gt;{child_records}'.  The primary key of the
     `MyStuff::MyChildRecord' objects will be the primary key for the
     `MyStuff::MyRecord' objects plus the added field '`ChildID''.  The
     index on the hash is referred to hereafter as the 'child group name'.

Class Methods you probably won't override
.........................................

   There is only one of these.

ADD_FIELDS
     This method is called on the class in order to add new fields.  This
     is usually used by `Win32::ASP::DBRecordGroup' objects to add
     `Win32::ASP::Field::dispmeta' objects to the underlying
     `Win32::ASP::DBRecord' object.  `Win32::ASP::Field::dispmeta' objects
     are frequently used to display more than one field in a column when
     displaying a table of records (i.e. one field above the other).

Instance Methods
----------------

new
...

   This is a basic new method.  Simply creates an anonymous hash and
returns a reference.  The new method is not responsible for reading data
or anything else.  Just creating a new record object.  You will probably
not need to override this method.

init
....

   This is used for initializing new records prior to being edited.  The
code in ASP land for throwing up the edit screen when creating a new
record looks something like this:

     use MyStuff::MyRecord;

     $record = MyStuff::MyRecord->new;
     $record->init;
     $record->edit;
     $data = 'edit';
     $viewtype = 'edit';

   This is then followed by the <FORM> section.

   Note that init modifies orig, not edit.  Once orig is modified, the
edit method is used to place the record in edit mode.

read
....

   The read method is used, coincidentally, to read a record from the
database.  It should be passed an array comprised of the primary key
values for the record desired.

   The read method is responsible for reading all appropriate values for
the record, and for reading any child records for which the child group
name shows up in `$self'.  The implications of this are important for
providing appropriate behavior when update is called.

   The actual reading in of data from the ADO Recordset object is
implemented by _read.  This is done so that `Win32::ASP::DBRecordGroup'
object can execute a query and then make calls to _read for each record
returned.

_read
.....

   The _read method is responsible for reading the data from the ADO
Recordset object ($result) and entering it into the object.  It does this
by looping over the fields in _FIELDS and calling read on each of them
with the appropriate parameters.  Note that _read accepts the optional
parameter $columns and passes this along in the call to read on the
`Win32::ASP::Field' objects.  This is to minimize unneeded value retrieval
calls when `Win32::ASP::DBRecordGroup' objects are only interested in a
few fields.  If $columns is a reference to a hash, it will be interpreted
as a list of the fieldnames of note.  However, to allow for more
flexibility in implementation, the decision as to whether or not the field
will actually be read is still left up to the `Win32::ASP::Field' object.

   In addition, _read is responsible for calling can_view on the resultant
record object to see whether the user is allowed to view this record.  If
can_view returns false, _read throws a
`Win32::ASP::Error::DBRecord::no_permission' exception

read_deep
.........

   Since the read method is responsible for reading in all child records
for which there is an entry in `$self', the read_deep method simply
creates an entry in the `$self' hash for each key in the hash returned
from _CHILDREN.

post
....

   The post method takes data returned from a POST action and enters it
into the `Win32::ASP::DBRecord' object.  Of note, post takes a `$row' as a
parameter.  This is used to identify which row of a table is of interest
when being used for editing `Win32::ASP::DBRecordGroup' objects.

   The method simply calls post on each of the `Win32::ASP::Field' objects.

   It also posts the data for all of the child records.  The presumption
is that if the records are really child records, one would generally edit
the whole mess at one time and that they will then want to be posted.  So
it creates new child objects of the appropriate
`Win32::ASP::DBRecordGroup' classes and calls post on them.

insert
......

   The insert method is responsible for taking the data and writing it to
the database.  If there are child records associated with object, those
are written as well.  Everything is wrapped in a transaction so that a
failure to write child records for any reason will roll back the
transaction.

   The insert method is passed a list of fields that should always be
written.  By default, the insert method will only write values that are
considered editable (as determined by calling can_edit on the field
object) <Bor> that show up in the passed list of fields.  This enables one
to define certain fields as read only, but still modify them within the
context of actions or other code.  Also, values are only written if they
are defined in the `$self-&gt;{edit}' hash.  It is generally considered
poor form to write NULL values to the database (especially in SQL Server
6.5 as this results in a 2K page being allocated for NULL text objects:).

   The values are prepared for inserting by calling as_write_pair on the
`Win32::ASP::Field' objects.  The array of write pairs is then passed to
the insert method on the `Win32::DB' object.  The return from that call is
the ADO Recordset object, which is then passed to set_inserted so that
auto generated Primary Key values can be retrieved

   It then deals with the child record groups as needed.  The defined
objects have set_prop used to propagate the primary key values onto the
child objects.  The insert method can then be called to insert the entire
group.

   The insert method returns a list of all write pairs that were inserted.
This so that implementations that override insert can make use of that
information (this is most commonly done for logging purposes - other
records are inserted into logging tables to indicate who did what when,
and having insert return the information makes that much easier.).

set_inserted
............

   This method is responsible for retrieving the Primary Key values on
newly inserted records.  Most useful when one of those Primary Key values
is an autonumber field.

update
......

   This is the single largest, ugliest morass of code in
`Win32::ASP::DBRecord'.  Yeach.  Think of it as a slightly uglier insert,
though, and it's a little easier to understand.

   First we start a transaction and call can_update.  The can_update
method will call read in turn (no way to know if we can update a record if
we don't know what was in it).

   If can_update returns false, we throw a
`Win32::ASP::Error::DBRecord::no_permission' exception and get out of
here.  Otherwise, we procede to call `verify_record' and
`verify_timestamp'.  If neither of those throw exceptions, we continue on.

   The method then creates <C$constraint>, a SQL WHERE condition suitable
for indentifying the record of interest based on the Primary Key.

   It then starts building a list of write pairs.  It also adds those
pairs to `@retvals', which will contain a list of fields, new values, and
old values for any field that changed.  Note that we only update fields
for which can_edit returns true and that have changed, or that are
mentioned in `@ext_fields', the passed parameter list.  Fields updated as
a result of being in `@ext_fields' are not mentioned in the list of
changed fields that is returned.

BUGS
====

Triple level children
     The implementation of child records does not deal properly with
     situation in which the child records have children themselves.  This
     issue will be resolved when I have time.


File: pm.info,  Node: Win32/ASP/DBRecordGroup,  Next: Win32/ASP/Error,  Prev: Win32/ASP/DBRecord,  Up: Module List

an abstract parent class for representing groups of database records
********************************************************************

NAME
====

   Win32::ASP::DBRecordGroup - an abstract parent class for representing
groups of database records

SYNOPSIS
========

DESCRIPTION
===========

   The main purpose of `Win32::ASP::DBRecordGroup'is to be subclassed.  It
implements a generic set of default behavior for the purpose of reading a
group of records from a table, displaying that group of records in an HTML
table, and allowing edits to that group if applicable.  All
`Win32::ASP::DBRecordGroup' classes rely upon a `Win32::ASP::DBRecord'
class that implements the underlying record.

Internal Data Structure
-----------------------

   The internal data structure of a instance of
`Win32::ASP::DBRecordGroup' consists of the following elements, all
optional:

orig
     This is a reference to an array of DBRecord objects storing data read
     from the database.

edit
     This is a reference to an array of DBRecord objects storing the
     currently modified data.

Class Methods
-------------

   Class methods were used to implement access to class properties.  Since
Perl doesn't enforce a distinction between class and instance methods,
these methods can be called on both class names and on instances of the
class, which ends up being incredibly useful.  I strongly recommend against
ever calling these methods using subroutine notation (i.e. `&amp;_DB' or
`&amp;_PRIMARY_KEY').  Perl methods execute in the namespace in which they
were defined, which means that if you further subclass and define a new
implementation of those methods, any methods you don't override that were
in the parent class will call the parent class's versions of those
methods.  That's bad.  Always call these methods with the arrow notation
and you'll be safe.

Mandatory Class Methods
.......................

   These class methods will be overridden in every child class.

_DB
     The _DB method should return the `Win32::ASP::DB' (or subclass
     there-of) object that is used for database access.  A frequent
     implementation looks like this:

          sub _DB {
            return $main::TheDB;
          }

_TYPE
     The _TYPE method should return the name of the `Win32::ASP::DBRecord'
     subclass that implements the underlying records for this
     DBRecordGroup object.

_QUERY_METAS
     The _QUERY_METAS method should return a reference to a hash of
     subroutines that implement more complicated querying behavior.  The
     subroutines will be passed the appropriate query specification and
     should return legal SQL for inclusion within a query.  Of note, for
     performance reasons the method is usually implemented like so:

          sub _QUERY_METAS {
            return $MyStuff::MyRecordGroup::query_metas;
          }

          $MyStuff::MyRecordGroup::query_metas = {

          Status => sub {
            my($values) = @_;
            $values or return;
            return "Status LIKE '[$values]'";
          },

          };

     The above Status query_meta presumes a single character status field
     and that the passed value indicates a list of desired status values.
     For instance, the status values might be N for new records, P for in
     process records, and F for finished records.  Using the above
     query_meta, a user could query for `Status=NP', which would indicate
     they desired new and in process records.  They could get the same
     results by querying for `!F'.

     Note that there is a security hole in the above code - if a user
     queries for `Status=N] GO do_something_ugly_here', they will
     effectively "jump" out of the LIKE statement and may be able to
     execute arbitrary SQL code.  Defending against this possibility is
     left as an excercise for the reader.

Optional Class Methods
......................

   These class methods can be overriden in a child class.

_MIN_COUNT
     The _MIN_COUNT method defines the minimum number of records to
     display when allowing the user to edit a group of records.

_NEW_COUNT
     The _NEW_COUNT method defines the minimum number of blank records to
     display when allowing the user to edit a group of records.

Instance Methods
----------------

new
...

   This is a basic new method.  It simply creates an anonymous hash and
returns a reference.  The new method is not responsible for reading data
or anything else.  Just creating a new record group object.  You will
probably not need to override this method.

query
.....

   This is the heart of the DBRecordGroup class.  The method is passed
three parameters: a reference to a hash of constraints, a string
specifying how to order the results, and a string specifying a list of
columns to retrieve.

   The hash of constraints should be indexed on the field name (or
query_meta name).  If the index references a query_meta, the value will be
passed to the query_meta subroutine.  If the index doesn't reference a
query_meta, the field will be tested for equality with the specified value.
The specified value will be formatted by the field's as_sql method before
being included in the SQL.  All of the constraints will be ANDed together
to form the WHERE clause in the SQL.

   The order string should be a comma separated list of field names.  Bare
field names will be sorted in ascending order; field names preceded by a
minus sign will be sorted in descending order.

   The columns string should be one of three things: empty, an asterisk,
or a comma separated list of field names.  If the string is absent or an
asterisk, the query will retrieve all the columns  If a comma separated
list is specified, the query will only retrieve those columns.  The
advantage of this is that queries can be optimized to return only the
information that will be displayed to the user.  Keep in mind, however,
that if the DBRecord object requires specific fields in order to make
determinations about viewability or the like, those columns need to be
specified in the column list.  As a result, query is frequently overriden
to automatically append those columns to the column list before call
`SUPER::query'.

   After setting up the SQL for the query, query calls `exec_sql' on the
appropriate Win32::ASP::DB object (determined by calling `< $self-'_DB
>>).  It then iterates over the result set returned, creating new DBRecord
objects of the appropriate class and calling _read on them.  The call to
_read is wrapped in a try block - if the user doesn't have rights to view
the record, _read will throw an exception.  That exception will be trapped
and the record won't be be appended to the array of DBRecord objects.

   Another common modification to query involves adding constraints to all
queries to explicitly call query_metas that are responsible for
ascertaining viewability.  This can greatly improve performance - if the
user asks for every record in the system, the query handles the weeding out
of those records that are not viewable, rather than reading the data and
letting _read throw an exception.  Again, this can be easily handled by
overriding the method and then calling `SUPER::query'.

query_deep
..........

index_hash
..........

insert
......

delete
......

should_update
.............

update
......

edit
....

merge_inner
...........

split_inner
...........

post
....

add_extras
..........

set_prop
........

gen_table
.........

get_QS_constraints
..................

make_QS_constraints
...................

debug_dump
..........


File: pm.info,  Node: Win32/ASP/Error,  Next: Win32/ASP/Extras,  Prev: Win32/ASP/DBRecordGroup,  Up: Module List

an abstract parent class for implementing exceptions in Win32::ASP::DB
**********************************************************************

NAME
====

   Win32::ASP::Error - an abstract parent class for implementing
exceptions in Win32::ASP::DB

SYNOPSIS
========

     use Win32::ASP::Error;

     package Win32::ASP::Error::DBRecord;
     @Win32::ASP::Error::DBRecord::ISA = qw/Win32::ASP::Error/;

     package Win32::ASP::Error::DBRecord::no_permission;
     @Win32::ASP::Error::DBRecord::no_permission::ISA = qw/Win32::ASP::Error::DBRecord/;

     #Parameters:  action, identifier

     sub _as_html {
       my $self = shift;

     my $action = $self->action;
     my $identifier = $self->identifier;
     return <<ENDHTML;
       You are not allowed to $action $identifier.<P>
       ENDHTML
       }

     throw Win32::ASP::Error::DBRecord::no_permission (action => 'view', identifier => $identifier);

DESCRIPTION
===========

Overview
--------

   `Win32::ASP::Error' is the abstract parent class used for implementing
exceptions in the Win32::ASP::DB system.  It inherits from
`Error::Unhandled', which allows exceptions to handle themselves if the
calling program leaves the exception unhandled, and `Class::SelfMethods',
which allows instances to override methods and provides for
method/attribute calling equivalence.

Utilization
-----------

   In general, subclasses of `Win32::ASP::Error' implement the as_html
method (properly implemented as `_as_html' so that instances can override
it if necessary - see the `Class::SelfMethods' documentation for more
explanation).  This method should return properly formatted HTML that
describes the condition that led to the exception and, if applicable,
provides instruction to the user about how to rectify the problem.

   The return value from the title method is used for the <TITLE> block in
the returned web page.


File: pm.info,  Node: Win32/ASP/Extras,  Next: Win32/ASP/Field,  Prev: Win32/ASP/Error,  Up: Module List

a extension to Win32::ASP that provides more methods
****************************************************

NAME
====

   Win32::ASP::Extras - a extension to Win32::ASP that provides more
methods

SYNOPSIS
========

     use Win32::ASP::Extras;

     Win32::ASP::Set('my_hash',
         { fullname => 'Toby Everett',
           username => 'EverettT',
           role => 'Editor'
         } );

     Win32::ASP::Redirect('userinfo.asp', reason => "I just feel like redirecting.");

     exit;

     use Win32::ASP::Extras;

     my $userinfo = Win32::ASP::Get('my_hash');

     foreach my $i (sort keys %{$userinfo}) {
       $main::Response->Write("$i $userinfo->{$i}<P>\n");
     }

     exit;

DESCRIPTION
===========

Installation instructions
-------------------------

   This installs with MakeMaker.

   To install via MakeMaker, it's the usual procedure - download from CPAN,
extract, type "perl Makefile.PL", "nmake" then "nmake install". Don't do
an "nmake test" because the ASP objects won't be available and so won't
work properly.

Function Reference
==================

use Win32::ASP::Extras;
-----------------------

   This imports the following methods into the Win32::ASP namespace.
There is no need to `use Win32::ASP;' in order to use Win32::ASP::Extras;.
The modules are independent of each other and only share a namespace.

   To be more precise, `use Win32::ASP::Extras'' loads everything into
`Win32::ASP::Extras' and then aliases the symbol table entries over into
`Win32::ASP'.  This is to avoid any weirdness with respect to AutoLoader.

FormatURL Url [, HASH]
----------------------

   This is designed to take a base URL and a hash of parameters and return
the properly assembled URL.  It does, however, have some weird behavior.

   If the first character of the URL *is not* a forward slash and
`$main::WEBROOT' is defined, the function will automatically prepend
`$main::WEBROOT/' to the URL.  This has the side effect of making 95% of
URLs *absolute* relative to `$main::WEBROOT', if it is defined.  This
makes it easier to move Webs around just by changing `$main::WEBROOT'.

   If the first character of the URL *is* a forward slash, the URL is left
unchanged.

   If the first characters are "`./'", the "`./'" is stripped off and the
URL left unchanged.  This allows one to specify relative URLs - just put a
"`./'" in front of it.

   The parameters are URLEncoded, but the keys for them are not.  The
resultant parameter list is HTML encoded so that `&timestamp' doesn't
become `xtamp' (`&times;' encodes a multiplication symbol).

QueryStringList
---------------

   This returns a list of QueryString keys and values.  It does not deal
with multi-valued parameters.

Redirect Url [, HASH]
---------------------

   A safe redirect that redirects and then absolutely and positively
terminates your program.  If you thought `$Response-'Redirect> behaved
liked die and were disappointed to discover it didn't, mourn no longer.

   It takes a base URL and a hash of parameters.  The URL will be built
using `FormatURL'.

MyURL
-----

   This return the URL used to access the current page, including its
QueryString.  Because it uses QueryStringList, it doesn't properly deal
with multi-valued parameters.

CreatePassURLPair
-----------------

   The function returns both `passurl' and the result from calling MyURL.
The return values are suitable for inclusion in a hash for passing to
`FormatURL'.  The PassURL functions are generally used for dealing with
expired sessions.  If the session expires, the `Redirect' is passed
CreatePassURLPair for the parameters.  That page then explains to the user
what is going on and has a link back to the login page along with
PassURLPair.  The login page can then use GetPassURL to extract the URL
from the QueryString and redirect to that URL.

GetPassURL
----------

   This extracts the `passurl' value from the QueryString.

PassURLPair
-----------

   This returns `passurl' along with the result from calling GetPassURL.
The return values are suitable for inclusion in a hash for passing to
`FormatURL'.

StampPage
---------

   This returns HTML that says:

     Refresh this page.

   The text `this page' is a link to the results of MyURL.

Set
---

   Set and Get can be used to store arbitrary Perl objects in `$Session'.
It uses Data::Dumper to store things and eval to retrieve them.  Notice
that this is safe *only* because *we* are the only ones who can store
stuff in `$Session'.

   <LECTURE_MODE>

   Do *NOT*, I repeat, do *NOT* use Data::Dumper to serialize a Perl
object and then stuff it in a user's cookie, presuming that you can then
use eval to extract it when they pass it back to you. If you do, you
deserve to have someone stuff `system("del /s *.*")' or some such funny
Perl code in that cookie and then visit your web site.  Never, ever, ever
use eval on code that comes from an untrusted source.  If you need to do
so for some strange reason, take a look at the Safe module, but be careful.

   </LECTURE_MODE>

   Oh, the call takes two parameters, the name to store it under and the
thing to store (can be a reference to a hash or some other neat goodie).
Keep in mind that references to CODE objects (i.e. anonymous subroutines)
or `Win32::OLE' objects or anything like that will not make it.

Get
---

   Takes a parameter and returns the thing.  Both Set and Get use the same
memoization cache to improve performance.  Take care if you modify the
thing you get back from Get - future calls to Get will return the modified
thing (even though it hasn't been changed in `$Session').  Calls to Set
empty the memoization cache so that the next call to Get will reload it
from `$Session' and add it to the cache.


File: pm.info,  Node: Win32/ASP/Field,  Next: Win32/ASP/Profile,  Prev: Win32/ASP/Extras,  Up: Module List

an abstract parent class for representing database fields, used by Win32::ASP::DBRecord
***************************************************************************************

NAME
====

   Win32::ASP::Field - an abstract parent class for representing database
fields, used by Win32::ASP::DBRecord

SYNOPSIS
========

     use Win32::ASP::Field;

     %hash = (
         Win32::ASP::Field->new(
     		name => 'RecordID',
     		sec  => 'ro',
     		type => 'int',
     		desc => 'Record ID',
     	),

     Win32::ASP::Field->new(
     			name => 'SemiSecureField',
     			sec  => sub {
     				my $self = shift;
     				my($record) = @_;

     return $record->role eq 'admin' ? 'rw' : 'ro';
     			},
     			type => 'varchar',
     			desc => 'Semi Secure Field',
     		),

     Win32::ASP::Field->new(
     			name => 'Remarks',
     			sec  => 'rw',
     			type => 'text',
     		),
     	);

DESCRIPTION
===========

Background
----------

   Field objects are very strange Perl objects.  Perl is class based, not
prototype based.  Unfortunately, unless you want to create a separate
class for every mildly wierd field in your database, a class based system
is sub-optimal for our purposes.  To get around this I implemented a
"weak" form of a prototype based language.

   The major parent is `Class::SelfMethods'.  It provides an AUTOLOAD that
implements the desired behavior.  In a nutshell, when asked to resolve a
method it does the following:

   * First it checks for whether the object has a property with that name.
     If it does and the property is not a code reference, it returns the
     value.

   * If the property is a code reference, it evaluates the code reference
     on the object with the passed parameters.  This means that you can
     define "instance" (not class) methods by placing anonymous
     subroutines in the instance.  These override the class method. If you
     need to call the equivalent of `SUPER::', call `_method' on the
     object.

   * If the property does not exist, it attempts to call `_method' on the
     object.  Thus, calling read on an instance calls the _read method in
     the class definition if there is no matching property.  If the _read
     method exists, AUTOLOAD will not get called again.  On the other hand
     if it does not exist, rather than call `__read', the AUTOLOAD
     subroutine will return empty-handed. This way, if the desired
     property is not defined for the object, undef will be the default
     behavior.

   It is important to understand the above "hierarchy of behavior" if you
are to make full use of the customizability of Field objects.  In a
nutshell, when creating new proper classes all methods should be defined
with a leading underscore, but called without the leading underscore, so
that they can be over-ridden as needed.  One should never directly access
the hash `$self', but always let AUTOLOAD access it by calling the method
with that name.  That way instance variables can be implemented as
subroutines if need be.  It also makes it easy to provide "default"
behavior by implementing a method.  The only time a method should be
called with a leading underscore is when an instance-defined method needs
to call the method it is over-riding.

Methods
-------

   Except for new, which is discussed here, the majority of these are
farther down under `' in this node.

new
...

   The new method for `Win32::ASP::Field' is rather strange.  It returns
two values - the name along with the `Win32::ASP::Field' object.  This
makes it much easier to create hashes of `Win32::ASP::Field' objects.  The
parameters passed to the new method should be the desired properties for
the new object.

   One oddity is that the type property will be used to autoload
`Win32::ASP::Field::'type and the returned object will be of that class.
This makes it possible to create arbitrary `Win32::ASP::Field' objects
without an explicit list of use statements at the top.

   For an example of how to use new, see the `' in this node at the top of
the POD.

   For a discussion of how new treats passed parameters that have a name
that starts with an underscore, see `Meta properties|' in this node.

Properties
----------

Required
........

name
     This is the name of the field.  Unlike the other properties, it is
     not passed the `$record' in question.

sec
     This specified whether the field is read-write (`'rw'') or read-only
     (`'ro'').  Note, you can implement this as a subroutine and it gets
     passed `$record'.  If it is not implemented or returns a value not
     equal to one of the above, it is presumed that the value is not
     accessible.  Note that `$record' may not be fully formed when seq is
     called in _read.  You may wish to return `'ro'' if in doubt.

Optional
........

reqd
     If this returns true than the field is required to be filled out when
     writing.

desc
     This is the friendly description for the object.  This gets used for
     column headings in tables.  If not specified, it defaults to name.

help
     This is the text that displays in the browser status area when the
     mouse is placed over the edit area in edit mode

size
     This is used for TEXT and TEXTAREA form fields to define their width.

maxl
     This is used to specify the maximum length of a varchar field.

textrows
     This is used to specify the number of rows for a TEXTAREA form field.

formname
     This is used to deal with situations where a field in a child record
     has the same name as a field in a parent record.  This would, of
     course, complicate the resultant HTML form.  To deal with this
     situation, specify formname.  If not specified, the default method
     will return name.

writename
     This is used to indicate the actual SQL field used for recording. It
     is frequently used in conjunction with as_write.  It can sometimes be
     very handy to use a subroutine for writename.  As a subroutine, it
     gets passed $value.  If it needs the whole record to make its
     decision, you will need to intercept the as_write_pair method.

     Say, for instance, that you have a logging record with a `SmallValue'
     field that is a 50 character `varchar' and a `LargeValue' field that
     is a text field.  The idea is that for short strings you won't incur
     much cost from the `LargeValue' field because uninitialized text
     fields don't create a 2K page.  If the string is longer, however, you
     want to write to the LargeValue field.  If the percentage of short
     strings is 50%, the solution would save ~49.7% on space requirements.
     The penalty of the unused `varchar' for the long strings is small
     contrasted with the savings by not using the text field on the short
     ones.

     In that situation, one might implement writename like this:

          writename => sub {
          	my $self = shift;
              my($value) = @_;

          return length($value) > 50 ? 'LargeValue' : 'SmallValue';
          	}

     The discussion of read includes an appropriate instance level method
     to round out this demonstration. No implementation of as_write is
     required because the formatting for `varchar' and text fields is the
     same.

option_list
     This should return an anonymous array of options that will be
     provided to the user when editing the field.  Its presence indicates
     to as_html_edit_rw the intention to use as_html_options.

Meta
....

   Meta properties are a funky way of executing additional code at the
time of object creation.  The new method accepts a parameter list and
returns two values - the name of the field and the field object itself.
The advantage of this is that it makes creating a hash of field object much
easier.  On the other hand, it requires some excessively fancy notation to
make method calls on the newly created object while in the hash specifier.
However, there's any easy way to indicate when you want a parameter to be
a method call.  Since parameters don't start with underscores and all
actual implementations in class code do, it makes sense to start meta
properties with an underscore.  The new method simply scans the list of
parameters for those that start with an underscore and strips them out of
the parameter hash for later use.  The value of the parameter should be an
anonymous array of parameters to the method.

   Typical use of meta properties is to provide code for creating commonly
used instance methods.

_standard_option_list
     This meta property sets up writename, as_write, and option_list for
     use with a fairly standard option list that uses a "hidden" code
     field and a lookup table that has friendly descriptions.  Example
     usage might look like so:

          _standard_option_list => [
          	class     => 'MyDatabase::MyRecord',
          	writename => 'LookupCode',
          	table     => 'LookupCodes',
          	field     => 'LookupCode',
          	desc      => 'Description'
          ],

     Note that although the method is expecting a hash of properties, the
     parameter list is stored in an anonymous array when passed in during
     the new method.

     Of note, the as_write and option_list methods are implemented to help
     minimize SQL traffic. The first call to the option_list method will
     result in setting `$self->{option_list}' to a reference to the
     anonymous array before returning that array.  Further calls will
     automatically return that array based on the behavior of the AUTOLOAD
     method in `Class::SelfMethods'.  See the entry for group for a
     discussion of the behavior for as_write.

    class
          This specifies the `Win32::ASP::DBRecord' subclass to which this
          field belongs.  This will be used later to access the _FIELDS
          hash and the _DB object.

    writename
          This specifies the field within the record object that will be
          written.

    table
          This specifies the name of the table that contains the list of
          codes and the friendly descriptions.

    field
          This specifies the name of the field within that table that
          contains the code.  Frequently, but not always, this will be the
          same as writename.

    desc
          This specifies the name of the description field in the lookup
          table.

    group
          This specifies whether there are likely to be multiple calls to
          as_write.  If not present or set to a false value, as_write will
          only lookup the passed value.  If set to a true value, the first
          call to as_write will lookup all the codes and store them in a
          hash for further reference.  This will reduce SQL traffic in
          situations where an `Win32::ASP::DBRecord' object is used within
          a `Win32::ASP::DBRecordGroup' for editing records.
          Unfortunately, the code isn't smart enough to know whether it is
          being used in a group or on its own, so you have to hard code
          it.  On the other hand, if you need that level of flexibility,
          you can roll your own methods.

INTERNALS
---------

   This is where internal methods are discussed with an eye towards
over-riding them if need be.

Checkers
........

   These are quick little methods to provided standardized ways of
checking certain boolean "properties"

can_view
     The can_view method is used to determine if someone has view
     privileges on a given field. The default implementation, `_can_view',
     tests `$self->sec($record)' for equivalence with '`ro'' or 'rw'.

     Implementations can expect the $record as a parameter and should
     return 1 or 0 as appropriate.

can_edit
     The can_edit method is used to determine if someone has edit
     privileges on a given field. The default implementation, `_can_edit',
     tests `$self->sec($record)' for equivalence with 'rw'.

     Implementations can expect the $record as a parameter and should
     return 1 or 0 as appropriate.

is_option_list
     The is_option_list method is used to determine if a field should be
     displayed using an option list.  The default implementation,
     `_is_option_list', tests for the existence of `$self->{option_list}'.
     This is technically verboten, but it's a performance improvement
     over returning the full option_list in order to test for it.  If you
     implement a subclass that implements option_list, you should also
     implement `_is_option_list'.

     Implementations can expect $record and $data as a parameter and
     should return 1 or 0 as appropriate.

Loaders
.......

   These methods are used to load a record with a given field.

read
     The read method is used to read a specific field out of `$results'
     into `$record'.  The default implementation, _read, first calls
     `$self->can_view' and then retrieves the appropriate value (if
     present) from the results set and places it in `$record->{orig}' as
     appropriate.

     In addition to the parameters `$record', the `Win32::ASP::DBRecord'
     that will receive the data, and `$results', the ADO Recordset object
     containing the data, the read method is passed the parameter
     $columns.  If $columns contains a reference to a hash and
     <$self-&gt;name> doesn't return a true value, the data should not be
     read.  This improves performance when the `Win32::ASP::DBRecord'
     object is part of a `Win32::ASP::DBRecordGroup' that is being used to
     retrieve data from a query where only some of the fields will be
     displayed.

     The properly written read for the writename function displayed long
     ago would be:

          read => sub {
          	my $self = shift;
              my($record, $results, $columns) = @_;

          my $name = $self->name;
              ref($columns) and !$columns->{$name} and return;
              $self->can_view($record) or return;

          $record->{orig}->{$name} = undef;
              $results->Fields->Item('SmallValue') and $record->{orig}->{$name} = $results->Fields->Item('SmallValue')->Value;
          if ($record->{orig}->{$name} eq '') {
                $results->Fields->Item('LargeValue') and $record->{orig}->{$name} = $results->Fields->Item('LargeValue')->Value;
          }
          	},

post
     The post method is used to read a specific field into `$results' from
     the POST data.  It also takes `$row' as a parameter.  If `$row' is
     defined, it presumes that it is dealing with a DBRecord that is a
     member of a DBRecordGroup and should retrieve the appropriately
     indexed value from the multi-valued POST data.  If it is not defined,
     it presumes that it is dealing with single-valued POST data.

     It assigns the value into `$record->{edit}' as appropriate.  It also
     tests for whether the POST data contains any non-whitespace
     characters and assigns undef if it does not.

HTML Formatters
...............

   These methods are used to format a given value as HTML.

as_html
     The as_html method is the accepted external interface for displaying
     a field in HTML.  It takes three parameters, `$record', $data, and
     `$viewtype', and returns the appropriate HTML.

     The default implementation, `_as_html', first checks for whether the
     `$record' is viewable.  If it is not, it simply returns.  It then
     checks to see if `$viewtype' is 'edit'.  If it is, it calls
     `$self->can_edit($record)' to determine if the field is editable.  If
     it is, it calls as_html_edit_rw or as_html_options based on
     is_option_list.  If it isn't editable but `$viewtype' is 'edit', it
     calls as_html_edit_ro.  Finally, if we aren't in 'edit' mode, it
     calls as_html_view.

as_html_view
     The as_html_view method takes two parameters, `$record' and $data,
     and returns the appropriate HTML.

     The default implementation, `_as_html_view', first extracts $value
     from `$record' using $data and `$self->name'.  If it is defined, it
     returns it, otherwise it returns '`&nbsp;''.  It runs the string
     through HTMLEncode to enable it to pass HTML meta-characters.

     This is over-ridden in `Win32::ASP::Field::bit' to return '`Yes'' or
     'No' and in `Win32::ASP::Field::timestamp' to return nothing
     (timestamp is not the same as `datetime').

as_html_edit_ro
     The as_html_edit_ro method takes two parameters, `$record' and $data,
     and returns the appropriate HTML.

     The default implementation, `_as_html_edit_ro', first extracts $value
     from `$record' using $data and `$self->name'.  It concatenates a
     HIDDEN INPUT field with the results of `$self->as_html_view($record,
     $data)'.

     This method is over-riden in `Win32::ASP::Field::timestamp' to encode
     $value as hex (since timestamp values are binary and thus not healthy
     HTML).

as_html_edit_rw
     The as_html_edit_rw method takes two parameters, `$record' and $data,
     and returns the appropriate HTML.

     The default implementation, `_as_html_edit_rw', first extracts $value
     from `$record' using $data and `$self->name'.  It then creates an
     appropriate TEXT INPUT field. Note the call to
     `$self-'as_html_mouseover>, which returns the appropriate parameters
     to implement the help support.

     The method is over-ridden by `Win32::ASP::Field::bit' to display a
     Yes/No radio pair and by `Win32::ASP::Field::text' to display a
     TEXTAREA.

as_html_options
     The as_html_options method takes two parameters, `$record' and $data,
     and returns the appropriate HTML.

     The default implementation, `_as_html_options', first extracts $value
     from `$record' using $data and `$self->name'.  It then loops over the
     values returned from `$self->option_list' and creates a SELECT
     structure with the appropriate OPTION entries.  It specified SELECTED
     for the appropriate one based on $value.

as_html_mouseover
     The as_html_mouseover method takes two parameters, `$record' and
     $data, and returns the appropriate string with `onMouseOver' and
     `onMouseOut' method for inclusion into HTML.

     The default implementation, `_as_html_mouseover', ignores the passed
     parameters and builds JavaScript for setting `window.status' to
     `$self->help'.

SQL Formatters
..............

   Values need to be formatted as legal SQL for the purposes of being
included in query strings.

check_value
     The check_value method is responsible for field level checking of
     $value.  Note that this code does not have access to the entire
     record, and so record-based checking should be left to the
     check_value_write method discussed later.  If the check fails,
     check_value should throw an error. Ideally, the error will either be
     of class `Win32::ASP::Error::Field::bad_value' or a subclass thereof.
     There should be no checking for "requiredness" at this level (simply
     because in many situations it wouldn't be called and so putting it
     here lends false hope).  The default implementation in
     `Win32::ASP::Field' does no checking what-so-ever and is merely
     provided as a prototype.

     The method is over-ridden by `Win32::ASP::Field::bit' to verify that
     the value is a 0 or 1 (bit fields never allow NULLs), by
     `Win32::ASP::Field::datetime' to use
     `Win32::ASP::Field::_clean_datetime' which use OLE to verify a
     datetime value, by `Win32::ASP::Field::int' to verify that the value
     is an integer, and by `Win32::ASP::Field::varchar' to verify that it
     doesn't exceed the maximum length.

as_sql
     The as_sql method is responsible for formatting of $value for
     inclusion in SQL.  Since this code will be called during the query
     phase, it doesn't have access to an entire record.  The default
     implementation in `Win32::ASP::Field' does nothing at all and is
     merely provided as a prototype.

     The method is, therefore, implemented by almost every subclass of
     `Win32::ASP::Field', with the exception of
     `Win32::ASP::Field::dispmeta' and `Win32::ASP::Field::timestamp',
     which are never used to query or write to the database.

Writing Formatters
..................

   The writing formatters are responsible for preparing the output for
updating or inserting records.  Some of these have access to the full
`$record' object, and others only have access to the $value.  In order to
decentralize management of the constraint checking, it would be useful if
some `$record' object checking could be pushed out to the field objects.
At the same time, there are situations where a fully formed `$record'
object is not available for field level checking.  As a result, there is a
profusion of the various formatters and checkers.  Rather than discussing
them in a top-down fashion, I will start from the bottom as things may
make more sense that way.

as_write
     The as_write method gets passed $value and returns the value that
     will be paired with writename for writing to the database.  Note that
     it does not get passed the full record - otherwise it would be
     difficult to call as_write from an overridden as_write.

     For example, to implement as_write for looking up a value in a
     database (obviously just for demonstration purposes - normally you
     would use _standard_option_list), one might use:

          as_write => sub {
          		my $self = shift;
            my($value) = @_;

          my $results = MyDatabase::MyRecord->_DB->exec_sql(<<ENDSQL, error_no_records => 1);
          	SELECT LookupCode FROM LookupCodes WHERE Description = '$value'
          	ENDSQL
              return MyDatabase::MyRecord->_FIELDS->{$self->writename($value)}->as_write($results->Fields->('LookupCode')->Value);
          	},

     That last return line is rather ugly, so let me dissect it:

        * `$self->writename' returns the fieldname to which the return
          value will actually get written.

        * `MyDatabase::MyRecord->_FIELDS' returns the hash of field
          objects for whatever class is involved.

        * `MyDatabase::MyRecord->_FIELDS->{$self->writename}' returns the
          actual field object of interest.

        * as_write is then called on that object with the value returned
          by looking up the appropriate result in the database.

     The main reason for the last line is so that it will properly format
     the return value using whatever type of field the writename is.  This
     shouldn't be an issue for common fields, but it could be for
     date/time values in some circumstances.

check_value_write
     This is the first of the methods that have access to a full
     `$record'.  It gets passed both `$record' and $data and as such can
     check a given field against other fields in the record.  The default
     implementation calls check_value on the appropriate $value.  If the
     check fails for whatever reason, check_value_write should throw an
     exception.

as_write_pair
     The method as_write_pair is the accepted entry point for formatting a
     value for writing to the database.  It accepts `$record' and $data,
     so it can call check_value_write to perform record-dependent field
     validation.  It returns a hash composed of two key/value pairs: field
     should supply the fieldname to write to and value should supply the
     properly formatted data for inclusion into SQL.  Note that if, for
     some reason, the functionality usually supplied by writename requires
     knowledge of the entire record, that functionality should be subsumed
     into as_write_pair.


