Skip to content

Commit

Permalink
BUGFIX: Parent/Child implicit trait results depend on build order. (#…
Browse files Browse the repository at this point in the history
…1717)

- When a parent with an implicit trait is built at the same time as a child
that inherits, and calls, that trait, the result changes based on which is built first.

- This commit ensures all implicit traits are run within the correct context.
  • Loading branch information
CodeMeister authored Jan 31, 2025
1 parent 9ff844c commit d69f305
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 2 deletions.
4 changes: 4 additions & 0 deletions lib/factory_bot/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ def define_trait(trait)
@defined_traits.add(trait)
end

def defined_traits_names
@defined_traits.map(&:name)
end

def register_enum(enum)
@registered_enums << enum
end
Expand Down
12 changes: 10 additions & 2 deletions lib/factory_bot/factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ def initialize(name, options = {})
end

delegate :add_callback, :declare_attribute, :to_create, :define_trait, :constructor,
:defined_traits, :inherit_traits, :append_traits, to: :@definition
:defined_traits, :defined_traits_names, :inherit_traits, :append_traits,
to: :@definition

def build_class
@build_class ||= if class_name.is_a? Class
Expand Down Expand Up @@ -85,7 +86,7 @@ def names
def compile
unless @compiled
parent.compile
parent.defined_traits.each { |trait| define_trait(trait) }
inherit_parent_traits
@definition.compile(build_class)
build_hierarchy
@compiled = true
Expand Down Expand Up @@ -153,6 +154,13 @@ def parent
end
end

def inherit_parent_traits
parent.defined_traits.each do |trait|
next if defined_traits_names.include?(trait.name)
define_trait(trait.clone)
end
end

def initialize_copy(source)
super
@definition = @definition.clone
Expand Down
4 changes: 4 additions & 0 deletions lib/factory_bot/trait.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ def initialize(name, &block)
end
end

def clone
Trait.new(name, &block)
end

delegate :add_callback, :declare_attribute, :to_create, :define_trait, :constructor,
:callbacks, :attributes, :klass, :klass=, to: :@definition

Expand Down
26 changes: 26 additions & 0 deletions spec/acceptance/traits_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
gender { "Female" }
end

trait :make_it_great do
great
end

factory :great_user do
great
end
Expand All @@ -50,6 +54,12 @@
end
end

factory :greatest_user do
trait :great do
great { "GREATEST EVER!!!" }
end
end

factory :admin, traits: [:admin]

factory :male_user do
Expand Down Expand Up @@ -177,6 +187,22 @@
end
end

context "factory with implicit traits called by child" do
it "calls the correct trait when parent built first" do
user = FactoryBot.create(:user, :make_it_great)

expect(user.great).to eq "GREAT!!!"
end

it "calls the correct trait when child built first" do
greatest = FactoryBot.create(:greatest_user, :make_it_great)
user = FactoryBot.create(:user, :make_it_great)

expect(user.great).to eq "GREAT!!!"
expect(greatest.great).to eq "GREATEST EVER!!!"
end
end

context "child factory created where trait attributes are inherited" do
subject { FactoryBot.create(:child_male_user) }
its(:gender) { should eq "Male" }
Expand Down

0 comments on commit d69f305

Please sign in to comment.