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

NAME

Class::ReluctantORM::SubClassByRow - ORM base class for behavior inheritance

SYNOPSIS

  package ShipType;
  use base 'Class::ReluctantORM::SubClassByRow';
  __PACKAGE__->build_class(
    schema => 'highseas',
    table => 'ship_types',
    primary_key => 'ship_type_id',
    subclass_column => 'subclass_name',
    subclass_preload => 1,
  );

  Ship->has_one('ShipType');

  package ShipType::Galleon;
  sub set_sail { ... }

  package ShipType::Rowboat;
  sub set_sail { ... }

  package main;
  my $ship = Ship->fetch_deep(with => { ship_type => {} }, where => ...);
  my $st = $ship->ship_type();
  $st->isa('ShipType::Galleon');  # true if the ship_type->subclass_name eq Galleon
  $st->isa('ShipType');           # true
  $st->set_sail();                # polymorphic

DESCRIPTION

CRO usually treats rows as instances, and tables as classes. But what if what you are storing classes in the tables? This base module allows you to treat a table's rows as both classes and instances (an instance of a metaclass, basically). This is most useful for "type tables" in which you want the behavior of an object to vary with its type. In otherwords, this can provide for behavioral inheritance and polymorphism (by implementing methods in your specific subclasses).

TODO - compatible with CRO::Static?

INSTANTIATION BEHAVIOR

Whenever a ShipType object is fetched from the database, we examine the column named by the subclass_column to determine which subclass it should be (if the value of the column contains colons, it is used as-is; if no colons are present, the base class is prefixed (ie, 'Dread::Naught' => Dread::Naught, 'Galleon' => 'ShipType::Galleon'. That subclass is loaded (if it hasn't been loaded already), we confirm that the subclass ISA ShipType (if not, we push ShipType to the front of its @ISA), an optional class initter is run (postload_init) and then the object is created and blessed as the specified subclass.

SubClassByRow also makes arrangements so that metadata requests on a subclass will be handled by the superclass.

CLASS CONFIGURATION

YourBaseClass->build_class(%options);

This class method sets up your classes. In addition to the options permitted to Class::ReluctantORM->build_class, you may also use:

subclass_column

Required string. Column to examine when instantiating objects to determine class of new object.

subclass_preload

Optional boolean, default false. If true, we'll try to load any modules under the directory where the superclass lives (ie, we'll try to load anything under ShipType/*.pm, ShipType/*/*.pm, etc; anything that then claims to be a ShipType will be registered). If false, class loading will occur on an ad hoc basis as rows are returned and new subclass_column values are seen.

my $thing = YourClass->new(%attributes);

Overrides the default CRO constructor. Acts as a factory method - based on the value of the sublass_column attribute, determines the actual class to bless the object into. Loads the class if neccesary.

$subclass->_postload_init();

Optionally, you can define this method. It will be called the first time the subclass is loaded, but is gaurenteed to be called after the class's metadata is established. Default implementation is a no-op.

AUTHOR

Clinton Wolfe