construction
Construction & persistence customisation
ORM::Factory is ORM-agnostic at its core ā every build strategy routes
instantiation and persistence through the Persistence adapter.
Three hooks override that adapter when the default behaviour does not fit:
initialize-withā replaces the constructor (theinstantiatecall).to-createā replaces the persistence (thepersistcall oncreate).skip-createā turnscreateintobuild(no persistence at all).
Each hook can be set per-factory (inside the factory block), globally
(at the top level of define), or both. Per-factory wins over
global; child factories inherit the parent hook but can override it.
initialize-with
By default build / create / build-stubbed instantiate via the
adapter's instantiate, which calls $class.new(|%attrs). Override that with
a per-factory hook:
define {
.factory: 'user', {
.fname: 'Greg';
.role: 'member';
.initialize-with: -> $eval {
User.new(|$eval.attributes, :via('hook'));
};
};
};
ORM::Factory.build('user').via; # 'hook'The block receives the evaluator and returns the instance.
attributes-for deliberately bypasses the hook ā it returns a plain hash.
The attributes helper
Inside the hook, $eval.attributes is the resolved persisted-attribute hash
(transients and association attributes excluded ā the same view
attributes-for returns). Use it to delegate to a constructor that expects
positional args or a different keyword shape:
.initialize-with: -> $eval {
User.from-hash($eval.attributes);
};Global initialize-with
A top-level initialize-with applies to every factory that has no
per-factory hook:
define {
.initialize-with: -> $eval {
$eval.factory.lookup-class.new(|$eval.attributes, :via('global'));
};
.factory: 'user', { .fname: 'Greg' };
};
ORM::Factory.build('user').via; # 'global'ORM::Factory.global-initialize-with returns the registered global hook;
ORM::Factory.reload clears it.
to-create
to-create replaces the persistence step in create. The default adapter
calls .save-bang (or .save) on the instance; the hook takes the instance
and the evaluator and is responsible for persisting it however the target
store wants:
define {
.factory: 'document', {
.title: 'Hello';
.to-create: -> $doc, $eval {
$mongo.insert($doc.title);
$doc;
};
};
};before-create fires before to-create; after-create fires after. build
and build-stubbed never consult to-create.
Global to-create
define {
.to-create: -> $instance, $eval {
$persistence-bus.send($instance);
};
.factory: 'user', { .fname: 'Greg' };
};ORM::Factory.global-to-create returns the registered global hook;
ORM::Factory.reload clears it.
skip-create
skip-create makes create behave like build ā no persistence happens, but
every callback still fires:
define {
.factory: 'ephemeral', {
.title: 'in-memory only';
.skip-create;
};
};
ORM::Factory.create('ephemeral').saved; # falsy ā never persistedA common shape is a global skip-create plus the few factories that do
persist via an explicit to-create:
define {
.skip-create;
.factory: 'log-event', { .text: 'noop' };
.factory: 'invoice', {
.total: 0;
.to-create: -> $inv, $eval { $billing.commit($inv); }; # overrides global skip
};
};ORM::Factory.global-skip-create returns the global flag (or undefined);
ORM::Factory.reload clears it.
Resolution order
For each build, the hook is resolved by walking child ā parent ā global. The first match wins:
The factory's own hook;
each parent factory's hook, child-first;
the global hook from
define;the adapter default (
instantiate/persist).
For create, to-create and skip-create share the same chain ā whichever
appears first wins, so a child to-create can override a parent skip-create
(or vice versa).
Interaction with callbacks
The callback timeline is the same as the default path, with the hooks substituted in:
| Strategy | Step 1 | Step 2 | Step 3 | Step 4 |
|---|---|---|---|---|
build | initialize-with | after build | ā | ā |
create | initialize-with | after build | before createā persistence (to-create / skip-create / adapter) ā after create | |
build-stubbed | initialize-with | stub (adapter) | after stub | ā |
attributes-for | (bypassed ā returns the attribute hash directly) |