Cookbook
Common recipes for ORM::Factory, each self-contained.
Sequences and unique attributes
Sequences keep unique-violating attributes unique across builds without
hard-coding values:
| define {
.sequence: 'email', -> $n { "user{$n}\@example.com" };
.factory: 'user', {
.fname: 'Greg';
.email: { ORM::Factory.generate('email') };
};
};
ORM::Factory.build('user').email; # user1@example.com
ORM::Factory.build('user').email; # user2@example.com
|
For per-factory isolation, declare the sequence inline:
| .factory: 'user', {
.sequence: 'serial';
.email: { "user{$.serial}\@example.com" };
};
|
Variants and variant composition
1
2
3
4
5
6
7
8
9
10
11
12
13 | define {
.factory: 'user', {
.fname: 'Greg';
.role: 'user';
.variant: 'admin', { .role: 'admin' };
.variant: 'active', { .active: True };
.variant: 'super-admin', { admin; active }; # composes other variants
};
};
ORM::Factory.create('user', 'admin', 'active'); # apply at build time
ORM::Factory.create('user', 'super-admin'); # one variant pulling two more
|
Associations
Implicit (attribute name matches a factory):
| define {
.factory: 'user', { .fname: 'Greg' };
.factory: 'post', { .title: 'Hi'; .user }; # bare reference
};
|
Explicit, with overrides and variants:
| .factory: 'post', {
.title: 'Hi';
.association: 'author', :factory<user>, 'admin', :fname<Pat>;
};
|
has_many via callback + transient count:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | .factory: 'user', {
.fname: 'Greg';
.transient: {
.post-count: 0;
};
.after: 'create', -> $u, $e {
ORM::Factory.create-list('post', $e.post-count, :author($u))
if $e.post-count > 0;
};
};
ORM::Factory.create('user', :post-count(3));
|
Transient attributes and the evaluator
Transient attributes are visible to dynamic attributes and callbacks but never
make it into the persisted attribute hash:
| .factory: 'user', {
.transient: {
.password: 'plaintext';
};
.password-digest: { Digest.sha1($.password) };
.after: 'build', -> $u, $e {
$u.session-token = "token-{$e.password}";
};
};
|
Custom strategies, to-create, and initialize-with
Per-factory persistence override:
| .factory: 'audit-event', {
.name: 'view';
.to-create: -> $event, $eval {
AuditLog.append($event); # writes to a flat file, not the DB
};
};
|
A registered custom strategy:
| class JsonStrategy does ORM::Factory::Strategy {
method to-sym { 'json' }
method result($eval) { to-json($eval.attributes-hash) }
method association($name, @v, %o) { ORM::Factory.build($name, |@v, |%o) }
}
ORM::Factory.register-strategy('json', JsonStrategy);
ORM::Factory.json('user'); # returns a JSON string
|
Using ORM::Factory with no ORM
Plain Raku classes work with the generic adapter:
1
2
3
4
5
6
7
8
9
10
11
12
13 | class User {
has Str $.fname is rw;
method save-bang { self } # or .save, or omit if you only build
}
define {
.factory: 'user', :class(User), {
.fname: 'Greg';
};
};
ORM::Factory.build('user'); # User.new(fname => 'Greg')
ORM::Factory.create('user'); # then .save-bang
|
For a non-AR, non-trivial persistence layer, write a custom adapter — see
Persistence.
Pairing with a data-generation library
ORM::Factory deliberately ships no fake-data generator. Pair it with
whatever you like:
| use Fake::Person; # any Faker-equivalent
define {
.sequence: 'email-id';
.factory: 'user', {
.fname: { Fake::Person.first-name };
.lname: { Fake::Person.last-name };
.email: { Fake::Person.email(:id(ORM::Factory.generate('email-id'))) };
};
};
|
Sequences (for guaranteed uniqueness) and the data generator (for realism)
play together cleanly: use the sequence to scope the random value.
See Concurrency and Performance for the
guarantees and the regression guards.