Repository navigation
Fold definition hierarchy into Factory - #1847
Draft
neilvcarvalho wants to merge 1 commit into
Draft
neilvcarvalho wants to merge 1 commit into
neilvcarvalho wants to merge 1 commit into
Conversation
neilvcarvalho
added this pull request to stack #1849
October 6, 2026 14:48
neilvcarvalho
marked this pull request as draft
October 6, 2026 14:51
This was referenced Oct 6, 2026
neilvcarvalho
force-pushed
the
nc/definition-hierarchy-fold
branch
from
October 6, 2026 15:31
b44083d to
9bf728a
Compare
Resolving a factory's callbacks, constructor and to_create through its parent chain built one anonymous subclass of DefinitionHierarchy per factory and relied on Ruby method lookup and super() to perform the inheritance. The chain was hard to follow, had to be rebuilt on every clone, and NullFactory existed partly to terminate it. Replace the class chain with three methods on Factory that fold over the parent: callbacks are the parent's callbacks followed by the definition's own, and the constructor and to_create are the definition's own or, failing that, the parent's. NullFactory ends the recursion with the global values from Internal, which is exactly what the root DefinitionHierarchy delegated to before. This deletes DefinitionHierarchy, the hierarchy_class, hierarchy_instance and build_hierarchy methods on Factory, and the hierarchy-related delegations on NullFactory. Behavior is unchanged: callbacks still run in global, ancestor, then own order, and to_create and initialize_with still resolve from the nearest definition outward. This is the first step of a sequence of refactors toward a compiled, non-cloning core.
neilvcarvalho
force-pushed
the
nc/definition-hierarchy-fold
branch
from
October 13, 2026 13:15
9bf728a to
3d5cecb
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
This is the first of a sequence of refactors toward a compiled, non-cloning core. Each step is meant to leave the acceptance suite green and delete machinery that later steps no longer need.
What changed
Resolving a factory's callbacks, constructor and
to_createthrough its parent chain used to build one anonymous subclass ofDefinitionHierarchyper factory and rely on Ruby method lookup andsuper()to perform the inheritance. This replaces that with three methods onFactorythat fold over the parent:NullFactoryends the recursion by answering withInternal.callbacks,Internal.constructorandInternal.to_create, which is exactly what the rootDefinitionHierarchydelegated to before. Callback order is unchanged: global callbacks first, then each ancestor's in parent-to-child order, then the factory's own.definition.callbackson a compiled definition already includes callbacks aggregated from traits, so no trait handling moves.Deleted
lib/factory_bot/definition_hierarchy.rband itsrequireinlib/factory_bot.rb.Factory#hierarchy_class,Factory#hierarchy_instance,Factory#build_hierarchy, thebuild_hierarchycall inFactory#compile, and the@hierarchy_class/@hierarchy_instanceresets inFactory#initialize_copy.NullFactory#hierarchy_classand thecallbacks,constructorandto_createdelegations onNullFactory. The spec forNullFactoryis updated to match.Public behavior is unchanged. No acceptance spec or feature was edited.
Verification
All runs on Ruby 4.0.1.
bundle exec standardrb: no offenses.bundle exec rake all_specs(default Gemfile): 334 unit examples, 445 acceptance examples, 4 Cucumber scenarios, all passing.BUNDLE_GEMFILE=gemfiles/main.gemfile bundle exec rake all_specs: same counts, all passing.BUNDLE_GEMFILE=gemfiles/7.0.gemfile bundle exec rake all_specs: unit and Cucumber passing. The acceptance suite passed in 12 of 14 runs; the two failures were both this timing assertion:That example asserts a strictly positive duration for the
factory_bot.compile_factorynotification, whichDefinition#compileemits with no block, so the measured span contains no factory code at all. On ActiveSupport 7.0 the subscriber uses wall-clockTime.now, and a direct measurement of that span in a warm process reports zero about half the time on bothmainand this branch (1562 of 3000 samples onmain, 1702 of 3000 here). It passes in a fresh process because the first notification is slower (0 zero durations in 60 fresh processes on either checkout).mainhappened to pass 15 of 15 full acceptance runs under the same gemfile, so the flake rate is low in practice, but it is a property of the spec rather than of this change. I did not loosen the spec.