I figured it would probably be worth discussing the design and interface of
PGObject, PGObject::Simple, and PGObject::Simple::Role here as well as
other areas I am working on directly.  The structure is somewhat different
from what we have and the design choices somewhat based on lessons learned
so far.  There are basically three classes here with specific
responsibilities.

PGObject's structure is divided into a "bottom half" and a "top half" where
the bottom-half is database-facing and the top-half is developer-facing.
 With a top-half written, the developer should only need to hit the bottom
half when writing new top-half components.  All application code goes
through the top half.

PGObject

PGObject is the bottom half module.  It is designed to service multiple
top-half paradigms (the Simple paradigm is described below, but also
working on a CompositeType paradigm which probably won't be ready initially
yet).  PGObject has effectively one responsibility:  coordinate between
application components and the database.  This is split into two
sub-responsibilities:

1.  Locate and run stored procedures
2.  Encode/decode data for running in #1 above.

Specifically outside the responsibility of PGObject is anything to do with
managing database connections, so every call to a database-facing routine
(locating or running a stored procedure) requires a database handle to be
passed to it.

The reason for this is that the database handles should be managed by the
application not our CPAN modules and this needs to be flexible enough to
handle the possibility that more than one database connection may be needed
by an application.  This is not a problem because developers will probably
not call these functions unless they are writing their own top-half
paradigms (in which case the number of places in their code where they
issue calls to these functions will be very limited).

A hook is available to retrieve only functions with a specified first
argument type.  If more than one function is found that matches, an
exception is thrown.

The Simple top-half paradigm (below) has a total of two such calls, and
that's probably typical.

The encoding/decoding system is handled by a few simple rules, some of
which are already in place in our current 1.4 code, but others are not.

On delivery to the database, any parameter that can('to_db') runs that
method and inserts the return value in place of the parameter in the stored
procedure.  This allows one to have objects which specify how they
serialize.  Bigfloats can serialize as numbers, Datetime subclasses can
serialize as date or timestamp strings, and more complex types could
serialize however is deemed appropriate (to JSON, a native type string
form, a composite type string form, etc).

On retrieval from the database, the type of each column is checked against
a type registry (sub-registries may be used for multiple application
support, and can be specified at call time as well).  If the type is
registered, the return value is passed to the $class->from_db method and
the output returned in place of the original value.  This allows for any
database type to be mapped back to a handler class.

Currently PGObject::Type is a reserved namespace for dealing with released
type handler classes.

PGObject::Simple

The second-level modules outside of a few reserved namespaces designate
top-half paradigms for interacting with stored procedures.  Currently only
Simple is supported.  This works in a similar way and with a similar
interface to LedgerSMB::DBObject but with a few key differences:

1.  This must be subclassed to be used by an application and a method
provided to retrieve or generate the appropriate database connection.  This
allows application-specific wrappers which can interface with other db
connection management logic.

2.  $object->call_procedure uses a different syntax more like
$object->exec_method.  $object->call_dbmethod is used instead of
$object->exec_method for consistency's sake.  This could be wrapped for the
sake of backwards compatibility.

3.  All options for PGObject->call_procedure supported including running
aggregates, order by, etc.  This means more options available for things
like gl reports database-side.

4.  $object->call_dbmethod uses the args argument totally differently (this
cannot be wrapped --- for enumerated arguments use call_procedure).
 Instead of an arrayref of arguments, we have a hashref, where the names
take precedence over properties.  If I want to have a ->save_as_new method,
I can add args => {id => undef} to ensure that undef will be used in place
of $self->{id}.

5.  Both call_procedure and call_dbmethod are supported both from the
package and object.  So you can MyClass->call_dbmethod(...) and
$myobj->call_dbmethod.  Naturally if the procedure takes args, you will
need to specify them or it will just submit nulls.

This means that a lot of areas where the LedgerSMB code is remarkably
brittle due to being dependent on enumerated arguments we can make it
robust and easily.

PGObject::Simple::Role

This is a Moose role handler for PGObject::Simple.  It handles a few things
better than our current routines.

One of the main features it has is the ability to declaratively define db
methods.  So instead of:

sub int {
    my $self = @_;
    return $self->call_dbmethod(funcname => 'foo_to_int');
}

You can just

dbmethod( int => (funcname => 'foo_to_int'));

We will probably move dbmethod off into another package so that it can be
imported early and used elsewhere as well.  This would allow it to be
called without the outermost parentheses.

-- 
Best Wishes,
Chris Travers

Efficito:  Hosted Accounting and ERP.  Robust and Flexible.  No vendor
lock-in.
http://www.efficito.com/learn_more.shtml
------------------------------------------------------------------------------
CenturyLink Cloud: The Leader in Enterprise Cloud Services.
Learn Why More Businesses Are Choosing CenturyLink Cloud For
Critical Workloads, Development Environments & Everything In Between.
Get a Quote or Start a Free Trial Today. 
http://pubads.g.doubleclick.net/gampad/clk?id=119420431&iu=/4140/ostg.clktrk
_______________________________________________
Ledger-smb-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ledger-smb-devel

Reply via email to