Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the initial value of a sequence to be lazy loaded #1674

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

ajesler
Copy link

@ajesler ajesler commented Jul 19, 2024

Allow the initial value of a sequence to be evaluated on first use

In order to be able to use factory_bot to generate seeds for a database where the database might not have the schema set up yet or might have data already populated, it is useful to be able to defer assigning the initial value to a sequence until it is needed.
Currently, factory_bot needs the initial value of the sequence to be defined when the factory file is loaded.

This lazy loading behaviour has been very handy for use when we need to create objects that have unique sequential id in formats specific to third party systems. We can use these factories in our development and CI environments without having to think about which values have already been used.

I found #1434 and thought it was worth submitting this PR.

This PR adds a lazy option to the sequence method, where a Proc can be passed that will be called the first time as sequence is used, to set the initial value.
After the initial #next call, these sequences behave exactly the same as any other factory_bot sequence.
Rewinding a lazy sequence uses the originally calculated value, it does not re-call the lazy Proc object.

Example usage:

FactoryBot.define do
  sequence(:email, lazy: -> {  User.count + 1 }) { "user#{_1}@example.com" }
end

generate(:email) # [email protected] if you already have 41 users

The way we initially solved this was to pass in a LazyEnumeratorAdapter instance (as defined in the PR) as the initial value, eg
sequence(:name, LazyEnumeratorAdaper.new { User.count }) { "User#{_1}" }.

This can be done without any changes to factory_bot, but I thought this functionality might be useful to others.

I'm happy to remove the lazy option and instead do detection on whether the initial arg is a Proc object, but I like that the lazy arg name sets expectations about when this will be evaluated. Although it does result in the need for a whole extra class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant