Skip to content

Bare-name DSL

ORM::Factory::DSL is an opt-in module that defines factories without the leading-dot method-call syntax. It is plain Raku (exported subs over a dynamic builder), so it carries no slang, no macro, no precompilation risk, and full editor tooling. The canonical dotted form is unchanged and remains the default.

Use this module instead of ORM::Factory; it re-exports the build and query helpers, so it is self-contained.

1
use ORM::Factory::DSL;

The three forms

All three define the same factory and dispatch to the same builders. Pick per taste; they can be mixed in one block.

Canonical dotted form (use ORM::Factory):

1
2
3
4
5
6
7
define {
  .factory: 'user', {
    .fname: 'Greg';
    .email: { generate('email') };
    .variant: 'admin', { .role: 'admin' };
  };
};

Bare-name keyword form (use ORM::Factory::DSL):

1
2
3
4
5
6
7
define {
  factory 'user', {
    attr 'fname', 'Greg';
    attr 'email', { generate('email') };
    variant 'admin', { attr 'role', 'admin' };
  };
};

Colon-pair form (also ORM::Factory::DSL):

1
2
3
4
5
6
7
8
define {
  factory 'user', {
    attrs(
      :fname<Greg>,
      :email({ generate('email') }),
    );
  };
};

Keywords

Every DSL keyword is available as a bare sub: factory, variant, transient, sequence, association, before, after, callback, to-create, initialize-with, skip-create, variants-for-enum, plus define and modify. They dispatch to the active builder, so nested blocks (factory, variant, transient) retarget correctly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
define {
  sequence 'email', -> $n { "user{$n}\@example.com" };

  factory 'user', {
    attr 'fname', 'Greg';
    attr 'email', { generate('email') };

    variant 'admin', { attr 'role', 'admin' };

    after 'build', -> $user, $ { $user.role //= 'member' };
  };

  factory 'post', {
    attr 'title', 'Hello';
    association 'author', :factory<user>;
  };
};

build('user', 'admin').role;     # 'admin'
build('post').author.fname;      # 'Greg'

Attributes: attr vs attrs

attr NAME, VALUE is the explicit setter. A Callable value is a dynamic (lazily evaluated) attribute, the same as the dotted .name: { ... } form. attr preserves declaration order, so it is the form to use for a dependent chain that must read in a fixed order:

1
2
3
4
factory 'user', {
  attr 'fname', 'Greg';
  attr 'slug',  { $_.fname.lc };   # reads a prior attribute
};

attrs(:NAME<VALUE>, ...) takes a colon-pair list. A Callable value is again a dynamic attribute. Because the pairs arrive as named arguments, their order is not preserved across the list; use sequential attr calls when order matters.

1
2
3
4
5
6
7
factory 'user', {
  attrs(
    :fname<Greg>,
    :lname<Donald>,
    :email({ generate('email') }),
  );
};

A dynamic value must be a flexible-arity { ... } block, not a strict -> { ... }, because the evaluator passes the block an argument (the same rule as the dotted form).

Building

The build and query helpers are re-exported, so no separate use ORM::Factory is needed: build, create, build-stubbed, attributes-for, the -list and -pair variants, generate, generate-list, and reload.