Allow the initial value of a sequence to be lazy loaded #1674
+130
−2
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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:
The way we initially solved this was to pass in a
LazyEnumeratorAdapter
instance (as defined in the PR) as the initial value, egsequence(: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 thelazy
arg name sets expectations about when this will be evaluated. Although it does result in the need for a whole extra class.