factory_bot 6.5.5 → 6.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ffc4ecdfaad4faf3f66a090e8a67f73d399e6df92245cf77907b6483cc168b6d
4
- data.tar.gz: f0445ea4b16381a1126775e0b127b3c4afd34ac23e9b0dd19930b84e267f8866
3
+ metadata.gz: 5268f67c49f294395edabe86ef2bf7bdd3644db872328f0134d832b8cbfe5396
4
+ data.tar.gz: 5e51702b4c819af99587ea2355b43b5770b228e1f0b81483b1bc9aa28806c630
5
5
  SHA512:
6
- metadata.gz: 7b9e9db68e4fc0a222b6229c75391d23bc5014dfadd2e356bc74f037537749c60ee52f0cf4038bff5e5ba7488e9f4623db13a3b0b8d6f6a91fed1c41b0d5faac
7
- data.tar.gz: d8002e39646803600784bb64cd1e611ecc0f056e2538a5496ca63d9804cb847582508b7558531fc5a15442077696de17316a7120abba8c1cbbe93cda71aa99b8
6
+ metadata.gz: 80f08c8d63755aa32390aadfd2b5c653915a2d8920b58d56b89a6091c029a0194a007b5f6c99277f94c175a0c0c83463ce24d5849268ac8d3ab76c059379116d
7
+ data.tar.gz: f092552d291c38644d02d77a73722b5966cc33397a1dcd06cb90164da65779c14f8a0352c77fd19890d30ccbe112d66a21c130865486da0b8059168e3f4a30e7
data/GETTING_STARTED.md CHANGED
@@ -17,9 +17,9 @@ at any time.
17
17
  Getting Started
18
18
  ===============
19
19
 
20
- * [Setup](#setup)
21
- + [Update Your Gemfile](#update-your-gemfile)
22
- + [Configure your test suite](#configure-your-test-suite)
20
+ - [Setup](#setup)
21
+ - [Update Your Gemfile](#update-your-gemfile)
22
+ - [Configure your test suite](#configure-your-test-suite)
23
23
  - [RSpec](#rspec)
24
24
  - [Test::Unit](#testunit)
25
25
  - [Cucumber](#cucumber)
@@ -27,76 +27,77 @@ Getting Started
27
27
  - [Minitest](#minitest)
28
28
  - [Minitest::Spec](#minitestspec)
29
29
  - [minitest-rails](#minitest-rails)
30
- * [Defining factories](#defining-factories)
31
- + [Factory name and attributes](#factory-name-and-attributes)
32
- + [Specifying the class explicitly](#specifying-the-class-explicitly)
33
- + [Hash attributes](#hash-attributes)
34
- + [Best practices](#best-practices)
35
- + [Definition file paths](#definition-file-paths)
36
- + [Static Attributes](#static-attributes)
37
- * [Using factories](#using-factories)
38
- + [Build strategies](#build-strategies)
39
- + [Attribute overrides](#attribute-overrides)
40
- + [`build_stubbed` and `Marshal.dump`](#build_stubbed-and-marshaldump)
41
- * [Aliases](#aliases)
42
- * [Dependent Attributes](#dependent-attributes)
43
- * [Transient Attributes](#transient-attributes)
44
- + [With other attributes](#with-other-attributes)
45
- + [With `attributes_for`](#with-attributes_for)
46
- + [With callbacks](#with-callbacks)
47
- + [With associations](#with-associations)
48
- * [Method Name / Reserved Word Attributes](#method-name--reserved-word-attributes)
49
- * [Inheritance](#inheritance)
50
- + [Nested factories](#nested-factories)
51
- + [Assigning parent explicitly](#assigning-parent-explicitly)
52
- + [Best practices](#best-practices-1)
53
- * [Associations](#associations)
54
- + [Implicit definition](#implicit-definition)
55
- + [Explicit definition](#explicit-definition)
56
- + [Inline definition](#inline-definition)
57
- + [Specifying the factory](#specifying-the-factory)
58
- + [Overriding attributes](#overriding-attributes)
59
- + [Association overrides](#association-overrides)
60
- + [Build strategies](#build-strategies-1)
61
- + [`has_many` associations](#has_many-associations)
62
- + [`has_and_belongs_to_many` associations](#has_and_belongs_to_many-associations)
63
- + [Polymorphic associations](#polymorphic-associations)
64
- + [Interconnected associations](#interconnected-associations)
65
- * [Sequences](#sequences)
66
- + [Global sequences](#global-sequences)
67
- + [With dynamic attributes](#with-dynamic-attributes)
68
- + [As implicit attributes](#as-implicit-attributes)
69
- + [Inline sequences](#inline-sequences)
70
- + [Initial value](#initial-value)
71
- + [Without a block](#without-a-block)
72
- + [Aliases](#aliases-1)
73
- + [Rewinding](#rewinding)
74
- + [Uniqueness](#uniqueness)
75
- * [Traits](#traits)
76
- + [Defining traits](#defining-traits)
77
- + [As implicit attributes](#as-implicit-attributes-1)
78
- + [Attribute precedence](#attribute-precedence)
79
- + [In child factories](#in-child-factories)
80
- + [Using traits](#using-traits)
81
- + [With associations](#with-associations-1)
82
- + [Traits within traits](#traits-within-traits)
83
- + [With transient attributes](#with-transient-attributes)
84
- + [Enum traits](#enum-traits)
85
- * [Callbacks](#callbacks)
86
- + [Default callbacks](#default-callbacks)
87
- + [Multiple callbacks](#multiple-callbacks)
88
- + [Global callbacks](#global-callbacks)
89
- + [Symbol#to_proc](#symbolto_proc)
90
- * [Modifying factories](#modifying-factories)
91
- * [Building or Creating Multiple Records](#building-or-creating-multiple-records)
92
- * [Linting Factories](#linting-factories)
93
- * [Custom Construction](#custom-construction)
94
- * [Custom Strategies](#custom-strategies)
95
- * [Custom Callbacks](#custom-callbacks)
96
- * [Custom Methods to Persist Objects](#custom-methods-to-persist-objects)
97
- * [ActiveSupport Instrumentation](#activesupport-instrumentation)
98
- * [Rails Preloaders and RSpec](#rails-preloaders-and-rspec)
99
- * [Using Without Bundler](#using-without-bundler)
30
+ - [Defining factories](#defining-factories)
31
+ - [Factory name and attributes](#factory-name-and-attributes)
32
+ - [Specifying the class explicitly](#specifying-the-class-explicitly)
33
+ - [Hash attributes](#hash-attributes)
34
+ - [Best practices](#best-practices)
35
+ - [Definition file paths](#definition-file-paths)
36
+ - [Static Attributes](#static-attributes)
37
+ - [Using factories](#using-factories)
38
+ - [Build strategies](#build-strategies)
39
+ - [Attribute overrides](#attribute-overrides)
40
+ - [build\_stubbed and Marshal.dump](#build_stubbed-and-marshaldump)
41
+ - [Aliases](#aliases)
42
+ - [Dependent Attributes](#dependent-attributes)
43
+ - [Transient Attributes](#transient-attributes)
44
+ - [With other attributes](#with-other-attributes)
45
+ - [With attributes\_for](#with-attributes_for)
46
+ - [With callbacks](#with-callbacks)
47
+ - [With associations](#with-associations)
48
+ - [Method Name / Reserved Word Attributes](#method-name--reserved-word-attributes)
49
+ - [Inheritance](#inheritance)
50
+ - [Nested factories](#nested-factories)
51
+ - [Assigning parent explicitly](#assigning-parent-explicitly)
52
+ - [Best practices](#best-practices-1)
53
+ - [Associations](#associations)
54
+ - [Implicit definition](#implicit-definition)
55
+ - [Explicit definition](#explicit-definition)
56
+ - [Inline definition](#inline-definition)
57
+ - [Specifying the factory](#specifying-the-factory)
58
+ - [Overriding attributes](#overriding-attributes)
59
+ - [Association overrides](#association-overrides)
60
+ - [Build strategies](#build-strategies-1)
61
+ - [has\_many associations](#has_many-associations)
62
+ - [has\_and\_belongs\_to\_many associations](#has_and_belongs_to_many-associations)
63
+ - [Polymorphic associations](#polymorphic-associations)
64
+ - [Interconnected associations](#interconnected-associations)
65
+ - [Sequences](#sequences)
66
+ - [Global sequences](#global-sequences)
67
+ - [With dynamic attributes](#with-dynamic-attributes)
68
+ - [As implicit attributes](#as-implicit-attributes)
69
+ - [Inline sequences](#inline-sequences)
70
+ - [Initial value](#initial-value)
71
+ - [Without a block](#without-a-block)
72
+ - [Aliases](#aliases-1)
73
+ - [Rewinding](#rewinding)
74
+ - [Uniqueness](#uniqueness)
75
+ - [Traits](#traits)
76
+ - [Defining traits](#defining-traits)
77
+ - [As implicit attributes](#as-implicit-attributes-1)
78
+ - [Attribute precedence](#attribute-precedence)
79
+ - [In child factories](#in-child-factories)
80
+ - [As mixins](#as-mixins)
81
+ - [Using traits](#using-traits)
82
+ - [With associations](#with-associations-1)
83
+ - [Traits within traits](#traits-within-traits)
84
+ - [With transient attributes](#with-transient-attributes)
85
+ - [Enum traits](#enum-traits)
86
+ - [Callbacks](#callbacks)
87
+ - [Default callbacks](#default-callbacks)
88
+ - [Multiple callbacks](#multiple-callbacks)
89
+ - [Global callbacks](#global-callbacks)
90
+ - [Symbol#to\_proc](#symbolto_proc)
91
+ - [Modifying factories](#modifying-factories)
92
+ - [Building or Creating Multiple Records](#building-or-creating-multiple-records)
93
+ - [Linting Factories](#linting-factories)
94
+ - [Custom Construction](#custom-construction)
95
+ - [Custom Strategies](#custom-strategies)
96
+ - [Custom Callbacks](#custom-callbacks)
97
+ - [Custom Methods to Persist Objects](#custom-methods-to-persist-objects)
98
+ - [ActiveSupport Instrumentation](#activesupport-instrumentation)
99
+ - [Rails Preloaders and RSpec](#rails-preloaders-and-rspec)
100
+ - [Using Without Bundler](#using-without-bundler)
100
101
 
101
102
  Setup
102
103
  -----
@@ -354,7 +355,7 @@ user = build(:user, account:, friends:)
354
355
 
355
356
  [omitting values]: https://docs.ruby-lang.org/en/3.1/syntax/literals_rdoc.html#label-Hash+Literals
356
357
 
357
- ### `build_stubbed` and `Marshal.dump`
358
+ ### build_stubbed and Marshal.dump
358
359
 
359
360
  Note that objects created with `build_stubbed` cannot be serialized with
360
361
  `Marshal.dump`, since factory\_bot defines singleton methods on these objects.
@@ -435,7 +436,7 @@ create(:user, rockstar: false).name
435
436
  #=> "John Doe"
436
437
  ```
437
438
 
438
- ### With `attributes_for`
439
+ ### With attributes_for
439
440
 
440
441
  Transient attributes will be ignored within attributes\_for and won't be set on
441
442
  the model, even if the attribute exists or you attempt to override it.
@@ -641,7 +642,7 @@ end
641
642
 
642
643
  Or inline using attributes from the factory:
643
644
 
644
- ```rb
645
+ ```ruby
645
646
  factory :post do
646
647
  # ...
647
648
  author_last_name { "Writely" }
@@ -735,7 +736,7 @@ factory :post do
735
736
  author strategy: :build # <<< this does *not* work; causes author_id to be nil
736
737
  ```
737
738
 
738
- ### `has_many` associations
739
+ ### has_many associations
739
740
 
740
741
  There are a few ways to generate data for a `has_many` relationship. The
741
742
  simplest approach is to write a helper method in plain Ruby to tie together the
@@ -862,7 +863,7 @@ build(:user_with_posts, posts_count: 15).posts.length # 15
862
863
  build_stubbed(:user_with_posts, posts_count: 15).posts.length # 15
863
864
  ```
864
865
 
865
- ### `has_and_belongs_to_many` associations
866
+ ### has_and_belongs_to_many associations
866
867
 
867
868
  Generating data for a `has_and_belongs_to_many` relationship is very similar
868
869
  to the above `has_many` relationship, with a small change: you need to pass an
@@ -1152,7 +1153,7 @@ When working with uniqueness constraints, be careful not to pass in override val
1152
1153
 
1153
1154
  In this example the email will be the same for both users. If email must be unique, this code will error:
1154
1155
 
1155
- ```rb
1156
+ ```ruby
1156
1157
  factory :user do
1157
1158
  sequence(:email) { |n| "person#{n}@example.com" }
1158
1159
  end
@@ -1418,7 +1419,7 @@ create :invoice, :with_amount, amount: 2
1418
1419
 
1419
1420
  Given an Active Record model with an enum attribute:
1420
1421
 
1421
- ```rb
1422
+ ```ruby
1422
1423
  class Task < ActiveRecord::Base
1423
1424
  enum status: {queued: 0, started: 1, finished: 2}
1424
1425
  end
@@ -1428,7 +1429,7 @@ end
1428
1429
  factory\_bot will automatically define traits for each possible value of the
1429
1430
  enum:
1430
1431
 
1431
- ```rb
1432
+ ```ruby
1432
1433
  FactoryBot.define do
1433
1434
  factory :task
1434
1435
  end
@@ -1440,7 +1441,7 @@ FactoryBot.build(:task, :finished)
1440
1441
 
1441
1442
  Writing the traits out manually would be cumbersome, and is not necessary:
1442
1443
 
1443
- ```rb
1444
+ ```ruby
1444
1445
  FactoryBot.define do
1445
1446
  factory :task do
1446
1447
  trait :queued do
@@ -1465,7 +1466,7 @@ desired, it is possible to disable the feature by setting
1465
1466
  In that case, it is still possible to explicitly define traits for an enum
1466
1467
  attribute in a particular factory:
1467
1468
 
1468
- ```rb
1469
+ ```ruby
1469
1470
  FactoryBot.automatically_define_enum_traits = false
1470
1471
 
1471
1472
  FactoryBot.define do
@@ -1480,7 +1481,7 @@ specifically tied to Active Record enum attributes.
1480
1481
 
1481
1482
  With an array:
1482
1483
 
1483
- ```rb
1484
+ ```ruby
1484
1485
  class Task
1485
1486
  attr_accessor :status
1486
1487
  end
@@ -1494,7 +1495,7 @@ end
1494
1495
 
1495
1496
  Or with a hash:
1496
1497
 
1497
- ```rb
1498
+ ```ruby
1498
1499
  class Task
1499
1500
  attr_accessor :status
1500
1501
  end
data/NEWS.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # News
2
2
 
3
+ ## 6.5.6 (October 22, 2025)
4
+
5
+ * Fix: Enforce association override precedence over trait foreign keys by @JinOketani in [#1768](https://github.com/thoughtbot/factory_bot/pull/1768)
6
+ * Build: Add ostruct as a development dependency by @ydah in [#1778](https://github.com/thoughtbot/factory_bot/pull/1778)
7
+ * Build: Bump standard from v1.44.0 to v1.51.1 by @ydah in [#1779](https://github.com/thoughtbot/factory_bot/pull/1779)
8
+ * Build: Add Ruby 3.4 to CI matrix by @ydah in [#1780](https://github.com/thoughtbot/factory_bot/pull/1780)
9
+ * Build: Remove unnecessary development dependencies by @ydah in [#1781](https://github.com/thoughtbot/factory_bot/pull/1781)
10
+ * Build: update gem versions and dependencies by @ydah in [#1782](https://github.com/thoughtbot/factory_bot/pull/1782)
11
+ * Build: revert removal of mutex_m by @vburzynski in [#1784](https://github.com/thoughtbot/factory_bot/pull/1784)
12
+ * Refactor: factory calculator cleanup by @vburzynski in [#1770](https://github.com/thoughtbot/factory_bot/pull/1770)
13
+ * Chore(ci): Bump actions/checkout from 4 to 5 by @dependabot[bot] in [#1765](https://github.com/thoughtbot/factory_bot/pull/1765)
14
+ * Chore(specs): tag slow specs by @vburzynski in [#1776](https://github.com/thoughtbot/factory_bot/pull/1776)
15
+ * Docs: Update RELEASING.md by @vburzynski in [#1763](https://github.com/thoughtbot/factory_bot/pull/1763)
16
+ * Docs: Update link to FactoryGirl upgrade guide by @imRohan in [#1769](https://github.com/thoughtbot/factory_bot/pull/1769)
17
+ * Docs: Fix some typos by @ydah in [#1783](https://github.com/thoughtbot/factory_bot/pull/1783)
18
+ * Docs(yard): resolve yard doc warnings by @vburzynski in [#1764](https://github.com/thoughtbot/factory_bot/pull/1764)
19
+ * Docs(yard): ruby syntax highlighting in yard docs by @djbender in [#1777](https://github.com/thoughtbot/factory_bot/pull/1777)
20
+
3
21
  ## 6.5.5 (August 15, 2025)
4
22
 
5
23
  * Feat: Adds developer console features (CodeMeister)
@@ -118,7 +136,7 @@
118
136
  * Docs: re-write into mdBook (Mike Burns, Sara Jackson, Stefanni Brasil)
119
137
  * Docs: clarify that automatic trait definitions could introduce new linting errors (Lawrence Chou).
120
138
  * Internal: skip TruffleRuby on Rails 5.0, 5.1, 5.2 (Andrii Konchyn).
121
- * Internal: fix typoes throughout codebase (Yudai Takada).
139
+ * Internal: fix typos throughout codebase (Yudai Takada).
122
140
  * Internal: run CI on `actions/checkout` v3 (Yudai Takada).
123
141
  * Internal: follow standardrb code style (Yudai Takada).
124
142
  * Internal: stop using Hound (Daniel Nolan).
data/README.md CHANGED
@@ -1,20 +1,17 @@
1
- # factory_bot [![Build Status][ci-image]][ci] [![Code Climate][grade-image]][grade] [![Gem Version][version-image]][version]
1
+ # factory_bot
2
+
3
+ [![Build Status][ci-image]][ci] [![Code Climate][grade-image]][grade] [![Gem Version][version-image]][version]
2
4
 
3
5
  factory_bot is a fixtures replacement with a straightforward definition syntax, support for multiple build strategies (saved instances, unsaved instances, attribute hashes, and stubbed objects), and support for multiple factories for the same class (user, admin_user, and so on), including factory inheritance.
4
6
 
5
7
  If you want to use factory_bot with Rails, see
6
8
  [factory_bot_rails](https://github.com/thoughtbot/factory_bot_rails).
7
9
 
8
- _[Interested in the history of the project name?][NAME]_
9
-
10
-
11
- ### Transitioning from factory\_girl?
10
+ Interested in the history of the project name? You can find the history [here](https://github.com/thoughtbot/factory_bot/blob/main/NAME.md)
12
11
 
13
- Check out the [guide](https://github.com/thoughtbot/factory_bot/blob/v4.9.0/UPGRADE_FROM_FACTORY_GIRL.md).
12
+ Transitioning from factory\_girl? Check out the [upgrade guide](https://github.com/thoughtbot/factory_bot/blob/v4.9.0/UPGRADE_FROM_FACTORY_GIRL.md).
14
13
 
15
-
16
- Documentation
17
- -------------
14
+ ## Documentation
18
15
 
19
16
  See our extensive reference, guides, and cookbook in [the factory_bot book][].
20
17
 
@@ -27,8 +24,7 @@ Rails, see [the factory_bot wiki][].
27
24
  [the factory_bot book]: https://thoughtbot.github.io/factory_bot
28
25
  [the factory_bot wiki]: https://github.com/thoughtbot/factory_bot/wiki
29
26
 
30
- Install
31
- --------
27
+ ## Install
32
28
 
33
29
  Run:
34
30
 
@@ -42,13 +38,11 @@ To install the gem manually from your shell, run:
42
38
  gem install factory_bot
43
39
  ```
44
40
 
45
- Supported Ruby versions
46
- -----------------------
41
+ ## Supported Ruby versions
47
42
 
48
- Supported Ruby versions are listed in [`.github/workflows/build.yml`](https://github.com/thoughtbot/factory_bot/blob/main/.github/workflows/build.yml)
43
+ Supported Ruby versions are listed in `.github/workflows/build.yml` ([source](https://github.com/thoughtbot/factory_bot/blob/main/.github/workflows/build.yml))
49
44
 
50
- More Information
51
- ----------------
45
+ ## More Information
52
46
 
53
47
  * [Rubygems](https://rubygems.org/gems/factory_bot)
54
48
  * [Stack Overflow](https://stackoverflow.com/questions/tagged/factory-bot)
@@ -56,10 +50,8 @@ More Information
56
50
  * [GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS](https://robots.thoughtbot.com/)
57
51
 
58
52
  [GETTING_STARTED]: https://github.com/thoughtbot/factory_bot/blob/main/GETTING_STARTED.md
59
- [NAME]: https://github.com/thoughtbot/factory_bot/blob/main/NAME.md
60
53
 
61
- Useful Tools
62
- ------------
54
+ ## Useful Tools
63
55
 
64
56
  * [FactoryTrace](https://github.com/djezzzl/factory_trace) - helps to find unused factories and traits.
65
57
  * [ruby-lsp-factory_bot](https://github.com/donny741/ruby-lsp-factory_bot) / [ruby-lsp-rails-factory-bot](https://github.com/johansenja/ruby-lsp-rails-factory-bot) - integration with [ruby-lsp](https://github.com/Shopify/ruby-lsp) to provide intellisense
@@ -9,6 +9,7 @@ module FactoryBot
9
9
  @attribute_names_assigned = []
10
10
  end
11
11
 
12
+ # constructs an object-based factory product
12
13
  def object
13
14
  @evaluator.instance = build_class_instance
14
15
  build_class_instance.tap do |instance|
@@ -19,6 +20,7 @@ module FactoryBot
19
20
  end
20
21
  end
21
22
 
23
+ # constructs a Hash-based factory product
22
24
  def hash
23
25
  @evaluator.instance = build_hash
24
26
 
@@ -29,6 +31,8 @@ module FactoryBot
29
31
 
30
32
  private
31
33
 
34
+ # Track evaluation of methods on the evaluator to prevent the duplicate
35
+ # assignment of attributes accessed and via `initialize_with` syntax
32
36
  def method_tracking_evaluator
33
37
  @method_tracking_evaluator ||= Decorator::AttributeHash.new(
34
38
  decorated_evaluator,
@@ -67,12 +71,15 @@ module FactoryBot
67
71
  attribute_names_to_assign - association_names
68
72
  end
69
73
 
74
+ # Builds a list of attributes names that should be assigned to the factory product
70
75
  def attribute_names_to_assign
71
- @attribute_names_to_assign ||=
72
- non_ignored_attribute_names +
73
- override_names -
74
- ignored_attribute_names -
75
- aliased_attribute_names_to_ignore
76
+ @attribute_names_to_assign ||= begin
77
+ # start a list of candidates containing non-transient attributes and overrides
78
+ assignment_candidates = non_ignored_attribute_names + override_names
79
+ # then remove any transient attributes (potentially reintroduced by the overrides),
80
+ # and remove ignorable aliased attributes from the candidate list
81
+ assignment_candidates - ignored_attribute_names - attribute_names_overriden_by_alias
82
+ end
76
83
  end
77
84
 
78
85
  def non_ignored_attribute_names
@@ -99,31 +106,63 @@ module FactoryBot
99
106
  attribute_names + override_names + @build_class.instance_methods
100
107
  end
101
108
 
102
- ##
103
- # Creat a list of attribute names that will be
104
- # overridden by an alias, so any defaults can
105
- # ignored.
109
+ # Builds a list of attribute names which are slated to be interrupted by an override.
110
+ def attribute_names_overriden_by_alias
111
+ @attribute_list
112
+ .non_ignored
113
+ .flat_map { |attribute|
114
+ override_names.map do |override|
115
+ attribute.name if ignorable_alias?(attribute, override)
116
+ end
117
+ }
118
+ .compact
119
+ end
120
+
121
+ # Is the attribute an ignorable alias of the override?
122
+ # An attribute is ignorable when it is an alias of the override AND it is
123
+ # either interrupting an assocciation OR is not the name of another attribute
106
124
  #
107
- def aliased_attribute_names_to_ignore
108
- @attribute_list.non_ignored.flat_map { |attribute|
109
- override_names.map do |override|
110
- attribute.name if aliased_attribute?(attribute, override)
111
- end
112
- }.compact
125
+ # @note An "alias" is currently an overloaded term for two distinct cases:
126
+ # (1) attributes which are aliases and reference the same value
127
+ # (2) a logical grouping of a foreign key and an associated object
128
+ def ignorable_alias?(attribute, override)
129
+ return false unless attribute.alias_for?(override)
130
+
131
+ # The attribute alias should be ignored when the override interrupts an association
132
+ return true if override_interrupts_association?(attribute, override)
133
+
134
+ # Remaining aliases should be ignored when the override does not match a declared attribute.
135
+ # An override which is an alias to a declared attribute should not interrupt the aliased
136
+ # attribute and interrupt only the attribute with a matching name. This workaround allows a
137
+ # factory to declare both <attribute> and <attribute>_id as separate and distinct attributes.
138
+ !override_matches_declared_attribute?(override)
113
139
  end
114
140
 
115
- ##
116
- # Is the override an alias for the attribute and not the
117
- # actual name of another attribute?
141
+ # Does this override interrupt an association?
142
+ # When true, this indicates the aliased attribute is related to a declared association and the
143
+ # override does not match the attribute name.
144
+ #
145
+ # @note Association overrides should take precedence over a declared foreign key attribute.
118
146
  #
119
- # Note: Checking against the names of all attributes, resolves any
120
- # issues with having both <attribute> and <attribute>_id
121
- # in the same factory.
147
+ # @note An override may interrupt an association by providing the associated object or
148
+ # by providing the foreign key.
122
149
  #
123
- def aliased_attribute?(attribute, override)
124
- return false if attribute_names.include?(override)
150
+ # @param [FactoryBot::Attribute] aliased_attribute
151
+ # @param [Symbol] override name of an override which is an alias to the attribute name
152
+ def override_interrupts_association?(aliased_attribute, override)
153
+ (aliased_attribute.association? || association_names.include?(override)) &&
154
+ aliased_attribute.name != override
155
+ end
125
156
 
126
- attribute.alias_for?(override)
157
+ # Does this override match the name of any declared attribute?
158
+ #
159
+ # @note Checking against the names of all attributes, resolves any issues with having both
160
+ # <attribute> and <attribute>_id in the same factory. This also takes into account ignored
161
+ # attributes that should not be assigned (aka transient attributes)
162
+ #
163
+ # @param [Symbol] override the name of an override
164
+ def override_matches_declared_attribute?(override)
165
+ attribute_names.include?(override)
127
166
  end
128
167
  end
129
168
  end
@@ -36,14 +36,13 @@ module FactoryBot
36
36
 
37
37
  compile
38
38
 
39
- strategy = StrategyCalculator.new(build_strategy).strategy.new
39
+ strategy = Strategy.lookup_strategy(build_strategy).new
40
40
 
41
41
  evaluator = evaluator_class.new(strategy, overrides.symbolize_keys)
42
42
  attribute_assigner = AttributeAssigner.new(evaluator, build_class, &compiled_constructor)
43
43
 
44
44
  observer = CallbacksObserver.new(callbacks, evaluator)
45
- evaluation =
46
- Evaluation.new(evaluator, attribute_assigner, compiled_to_create, observer)
45
+ evaluation = Evaluation.new(evaluator, attribute_assigner, compiled_to_create, observer)
47
46
 
48
47
  evaluation.notify(:before_all, nil)
49
48
  instance = strategy.result(evaluation).tap(&block)
@@ -17,8 +17,8 @@ module FactoryBot
17
17
 
18
18
  def self.find_by_uri(uri)
19
19
  uri = uri.to_sym
20
- (FactoryBot::Internal.sequences.to_a.find { |seq| seq.has_uri?(uri) }) ||
21
- (FactoryBot::Internal.inline_sequences.find { |seq| seq.has_uri?(uri) })
20
+ FactoryBot::Internal.sequences.to_a.find { |seq| seq.has_uri?(uri) } ||
21
+ FactoryBot::Internal.inline_sequences.find { |seq| seq.has_uri?(uri) }
22
22
  end
23
23
 
24
24
  def initialize(name, *args, &proc)
@@ -0,0 +1,15 @@
1
+ require "factory_bot/strategy/build"
2
+ require "factory_bot/strategy/create"
3
+ require "factory_bot/strategy/attributes_for"
4
+ require "factory_bot/strategy/stub"
5
+ require "factory_bot/strategy/null"
6
+
7
+ module FactoryBot
8
+ module Strategy
9
+ def self.lookup_strategy(name_or_object)
10
+ return name_or_object if name_or_object.is_a?(Class)
11
+
12
+ FactoryBot::Internal.strategy_by_name(name_or_object)
13
+ end
14
+ end
15
+ end
@@ -83,20 +83,20 @@ module FactoryBot
83
83
  # (see #strategy_method_pair)
84
84
  # @return [Array<Hash>] pair of attribute hashes for the factory
85
85
 
86
- # @!method strategy_method
86
+ # @!method strategy_method(name, traits_and_overrides, &block)
87
87
  # @!visibility private
88
88
  # @param [Symbol] name the name of the factory to build
89
89
  # @param [Array<Symbol, Symbol, Hash>] traits_and_overrides splat args traits and a hash of overrides
90
90
  # @param [Proc] block block to be executed
91
91
 
92
- # @!method strategy_method_list
92
+ # @!method strategy_method_list(name, amount, traits_and_overrides, &block)
93
93
  # @!visibility private
94
94
  # @param [Symbol] name the name of the factory to execute
95
95
  # @param [Integer] amount the number of instances to execute
96
96
  # @param [Array<Symbol, Symbol, Hash>] traits_and_overrides splat args traits and a hash of overrides
97
97
  # @param [Proc] block block to be executed
98
98
 
99
- # @!method strategy_method_pair
99
+ # @!method strategy_method_pair(name, traits_and_overrides, &block)
100
100
  # @!visibility private
101
101
  # @param [Symbol] name the name of the factory to execute
102
102
  # @param [Array<Symbol, Symbol, Hash>] traits_and_overrides splat args traits and a hash of overrides
@@ -1,3 +1,3 @@
1
1
  module FactoryBot
2
- VERSION = "6.5.5".freeze
2
+ VERSION = "6.5.6".freeze
3
3
  end
data/lib/factory_bot.rb CHANGED
@@ -11,12 +11,7 @@ require "factory_bot/configuration"
11
11
  require "factory_bot/errors"
12
12
  require "factory_bot/factory_runner"
13
13
  require "factory_bot/strategy_syntax_method_registrar"
14
- require "factory_bot/strategy_calculator"
15
- require "factory_bot/strategy/build"
16
- require "factory_bot/strategy/create"
17
- require "factory_bot/strategy/attributes_for"
18
- require "factory_bot/strategy/stub"
19
- require "factory_bot/strategy/null"
14
+ require "factory_bot/strategy"
20
15
  require "factory_bot/registry"
21
16
  require "factory_bot/null_factory"
22
17
  require "factory_bot/null_object"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: factory_bot
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.5.5
4
+ version: 6.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Clayton
8
8
  - Joe Ferris
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-15 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: cucumber
70
+ name: mutex_m
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: mutex_m
84
+ name: ostruct
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -233,12 +233,12 @@ files:
233
233
  - lib/factory_bot/registry.rb
234
234
  - lib/factory_bot/reload.rb
235
235
  - lib/factory_bot/sequence.rb
236
+ - lib/factory_bot/strategy.rb
236
237
  - lib/factory_bot/strategy/attributes_for.rb
237
238
  - lib/factory_bot/strategy/build.rb
238
239
  - lib/factory_bot/strategy/create.rb
239
240
  - lib/factory_bot/strategy/null.rb
240
241
  - lib/factory_bot/strategy/stub.rb
241
- - lib/factory_bot/strategy_calculator.rb
242
242
  - lib/factory_bot/strategy_syntax_method_registrar.rb
243
243
  - lib/factory_bot/syntax.rb
244
244
  - lib/factory_bot/syntax/default.rb
@@ -266,7 +266,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
266
266
  - !ruby/object:Gem::Version
267
267
  version: '0'
268
268
  requirements: []
269
- rubygems_version: 3.6.2
269
+ rubygems_version: 3.7.1
270
270
  specification_version: 4
271
271
  summary: factory_bot provides a framework and DSL for defining and using model instance
272
272
  factories.
@@ -1,26 +0,0 @@
1
- module FactoryBot
2
- # @api private
3
- class StrategyCalculator
4
- def initialize(name_or_object)
5
- @name_or_object = name_or_object
6
- end
7
-
8
- def strategy
9
- if strategy_is_object?
10
- @name_or_object
11
- else
12
- strategy_name_to_object
13
- end
14
- end
15
-
16
- private
17
-
18
- def strategy_is_object?
19
- @name_or_object.is_a?(Class)
20
- end
21
-
22
- def strategy_name_to_object
23
- FactoryBot::Internal.strategy_by_name(@name_or_object)
24
- end
25
- end
26
- end