NAME
Tangram::Type - root of the Type hierarchy
DESCRIPTION
Tangram::Type is the root of a hierarchy of classes that are responsible mapping individual field to SQL entities (columns in the simplest cases).
Adding support for new types amounts to adding subclasses to Tangram::Type.
Type objects are singletons: there exists exactly one instance per Type concrete subclass.
DISCLAIMER
Some methods in the Type hierarchy are going to be reexamined and perhaps reworked. Only the methods documented here have a good chance to work in future releases.
CLASS METHODS
instance()
Returns this Type's sole instance.
INSTANCE METHODS
expr($type, $expr, @remotes)
Returns a new Expr object. See Tangram::Expr for a description of the arguments.
WRITING NEW TYPES
Tangram is organized in several subsystems, described below.
Schema is the repository for information about all the persistent aspects of a system: classes, inheritance relationships, fields, etc. It also contains graph-traversal algorithms, which are not currently documented.
Storage deals with objects as a whole: insertion, updating, multiple load detection, cycle handling, transactions, connections. It also serves as an entry point in the system. Storage does not manipulate fields directly.
Cursor deals with polymorphic retrieval of objects. It builds SELECT statements on the basis of the information in the hash. Cursor does not manipulate fields directly either.
The Type hierarchy deals with individual fields, and not with entire objects. More about it in a moment.
The Expr hierarchy deals with entities on the remote side; this includes expressions proper, Filters and Remotes.
Types are responsible for performing the mapping between a field of a given Perl type and a relational entity. The simplest Types merely transfer between one Perl field and one column. Sometimes it makes sense to have several mappings (and hence several Types) for the same Perl type; for example, Perl arrays can be mapped either using a link table, or one or several columns that live on the element's table.
Users don't deal with Type objects directly: they indicate that a series of fields should be mapped in a certain way by putting the fields under a given 'typetag' in the field hash. The type registers itself with Tangram by adding a typetag in the %Tangram::Schema::TYPES hash. The value is the Type object. Up to now all Types have been singletons, but this is not a rule.
Anybody who's planning to write new Types should examine Scalar.pm first. It contains very simple mappings between one field and one column.
A Type must implement the methods described below. Keep the following facts in mind while reading further:
1. A Type is responsible for transfering all the *direct* fields for a given *class*. This excludes inherited fields. OTOH, the same Type can be called more than once for the same object, because the same Type may be used in several classes that appear in a particular object's inheritance graph.
* reschema($self, $members, $class)
This method is called when the schema hash is being converted into a Schema object. The Type finds all the fields it is responsible for mapping in the Perl structure refered by $members. The Type decides the exact format of this structure.
* sub cols($self, $members)
Called when building SQL statements; the Type returns a list of columns to be inserted in the statement. Note that this concerns *all* the fields managed by this Type in this object.
* sub read($self, $row, $obj, $members, $storage)
Called when moving data from the database to the object. The Type removes scalars from the array passed in $row and puts them in the right place in $obj, the object being loaded. $storage is the Storage that is being loaded from.
* sub save($self, $cols, $vals, $obj, $members, $storage, $table, $id)
Called when inserting or updating objects. The Type puts the name of the columns it maps in the $cols array, and copies the necessary values from $obj (the object being saved) to the $vals array according to the fields declared in $members. For simple types, these parameters suffice, but relationship Types need more: $storage is the storage being loaded from; $table is the table that contains the direct fields for this class; and $id is the id of the object being loaded.
The next method is needed only by Types that support querying on a field's value on database side. Ignore it at first.
* query_expr($self, $obj, $members, $tid, $storage)
Called when building a Remote. The Type returns a list of Expr objects, one per(remote) field. The Type returns a list of Expr's, one per controlled field.
Ref and Collections have in common that they don't load their controlled fields upfront. Their read() method tie() the controlled fields to a package that will demand-load the final value of the field - if it's ever needed. The exact procedure for achieving this is not imposed by Tangram itself, in fact, a Type has the liberty of doing just anything it sees fit, if it can manage it with the arguments that it gets passed by the higher layer. Ref ties a field to the RefOnDemand package, and Collections tie to CollOnDemand. These two packages will probably merge at someday.
Ref and those collections that contain references to other objects must deal cycles. This can be done quite easily but I don't have to go into those details right now.
Collections (of references of or values (the so-called 'flat' collections)) typically save their state when the demand-load is triggered. Later, when the collection is saved, the Collection compares the current field state (iow collection content) with the state at load time, and updates the database accordingly.
Types that need to remember field state at load time should store it under $storage->{scratch}{TYPE_CLASS}, and typically under $storage->{scratch}{TYPE_CLASS}{OBJECT_ID}{FIELD}. Coll defines two utility functions to help manage load-time state:
* set_load_state($self, $storage, $obj, $field, $state)
Remember $state of the $field of $obj, retrieved from $storage.
* get_load_state($self, $storage, $obj, $member)
Retrieve that state.