NAME

App::Repository - Logical data access layer for the App::Context Framework, providing a uniform API to access data in databases, file systems, remote web sites, etc.

SYNOPSIS

   use App::Repository;

   $context = App->context();
   $repository = $context->service("Repository");  # or ...
   $repository = $context->repository();

   $rep = Repository::Base->new();        # looks for %ENV, then config file
   $rep = Repository::Base->new("sysdb"); # looks for %ENV, then config file using "sysdb"
   $rep2 = $rep->new();                              # copies attributes of existing $rep
   $rep = Repository::Base->new(@positional_args);   # undefined for Repository::Base
   $config = {
     'repository' => {
       'db' => {
         'arg1' => 'value1',
         'arg2' => 'value2',
       },
       'rep2' => {
         'arg1' => 'value1',
         'arg2' => 'value2',
       },
     },
   };
   $rep = Repository::Base->new($config);
   $rep = Repository::Base->new("rep2",$config);

   ###################################################################
   # The following methods are needed for SQL support
   ###################################################################

   $errmsg = $rep->error();       # returns the error string for prev op ("" if no error)
   $numrows = $rep->numrows();    # returns the number of rows affected by prev op
   print $rep->error(), "\n";

   # DATA TYPE HELPER METHODS
   $repdate = $rep->format_repdate($date_string);   # free-form date string as entered by a person

   # META-DATA: (about the tables)
   $rep->_load_rep_metadata();
   $rep->_load_table_metadata($tablename);
   $typenames    = $rep->get_type_names();                        # print "@$typenames\n";
   $typelabels   = $rep->get_type_labels();                       # print "%$typelabels\n";
   $typedef      = $rep->get_type_def($typename);                 # print "%$type\n";
   $tablenames   = $rep->get_table_names();                       # print "@$tablenames\n";
   $tablelabels  = $rep->get_table_labels();                      # print "%$tablelabels\n";
   $table_def    = $rep->get_table_def($tablename);               # print "%$table\n";
   $columnnames  = $rep->get_column_names($tablename);            # print "@$columnnames\n";
   $columnlabels = $rep->get_column_labels($tablename);           # print "%$columnlabels\n";
   $column_def   = $rep->get_column_def($tablename,$columnname);  # print "%$column\n";

   #################################################
   # RELATIONAL
   #################################################

   ... (see App::Repository::DBI) ...

   $relation_names  = $rep->get_relation_names($table);
   $relation_labels = $rep->get_relation_labels($table);
   $relation_def    = $rep->get_relation_def($table, $relation_name);
   @keys            = $rep->get_related_keys($table, $key, $relation_name);

   #################################################
   # OBJECT-ORIENTED
   #################################################

   # OBJECT-ORIENTED
   $class = $table;
   $obj = $rep->object($class, $key);

   # OBJECT-ORIENTED (on RepositoryObject)
   $relation_names  = $obj->get_relation_names();
   $relation_labels = $obj->get_relation_labels();
   $relation_def    = $obj->get_relation_def($relation_name);
   @objs            = $obj->get_related_objects($relation_name);

   #################################################
   # TECHNICAL
   #################################################

   $rep->commit();
   $rep->rollback();
   $rep->import_rows($table, $columns, $file, $options);
   $rep->export_rows($table, $columns, $file, $options);

DESCRIPTION

A Repository is a means by which data may be stored somewhere or retrieved from somewhere without knowing what underlying technology is storing the data.

A Repository is the central persistence concept within the App. A Repository does not present a uniquely object-oriented view of its data. Rather it presents a "logical relational" data model. It does not return objects, but rows of data.

The "logical data model" means that a developer can program to the data model which usually comes out of system requirements analysis, closely modelling the business. All of the changes to this logical data model that are incorporated during physical database design are abstracted away, such as:

* physical table naming,
* physical column naming,
* normalization of data into parent tables, and
* splitting of tables based on various physical constraints.

This could be called object-to-relational mapping, but it is more accurately called logical-to-physical-relational mapping.

Despite the fact that the Repository is a relational data storage abstraction, persistent objects (i.e. RepositoryObjects) can be built to save and restore their state from a Repository. Furthermore, the built-in support for non-scalar fields (references to arbitrarily complex perl data structures) and the ability for RepositoryObjects to encapsulate more than one row of data, makes the technology quite fit for object-oriented development.

The design of the Repository is based around three important uses of data.

* Transaction Processing
* Batch Processing
* Report Generation

(more about this later)

The Repository abstraction seeks to solve the following problems.

* objects may have attributes that come from multiple sources
* caching
* isolated from physical database changes
* transactions
* data source independence
* no save/restore
* devel/test/prod environments

What follows are some developing thoughts on this API...

* The API should have two levels:
   = physical
     - no error-checking/defaults/security
     - provided by the driver
     - based on a physical table segment
     - application should never call this (private methods)
   = logical
     - error-checking
     - constraints (foreign key, check constraints)
     - column-level and row-level security
     - support transactions, caching, volatility
     - auditing

* Isolation levels
   = do writers block readers, etc.

Class Group: Repository

The following classes might be a part of the Repository Class Group.

  • Class: App::Repository

  • Class: App::Repository::DBI

  • Class: App::Repository::File

  • Class: App::Repository::BerkeleyDB

  • Class: App::Repository::LDAP

  • Class: App::Repository::HTML - for data stored in a web page

  • Class: App::Repository::SOAP - remote data storage

  • Class: App::Repository::Cache - use the Cache::Cache module

  • Class: App::Repository::SPOPS - maybe?

  • Class: App::Repository::Tangram - maybe?

  • Class: App::Repository::Alzabo - maybe?

  • Class: App::Repository::ClassDBI - maybe?

Class: App::Repository

A Repository is a means by which data may be stored somewhere without knowing what underlying technology is storing the data.

* Throws: App::Exception::Repository
* Since:  0.01

Class Design

...

Methods

new()

The constructor is inherited from App::Service.

_connect()

* Signature: $repository->_connect();
* Param:     void
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$repository->_connect();

Connects to the repository. Most repositories have some connection initialization that takes time and therefore should be done once. Then many operations may be executed against the repository. Finally the connection to the repository is closed (_disconnect()).

The default implementation of _connect() does nothing. It is intended to be overridden in the subclass (if necessary).

_disconnect()

* Signature: $repository->_disconnect();
* Param:     void
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$repository->_disconnect();

Disconnects from the repository.

The default implementation of _disconnect() does nothing. It is intended to be overridden in the subclass (if necessary).

All implementations of _disconnect() by a subclass must be sensitive to whether the object is actually currently connected to the repository. Thus, _disconnect() should be callable without negative consequences even when the repository is already disconnected.

_is_connected()

* Signature: $connected = $repository->_is_connected();
* Param:     void
* Return:    $connected         integer
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

if ($repository->_is_connected()) {
    ...
}

Reports whether a connection currently exists to the repository.

The default implementation of _is_connected() returns true (1) always. It is intended to be overridden in the subclass (if necessary).

Public Methods

error()

* Signature: $errormsg = $repository->error();
* Param:     void
* Return:    $errormsg          string
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

print $repository->error(), "\n";

Returns the error string associated with the last operation (or "" if there was no error).

The default implementation of error() simply returns the attribute {error} which must be cleared at the beginning of every operation and set when appropriate.

It is intended to be overridden in the subclass (if necessary).

numrows()

* Signature: $nrows = $repository->numrows();
* Param:     void
* Return:    $numrows           integer
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$nrows = $repository->numrows();

Returns the number of rows affected by the last operation.

The default implementation of numrows() simply returns the attribute {numrows} which must be set to 0 at the beginning of every operation and set to a higher number when appropriate.

It is intended to be overridden in the subclass (if necessary).

get()

* Signature: $value = $rep->get ($table, $key,    $col,  $options); [tbd]
* Signature: $value = $rep->get ($table, $params, $col,  $options); [tbd]
* Signature: @row   = $rep->get ($table, $key,    $cols, $options); [tbd]
* Signature: @row   = $rep->get ($table, $params, $cols, $options); [tbd]
* Param:     $table     string
* Param:     $key       string
* Param:     $params    undef,HASH
* Param:     $col       string
* Param:     $cols      ARRAY
* Param:     $options   undef,HASH
* Return:    $value     any
* Return:    @row       any
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$value  = $rep->get($table, $key,     $col,   \%options);
$value  = $rep->get($table, \%params, $col,   \%options);
@row    = $rep->get($table, $key,     \@cols, \%options);
@row    = $rep->get($table, \%params, \@cols, \%options);

tbd.

set()

* Signature: $nrows = $rep->set($table, $key,    $col, $value, $options); [tbd]
* Signature: $nrows = $rep->set($table, $params, $col, $value, $options); [tbd]
* Param:     $table     string
* Param:     $key       string
* Param:     $params    undef,HASH
* Param:     $col       string
* Param:     $value     any
* Param:     $options   undef,HASH
* Return:    $nrows     integer
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$nrows = $rep->set($table, $key,     $col, $value, \%options);
$nrows = $rep->set($table, \%params, $col, $value, \%options);

tbd.

get_row()

* Signature: $row = $rep->get_row ($table, $key,    $cols, $options);
* Signature: $row = $rep->get_row ($table, $params, $cols, $options);
* Param:     $table     string
* Param:     $key       string
* Param:     $params    undef,HASH
* Param:     $cols      ARRAY
* Param:     $options   undef,HASH
* Return:    $row       ARRAY
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$row = $rep->get_row($table, $key,     \@cols, \%options);
$row = $rep->get_row($table, \%params, \@cols, \%options);

tbd.

set_row()

* Signature: $nrows = $rep->set_row($table, $key,    $cols, $row, $options);
* Signature: $nrows = $rep->set_row($table, $params, $cols, $row, $options);
* Signature: $nrows = $rep->set_row($table, $params, $cols, $rowhash, $options);
* Signature: $nrows = $rep->set_row($table, $hash,   undef, undef,$options);
* Signature: $nrows = $rep->set_row($table, $params, $hash, undef,$options);
* Param:     $table     string
* Param:     $cols      ARRAY
* Param:     $row       ARRAY
* Param:     $rowhash   HASH
* Param:     $key       string
* Param:     $hash      HASH
* Param:     $params    undef,HASH
* Param:     $options   undef,HASH
* Return:    $nrows     integer
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$nrows = $rep->set_row($table, $key,     \@cols, $row, \%options);
$nrows = $rep->set_row($table, \%params, \@cols, $row, \%options);
$nrows = $rep->set_row($table, undef,    \@cols, $row, \%options);

tbd.

get_column()

* Signature: $colvalues = $rep->get_column ($table, $params, $col, $options);
* Param:     $table     string
* Param:     $params    undef,HASH
* Param:     $col       string
* Param:     $options   undef,HASH
* Return:    $colvalues ARRAY
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$colvalues = $rep->get_column ($table, \%params, $col, \%options);

tbd.

get_rows()

* Signature: $rows = $rep->get_rows($table, $params, $cols, $options);
* Signature: $rows = $rep->get_rows($table, $keys,   $cols, $options);
* Param:     $table     string
* Param:     $params    undef,HASH
* Param:     $keys      ARRAY
* Param:     $cols      ARRAY
* Param:     $options   undef,HASH
* Return:    $rows      ARRAY
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$rows = $rep->get_rows ($table, \%params, \@cols, \%options);
$rows = $rep->get_rows ($table, \%params, $col,   \%options);
$rows = $rep->get_rows ($table, \@keys,   \@cols, \%options);

tbd.

set_rows()

* Signature: $nrows = $rep->set_rows($table, $keys, $cols, $rows, $options);
* Param:     $table     string
* Param:     $keys      undef,ARRAY
* Param:     $cols      ARRAY
* Param:     $rows      ARRAY
* Param:     $options   undef,HASH
* Return:    $nrows     integer
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$nrows = $rep->set_rows($table, \%params, \@cols, $rows, \%options);
$nrows = $rep->set_rows($table, undef,    \@cols, $rows, \%options);
$nrows = $rep->set_rows($table, \@keys,   \@cols, $rows, \%options);

tbd.

get_hash()

* Signature: $values = $rep->get_hash ($table, $key,    $cols, $options);
* Signature: $values = $rep->get_hash ($table, $params, $cols, $options);
* Param:     $table     string
* Param:     $cols      ARRAY,undef
* Param:     $key       string
* Param:     $params    undef,HASH
* Param:     $options   undef,HASH
* Return:    $values    HASH
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$values = $rep->get_hash ($table, $key,     \@cols,   \%options);
$values = $rep->get_hash ($table, \%params, \@cols,   \%options);
$values = $rep->get_hash ($table, $key,     undef,    \%options);
$values = $rep->get_hash ($table, \%params, undef,    \%options);

tbd.

get_hashes()

* Signature: $hashes = $rep->get_hashes ($table, $key,    $cols, $options);
* Signature: $hashes = $rep->get_hashes ($table, $params, $cols, $options);
* Param:     $table        string
* Param:     $cols         ARRAY,undef
* Param:     $key          string
* Param:     $params       undef,HASH
* Param:     $options      undef,HASH
* Return:    $hashes       ARRAY
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$hashes = $rep->get_hashes ($table, $key,     \@cols,   \%options);
$hashes = $rep->get_hashes ($table, \%params, \@cols,   \%options);
$hashes = $rep->get_hashes ($table, $key,     undef,    \%options);
$hashes = $rep->get_hashes ($table, \%params, undef,    \%options);

tbd.

get_object()

* Signature: $object = $rep->get_object ($table, $key,    $cols, $options);
* Signature: $object = $rep->get_object ($table, $params, $cols, $options);
* Param:     $table     string
* Param:     $cols      ARRAY,undef
* Param:     $key       string
* Param:     $params    undef,HASH
* Param:     $options   undef,HASH
* Return:    $object    App::RepositoryObject
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$object = $rep->get_object ($table, $key,     \@cols,   \%options);
$object = $rep->get_object ($table, \%params, \@cols,   \%options);
$object = $rep->get_object ($table, $key,     undef,    \%options);
$object = $rep->get_object ($table, \%params, undef,    \%options);

tbd.

get_objects()

* Signature: $objects = $rep->get_objects ($table, $key,    $cols, $options);
* Signature: $objects = $rep->get_objects ($table, $params, $cols, $options);
* Param:     $table        string
* Param:     $cols         ARRAY,undef
* Param:     $key          string
* Param:     $params       undef,HASH
* Param:     $options      undef,HASH
* Return:    $objects      ARRAY
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$objects = $rep->get_objects ($table, $key,     \@cols,   \%options);
$objects = $rep->get_objects ($table, \%params, \@cols,   \%options);
$objects = $rep->get_objects ($table, $key,     undef,    \%options);
$objects = $rep->get_objects ($table, \%params, undef,    \%options);

tbd.

get_hash_of_values_by_key()

* Signature: $hashes = $rep->get_hash_of_values_by_key ($table, $params, $valuecol, $keycol, $options);
* Param:     $table        string
* Param:     $params       undef,HASH
* Param:     $valuecol     string
* Param:     $keycol       string
* Param:     $options      undef,HASH
* Return:    $hash         HASH
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$hash = $rep->get_hash_of_values_by_key ($table, \%params, $valuecol, $keycol, \%options);

tbd.

get_hash_of_hashes_by_key()

* Signature: $hashes = $rep->get_hash_of_hashes_by_key ($table, $params, $cols, $keycol, $options);
* Param:     $table        string
* Param:     $params       undef,HASH
* Param:     $cols         ARRAY
* Param:     $keycol       string
* Param:     $options      undef,HASH
* Return:    $hash         HASH
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$hash = $rep->get_hash_of_hashes_by_key ($table, \%params, $cols, $keycol, \%options);

tbd.

set_hash()

* Signature: $nrows = $rep->set_hash ($table, $key,    $cols, $values, $options);
* Signature: $nrows = $rep->set_hash ($table, $params, $cols, $values, $options);
* Param:     $table     string
* Param:     $key       string
* Param:     $params    undef,HASH
* Param:     $cols      ARRAY,undef
* Param:     $options   undef,HASH
* Return:    $nrows     integer
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$nrows = $rep->set_hash ($table, $key,     \@cols, $values, \%options);
$nrows = $rep->set_hash ($table, $key,     undef,  $values, \%options);
$nrows = $rep->set_hash ($table, undef,    \@cols, $values, \%options);
$nrows = $rep->set_hash ($table, undef,    undef,  $values, \%options);
$nrows = $rep->set_hash ($table, \%params, \@cols, $values, \%options);
$nrows = $rep->set_hash ($table, \%params, undef,  $values, \%options);

tbd.

format_repdate()

* Signature: $date = $repository->format_repdate($freeform_date);
* Param:     $freeform_date     string
* Return:    $date              string
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

foreach $freeform_date ("1/2/01", "1-Jan-2003", "january 13, 2000",
        "2000/1/5", "15 jan 99") {
    print "$freeform_date: ", $rep->format_repdate($freeform_date), "\n";
}

The format_repdate() method takes a free-form date string (such as a human might type into a form field) using many varieties of upper and lower case, punctuation, and ordering, and turns it into a date in canonical YYYY-MM-DD form for storage in the repository.

get_type_names()

* Signature: $typenames = $repository->get_type_names();
* Param:     void
* Return:    $typenames         []
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$typenames = $rep->get_type_names();
print join(",", @$typenames), "\n";

Returns the standard set of type names for columns in a repository. These are perl-friendly type names which are useful to do data validation.

* string
* text
* integer
* float
* date
* time
* datetime
* binary

get_type_labels()

* Signature: $typelabels = $repository->get_type_labels();
* Param:     void
* Return:    $typelabels        {}
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$typelabels = $rep->get_type_labels();
foreach (sort keys %$typelabels) {
    print "$_ => $typelabels->{$_}\n";
}

Returns a hash of all of the repository types and the labels which should be used when displaying them to the user through the user interface.

* string   => "Characters"
* text     => "Text"
* integer  => "Integer"
* float    => "Number"
* date     => "Date"
* time     => "Time"
* datetime => "Date and Time"
* binary   => "Binary Data"

get_type_def()

* Signature: $typedef = $rep->get_type_def($typename);
* Param:     $typename          string
* Return:    $typedef           {}
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$typedef = $rep->get_type_def("string");
print "$typedef->{name} $typedef->{label}\n";

Gets a reference to a "type definition", which allows you to access all of the attributes of the requested type (currently only "name" and "label").

get_table_names()

* Signature: $tablenames = $rep->get_table_names();
* Param:     void
* Return:    $tablenames        []
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$tablenames = $rep->get_table_names();
print join(",", @$tablenames), "\n";

Returns the set of table names in the repository.

get_table_labels()

* Signature: $tablelabels = $rep->get_table_labels();
* Param:     void
* Return:    $tablelabels       {}
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$tablelabels = $rep->get_table_labels();
foreach (sort keys %$tablelabels) {
    print "$_ => $tablelabels->{$_}\n";
}

Returns a hash of all of the tables and the labels which should be used when displaying them to the user through the user interface.

get_table_def()

* Signature: $table_def = $rep->get_table_def($tablename);
* Param:     $tablename         string
* Return:    $table_def          {}
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$table_def = $rep->get_table_def($tablename);
print "$table_def->{name} $table_def->{label}\n";

Gets a reference to a "table definition", which allows you to access all of the attributes of the requested table. By default, this is only "name" and "label". However, for various types of repositories, there may be additional attributes for a table.

get_column_names()

* Signature: $columnnames = $rep->get_column_names($tablename);
* Param:     $tablename         string
* Return:    $columnnames       []
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$columnnames = $rep->get_column_names($tablename);
print join(",", @$columnnames), "\n";

Returns the set of column names for the requested table in a repository.

get_column_labels()

* Signature: $columnlabels = $rep->get_column_labels($tablename);
* Param:     $tablename         string
* Return:    $columnlabels      {}
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$columnlabels = $rep->get_column_labels($tablename);
foreach (sort keys %$columnlabels) {
    print "$_ => $columnlabels->{$_}\n";
}

Returns a hash of all of the column names and the labels which should be used when displaying them to the user through the user interface.

get_column_def()

* Signature: $column_def = $rep->get_column_def($tablename,$columnname);
* Param:     $tablename         string
* Param:     $columnname        string
* Return:    $column_def         {}
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$column_def = $rep->get_column_def($tablename,$columnname);
print "$column_def->{name} $column_def->{label} $column_def->{type}\n";

Gets a reference to a "column definition", which allows you to access all of the attributes of the requested column.

By default, this is only "name", "label", and "type". However, for various types of repositories, there may be additional attributes for a column.

Methods: Transaction Control

begin_work()

* Signature: $rep->begin_work();
* Param:     void
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$rep->begin_work();

commit()

* Signature: $rep->commit();
* Param:     void
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$rep->commit();

rollback()

* Signature: $rep->rollback();
* Param:     void
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$rep->rollback();

Methods: Import/Export Data From File

import_rows()

* Signature: $rep->import_rows($table, $columns, $file);
* Signature: $rep->import_rows($table, $columns, $file, $options);
* Param:     $table        string
* Param:     $columns      ARRAY     names of columns of the fields in the file
* Param:     $file         string
* Param:     $options      named
* Param:     replace       boolean   rows should replace existing rows based on unique indexes
* Param:     field_sep     char      character which separates the fields in the file (can by "\t")
* Param:     field_quote   char      character which optionally encloses the fields in the file (i.e. '"')
* Param:     field_escape  char      character which escapes the quote chars within quotes (i.e. "\")
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$rep->import_rows("usr","usr.dat");

# root:x:0:0:root:/root:/bin/bash
$rep->import_rows("usr",
    [ "username", "password", "uid", "gid", "comment", "home_directory", "shell" ],
    "/etc/passwd" ,
    { field_sep => ":", });

export_rows()

* Signature: $rep->export_rows($table, $columns, $file);
* Signature: $rep->export_rows($table, $columns, $file, $options);
* Param:     $table        string
* Param:     $file         string
* Param:     $options      named
* Param:     columns       ARRAY     names of columns of the fields in the file
* Param:     replace       boolean   rows should replace existing rows based on unique indexes
* Param:     field_sep     char      character which separates the fields in the file (can by "\t")
* Param:     field_quote   char      character which optionally encloses the fields in the file (i.e. '"')
* Param:     field_escape  char      character which escapes the quote chars within quotes (i.e. "\")
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$rep->export_rows("usr","usr.dat");

# root:x:0:0:root:/root:/bin/bash
$rep->export_rows("usr", "passwd.dat" ,{
    field_sep => ":",
    columns => [ "username", "password", "uid", "gid", "comment", "home_directory", "shell" ],
});

Methods: Locking (Concurrency Management)

Methods: Miscellaneous

summarize_rows()

* Signature: $summarized_rows = $rep->summarize_rows($table, $rows, $columns, $summary_keys, $options);
* Param:     $table            string
* Param:     $rows             [][]
* Param:     $columns          []
* Param:     $summary_keys       []
* Param:     $formulas         {}
* Return:    $summarized_rows  []
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

@rows = (
    [ 5, "Jim", "Green", 13.5, 320, ],
    [ 3, "Bob", "Green",  4.2, 230, ],
    [ 9, "Ken", "Green", 27.4, 170, ],
    [ 2, "Kim", "Blue",  11.7, 440, ],
    [ 7, "Jan", "Blue",  55.1,  90, ],
    [ 1, "Ben", "Blue",  22.6, 195, ],
);
@columns = ( "id", "name", "team", "rating", "score" );
@summary_keys = ( "team" );

$summarized_rows = $rep->summarize_rows(\@rows, \@columns, \@summary_keys, \%formulas);

@rows = (
    { id=>5, name=>"Jim", team=>"Green", rating=>13.5, score=>320, },
    { id=>3, name=>"Bob", team=>"Green", rating=> 4.2, score=>230, },
    { id=>9, name=>"Ken", team=>"Green", rating=>27.4, score=>170, },
    { id=>2, name=>"Kim", team=>"Blue",  rating=>11.7, score=>440, },
    { id=>7, name=>"Jan", team=>"Blue",  rating=>55.1, score=> 90, },
    { id=>1, name=>"Ben", team=>"Blue",  rating=>22.6, score=>195, },
);
@columns = ( "rating", "score" );  # summarize a subset of the columns
@summary_keys = ( "team" );
%options = (
    ext_summaries => \%summaries,         # extended summaries
    ext_summary_columns => [ "rating", "score", "team", ],   # optional
    ext_summary_functions => {  # optional
        sum      => 1,
        count    => 1,
        sum_sq   => 1,
        distinct => 1,
        min      => 1,
        max      => 1,
        average  => 1,  # requires sum, count
        median   => 1,  # requires distinct
        mode     => 1,  # requires min, max
        stddev   => 1,  # requires sum, sum_sq, count
    },
);

# returns the "natural" summaries
$summarized_rows = $rep->summarize_rows(\@rows, \@columns, \@summary_keys, \%options);

sort()

* Signature: $sorted_rows = $rep->sort($rows, $sortkeys);
* Signature: $sorted_rows = $rep->sort($rows, $sortkeys, $sorttype);
* Signature: $sorted_rows = $rep->sort($rows, $sortkeys, $sorttype, $sortdir);
* Param:     $rows             [][]
* Param:     $sortkeys       []
* Param:     $sorttype         []
* Param:     $sortdir          []
* Return:    $sorted_rows      []
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: (to sort arrayrefs)

  @rows = (
      [ 5, "Jim", "Green", 13.5, 320, ],
      [ 3, "Bob", "Green",  4.2, 230, ],
      [ 9, "Ken", "Green", 27.4, 170, ],
      [ 2, "Kim", "Blue",  11.7, 440, ],
      [ 7, "Jan", "Blue",  55.1,  90, ],
      [ 1, "Ben", "Blue",  22.6, 195, ],
  );
  # @columns = ( "id", "name", "team", "rating", "score" ); # not needed
  @sortkeys = ( 2, 4 );          # "team", "score" (descending)
  @sorttype = ( "C", "N" );      # Character, Numeric
  @sortdir = ( "asc", "desc" );  # Ascending, Descending
  $sorted_rows = $rep->sort(\@rows, \@sortkeys, \@sorttype, \@sortdir);

OR (to sort hashrefs)

  @rows = (
      { id => 5, name => "Jim", team => "Green", rating => 13.5, score => 320, },
      { id => 3, name => "Bob", team => "Green", rating =>  4.2, score => 230, },
      { id => 9, name => "Ken", team => "Green", rating => 27.4, score => 170, },
      { id => 2, name => "Kim", team => "Blue",  rating => 11.7, score => 440, },
      { id => 7, name => "Jan", team => "Blue",  rating => 55.1, score =>  90, },
      { id => 1, name => "Ben", team => "Blue",  rating => 22.6, score => 195, },
  );
  # @columns = ( "id", "name", "team", "rating", "score" ); # not needed
  @sortkeys = ( "team", "score" );          # "team", "score" (descending)
  @sorttype = ( "C", "N" );      # Character, Numeric
  @sortdir = ( "asc", "desc" );  # Ascending, Descending
  $sorted_rows = $rep->sort(\@rows, \@sortkeys, \@sorttype, \@sortdir);

evaluate_expressions()

* Signature: $nrows = $rep->evaluate_expressions($table, $params, $cols, $rows, $options);
* Param:     $table     string
* Param:     $params    HASH,scalar
* Param:     $cols      ARRAY
* Param:     $rows      ARRAY
* Param:     $options   undef,HASH
* Return:    $nrows     integer
* Throws:    App::Exception::Repository
* Since:     0.50

Sample Usage:

$rep->evaluate_expressions($table, $params, \@cols, $rows, \%options);

tbd.

serial()

* Signature: $serial_num = $repository->serial($category);
* Param:     $category          string
* Return:    $serial_num        integer
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$serial_num = $repository->serial($category);

Methods: Metadata

_load_rep_metadata()

* Signature: $repository->_load_rep_metadata();
* Param:     void
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$self->_load_rep_metadata();

Initializes the repository metadata information from the config.

* List of tables (+ displayable labels)
* List of column types (+ displayable labels)

Then it calls _load_rep_metadata_from_source() in order for the repository itself to be consulted for its metadata information.

_load_rep_metadata_from_source()

* Signature: $repository->_load_rep_metadata_from_source();
* Param:     void
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$repository->_load_rep_metadata_from_source();

Loads repository metadata from the repository itself (to complement metadata in the configuration and perhaps override it).

The default implementation does nothing. It is intended to be overridden in the subclass (if the repository has any sort of metadata).

_load_table_metadata()

* Signature: $self->_load_table_metadata();
* Param:     void
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$self->_load_table_metadata();

First it calls _load_table_metadata_from_source() in order for the repository itself to be consulted for any metadata information for the about the table.

Then it initializes the repository metadata information for that table from the config information.

* List of columns (+ displayable labels, types)
* List of column types (+ displayable labels)

Then it determines the set of required columns whenever selecting data from the table and clears the cache of selected rows for the table.

_load_table_metadata_from_source()

* Signature: $repository->_load_table_metadata_from_source();
* Param:     void
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$self->_load_table_metadata_from_source();

Loads metadata for an individual table from the repository itself (to complement metadata in the configuration and perhaps override it).

The default implementation does nothing. It is intended to be overridden in the subclass (if the repository has any sort of metadata).

Methods: Miscellaneous

_init()

* Signature: $repository->_init();
* Param:     defer_connection     integer
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$self->_init();

Every Service constructor (Repository is derived from Service) will invoke the _init() method near the end of object construction.

The standard behavior for repositories (implemented here) in _init() is to initialize the "numrows" and "error" attributes, call _init2(), connect to the repository, and load the repository metadata.

_init2()

* Signature: $repository->_init2();
* Param:     defer_connection    integer
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$self->_init2();

The default behavior of _init2() does nothing and is intended to be overridden (if necessary) in the subclass which implements the details of access to the physical data store.

service_type()

Returns 'Repository'.

* Signature: $service_type = App::Repository->service_type();
* Param:     void
* Return:    $service_type  string
* Since:     0.01

$service_type = $widget->service_type();

current_datetime()

Returns 'Repository'.

* Signature: $current_datetime = App::Repository->current_datetime();
* Param:     void
* Return:    $current_datetime  string
* Since:     0.01

$current_datetime = $widget->current_datetime();

rows_by_indexed_values()

* Signature: &App::Repository::rows_by_indexed_values($a,$b);
* Param:     $a            []
* Param:     $b            []
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

@data = (
    [ 5, "Jim", "Red",    13.5, ],
    [ 3, "Bob", "Green",   4.2, ],
    [ 9, "Ken", "Blue",   27.4, ],
    [ 2, "Kim", "Yellow", 11.7, ],
    [ 7, "Jan", "Purple", 55.1, ],
);

@App::Repository::sort_keys = ( 1, 3, 2 );
@App::Repository::sort_types = ("C", "N", "C");
@App::Repository::sort_dirs = ("asc", "desc", "desc");
# OR @App::Repository::sort_dirs = ("_asc", "_desc", "_desc");
# OR @App::Repository::sort_dirs = ("UP", "DOWN", "DOWN");

@sorted_data = sort rows_by_indexed_values @data;

The rows_by_indexed_values() function is used to sort rows of data based on indexes, data types, and directions.

hashes_by_indexed_values()

* Signature: &App::Repository::hashes_by_indexed_values($a,$b);
* Param:     $a            []
* Param:     $b            []
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

@data = (
    { size => 5, name => "Jim", color => "Red",    score => 13.5, ],
    { size => 3, name => "Bob", color => "Green",  score =>  4.2, ],
    { size => 9, name => "Ken", color => "Blue",   score => 27.4, ],
    { size => 2, name => "Kim", color => "Yellow", score => 11.7, ],
    { size => 7, name => "Jan", color => "Purple", score => 55.1, ],
);

@App::Repository::sort_keys = ( "size", "color", "name" );
@App::Repository::sort_types = ("C", "N", "C");
@App::Repository::sort_dirs = ("asc", "desc", "desc");
# OR @App::Repository::sort_dirs = ("_asc", "_desc", "_desc");
# OR @App::Repository::sort_dirs = ("UP", "DOWN", "DOWN");

@sorted_data = sort hashes_by_indexed_values @data;

The hashes_by_indexed_values() function is used to sort rows of data based on indexes, data types, and directions.

DESTROY()

* Signature: $self->DESTROY();
* Param:     void
* Return:    void
* Throws:    App::Exception::Repository
* Since:     0.01

Sample Usage: 

$self->DESTROY();   # never called explicitly. called by Perl itself.

The DESTROY() method is called when the repository object is release from memory. This happen when the calling program lets the variable holding the object reference go out of scope, sets the variable to something else, or exits the program without otherwise releasing the object.

The DESTROY() method simply calls disconnect() to make sure that all connection-related resources are freed. This is safe, assuming (correctly) that the disconnect() method may be called without negative consequences even when already disconnected from the repository.

ACKNOWLEDGEMENTS

* Author:  Stephen Adkins <spadkins@gmail.com>
* License: This is free software. It is licensed under the same terms as Perl itself.

SEE ALSO

App::Context, App::Service