Skip to content

Conversation

CodeMeister
Copy link
Contributor

@CodeMeister CodeMeister commented Oct 28, 2024

Fixes: #1680
Fixes: #1142

Addresses: #1708

When a factory has both <attribute> and <attribute>_id, each one is considered an alias for the other
and are not assigned correctly.

This request eliminates this issue by checking if the override is an existing attribute name, before matching against an alias.

  • AttributeAssigner refactored to ignore a matching attribute alias, if it also matches the name of another attribute.
  • Renamed two methods to give more clarity to their purpose.
  • Tests added for attribute assignment.

It works for any recorded aliases, not just '_id'.

@CodeMeister CodeMeister changed the title Fixes '<attribute>' and '<attribute>_id' conflict. BugFix: '<attribute>' and '<attribute>_id' conflict. Nov 5, 2024
@unused
Copy link

unused commented Nov 10, 2024

I had expected this problem to be much more difficult to resolve, but can't find anything wrong with your code, good job 🙌 When trying to understand the conflict ignored attributes - that as far as I've seen only applies to transient ones - makes the processing hard to understand. I think you managed well to make this easier to grasp.

end

def hash_instance_methods_to_respond_to
@attribute_list.names + override_names + @build_class.instance_methods
Copy link

@DTrejo DTrejo Mar 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use attribute_names? side note: just lost a number of hours on the bug this PR will solve 😢 , thank you for working to fix it! 🎉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DTrejo good call!

I've updated the code to call the method.

Thanks for your comments 🫶

  - AttributeAssigner refactored to ignore a
    matching attribute alias, if it also
    matches the name of another attribute.

  - Tests added for attribute assignment.
@neilvcarvalho neilvcarvalho force-pushed the attribute-alias-filtering branch from 3dde8e6 to d6fe17d Compare July 25, 2025 19:11
@neilvcarvalho neilvcarvalho force-pushed the attribute-alias-filtering branch from d6fe17d to 44eace5 Compare July 25, 2025 19:14
Copy link
Member

@neilvcarvalho neilvcarvalho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @CodeMeister - I took the liberty of improving the acceptance specs related to aliases, and added the edge case test you implemented.

Thank you so much for the great solution. It was very elegant.

@neilvcarvalho neilvcarvalho merged commit cb7971a into thoughtbot:main Jul 25, 2025
25 checks passed
@CodeMeister CodeMeister deleted the attribute-alias-filtering branch July 28, 2025 12:36
JinOketani added a commit to JinOketani/factory_bot that referenced this pull request Sep 12, 2025
…tbot#1767)

* Ensure association overrides take precedence over trait-defined foreign keys

  In 6.5.5, changes related to PR thoughtbot#1709 introduced an unintended behavior
  where a trait-defined `*_id` could take precedence over an explicit
  association override. This could lead to inconsistency between the
  association and its foreign key.

* Update `AttributeAssigner#aliased_attribute?` to prioritize associations

  When an association override is provided (e.g., `user: user_instance`),
  the corresponding trait-defined foreign key is ignored, keeping the object
  graph consistent with Rails/ActiveRecord expectations. The fix preserves
  the intent of PR thoughtbot#1709 regarding attribute/attribute_id conflicts.

* Add regression specs

  - Association override wins over trait-defined foreign key
  - Trait-defined foreign key applies when no association override is present
  - Multiple foreign-key traits remain supported

Example:

  Before (6.5.5):
    FactoryBot.build(:post, :with_user_id_999, user: user).user_id # => 999

  After (this change; consistent with 6.5.4):
    FactoryBot.build(:post, :with_user_id_999, user: user).user_id # => user.id

Related: thoughtbot#1709, thoughtbot#1767

---------

Co-authored-by: Jin Oketani <[email protected]>
JinOketani added a commit to JinOketani/factory_bot that referenced this pull request Sep 12, 2025
…tbot#1767)

* Ensure association overrides take precedence over trait-defined foreign keys

  In 6.5.5, changes related to PR thoughtbot#1709 introduced an unintended behavior
  where a trait-defined `*_id` could take precedence over an explicit
  association override. This could lead to inconsistency between the
  association and its foreign key.

* Update `AttributeAssigner#aliased_attribute?` to prioritize associations

  When an association override is provided (e.g., `user: user_instance`),
  the corresponding trait-defined foreign key is ignored, keeping the object
  graph consistent with Rails/ActiveRecord expectations. The fix preserves
  the intent of PR thoughtbot#1709 regarding attribute/attribute_id conflicts.

* Add regression specs

  - Association override wins over trait-defined foreign key
  - Trait-defined foreign key applies when no association override is present
  - Multiple foreign-key traits remain supported

Example:

  Before (6.5.5):
    FactoryBot.build(:post, :with_user_id_999, user: user).user_id # => 999

  After (this change; consistent with 6.5.4):
    FactoryBot.build(:post, :with_user_id_999, user: user).user_id # => user.id

Related: thoughtbot#1709, thoughtbot#1767
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Do not overwrite the dafault value Confusion around aliases for {attribute} and {attribute}_id

5 participants