repo.pl - repository object interface.
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");
db.pl, fci.pl
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.
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.
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.
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 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 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.
The following methods are used to create, delete, and modify persistent repository objects.
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.
Permanently deletes the instance specified by the given OID. Removes it from the database and extracts it from the WRM_REPO_TYPES index.
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.
The following functions are used to define and evolve repository object schemas. View definitions are handled seperately.
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.
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.
Undefines the named class type by dropping the class's table and deleting it's definition in the schema dbm.
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''.
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.
The following functions can be used to translate between repo's and their OID's, and to verify the type of a repo.
Takes a value of any type. returns TRUE if the value is a ref to a repository object, FALSE otherwise.
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.
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.
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.
Returns the type name associated with the given oid.
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.
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 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.
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.
Rex Jakobovits, rex@cs.washington.edu
gateway.pl