The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Oryx::Manual::Guts - Oryx internals documentation for developers

DESCRIPTION

This document is intended for hackers to grant an insight into how Oryx's pieces fit together and is intended for anyone wishing to extend Oryx... or for anybody who just wants to know how the sausage is made.

NOMENCLATURE

Oryx implements an object oriented meta-model, that is, information about persistent classes is carried around, usually as instances of meta classes, by the persistent classes themselves, instances of which are your persistent objects.

This makes it difficult to describe a meta-model which has components, or meta-components, which themselves are really just ordinary Perl objects.

Thus we could end up talking about instances of the Oryx::Value::String class being associated with an instance of the Oryx::Attribute class when mentioning how they relate to our persistant objects - which are themselves Oryx::Class derived instances. All of which can be tricky for the brain (well mine, at any rate) to keep a grip on.

So to make this slightly more coherent, we'll stick to the following nomenclature throughout this document:

class

When we say "class" we mean a subclass of Oryx::Class which you can instantiate into "objects" which are persistable in some storage backend - usually a database of sorts.

object

When we refer to "objects" we mean our persistent objects - that is, instances of subclasses of Oryx::Class which we're interested in storing in our database.

meta-type

We'll use "meta-type" when talking about meta classes of which Oryx::Attribute and Oryx::Association are two examples (not to be confused with instances of these described under meta-instances below).

meta-instance

These are actually objects themselves (but not of the kind described above under "object") and are considered meta data of the "class" in that they hold descriptive information about the "classes". Examples are instances of Oryx::Attribute and Oryx::Association.

meta-attribute

When we say: "meta-attribute", what we mean varies. To explain: two methods are available for accessing these and are inherited by all meta-types from Oryx::MetaClass. The methods are getMetaAttribute and setMetaAttribute. Exactly what these access depends on the context, that is to say, the term "meta" here is relative to the meta-type on which the method is called. So if we say:

    CMS::Page->attributes->{number}->getMetaAttribute('type');

We will get a string representing the type of the value which may be stored in this field of the object, most likely an 'Integer' in this case. On the other hand, if we say:

    CMS::Page->association->{paragraphs}->getMetaAttribute('class');

We will get the name of the target class of the 'paragraphs' association, probably CMS::Paragraph in this case, which is a class name - in fact a subclass of Oryx::Class - which is not an Oryx meta-type, but in this context, it is meta-data which this association needs in order to perform its duty.

ABSTRACT METAMODEL

Oryx implements an abstract metamodel which can be specialised to work with any storage backend. At the moment relational databases and a fast, pure Perl file-based database are supported, but the abstraction of the metamodel should allow one to add support for any other storage backend without changing how persistent classes are defined.

This abstract metamodel consist of four base meta-types namely: Oryx::Attribute, Oryx::Association, Oryx::Parent and Oryx::Class. Oryx::Class can be regarded as a special case meta-type in that it is the level at which our meta-model joins Perl's own built-in object model.

When a persistent class inherits from Oryx::Class, it doesn't have the implementation to be persisted in any given storage back-end, that is, the methods for creating, retrieving, updating, etc. are just stubs and will raise an exception if invoked. During a call to Oryx->connect(...), however, Oryx looks at the connection arguments and determines which concrete meta-types will be used thereafter. This is done by simply pushing either Class::DBI::Class or Class::DBM::Class onto @Oryx::Class::ISA, thereby effectively doing dynamic, run-time inheritance. So for example, if we say:

    use Oryx;
    use CMS::Page;
     
    UNIVERSAL::isa(CMS::Page, 'Oryx::Class')            # true
    UNIVERSAL::isa(CMS::Page, 'Oryx::DBI::Class')       # false
     
    Oryx->connect('dbi:Pg:dbnam=foo', $user, $pass);
    
    UNIVERSAL::isa(CMS::Page, 'Oryx::DBI::Class')       # true

whereas if we:

    Oryx->connect('dbm:Deep:datapath=/path/to/data');
    
    UNIVERSAL::isa(CMS::Page, 'Oryx::DBM::Class')       # true

From this point onwards, the correct concrete meta-types are used and constructed from the class metadata defined in the class' $schema. This happens in the class's import hook, but is usually deferred to happen at run-time (instead of compile-time); so in the first case above this would be Oryx::DBI::Association, Oryx::DBI::Attribute and Oryx::DBI::Parent. The actual connect() call is then delegated to the appropriate storage class: Oryx::DBI or Oryx::DBM, as the case may be. These storage classes handle low level connection, pinging the DB and caching database handles (indirectly via Ima::DBI) where applicable.

Each Oryx::Class derivative (or subclass) has any number of instances of the concrete meta-types associated with it. At the Oryx::Class level, this is done with inheritable class data using Class::Data::Inheritable (of all things!) for creating accessors (or "named closures"), typically by inspecting the $schema class variable, or, in the case of Oryx::Parent meta-instances, by inspecting the class' @ISA array.

From our class, we can access the meta-instances as folows:

    @attribs = values %{CMS::Page->attributes};            # all the attributes
    $assoc  = CMS::Page->associations->{paragraphs};       # single association

Oryx::Parent meta-instances are stored in an array ref, so this access looks a little different:

    @parents = @{CMS::Page->parents};      # all parents

Alternatively you can get all the meta-instances as an array using the convenient members method:

    @members = CMS::Page->members;

This will then contain a list of all the meta-instances describing our CMS::Page class.

NOTE: The meta-instances are constructed using import, so they'll be there after you use the class, but not if you just require it.

meta-types

Attributes

Attributes create individual fields of data with each row (columns). Attributes have a field name and a type.

Associations

Associations create relationships between Oryx objects. These relationship are mapped using Perl references, arrays, or hashes.

Parents

Oryx classes may subclass other Oryx classes, inheriting all fields the parents provide.

These all inherit from a common base meta-type: Oryx::MetaClass and they all implement a common interface described therein.

COMMON INTERFACE

The overall interface of each Oryx object defines six main methods. At the top level, each of these methods may perform some action as well as delegating additional actions out to each member class (see "meta-types"). Thus, the implementation of each of each of these methods in the Oryx class looks something like:

  sub create {
      # take some action to create the record
      $_->create(...) foreach $class->members;
      # finish up, store the data, return
  }
create

This method is called to create a new record in storage.

retrieve

This method is called to fetch an object from storage by the object's identifier.

update

After making changes to the object, this method records those changes to storage.

delete

This method deletes the object. The object is invalid after this method is called and should not be referred to anymore.

This method is responsible for searching by field and returning an array of matching objects.

construct

When searching and retrieving, both instantiate each instance returned using this method.

Each delegated class has a chance to modify the object as necessary during each of these calls.

CLASSES

Oryx has a few special unique parts that are used to define the rest. The most visible of these parts are the Oryx and Oryx::Class objects.

Each of the following headings describe each group of classes used by Oryx.

FRONT-END CLASSES

There are two classes that any Oryx user must be familiar with. The rest of the classes in the system work through the meta-model of Oryx and are not necessarily exposed directly to the user.

In order to establish a connection to storage, the user uses methods of the Oryx class. Then, to define a class, the user typically extends Oryx::Class to automatically configure the class from a meta-model representation.

The third front-end class is Oryx::Schema, which can be subclassed by the user to change groups of classes in the same storage schema.

Oryx

This class is responsible for initializing the general state of Oryx. It provides the connect() method which initializes the connection to storage, provides a method for deploying a whole schema, deploySchema().

Oryx::Class

By subclassing this class, an object allows metadata declared in the $schema package variable or XML in the __DATA__ section to be parsed and used to build class methods and schema information. Each persistent Oryx object should subclass this class.

Oryx::Schema

Provides a basic template for Oryx schemas. The schema used for an object is picked when a connection is made to storage. This schema is used by default. This schema class may be subclassed to change the table prefix for objects in the schema or make other modifications to the schema as a whole.

META-MODEL IMPLEMENTATION CLASSES

These objects are used under the hood to perform the basic row-level operations for a persistent Oryx class. All Oryx classes ultimately inherit functionality from Oryx::MetaClass. All will inherit functionality from either Oryx::DBM::Class or Oryx::DBI::Class depending on the storage type.

Oryx::MetaClass

This class provides access to an object's metadata.

Oryx::DBI::Class

This provides the basic functionality required to store an object into a DBI connected database.

Oryx::DBM::Class

This provides the basic functionality required to store an object into a DBM connected database.

META-MODEL MEMBER CLASSES

The member classes in the meta-model create additional functionality within a database row. These classes are associated with an object as configured by the schema of the class.

Oryx::Association

This is the base class for associations. Associations connect one Oryx object instance to another or to a group of others. Exactly how the association functions depends on the association type.

Oryx::Association::Array

This is an association class that is used for "Array" associations. This creates a one-to-many mapping. The mapping is performed using a Perl array.

Oryx::Association::Hash

This is an association class that is used for "Hash" associations. This creates a named one-to-many mapping. The mapping is performed using a Perl hash.

Oryx::Association::Reference

This is an association class that is used for "Reference" associations. This creates a one-to-one mapping. The mapping is performed using a simple Perl reference.

Oryx::Attribute

Attributes are added to Oryx objects to include primitive data associated with the object. Each attribute has a type associated with it, which are managed via Oryx::Value classes (see "ATTRIBUTE VALUE CLASSES").

Oryx::Parent

One of the main goals of Oryx is to map Perl objects into persistent data storage in a way that fits natrually into Perl's object-model. One of the important features of this object-model is the ability to subclass. The Oryx::Parent delivers the functionality required to make this work seamlessly.

STORAGE CLASSES

Currently, Oryx supports to types of backing stores via DBI or DBM::Deep. These classes are responsible for making the connections to those objects when the connect() method of Oryx is called. These are also responsible for making the work of deploying objects and schemas happen.

Oryx::DBI

This manages connectivity and deployment to a DBI connection.

Oryx::DBM

This manages connectivity and deployment to a DBM::Deep connection.

STORAGE UTILITY CLASSES

These are helpers used by Oryx::DBI and Oryx::DBM to do much of the grunt work over the storage connection.

Oryx::DBI::Util

This documents the interface that is actually implemented by drivers. Drivers must implement methods for testing for checking for and manipulating table columns, checking for and manipulating table definitions, manipulating sequences, manipulating indexes, discerning types, and enumerating rows.

Oryx::DBI::Util::mysql

This is the driver for DBD::mysql connections.

Oryx::DBI::Util::Pg

This is the driver for DBD::Pg connections.

Oryx::DBI::Util::SQLite

This is the driver for DBD::SQLite connections.

Oryx::DBM::Util

This is the analog for Oryx::DBI::Util that works with Oryx::DBM. Provides functionality for checking for and manipulating tables and sequences.

DBI STORAGE META-MODEL IMPLEMENTATION CLASSES

These classes all implement the specifics of the "META-MODEL MEMBER CLASSES". These simply implement the functionality for the Oryx::DBI storage object.

Oryx::DBI::Association
Oryx::DBI::Association::Array
Oryx::DBI::Association::Hash
Oryx::DBI::Association::Reference
Oryx::DBI::Attribute
Oryx::DBI::Parent

DBM STORAGE META-MODEL IMPLEMENTATION CLASSES

These classes all implement the specifics of the "META-MODEL MEMBER CLASSES". These simply implement the functionality for the Oryx::DBM storage object.

Oryx::DBM::Association
Oryx::DBM::Association::Array
Oryx::DBM::Association::Hash
Oryx::DBM::Association::Reference
Oryx::DBM::Attribute
Oryx::DBM::Class
Oryx::DBM::Parent

ATTRIBUTE VALUE CLASSES

These are used by Oryx::Attribute and Oryx::Association members to choose how to validate, load, and store information in each for each column. Each of these is a scalar tie object.

Each of these defined TIESCALAR(), FETCH(), and STORE(). See perltie for more details.

Oryx::Value

This provides a default implementation and the general interface for all the others.

Oryx::Value::Binary

This provides an implementation for BLOB-style binary data.

Oryx::Value::Boolean

This provides an implementation for a binary value of either 0 or 1.

Oryx::Value::Complex

This provides an implementation of YAML encoded references.

Oryx::Value::DateTime

This provides an implementation of date/time data.

Oryx::Value::Float

This provides an implementation of floating point data.

Oryx::Value::Integer

This provides an implementation of integer data.

Oryx::Value::Oid

This provides an implementation for object identifiers, which are used as primary keys for objects.

Oryx::Value::String

This provides an implementation for string data---generally shorter than 256 characters.

Oryx::Value::Text

This provides an implementation for long text data.

ACKNOLWEDGEMENTS

This documentation contributed by Andrew Sterling Hanenkamp <hanenkamp@cpan.org>

AUTHOR

Copyright (c) 2005 Richard Hundt <richard NO SPAM AT protea-systems.com>

LICENSE

Oryx may be used under the same terms as Perl itself.