getting-started
Getting started
This page walks you through defining your first factory, reading it back from the registry, and resetting state between tests.
Install
zef install --/test ORM::FactoryDefining a factory
define opens a definition block. Inside, the topic ($_) is
bound to a DefinitionBuilder, so DSL calls use the leading-dot shorthand:
use ORM::Factory;
define {
.factory: 'user', {
.fname: 'Greg';
.lname: 'Donald';
.email: { '[email protected]' };
};
};A static attribute takes any non-Callable value: .fname: 'Greg'. A
dynamic attribute takes a single Callable and is evaluated lazily:
.email: { '[email protected]' }.
Class inference and overrides
ORM::Factory camelizes the factory name ('user' ā User, 'super-admin' ā
SuperAdmin, 'team_lead' ā TeamLead) and looks the result up in GLOBAL.
A top-level class User { ⦠} in any compunit will be found.
When the camelized name doesn't match your class ā or the class lives in a namespace ā pass it explicitly:
define {
.factory: 'super-admin', :class(MyApp::Admin), {
.role: 'admin';
};
};To turn off name-based inference entirely (e.g. you only want explicit classes):
ORM::Factory.set-allow-class-lookup(False);lookup-class on a FactoryDefinition raises
X::ORM::Factory::UnknownClass if the class can't be resolved and no
override was supplied.
behave EVALs each spec file.
our class Foo { ⦠}declared during EVAL does not land inGLOBALthe way a normal compunit does, so name inference may miss it. In behave specs, either bind explicitly (BEGIN GLOBAL::<Foo> := Foo;) or pass:class(Foo)per factory.
Aliases
A factory can be registered under additional names:
define {
.factory: 'user', :aliases<author commenter>, {
.fname: 'Greg';
};
};
say ORM::Factory.factory-by-name('author').name; # 'user'
say ORM::Factory.aliases<commenter>; # 'user'Aliases collide with both factory names and other aliases ā the
duplicate-registration guard raises X::ORM::Factory::DuplicateAlias if you
try to claim a name that's already in use.
Reading the registry
Definitions land in a process-global registry keyed by name:
my %factories = ORM::Factory.factories; # all definitions
my $user = ORM::Factory.factory-by-name('user');
say $user.name; # 'user'
say $user.attributes.elems; # 3
say $user.attributes[0].name; # 'fname'
say $user.attributes[0].value; # 'Greg'Looking up an unregistered name raises X::ORM::Factory::UnknownFactory.
Variants
A variant is an alternate set of overrides scoped to one factory. Define
one with .variant, apply it inside the same factory body with a bare
leading-dot call:
define {
.factory: 'user', {
.fname: 'Greg';
.variant: 'admin', {
.role: 'admin';
};
.admin;
};
};The .admin call resolves against the registered variant name first. If the
name isn't a registered variant, it falls back to capturing an attribute
named admin.
Names that collide with the DSL
DSL method names ā factory, variant, transient, association,
add-attribute, before, after, initialize-with, to-create, modify,
skip-create ā cannot be captured by the FALLBACK shortcut. Use the explicit
add-attribute escape hatch:
.add-attribute: 'factory', 'acme'; # attribute literally named "factory"
.add-attribute: 'block', -> { 42 }; # a Callable literal as a static valueadd-attribute skips the FALLBACK indirection and stores exactly what you
pass: a Callable arg is treated as dynamic, anything else as static, with
no chance of colliding with a method name on the builder.
Resetting between tests
ORM::Factory.reload clears the registry. Run it before each test to keep
suites from leaking definitions:
use BDD::Behave;
use ORM::Factory;
describe 'my feature', {
before-each {
ORM::Factory.reload;
}
# ā¦
};For prove6, just call ORM::Factory.reload at the top of each test block.
See Tests for the full convention.
Diagnostics
The library raises typed exceptions under X::ORM::Factory::*:
| Exception | Trigger |
|---|---|
X::ORM::Factory::UnknownFactory | factory-by-name on an unregistered name. |
X::ORM::Factory::DuplicateFactory | Two .factory 'x', ⦠calls with the same name. |
X::ORM::Factory::DuplicateVariant | Two .variant 'x', ⦠calls in the same factory. |
X::ORM::Factory::UsageError | Argument shape the DSL can't make sense of (e.g. a variant applied with arguments). |