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

NAME

SPOPSx::Ginsu - SPOPS Extension for Generalized INheritance SUpport.

SYNOPSIS

1. Create a datasource class, for example MyDBI, which inherits from SPOPSx::Ginsu::DBI holds the package variables for the database connection (e.g. see t/MyDBI.pm).

2. Create a root base class, for example MyBaseObject, which inherits from the datasource class and SPOPSx::Ginsu and defines the base table (e.g. see t/MyBaseObject.pm).

3. Create your own sub-class of MyBaseObject which defines it's own fields (e.g. see t/Person.pm).

4. Create a configuration file which defines the package variables used by the datasource class to make the database connection (e.g. see t/my_dbi_conf.pm).

Assuming the files from steps 1-4 are MyDBI.pm, MyBaseObject.pm, MyObject.pm and my_dbi_conf.pm ...

  use my_dbi_conf;
  use MyObject;
 
  $obj = MyObject->new({field1 => 'value1', ... });
  $obj = $obj->save;
  $obj = MyObject->fetch($id);
  $obj = MyBaseObject->pm_fetch($id);

  $obj->remove;

DESCRIPTION

This is the base class for all Ginsu objects. SPOPS::DBI implements an inherited persistence mechanism for classes whose objects are each stored as a row in a single database table. Each class has its own table and all of the persistent fields are stored in that table. Ginsu extends this implementation to handle subclassing of such objects, including polymorphic retrieval. The fields of a given object are stored across several database tables, one for each parent class with persistent fields. A Ginsu object is simply an SPOPS::DBI object stored across multiple database tables.

All objects for which you want polymorphic access must share a base class whose table has a unique 'id' field and a 'class' field. In the example classes used for the tests (see the diagram in docs/Example.pdf), this class is called MyBaseObject. Suppose we have a VehicleImplementation class inheriting from MyBaseObject, which has the fields 'name' and 'owner'. And suppose VehicleImplementation has a subclass Aircraft which adds the field 'ceiling'. In this example, an Aircraft object will be stored into 3 tables, 'id' and 'class' in the base_table for MyBaseObject, 'name' and 'owner' in the base_table for VehicleImplementation and 'ceiling' in the base_table for Aircraft. Each table also has an id_field which is used to join the pieces of the object together from the 3 tables.

Also, unlike the typical usage of SPOPS objects, where the classes are created by SPOPS and have no corresponding .pm file, Ginsu objects are defined in a .pm file just like a standard Perl object, with a few additions. Each class must define the variables @ISA, $CONF, and $TABLE_DEF in the BEGIN block. The @ISA variable is standard Perl and $TABLE_DEF contains an SQL statement which creates the table for the corresponding class. The $CONF variable contains an SPOPS configuration hash with the configuration for this class only. The BEGIN block is followed by 'use' statements for the classes which are referenced in @ISA and the 'has_a' and 'links_to' parts of $CONF. Finally, after all of the use statements, it should have the line:

  __PACKAGE__->config_and_init;

By convention we put it as the last line of code in the file.

These conventions allow us to say ...

  use MyObject;

... just like we would 'use' any other Perl object.

OBJECT RELATIONSHIPS

SPOPS has configuration for 'has_a' and 'links_to' types of relationships between objects. These should continue to work just fine in Ginsu. However, I have proposed a more general framework for specifying these relationships, including defining auto-fetching/ saving/removing of related objects. This proposed syntax is described in detail in two posts to the Openinteract-Dev mailing list which can also be found in docs/new_has_a.txt and docs/update_to_new_has_a.txt

Neither SPOPS nor Ginsu fully implement this new configuration syntax, though there is some interest in eventually adding it to SPOPS. Since that's not yet happened Ginsu includes a temporary implementation of some of the features, namely the forward direction auto-fetch/save/remove. Ginsu looks for a configuration hash to be returned by a method named e_has_a().

METHODS

Public Class Methods

ROOT_OBJ_CLASS
 $class_name = CLASS->ROOT_OBJ_CLASS

Abstract method that must return the name of the root class whose table constains an autoincrement id field and the class field.

e_has_a
 $config_hash = CLASS->e_has_a

This is a temporary mechanism for returning a configuration hashref for the new style has-a relationships as specified in the OBJECT RELATIONSHIPS section above. Hopefully, this functionality will some day be included in SPOPS.

Default is an empty hashref. Sub-classes may override this to define their own configurations. Currently, nothing in this configuration is used during creation and initialization of the class, but only in the execution of pre/post-fetch/save/remove_action methods.

new
 $object = CLASS->new( $href )

Overrides the inherited new() method to allow the input hashref to initialize inherited fields. Also puts the class name in the 'class' field.

isa_classes
 $class_list = CLASS->isa_classes
 $class_list = $object->isa_classes

Returns an arrayref of all classes in this class's inheritance hierarchy which inherit from ROOT_OBJ_CLASS, including ROOT_OBJ_CLASS and the class itself. The list is ordered by proximity to current class in the inheritance tree, with the calling class always returned as the first element and ROOT_OBJ_CLASS as the last element in the list.

inherited_fields
 $field_list = CLASS->inherited_fields
 $field_list = $object->inherited_fields

Returns an arrayref of all inherited non-id field names for the current class. Does not include fields defined in the current class, only those from parent classes.

all_fields
 $field_list = CLASS->all_fields
 $field_list = $object->all_fields

Returns an arrayref of all fields for the current class, including inherited fields.

all_field_types
 $href = CLASS->all_field_types( \%p )
 $href = $object->all_field_types( \%p )

Returns a hashref of SQL types for each field (including inherited fields), where the types are those defined by DBI. Takes an optional hashref of parameters which are passed to db_discover_types().

config_and_init
 CLASS->config_and_init

Processes the configuration stored in $CONF for this class (via SPOPS::ClassFactory->create) and any classes related to it via 'links_to' relationships. Calls class_initialize (inherited). This is typically called by putting the line ...

 __PACKAGE__->config_and_init;

... as the last line of code in classes which inherit from SPOPSx::Ginsu.

fetch
 $object = CLASS->fetch( $id, \%p )

Overrides the SPOPS::DBI::fetch method and modifies the database SELECT. Instead of selecting from a single table it joins the tables by id to get all fields of the object, including inherited fields. This method currently ignores any column_group or alter_field specifications.

fetch_group
 $obj_list = CLASS->fetch_group( \%p )

Overrides the SPOPS::DBI::fetch_group method and modifies the database SELECT. Instead of selecting from a single table it joins the tables by id to get all fields of the object, including inherited fields. This also allows the where clause to include conditions on inherited fields. The objects fetched are of the type specified by the 'class' field in the Object table, not simply the class used to call fetch_group. In other words, it is a polymorphic fetch. This method currently ignores any column_group or alter_field specifications. If the class used to call the method does not have a table, the 'class' field of each fetched row is checked to make sure that it "isa" object of the calling class, otherwise it is excluded from the list returned.

fetch_count
 $count = CLASS->fetch_count( \%p )

Overrides the SPOPS::DBI::fetch_count method. If the class used to call the method does not have a table, the 'class' field of each fetched row is checked to make sure that it "isa" object of the calling class, otherwise it is excluded from the count.

pm_fetch
 $object = CLASS->pm_fetch( $id, \%p )

A polymorphic fetch. Identical to fetch, except the class used to perform the fetch is not the one used to call this method, but rather the class indicated for this id in the database. Same as a fetch_group with where clause being a simple id clause. This method currently ignores any column_group or alter_field specifications.

fetch_group_by_field
 $obj_list = CLASS->fetch_group_by_field( $field, \@vals, \%p )

Given a field name and a list of values, it fetches the objects whose specified field matches one of the values in the list. Simply creates the appropriate WHERE clause and calls fetch_group to return the corresponding arrayref of objects. Additional fetch_group parameters can be passed in the optional \%p hashref. If %p contains a 'where' field it is put at the end of the generated WHERE clause as follows:

 '<generated where> AND (<where from %p>)'

If %p contains a 'value' field, any generated values are added to the beginning of the list. This allows extra conditions to be added so one can do the following to get all Vehicles owned by Bob or Sally, except Bob's boat.

 $list = Vehicle->fetch_group_by_field( 'owner',
                                                                                [$bob->id, $sally->id],
                                      { where => 'id != ?',
                                        value = [ $bobs_boat->id ] }
                                     )
fetch_group_by_ids
 $obj_list = CLASS->fetch_group_by_ids( \@id_list, \%p )

Does a fetch_group_by_field on the id field and then sorts the returned objects according to the list of ids passed in.

Note that, while the objects returned are in the same order as the specified id's, there will be a one-to-one correspondence if and only if all corresponding objects are in the database and are not eliminated by an optional WHERE clause passed in %p.

Public Object Methods

save
 $object = $object->save( \%p )

Overrides the SPOPS::DBI::save method, saving the fields of the object in the appropriate tables. Takes an optional parameter hashref as input and returns the saved object.

compare
 $TorF = $object1->compare( $object2 )

Returns 1 if the two objects contain the same data, 0 otherwise. Note: does not compare the id fields.

as_string
 $str = $object->as_string

Overrides as_string method of parent class. Prints all fields, including inherited fields, and also prints contents of nested objects (though not fields containing hashrefs or arrayrefs).

Private Class Methods

_execute_multiple_record_query

Overrides method in SPOPS::DBI to fix bug with case-sensitive table names.

_build_conf
 $href = CLASS->_build_conf( $href )

Used recursively to build up a hashref containing the configuration info ($CONF variable contents) for all classes linked to this class via 'links_to' definitions (actually only those whose config has not yet been processed). All of these classes must be passed to SPOPS::ClassFactory->create() at the same time to properly process the configurations.

_get_main_alias
 $alias = CLASS->_get_main_alias

Returns the name of the main alias for a class (the key to use in $CONF). This method can be used even before the class's configuration has been processed.

_get_CONF
 $conf = CLASS->_get_CONF

Returns the $CONF variable for the class.

 $links_to = CLASS->_get_links_to

Returns the 'links_to' configuration hashref for the class.

_config_processed
 $TorF = CLASS->_config_processed

Returns 1 if the class's configuration has already been processed, 0 otherwise.

_isa_classes
 $href = CLASS->_isa_classes
 $href = CLASS->_isa_classes($href, $depth)

Recursively builds a hashref whose keys contain the names of all classes in this class's inheritance hierarchy which inherit from ROOT_OBJ_CLASS, including the class itself. The values in the hashref indicate level in the inheritance hierarchy, with the calling class being level 1 and ROOT_OBJ_CLASS being the maximum.

Private Object Methods

_remove_from_parent_tables
 $obj->_remove_from_parent_tables

Removes rows corresponding to this object's id from tables of parent classes.

Callback Methods

post_fetch_action
 $object = $object->post_fetch_action( \%p )

Called automatically immediately following a fetch operation. It calls the superclass's post_fetch_action() method then examines the configuration returned by e_has_a() and fetches any secondary objects specified for auto-fetching.

pre_save_action
 $object = $object->pre_save_action( \%p )

Called automatically immediately before a save operation. It calls the superclass's pre_save_action() method then examines the configuration returned by e_has_a() and saves any secondary objects specified for auto-saving. Fields containing auto-fetched secondary objects are temporarily modified to hold only the corresponding object ids (during the save process).

post_save_action
 $object = $object->post_save_action( \%p )

Called automatically immediately after a save operation. It calls the superclass's post_save_action() method then examines the configuration returned by e_has_a() and restores (or fetches) any fields specified for auto-fetching.

pre_remove_action
 $object = $object->pre_remove_action( \%p )

Called automatically immediately before a remove operation. It calls the superclass's pre_remove_action() method then examines the configuration returned by e_has_a() and removes any secondary objects specified for auto-removal. Then, if present, it removes any corresponding rows in 'links_to' tables. Finally it removes this object's row from the table corresponding to each inherited class.

BUGS / TODO

  • Currently refetch() and field_update() do NOT work for inherited fields.

  • The fetch_iterator() functionality has never been tested and is likely broken.

  • The column_group and alter_field functionality needs to be added back into fetch and fetch_group.

  • Strict fields functionality does not work.

CHANGE HISTORY

    $Log: Ginsu.pm,v $ Revision 1.60 2004/06/02 15:07:04 ray Synced with SPOPS-0.87, removed _execute_multiple_record_query(), updated version number.

    Revision 1.59 2004/04/23 18:05:31 ray Updated docs.

    Revision 1.58 2004/04/23 13:56:38 ray Renamed from ESPOPS::Object to SPOPSx::Ginsu, updated to sync with SPOPS-0.83, removed create_unless_exists() method.

COPYRIGHT

Copyright (c) 2001-2004 PSERC. All rights reserved.

and parts are

Copyright (c) 2001-2002 intes.net, inc.. All rights reserved.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

AUTHORS

  Ray Zimmerman, <rz10@cornell.edu>
  Raj Chandran, <rc264@cornell.edu>

SEE ALSO

  SPOPS(3)
  SPOPS::DBI(3)
  perl(1)