GSP
Quick Navigator

Search Site

Unix VPS
A - Starter
B - Basic
C - Preferred
D - Commercial
MPS - Dedicated
Previous VPSs
* Sign Up! *

Support
Contact Us
Online Help
Handbooks
Domain Status
Man Pages

FAQ
Virtual Servers
Pricing
Billing
Technical

Network
Facilities
Connectivity
Topology Map

Miscellaneous
Server Agreement
Year 2038
Credits
 

USA Flag

 

 

Man Pages
Oryx::Class(3) User Contributed Perl Documentation Oryx::Class(3)

Oryx::Class - abstract base class for Oryx classes

 # define a persistent class
 package CMS::Page;
 use base qw(Oryx::Class);
  
 # ... class meta-data here (see DEFINING CLASS META-DATA below) ...
  
 1;
  
 #===========================================================================
 # use a persistent class
 use CMS::Page;
  
 $page = CMS::Page->create({title => 'Life in the Metaverse'});
 $page = CMS::Page->retrieve($id);
  
 $page->update;
 $page->delete;
  
 @pages = CMS::Page->search({author => 'Richard Hun%'}, \@order, $limit, $offset);
  
 #===========================================================================
 # commit your changes
 $page->dbh->commit; # or simply ...
 $page->commit;
  
 #===========================================================================
 # attribute mutator
 $page->title('The Metamanic Mechanic');
 $tite = $page->title;
  
 #===========================================================================
 # reference association mutator
 $template_obj = $page->template;
 $page->template( $template_obj );
  
 #===========================================================================
 # array association accessor
 $page->paragraphs->[0] = $intro_para;
 $paragraph = $page->paragraphs->[42];
  
 #===========================================================================
 # array association operators
 $concl = pop   @{$page->paragraphs};
 $intro = shift @{$page->paragraphs};
 push    @{$page->paragraphs}, $concl;
 unshift @{$page->paragraphs}, $new_intro;
 splice  @{$page->paragraphs}, 1, 4, ($summary);
  
 #===========================================================================
 # hash association accessor
 $image_obj = $page->images->{logo};
 $page->images->{mug_shot} = $my_ugly_mug;
 @keys   = keys   %{$page->images};
 @values = values %{$page->images};
  
 #===========================================================================
 # support for Class::Observable
 Page->add_observer(sub {
     my ($item, $action) = @_;
     #...
 }); 
 $page->add_observer(...); # instance

Abstract base class for Oryx persistent classes.

These methods are overridden by the implementing Class class, i.e. Oryx::DBI::Class or Oryx::DBM::Class, for example, but the interfaces stay the same, so they are documented here.
create( \%proto )
creates a persistent object using "\%proto" to set up the initial state.
retrieve( $oid )
retrieves an object from storage by its object id
update
updates storage to persist and reflect changes in the object
delete
deletes the object from storage
search( \%param, [ \@order, $limit, $offset ] )
searches for objects with fields matching "\%param". SQL style "%" wildcards are supported. "\@order", $limit and $offset are optional. "\@order" is a list of columns which are used to sort the results, $limit is an integer which is used to limit the number of results, and $offset is used to exclude the first results up to that number. These last two arguments are useful for paging through search results.
commit
commits the transaction if your database supports it and AutoCommit is disabled, then you must do this.

Oryx::Class objects now unherit from Class::Observable thereby implementing a publish/subscribe system similar to triggers.

The signals are named according to the 6 interface methods prefixed with before_* and after_*, so the following signals are sent:

before_create
Handler is passed a hashref as argument with fields: "param", the search parameters, and "query", the SQL::Abstract where clause
after_create
Handler is passed a hashref as argument with fields: "param", the search parameters, and "proto", the hashref which will be blessed into an instance of this class (during 'construct')
before_retrieve
Handler is passed a hashref as argument with fields: "id", the id of the object to fetch, and "query", the SQL::Abstract where clause
after_retrieve
Handler is passed a hashref as argument with fields: "proto", the hashref which will be blessed into an instance of this class (during 'construct')
before_update
Handler is passed a hashref as argument with fields: "query", the SQL::Abstract where clause.
after_update
Handler takes no arguments.
before_delete
Handler is passed a hashref as argument with fields: "query", the SQL::Abstract where clause.
after_delete
Handler takes no arguments.
before_search
Handler is passed a hashref as argument with fields: "query", the SQL::Abstract where clause, "param", the search parameters, the "order" and "limit" parameters.
after_search
Handler is passed a hashref as argument with fields: "query", the SQL::Abstract where clause, "param", the search parameters, the "order" and "limit" parameters, and "objects", an arrayref of objects returned by the search.
before_construct
Handler is passed a hashref as argument with fields: "proto", the hashref which will be blessed into an instance of this class.
after_construct
Handler is passed a hashref as argument with fields: "object", the persistent object.

These methods are concrete.
init
Initialises the class data (see Class::Data::Inheritable)
import
Does the work of constructing the class' meta-instances (Oryx::Attribute, Oryx::Association and Oryx::Parent instances) from the $shema class variable or defined in the DATA section of the module if you have XML::DOM::Lite installed.
meta
Simple accessor to the class meta data.
construct( $class, $proto )
This is typically called from within the "create" and "retrieve" methods of the implementation class (Oryx::DBI::Class or <Oryx::DBM::Class, for example) which then blesses $proto into $class and then allows each class meta-instance to frobnicate it in turn if they have any need to, before handing the instance to you.
addAttribute( $meta )
Creates an Attribute meta-instance and associates it with the class.
addAssociation( $meta )
Creates an Association meta-instance and associates it with the class.
addMethod( $meta )
Does nothing at the moment as I cannot decide what such a method would be used for exactly.
addParent( $super )
Creates a Parent meta-instance and associates it with the class.
id
Returns the object id.
is_abstract
True if the class does not define any attributes. This is used for creating a special table for sharing sequences accross subclasses and for instantiating the correct subclass instance if "retrieve()" is called on an abstract class.
table
Returns the table name for this class.
name([ $name ])
Get or set the "name" meta-attribute for the class.
members
Return a list of all meta-instances (Attribute, Association, Method and Parent instances).
commit
calls $self->dbh->commit to commit the trasaction
schema
shortcut for $self->storage->schema. Read only.
remove_from_cache
Object method to remove it from the memory cache.

Creating persistent classes is simple, there is no need to create any database tables by hand as the DB schema is deployed automatically as needed (see "AUTOMATIC TABLE CREATION" below).

The following three steps illustrate how this is done:

Inherit from Oryx::Class or subclass thereof (see "INHERITANCE" below):
 package CMS::Page;
 use base qw(Oryx::Class);
    
Define meta-data (see "DEFINING CLASS META-DATA" below):
 our $schema = {
     attributes => [{
         name  => 'title',
         type  => 'String',
     },{
         name  => 'author',
         type  => 'String',
     },{
         name  => 'number',
         type  => 'Integer',
     }],
     associations => [{
         role  => 'paragraphs',
         class => 'CMS::Paragraph',
         type  => 'Array',
     },{
         role  => 'template',
         class => 'CMS::Template',
         type  => 'Reference',
     }],
 };
 
 1;
    
Connect to storage (see "CONNECTING TO STORAGE" below):
...far away in another piece of code...

 use CMS::Page;
 
 use Oryx;
 Oryx->connect(["dbi:Pg:dbname=cms", $usname, $passwd]);
 
 ...
    

Now we're ready to start using persistent CMS::Page objects (and friends).

Oryx::Class defines a create method (see Oryx::Class for more) which takes a hash reference as a constructor for setting up the object's initial state:

     use CMS::Page;
     my $page = CMS::Schema::Page->create({
         title  => 'Meta Model Mania',
         author => 'Sam Vilain',
     });

Once an object has been instatiated, attribute mutators can be used to get and set attributes on the object (see "ATTRIBUTES" below):

     $page->number(42);

Associations are similar except that we associate one object with another (see "ASSOCIATIONS" below), so we create an instance of the target class:

     my $paragraph1 = CMS::Paragraph->create({
         content => $some_block_of_text,
     });

And then, because the association mutator returns a reference to a tied object (an ARRAY in this case), we can:

     $page->paragraphs->[0] = $paragraph1;

Then update your object when done:

     $page->update;

Or if you no longer need it:

     $page->delete;

Finally, commit your changes:

     $page->commit;

Three ways of defining meta data for your persistent classes are supported as follows:
Tangram style using a $schema class variable:
 package CMS::Page;
 use base qw(Oryx::Class);
 
 our $schema = {
     attributes => [{
         name  => 'title',
         type  => 'String',
     },{
         name  => 'author',
         type  => 'String',
     }],
     associations => [{
         role  => 'paragraphs',
         class => 'CMS::Paragraph',
         type  => 'Array',
     },{
         role  => 'template',
         class => 'CMS::Template',
         type  => 'Reference',
     }],
 };
 
 1;
    
Class::DBI style adding members dynamically:
 package CMS::Paragraph;
 use base qw(Oryx::Class);
 
 __PACKAGE__->addAttribute({
     name  => 'content',
     type  => 'Text',
 });
 
 __PACKAGE__->addAttribute({
     name  => 'formatted',
     type  => 'Boolean',
 });
 
 __PACKAGE__->addAssociation({
     role  => 'images',
     class => 'CMS::Image',
     type  => 'Hash',
 });
 
 1;
    
If you have XML::DOM::Lite, put it in the DATA section:
 package CMS::Image;
 use base qw(Oryx::Class);
 
 1;
 __DATA__
 <Class>
   <Attribute name="alt_text" type="String" />
   <Attribute name="path_to_file" type="String" />
 </Class>
    

With Oryx, you never need to write a single line of SQL although you can if you want to in exactly the same way as you would when using Class::DBI (actually it's a ImA::DBI feature). Tables are named sensibly as pluralised versions of the class name with link table names equally intuitive.

To enable automatic table creation, you need to do the following near the top of your application before you use any of your classes:

 use Oryx ( auto_deploy => 1 );

Because the check to see if a table exists is made once when the class is first use'ed, the performance penalty for this is minimal in long running process environments such as mod perl. Otherwise when running in an environment where your code is recompiled each time the program is run, or you would like more control, you can leave auto_deploy turned off at the top level (which it is by default) and simply turn it on for each new class that you're adding to the schema as this method is inherited.

Attributes are declared as having a name and a type and as such are simply tied Oryx::Value derivatives (see Oryx::Value for details) which are generally associated with a field (or column) in the underlying database, and which have mutators which are automatically created in the class for getting and setting these values.

Certain attributes may also be declared with additional properties as relevant, for instance, attributes declared as type => "Float" support a precision property which describes the valid number of decimal places.

Input is checked when assigning values to attributes and return values are cast to the correct type using a combination of regular expressions, the Data::Types module, YAML or Class::Date where relevant. Where additional properties are set such as size or precision, these are checked also and your program will croak if types mismatch or overflow.

Several basic value data types are supported:
String
Varying character type (VARCHAR for most RDBMS). Input is checked using Data::Types::is_string and if the attribute is declared with a size property, the length is also checked.
Text
Corresponds to a SQL TEXT type; type checking is done using Data::Types::is_string, but no length checking is performed.
Boolean
Corresponds to a a SQL TINYINT or INT type and is checked for the values 0 or 1.
Binary
No checking is done here, but a BLOB or BYTEA or equivalent column type is created when the class is deployed.
Complex
This can be anything that can be (de)serialized using YAML and is stored internally in the DB in a column with a TEXT type.
DateTime
Uses Class::Date objects. You can pass either a Class::Date instance to the mutator as follows:

 use Class::Date qw(date);
 $page->date_created( date(localtime) );
    

or any value which is valid input to the Class::Date::new constructor this includes ARRAY refs etc. (see Class::Date for details).

Attributes declared as DateTime types additionaly support a format property which is used to set Class::Date::DATE_FORMAT for date formatting.

Float
Floating point number checked using Data::Types::is_float. Return value is done with Data::Types::to_float and precision checks are made if the attribute is declared with such.
Integer
Corresponds to INT or INTEGER SQL type. Input checks are performed using Data::Types::is_int.
Oid
This is also an integer type, but with the distinction that when a class is deployed to an RDBMS the column is constrained as a PRIMARY KEY.

Oryx implements the three most common ways in which associations between classes can be achieved natively with Perl. An object can be associated with another by simple reference, or we can use either ordered (ARRAY), or keyed (HASH) associations - so a field in one object (usually a blessed HASH reference) can be an ARRAY reference, for example, which could be filled with references to other objects (which themselves are persistent).

In RDBMS terms, this sort of to-many ordered relationship requires a link table with a column holding ordering information, which is exactly what happens under the hood, but Oryx makes it transparent for you using Perl's tie mechanism while managing the link table automagically.

Furthermore one can also have to-many ordered (Array) or to-many keyed (Hash) associations which are mixed - in other words one class can have an ARRAY (or HASH) reference which can contain instances of different classes (see "ABSTRACT CLASSES" below).

getting :

 my $a_template = $page->template;

setting :

 $page->template($another_template);

getting :

 my $para42 = $page->paragraphs->[42];

setting :

 $page->paragraph->[0] = $intro_para;

as well as all the usual push, pop, shift, unshift and splice.

getting :

 my $image_obj = $page->images->{logo};

setting :

 $page->images->{mug_shot} = $my_ugly_mug;

Retrieval is simple, just pass in the id (primary key) :

 my $page = CMS::Page->retrieve($page_id);

Searching uses 'LIKE' (assuming an RDBMS storage) :

 my @pages = CMS::Page->search({ author => '%Hundt%'});

NOTE : Searches don't search through superclass fields yet...

Inheritance works as you would expect.

So if we have the following :

 package CMS::Section;
 use base qw(Oryx::Class);
 
 # ... schema definition here ...
 
 1;
 
 package CMS::Paragraph;
 use base qw(CMS::Section);
 
 # ... schema definition here ...
 
 1;

You get exactly what you would normally get in Perl, that is :

 UNIVERSAL::isa('CMS::Paragraph', 'Oryx::Class')

holds true and attributes and associations defined in CMS::Section are available to CMS::Paragraph instances. So any class which has persistant class as an ancestor, can be treated and persisted in the same way as the ancestor. However, it is important to note that it gets its own table in the database.

For multiple persistent base classes :

 package Orange;
 use base qw(Food Fruit);

As long as Food and Fruit are Oryx::Class derivatives, the Force That Into the Database Drives the Object will make sure the proverbial Right Thing is Done.

Oryx uses a multiple table inheritance model (as opposed to putting all the instances for classes in an inheritance chain into the same table), each subclass instance has a corresponding superclass instance for each superclass (assuming said superclass is a derivative of Oryx::Class), so that attributes which exists in the superclass are stored (as a row) in the superclass' table, and are therefore fully fledged instances of the superclass.

You can access these superclass instances with the PARENT method as follows:

 my $parent_section_instance = $paragraph->PARENT('CMS::Section');

and then use this instance normally.

Updates and deletes cascade up the inheritance chain, as you'd expect.

Abstract classes to Oryx are simply classes which do not define any attributes, but may have associations. The effect is automatic.

Abstract classes behave slightly differently to concrete classes (which define attributes) in that if you retrieve an instance of an abstract class (by id or by accessing a member of an association), you get an instance of the sub class (the one which created the row in the abstract class's table).

This is particularly useful where you have an Array or Hash association between two classes and need to mix instances of different types in that association. As long as all the members of the array (or hash) inherit from the same abstract class, accessing them produces the expected result.

Consider the following case :

                    <ABSTRACT>
 +------+  <Array> +----------+
 | Page |----------| Fragment |
 +------+  frags   +----------+
 |______|          |__________|
                        /_\
                         |
               +---------+------+
               |                |
         +-----------+      +-------+
         | Paragraph |      | Image |
         +-----------+      +-------+
         |___________|      |_______|

Here the Paragraph and Image both inherit from the abstract Fragment class. When the frags Array association is accessed it may contain a mixture of both Paragraph and Image instances.

Thus you can say:

 $my_para = Paragraph->create({ ... });
 $my_page->frags->[42] = $my_para;
 
 $my_img = Image->create({ ... });
 $my_page->frags->[69] = $my_img;

pretty neat huh?

In the interest of consistency, objects are cached and are unique in memory. Therefore, if you retrieve an object more than once, each subsequent retrieve until the reference count on it has dropped to zero and has been eaten by the garbage collector, will return a reference to the same object.

This has a performance gain in certain situations too.

Richard Hundt <richard NO SPAM AT protea-systems.com>

This library is free software and may be used under the same terms as Perl itself.

Hey! The above document had some coding errors, which are explained below:
Around line 579:
You forgot a '=back' before '=head1'
2006-06-18 perl v5.32.1

Search for    or go to Top of page |  Section 3 |  Main Index

Powered by GSP Visit the GSP FreeBSD Man Page Interface.
Output converted with ManDoc.