I’m trying to create an attrbute that is an optional array with a nested struct.
I’ve tried
attribute :someArray, Types::Array.of(
Dry::Schema.optional do
attribute :someString, Types::String
attribute :someInteger, Types::Integer
).optional
But that results in an array of empty hashes. What is the correct way to do this?
Thank you for your help!
What part of this do you want to be optional? An array type allows itself to be empty unless you constrain it with min_size
.
If you want the key itself to be optional, declare it with attribute?
attribute? :someArray, Types::Array.default { [] } do
attribute :someString, Types::String
attribute :someInteger, Types::Integer
end
I do not want the key to be optional, I want the value of the array to be optional. The value of the array can be nil or it can be an array of structs.
Explicitly passing nil
is a little trickier, since it doesn’t seem to be compatible with inline nesting. But you can achieve that by decomposing the nested struct.
class Thing < Dry::Struct
attribute :someString, Types::String
attribute :someInteger, Types::Integer
end
class Test < Dry::Struct
attribute :someArray, Types::Array.of(Thing).optional
end
[1] pry(main)> Test.new
Dry::Struct::Error: [Test.new] :someArray is missing in Hash input
[2] pry(main)> Test.new({ someArray: nil })
=> #<Test someArray=nil>
[3] pry(main)> Test.new({ someArray: [] })
=> #<Test someArray=[]>
[4] pry(main)> Test.new({ someArray: [{}] })
Dry::Struct::Error: [Thing.new] :someString is missing in Hash input
[5] pry(main)> Test.new({ someArray: [{ someString: 'foo', someInteger: 123 }] })
=> #<Test someArray=[#<Thing someString="foo" someInteger=123>]>