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
andsetMetaAttribute
. 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.
- search
-
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.