Skip to content

Migrating from factory_bot

ORM::Factory is a Raku port of Ruby's factory_bot. Most concepts carry over one-for-one; this page lists the cases where the spelling changed.

Module and DSL entry point

factory_bot ORM::Factory
FactoryBot.define { ... } define { ... }
FactoryBot.modify { ... } ORM::Factory.modify: { ... }
FactoryBot.lint ORM::Factory.lint
factory :user do ... end .factory: 'user', { ... }

Names are Raku strings, not Ruby symbols: build(:user) becomes build('user').

Method-name conventions

Ruby snake_case becomes Raku kebab-case:

factory_bot ORM::Factory
build_stubbed build-stubbed
attributes_for attributes-for
to_create to-create
initialize_with initialize-with
skip_create skip-create
rewind_sequences rewind-sequences
register_strategy register-strategy

Ruby ! becomes -or-die; Ruby ? becomes is-:

factory_bot ORM::Factory
save! save-bang
persisted? is-persisted
new_record? is-new-record
valid? is-valid

Traits / variants

factory_bot's trait is renamed variant throughout, partly to free up the word "trait" (which means something specific in Raku) and partly because "variant" reads more clearly:

factory_bot ORM::Factory
trait :admin do ... end .variant: 'admin', { ... }
traits_for_enum :status .variants-for-enum: 'status', @values
automatically_define_enum_traits automatically-define-enum-variants (on by default; derives variants from an ORM::ActiveRecord enum)

Blocks

Ruby blocks with block-locals become Raku pointy blocks:

1
sequence(:email) { |n| "user#{n}@example.com" }
1
.sequence: 'email', -> $n { "user{$n}\@example.com" };

Class lookup

factory_bot infers the model class from the factory name and an class: option lets you override it. ORM::Factory does the same, but the toggle is explicit:

1
2
.factory: 'admin', :class(User), { ... };
ORM::Factory.set-allow-class-lookup(False);   # turn off auto-resolution

Persistence

factory_bot is tightly coupled to ActiveRecord. ORM::Factory defers to the persistence adapter protocol; ActiveRecord is one implementation (auto-detected when loaded). With no ORM installed, the generic adapter handles plain Raku classes.

X::RecordInvalid (raised from create) is the AR exception, the same as factory_bot's ActiveRecord::RecordInvalid.

What is intentionally missing

  • Data generationfactory_bot does not ship a Faker either. Use a Raku faker / data-generator next to it (see the cookbook).
  • Linting strategies beyond build and createfactory_bot lints create by default; ORM::Factory does the same and accepts :strategy<build> for the same reason.
  • reload of definitions on file changeORM::Factory.reload clears the registry, but there is no file-watcher; you re-run find-definitions yourself.

A side-by-side example

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# factory_bot
FactoryBot.define do
  sequence(:email) { |n| "user#{n}@example.com" }

  factory :user do
    fname { "Greg" }
    email { generate(:email) }

    trait :admin do
      role { "admin" }
    end
  end

  factory :post do
    title { "Hello" }
    author factory: :user, role: "admin"
  end
end

FactoryBot.create(:user, :admin)
FactoryBot.build_stubbed(:post)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# ORM::Factory
define {
  .sequence: 'email', -> $n { "user{$n}\@example.com" };

  .factory: 'user', {
    .fname: 'Greg';
    .email: { ORM::Factory.generate('email') };

    .variant: 'admin', {
      .role: 'admin';
    };
  };

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

ORM::Factory.create('user', 'admin');
ORM::Factory.build-stubbed('post');