NAME

repo.pl - repository object interface.


SYNOPSIS

  require "repo.pl";

  # Most common methods shown here.  See METHODS for others.

  # defining a type
  @patient_atts = ("name:char(20)", "sex:char(1)", "surgeon:repo");
  repo_define("Patient", @patient_atts);

  # creating instances
  $template->{name} = 'Smith';
  $template->{sex} = 'F';
  $oid = repo_new("Patient", $template);
  
  # iterating over a query
  $patients = repo_query("Patient");
  foreach $pat (@$patients) {
    $surg = repo_get($pat->{surgeon});  # retrieve surgeon object
    print "Patient: $pat->{name}, Surgeon: $surg->{name}\n";
  }

  # constrained & alphabetized query
  $females = repo_query("Patient", "sex = 'F'", "name");

  # updating an object
  $smith = repo_query_single("Patient", "name = 'Smith'"); # singleton query
  $surg_id = repo_lookup_id("Surgeon", "name = 'Jones'");  # get OID of surgeon Jones
  $smith->{surgeon} = $surg_id;  # assign surgeon Jones OID to Smith
  repo_update($smith);  # commit change to database

  # identity operators  
  if (is_repo($ref)) {
    $id = OID($ref);
    $type = repo_type($id);
    print "Object $id has type $type\n";
  }
  if (is_repo_group($ref)) {
    print repo_table($ref);
  }

  # registering a file without importing it into the FSA
  $fdt->{label} = "intraoperative photograph";
  $fdt->{locator} = "/usr/local/foo/photo.jpg";
  $fid = repo_register_file($fdt);

  # import a file into the FSA
  $fdt->{label} = "intraoperative photograph";
  $fdt->{source} = "/usr/local/foo/photo.jpg";
  $fid = repo_import_file($fdt);

  # when importing, you may specify a custom destination within the FSA
  $fid = repo_import_file($fdt, "patients/photos/pic100.jpg");


REQUIRES

db.pl, fci.pl


DESCRIPTION

The Repository Object API provides an object-relational data model to the CGI programmer by abstracting away the relational tables and allowing the data to be viewwed as collections of objects. Each object has a SCHEMA which determines it's structure, a set of VIEWS which determines how it is displayed, and a unique Object Identifier (OID) by which it can be efficiently referenced from other objects. The instances of an object type are maintined in a relational table, but the CGI programmer need not be concerned about the logical structure of the table representation, and instead operates on hashes whose values are bound to the row data.

Repo.pl defines query functions which utilize the relational query engine to retrieve rows which satisfy an SQL filter clause, and then allocates an in-memory hash for each row. The CGI programmer may iterate over the results, performing arbitrary computations, and updates are propagated back to the table, where they persist between invocations.

Repo.pl connects to two dbm files: WRM_REPO_TYPES, which is an mapping every OID to a type name, and WRM_SCHEMA_DEFS, which records the attribute names and types for each object class.

Repo.pl makes use of db.pl for connecting to and accessing the relational database, and fci.pl for accessing files in the FSA.


METHODS


Repo Retrieval

The following methods are used to retrieve repository objects from the database into memory. Some of the methods below accept an optional ``$filter'' parameter. For these methods, the query is constrained by the filter string which will be applied as a WHERE clause in an SQL query. The filter which should have the following form:

 column OPERATOR value 
   [ AND | OR column OPERATOR value ]*

where OPERATOR can be <, >, =, <=, >=, <>, LIKE, RLIKE or CLIKE.

repo_get($oid)

Retrieves the object with the given OID from the appropriate database table and instantiates it as a hash whose keys are the attribute names and whose values contain the object's values. The hash values may be updated directly, but repo_update must be invoked on the object to propagate the changes to the database. Returns a ref to the hash. If the object is not found, returns 0.

repo_lookup_id($type, $filter)

Returns the oid of a repo of the specified type whose values satisfy the filter clause. Returns 0 if there are no matches. Note: this does not instantiate the repo in memory, but only returns the OID. Use repo_query or repo_query_single to actually retrieve the objects. If there are multiple candidates, will return one of them at random.

repo_query($type, $filter, $order)

Repo_query returns a ref of list of refs to repos of the specified type whose values satisfy the filter clause. Returns 0 if there are no matches. The user may iterate over them using the standard foreach loop. See the synopsis for examples. The results may be ordered by specifying an attribute name in the optional $order clause. Comma-delimited multiple attributes may be specified for complete ordering, e.g.:

  repo_query("Patient", "sex = 'M'", "last_name, first_name");
repo_query_single($type, $filter)

Repo_query_single returns a ref to a repo of the specified type whose values satisfy the filter clauses. Returns 0 if there are no matches. If there are multiple candidates, will return one of them at random. Use repo_query for getting multiple results. This function is provided as a convenience for the common case where you expect only one answer and don't want to be handed a ref to a list of refs.


Repo Manipulation

The following methods are used to create, delete, and modify persistent repository objects.

repo_new($type, $template)

Creates a new persistent repository object of the type named by the string $type. A unique OID is assigned and returned. The optional $template parameter should be a ref to a hash whose keys are attribute names and whose values will be stored in the new object. Keys in $template which don't correspond to attributes of the schema will be ignored.

repo_delete($oid)

Permanently deletes the instance specified by the given OID. Removes it from the database and extracts it from the WRM_REPO_TYPES index.

repo_update($obj)

Takes a ref to an instantiated object and writes the object's values to the database. In-memory updates to a hash object are not made persistent until repo_update is explicitly called.


Repo Definition

The following functions are used to define and evolve repository object schemas. View definitions are handled seperately.

repo_define($type, @atts)

Defines a new repo type named $type. @atts should be a list of strings defining the attributes, of the form (``name1:type1'', ``name2:type2'', ...). The type definition is stored in $WRM_SCHEMA_DEFS, and a table is created in the database. Note that attributes of type ``file'' and ``repo'' will be converted to ints in the database table (to contain OID's).

An ``oid'' column will be added to the table schema, and an index on oid will be created.

The $type parameter may designate a parent class for this object using the syntax ``parent:newtype''. If a parent is designated, the child class will inherit all attribute names & types of the parent, in addition to the attributes named in the @atts list.

repo_evolve($type, @changes)

Use this function to add, drop, or rename the attributes of an existing schema. Instances of the class will be automatically updated to reflect the changes. $type should be the name of a schema, and @changes should be a list of strings describing the changes to be made. Changes can be:

  add:name:type               - add column with given name & type
  drop:name                   - drop column with given name
  rename:oldname:newname      - change column's name

For example, the following invocation renames a Patient's ``sex'' attribute to ``gender'', and adds a new ``age'' attribute:

 repo_evolve("Patient", ("rename:sex:gender", "add:age:int"));

Note: repo_evolve changes both the underlying table and the schema dbm index. The original table is copied into a temporary table then destroyed & recreated with the changed columns, and the data in the temporary table are then copied back into the new table.

repo_undefine($type)

Undefines the named class type by dropping the class's table and deleting it's definition in the schema dbm.

repo_attributes($type)

Retrieves the attribute names & types of a given repo type. $type may be a typename or a repo object. Returns a reference to a list of strings, each with the form ``attribute_name:attribute_type''.

repo_attribute_names($type)

Retrieves the attribute names of a given repo type. $type may be a typename or a repo object. Returns a reference to a list of strings.


Repo Identity Operators

The following functions can be used to translate between repo's and their OID's, and to verify the type of a repo.

is_repo($v)

Takes a value of any type. returns TRUE if the value is a ref to a repository object, FALSE otherwise.

is_repo_group($v)

Takes a value of any type. returns TRUE if the value is a ref to a group of repository objects (e.g. the results of a repo_query), FALSE otherwise.

OID($obj)

Takes a ref to an object and returns its OID. If the parameter is already an OID, just returns the OID. This function is often used to make other repo functions more flexible, allowing them to accept both refs and OID's as parameters.

May also take a ref to a group of objects (e.g. the results of a repo_query). In this case, it returns a string of OIDs separated by '+' signs. Useful for passing a group of OIDs as a form parameter.

OBJ($oid)

Swizzles a repo or group of repos, as needed. Takes an OID and returns a ref to the associated object. Queries the database and instantiates a new hash in memory. If the parameter is already an object reference, just returns the reference. This function is often used to make other repo functions more flexible, allowing them to accept both refs and OID's as parameters.

May also take a string of multiple OIDs separated by '+'. In this case, it instantiates each OID and returns a repo group, which is a ref to an array of refs to repo objects. Useful for retrieving a group of repos from a form parameter.

repo_type($oid)

Returns the type name associated with the given oid.


Repo File Access

A major class of Repo object is the File object. Eventually we will implement the File object as a truly object-oriented class, but for now its methods are divided between the repo API and the gateway API. The repo API includes functions for importing file data into the repository and registering it's metadata. Files are handled through a File Descriptor Template, whose fields are:

 label [char(100)]     : text label for identifying file
 domain [int]          : 'FSA', 'SERVER', or 'URL'.
 source [char(200)]    : Source pathname of file, could be a URL.
 locator [char(200)]   : Destination location in or other  FSA.
 mime_type [char(100)] : mime-type label 
   (see /usr/local/lib/netscape/mime.types)
 submit_date [date]     : date that file was imported 
   (DD-Mon-YYYY, e.g. '8-Jan-1998')
 submitted_by [char(10)] : login name of user who submitted file
 version [char(10)]    : for version history, if maintained
 context [char(100)]   : domain-dependent context info
 description [char(400)] : text describing this file

The fields of the FDT are maintained in the relational database under a table called File.

There are 3 classes of file that can be registered as repo objects: FSA, LOCAL, or REMOTE. FSA files are copied into the File Storage Area using the repo_import_file method, which has the advantage of assuring that the file will always be available for retrieval and that the file metadata will remain consistent with the file itself. LOCAL files are files accessible by the file system of the repository server, but are not copied into the FSA, so their metadata can become inconsistent if the file is moved or changed by an external agent. Finally, REMOTE files are designated by a URL. See gateway.pl for the implications of how these 3 classes of file are retrieved.

repo_register_file($fdt)

Use ``repo_register_file'' to create a repository object recording the metadata of a file. The file data itself is not copied into the repository. Use ``repo_import_file'' instead if data is to be copied. The $v parameter can be a reference to a File Description Template (FDT), or simply a string denoting the file's location. In the latter case, a default FDT will be generated. A new repository object is created, and the repo id is returned.

note: default assumption is that file to be registered is NOT in FSA. If it is in FSA, make sure $v->{domain} has value 'FSA'.

repo_import_file($fdt, $custom_dest)

repo_import_file should be used when the file data is to be copied into the repository's file storage area. The file description template ($fdt) must specify the file's source at a minimum (see repo_register_file for other attributes). An optional $custom_dest can be specified, in which case the given directory path will be created (if necessary) in the file storage area, otherwise a default location will be assigned. A new repository object is created, and the repo_id is returned. Returns 0 if the specified filename is unreadable. File is automatically registered. Do not preregister the file.

repo_import_file_from_handle($fh, $fdt, $custom_location)

Copies file data from specified file handle ($fh) into the file storage area, and registers the file metadata with the repository. Can be used with the CGI.pm file upload facility. An optional file description template ($FDT) can be specified (see repo_register_file), or a default will be generated. An optional $custom_location can be specified, in which case the given directory path will be created (if necessary) in the file storage area, otherwise a default location will be assigned. A new repository object is created, and the repo_id is returned. File is automatically registered. Do not preregister the file.

$v->{locator} or $fh must include name of originating file.


AUTHOR

Rex Jakobovits, rex@cs.washington.edu


SEE ALSO

gateway.pl