Skip to content

Commit de4bdb6

Browse files
Allow inline sequences in traits to have same name
Fixes #1257 When sequence rewinding was first introduced in #1078 it only applied to globally defined sequences. To get rewinding to work for inline sequences as well we registered them "privately" in the global registry in #1164. Unfortunately in #1164 we did not take inline sequences inside traits into consideration. Since trait names are not unique, it is possibly to get a `FactoryBot::DuplicateDefinitionError` when defining two sequences that have the same name in two traits that have the same name. This PR abandons the idea of "privately" registering inline sequences, and instead keeps a separate list of all the inline sequences just for the purpose of rewinding them. The downside here is that we are adding another public method to FactoryBot. But I think it is similar enough to the other `register_` methods, which are technically public but are left undocumented and not really treated as part of the FactoryBot API.
1 parent 0a07206 commit de4bdb6

File tree

5 files changed

+53
-8
lines changed

5 files changed

+53
-8
lines changed

lib/factory_bot.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def self.lint(*args)
7575

7676
class << self
7777
delegate :factories,
78+
:inline_sequences,
7879
:sequences,
7980
:traits,
8081
:callbacks,
@@ -108,12 +109,17 @@ def self.register_sequence(sequence)
108109
sequence
109110
end
110111

112+
def self.register_inline_sequence(sequence)
113+
inline_sequences.push(sequence)
114+
end
115+
111116
def self.sequence_by_name(name)
112117
sequences.find(name)
113118
end
114119

115120
def self.rewind_sequences
116121
sequences.each(&:rewind)
122+
inline_sequences.each(&:rewind)
117123
end
118124

119125
def self.register_trait(trait)

lib/factory_bot/configuration.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
module FactoryBot
22
# @api private
33
class Configuration
4-
attr_reader :factories, :sequences, :traits, :strategies, :callback_names
4+
attr_reader(
5+
:callback_names,
6+
:factories,
7+
:inline_sequences,
8+
:sequences,
9+
:strategies,
10+
:traits,
11+
)
512

613
def initialize
714
@factories = Decorator::DisallowsDuplicatesRegistry.new(Registry.new("Factory"))
@@ -10,6 +17,7 @@ def initialize
1017
@strategies = Registry.new("Strategy")
1118
@callback_names = Set.new
1219
@definition = Definition.new(:configuration)
20+
@inline_sequences = []
1321

1422
to_create(&:save!)
1523
initialize_with { new }

lib/factory_bot/definition_proxy.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,8 @@ def method_missing(name, *args, &block) # rubocop:disable Style/MethodMissing
117117
#
118118
# Except that no globally available sequence will be defined.
119119
def sequence(name, *args, &block)
120-
sequence_name = "__#{@definition.name}_#{name}__"
121-
sequence = Sequence.new(sequence_name, *args, &block)
122-
FactoryBot.register_sequence(sequence)
120+
sequence = Sequence.new(name, *args, &block)
121+
FactoryBot.register_inline_sequence(sequence)
123122
add_attribute(name) { increment_sequence(sequence) }
124123
end
125124

spec/acceptance/sequence_resetting_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,34 @@
9090
expect(user.email).to eq "[email protected]"
9191
expect(email).to eq "[email protected]"
9292
end
93+
94+
it "allows setting sequences within identically named traits" do
95+
define_class("User") { attr_accessor :email }
96+
define_class("Person") { attr_accessor :email }
97+
98+
FactoryBot.define do
99+
factory :user do
100+
trait :with_email do
101+
sequence(:email) { |n| "user#{n}@example.com" }
102+
end
103+
end
104+
105+
factory :person do
106+
trait :with_email do
107+
sequence(:email) { |n| "person#{n}@example.com" }
108+
end
109+
end
110+
end
111+
112+
build_list(:user, 2, :with_email)
113+
build_list(:person, 2, :with_email)
114+
115+
FactoryBot.rewind_sequences
116+
117+
user = build(:user, :with_email)
118+
person = build(:person, :with_email)
119+
120+
expect(user.email).to eq "[email protected]"
121+
expect(person.email).to eq "[email protected]"
122+
end
93123
end

spec/factory_bot/definition_proxy_spec.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,18 +106,20 @@ def build_proxy(factory_name)
106106

107107
it "creates a new sequence starting at 1" do
108108
proxy = build_proxy(:factory)
109+
109110
proxy.sequence(:sequence)
110111

111-
expect(FactoryBot::Sequence).to have_received(:new).
112-
with("__factory_sequence__")
112+
expect(FactoryBot::Sequence).to have_received(:new).with(:sequence)
113113
end
114114

115115
it "creates a new sequence with an overridden starting vaue" do
116116
proxy = build_proxy(:factory)
117-
proxy.sequence(:sequence, "C")
117+
override = "override"
118+
119+
proxy.sequence(:sequence, override)
118120

119121
expect(FactoryBot::Sequence).to have_received(:new).
120-
with("__factory_sequence__", "C")
122+
with(:sequence, override)
121123
end
122124

123125
it "creates a new sequence with a block" do

0 commit comments

Comments
 (0)