Object-Trampoline-1.42

NAME

Object::Trampoline - delay object construction, and optinally using the class' module, until a method is actually dispatched, simplifies runtime definition of handler classes.

SYNOPSIS

use Object::Trampoline;
# the real class name is added to the normal constructor
# and 'Object::Trampoline' used instead. the destination
# class' constructor is called when object is actually
# used for something.
#
# The database handle is what you'd normally expect, but
# the statement handle is a trampoline: it gets constructed
# on the first call via $sth->....
my $dbh = DBI->connect( ... );
my $sth = Object::Trampoline->prepare
(
$dbh, 'select foo from bar'
);
# or specify the package and args from a config file
# or via inherited data.
#
# the constructor lives in the destination class
# and has nothing to do with Object::Trampoline.
my %config = Config->read( $config_file_path );
my ( $class, $const, @argz )
= @config{ qw( class const args ) };
my $handle = Object::Trampoline->$const( $class, @argz );
# at this point ref $handle is 'Object::Trampoline::Bounce'.
$handle->frobnicate( @stuff );
# at this point ref $handle is $class
# there are times when it is helpful to delay using
# the object's class module until the object is
# instantiated. O::T::U adds the caller's package
# and a "use $class" before the constructor.
my $lazy = Object::Trampoline::Use->frobnicate( $class, @stuff );
my $result = $lazy->susan( 'spin_me' );
# Note: isa and can are overloaded. Calling $lazy->isa will
# convert the object and return the corret type.
# you use a module that does not check for forks and will
# fail if the object is created prior to forking. adding
# a sanity check in the constructor will delay the check
# until the object is used. using O::T::B delays the check
# but still makes the object available (e.g., for export
# to other modules).
packge WhatEver;
my $parent = $$;
sub new
{
my $obj = &construct;
$object->initialize( @_ );
$object
}
sub initialize
{
$$ != $parent
or die "Creating WhatEver in parent process";
# safe to create the object
my ( $obj, @argz ) = @_;
...
}
...
my $post_fork_object
= bless sub { WhatEver->new( ... ) }, 'Object::Trampoline::Bounce';
# there are times it is convienent to delay calling
# a lexical sub, say a factory object or lexically-
# scoped utility sub.
#
# lacking the name of a method and a class, this has to use
# different mechanism. calling O::T::delayed with a subref
# and arguments will delay calling the subref until the object
# is accessed.
#
# note that this will fail if $handler is not a subref; for
# calling methods by name use the syntax described above.
my $handler
= sub
{
# extract connect values at runtime.
DBI->connect( @Database{ qw( dsn usr pwd attr ) } );
};
my $dbh = Object::Trampoline->delayed( $handler, @argz );
# or
my $gen_dbh
= do
{
# extract DBConfig at compile time, delay
# constructing the object. this gives the
# advantage of validating the arguments at
# compile time, delaying the actual construction.
my $dsn = $Database{ dsn } or die "...";
my $user = ... ;
my $pass = ... ;
my $attr = ... ;
sub { DBI->connect( $dsn, $user, $pass, $attr ) }
};
my $dbh = Object::Trampoline->delayed( $gen_dbh );

DESCRIPTION

There are times when constructing an object is expensive or has to be delayed -- database handles in heavily forked apache servers are one example. This module creates a "trampoline" object: when called it replaces the object you have with the object you want. The module itself consists only of two AUTOLOADS: one with captures the constructor call, the other the first method call. The first class blesses a closure which creates the necessary object into the second class, which replces $_[0] with a new object and re-dispatches the call into the proper class.

Using an autoload as the constructor allows Object::Trampoline to use whatever constructor name the "real" class uses without having to pass it as another argument.

Delayed construction

Object::Trampoline uses whatever constructor the destination class calls (e.g., 'connect' for DBI) with the destination class is passed as the first argument.

Runtime classes

This can also be handy for specifying a handler class via config or command-line arguments since the final class is passed as an argument. If various handler classes share a constructor name then the first argument to Object::Trampoline can be determined at runtime:

This is useful when the constructor arguments themselvese are expensive to arrive at but the handler object must be defined in advance. This allows $mailer to be defined even if the constructor arguments are not available (or the construced class require-ed) yet.

Note that $mailconst has nothing to do with Object::Trampoline, but must be accessble to a $mailclass object.

Handle Catalogs

There are times when centeralizing the construction of a few standard handles into a single module seems helpful: all of the configuration issues can be pushed into a single place and anyone who uses the module can get access to some set of standard resources. The obvious downside to this is having to construct all of the objects.

Trampoline objects overcome this by not constructing anything [expensive] until it is really kneaded. Thus, a single "channel catalog" can be pushed into a single module (or small set of them).

At this point anyonen can use Our::Channel::Catalog and have immeidate access to the standard handles (which have their default values and list pushed into the revision control system).

A more realistic use of this puts the construction parameters into, say, LDAP (e.g., RH Directory) for shared use. The module can then isolate all the configuration issues into one place.

Combined with FindBin::libs and NEXT::init a group can inherit the necessary channels into a local catalog that varies by project or module. One way to handle this is a collection of default channel modules that are collected together via use base and NEXT::init into project-specific blocks of handles. This gives projects the flexability to generate a stock set of available handles without the overhead of fully instantiating them all for each piece of code that uses any of them.

Debugging with restricted resources.

There are times when objects must bind ports, access unique-login services, or otherwise compete from single- use resources. Trampoline objects can help here: by delaying the resource use until something is actually done with the object they allow debugging of startup issues. Obviously at some point there may be a resource collision, but at least this delays things until the last possible time.

Avoiding "circular use" situations

When multiple layers of inheritence are used to build up the metadata for an object there are times when the layering gets complicated. It may be that all of the data is where it needs to be by the time objects are created, but the "use" pragma is in an implicit BEGIN block, which can cause startup errors.

Object::Trampoline::Use delays using the constructing class until the object is actually used. This allows any configuration to be handled in time, even if some of the configuration involves objects from the included classes.

Say that a project's Defaults.pm reads the command line and folds in static default values. After that Channels.pm creates various channel object using the configuration values. Another overall configuration module might then use the Defaults and Channels to get its list of exported values.

Problems start when modules implementing the channels use values from the configuration module, leading to a circular use situation at startup.

Object::Trampoline::Use avoids this issue by allowing the channel objects to be configured in lower-level modules, with their class import and instantiation delayed until the objects are used. The configuration module can merrily hand out channel objects, who'se classes will not be use-ed until after the configuration stack is complete.

KNOWN BUGS

Not a bug, really, but if your constructor has side effects (e.g., opening log files) then delaying the construction will delay the side effects. Net result is that the side effects may have to migrate into the import where feasable or you just have to wait for the side effects to show up when the object is really used.

Object::Trampoline does not use any classes for itself. This puts the onus of use, require, or do of the module defining the class onto the caller.

Object::Trampoline::Use will use the module in the caller's package, but the use will be delayed until an object is use-ed. This will delay side effects of import calls (see previous item).

There is no way to pass arguments to the use call for a class in Object::Trampoline::Use. If that is necessary then either use the class with Object::Trampoline or write a wrapper class whose constructor uses the class with the appropriate arguments.

If your code depends on side-effects of the constructor manipulating the values in @_ then this module will not work for you: the call stack is copied to a lexical in order to create the constructor closure. This will not be a problem for any module I know of.

AUTHOR

Steven Lembark <lembark@wrkhors.com>

Module Install Instructions

To install Object::Trampoline, simply copy and paste either of the commands in to your terminal