Support for default attributes for JSON columns
johansenja opened this issue · comments
Problem this feature will solve
Currently, I can create a factory like this:
FactoryBot.define do
factory :a do
my_json_column do
{
"a" => 1,
"b" => 2,
"c" => 3,
}
end
end
end
and use it like this:
create :a, my_json_column: { "a" => 2, "b" => 3, "c" => 4 }
which means I have to specify every key which I want to appear in my_json_column
.
Desired solution
I'm wondering if it would be possible to do something like this:
FactoryBot.define do
factory :a do
json :my_json_column do # or maybe hash? or json_factory?
{
"a" => 1,
"b" => 2,
"c" => 3,
}
end
end
end
which would mean that I could only pass in the properties I cared about, and the rest would just be populated from the definition in the factory (similar to regular properties in factory).
a = create :a, my_json_column: { "a" => 2 }
a.my_json_column # { "a" => 2, "b" => 2, "c" => 3 }
Alternatives considered
I've considered doing something like this (or similar) as a workaround, though it doesn't seem that clean, especially if you are doing it a fair bit.
atrs = attributes_for :a
create :a, my_json_column: { **atrs.my_json_column, "a" => 2 }
But if anyone has any other suggestions then I'm all ears!
Additional context
Example use case:
Imagine you have a config
json column, which just stores some flexible key-value pairs for a model; if you have some sort of validation on this, which checks for various keys, it would be cumbersome to specify all of them every time you have a test which just tests a single one of those keys.
Thanks!
Hi @johansenja thanks for the suggestion. We have some questions about how you envision the API for this new feature to work, but on the meantime, can we suggest using transient attributes to achieve a similar result? We'll attach a repro script so you can see it in action, but the main idea is to use a default set of key-value pairs for the json hash you wish to override and you can merge it with the overridden input, like:
FactoryBot.define do
DEFAULT_OPTIONS = {"a" => 1}
factory :user do
transient do
override_options {{}}
end
sequence(:name) { |sequence| "name #{sequence}" }
after(:build) do |user, evaluator|
puts evaluator.override_options
user.options = DEFAULT_OPTIONS.merge(evaluator.override_options)
end
end
end
class FactoryBotTest < Minitest::Test
def test_attributes
user = FactoryBot.create(:user, override_options: {"a" => 3, "b" => 2})
assert_equal(user.options, {"a" => 3, "b" => 2})
end
end
With this you can have the syntactic sugar you wish to support default values on the json column and overrides certainly work! Let us know what you think, and please provide the reproduction script with the proposal, even if it doesn't work it helps envision a strategy.