NAME
Concierge::Desk::DeferredComponent - Stand-in that defers a 'defer' component's real construction until first use
VERSION
v0.13.0
SYNOPSIS
my $comp = Concierge::Desk::DeferredComponent->new(
name => 'reports',
class => 'Concierge::Reports',
payload => $payload,
);
# Nothing has been built yet. The first real method call triggers
# Concierge::Reports->new($payload) and caches the result:
my $result = $comp->get_signal_report('x');
# Subsequent calls delegate directly to the cached real object --
# no second construction attempt:
my $result2 = $comp->get_signal_report('y');
DESCRIPTION
Concierge::Desk::DeferredComponent is installed by "open_desk" in Concierge in place of a component marked defer => 1 in the desk's components config block, once that component has passed its open-time probe() check (see "probe_component" in Concierge::Desk::Component). It defers the component's real, potentially expensive new($payload) call until the first actual method call, then caches the resulting object (or, on failure, an Concierge::Desk::UnavailableComponent stand-in) and delegates every call -- including that first one -- to it from then on.
Modeled directly on Concierge::Desk::UnavailableComponent: same AUTOLOAD-based shape, opposite direction. UnavailableComponent permanently fails every call from the start; DeferredComponent builds the real thing on first use and then behaves exactly as if that real object had been installed at open_desk() time all along.
There is no self-replacement of $concierge->{$name} -- the proxy stays in place permanently and caches+delegates internally. This keeps the mechanism simple; the overhead of one extra method-forwarding call is negligible next to what a real component's methods typically do.
If the deferred build fails on first use, that failure is permanent for the life of this instance -- no retry is ever attempted, matching UnavailableComponent's existing precedent exactly. A fresh process (a new open_desk() call) re-attempts probing and deferred construction from scratch.
METHODS
new
my $comp = Concierge::Desk::DeferredComponent->new(
name => $component_name,
class => $component_class,
payload => $payload,
);
Constructor. Stores name (the component's key in the desk's components config), class (the component's real class name), and payload (exactly what will be passed to the real class's new() on first use) on the blessed object. Does not build or require the real class -- that happens lazily, in AUTOLOAD, on first method call.
AUTOLOAD
Any method call other than DESTROY is caught by AUTOLOAD. On the first such call, it requires class and calls $class->new($payload):
On success, the resulting object is cached and the original call is delegated to it.
On failure, a Concierge::Desk::UnavailableComponent stand-in is built and cached instead (never a live crash), and the original call is delegated to that -- returning the standard
{ success => 0, message => "Component '$name' unavailable: ..." }failure hashref.
Every subsequent call, regardless of outcome, delegates directly to the cached object -- the real component is never constructed more than once, and a failed first attempt is never retried.
DESTROY
Explicit no-op, so object teardown does not fall through to AUTOLOAD and inadvertently trigger the deferred build.
THE can()/isa() CAVEAT
Exactly as documented for Concierge::Desk::UnavailableComponent: AUTOLOAD intercepts calls to methods that don't otherwise exist, but it does not make can() or isa() report true for those methods, whether or not the real component has been built yet. Code that probes a component with can()/isa() before calling it will find the probe false on a DeferredComponent stand-in, even before any deferred build has occurred. The correct pattern, per "UNAVAILABLE COMPONENT SUBSTITUTION" in Concierge::Desk::Component, is to call the method directly and check $result->{success}, never to probe with can/isa first.
SEE ALSO
Concierge::Desk::Component -- the contract this class defers, and probe_component(), the shared helper used to revalidate a defer component before installing this stand-in.
Concierge::Desk::UnavailableComponent -- the stand-in this class delegates to internally if the deferred build itself fails.
Concierge -- see open_desk() for where this substitution happens.
AUTHOR
Bruce Van Allen <bva@cruzio.com>
LICENSE
This module is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0.