thoughtbot / thoughtbot/factory_bot
Generating nested attributes
Nobody has claimed this yet.
- Dominant language
- Ruby
- Stars
- 8.2k
- Forks
- 2.6k
- PR merge metrics
- No merged PRs in 30d
Description
Hi, I've been looking for a nice way to generate nested attributes with FactoryBot. There are already quite a few posts about this but I didn't find a solution which worked for me.
What I'd like to have is something like attributes_for which also resolves associations as hashes recursively. Below is a solution I came up with. (I am not sure whether this is the right place for this but maybe it is of some help.)
Register this strategy
class NestedAttributesForStrategy
Config = Struct.new(:associations, :nested_attributes)
def association(runner)
runner.run(:nested_attributes_for)
end
def result(evaluation)
config = Config.new([], {})
evaluation.notify(:nested_attributes_for, config)
result = evaluation.hash.merge(config.nested_attributes)
config.associations.each do |association|
result.delete(association)
end
result
end
end
FactoryBot.register_strategy(:nested_attributes_for, NestedAttributesForStrategy)
Define global trait
FactoryBot.define do
trait :with_nested_attributes do
transient do
nested_attributes_for { [] }
end
callback(:nested_attributes_for) do |config, ctx|
associations = ctx.nested_attributes_for
nested_attributes = config.nested_attributes
config.associations = associations
associations.each do |association|
nested_attributes[:"#{association}_attributes"] = ctx.send(association)
end
end
end
end
Use it in your factories
FactoryBot.define do
# The models below normally wouldn't make for a good example of nested attributes.
# Ignore OpenStruct. I used it so you don't need corresponding models for copy-paste testing.
factory :post, class: 'OpenStruct' do
with_nested_attributes
transient do
nested_attributes_for { %i[comments author] }
end
author
comments { [association(:comment)] }
content { 'Nice weather today!' }
end
factory :comment, class: 'OpenStruct' do
with_nested_attributes
transient do
nested_attributes_for { %i[author] }
end
association :author, name: 'Jon Doe'
message { 'I like your post' }
end
factory :author, class: 'OpenStruct' do
name { 'Jane Doe' }
end
end
Invoke
nested_attributes_for(:post).to_yaml
Result:
---
:content: Nice weather today!
:comments_attributes:
- :message: I like your post
:author_attributes:
:name: Jon Doe
:author_attributes:
:name: Jane Doe
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by comparing the proposed nested_attributes_for behavior with the existing attributes_for entry point and register_strategy API. Reproduce the post/comment/author example to assess recursive association hashes and *_attributes keys. No files or tests are named; done means a supported, tested approach for generating the requested nested hashes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ruby
- Domain
- testing
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100