migrating-from-factory-bot

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_botORM::Factory
FactoryBot.define { ... }define { ... }
FactoryBot.modify { ... }ORM::Factory.modify: { ... }
FactoryBot.lintORM::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_botORM::Factory
build_stubbedbuild-stubbed
attributes_forattributes-for
to_createto-create
initialize_withinitialize-with
skip_createskip-create
rewind_sequencesrewind-sequences
register_strategyregister-strategy

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

factory_botORM::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_botORM::Factory
trait :admin do ... end.variant: 'admin', { ... }
traits_for_enum :status.variants-for-enum: 'status', @values
automatically_define_enum_traitsautomatically-define-enum-variants (on by default; derives variants from an ORM::ActiveRecord enum)

Blocks

Ruby blocks with block-locals become Raku pointy blocks:

sequence(:email) { |n| "user#{n}@example.com" }
.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:

.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 generation — factory_bot does not ship a Faker either. Use a Raku faker / data-generator next to it (see the cookbook).

  • Linting strategies beyond build and create — factory_bot lints create by default; ORM::Factory does the same and accepts :strategy<build> for the same reason.

  • reload of definitions on file change — ORM::Factory.reload clears the registry, but there is no file-watcher; you re-run find-definitions yourself.

A side-by-side example

# 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)
# 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');

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.