Nested dry-struct serialization

Hi! Ultimately what I’m trying to do here is implement ActiveRecord serialization for dry-struct, as mentioned here: dry-struct and serializer

I have a struct with reference to another struct, as follows:

  class InventoryPayload < Dry::Struct
     attribute :items, Types::Array.of(Types.Instance(EventTypes::EventLineItem))
  end

  class EventLineItem < Dry::Struct
    transform_keys(&:to_sym)
    attribute :item_id, Types::Integer
    attribute :item_value_in_cents, Types::Integer
  end

When this is dumped to a hash, the initialization logic doesn’t seem to have a good way to turn the nested part of the hash back into a struct:

irb> EventTypes::InventoryPayload.new(items: [{item_id: 1, item_value_in_cents: 1}])
Dry::Struct::Error: [EventTypes::InventoryPayload.new] [{:item_id=>1, :item_value_in_cents=>1}] (Array) has invalid type for :items violates constraints (type?(EventTypes::EventLineItem, {:item_id=>1, :item_value_in_cents=>1}) failed)

The only thing I was able to get working is to add a method InventoryPayload that maps the input hash to EventLineItems. But this seems kind of odd - there should be some way to automatically coerce this? If not, that’s fine, but I’m wondering if I’m missing something.

Types.Instance is the problem here. The hash input will fail this check, what you want is a coercible type that will transform a hash into the struct. This is, in fact, how struct objects behave when you use them as types.

class InventoryPayload < Dry::Struct
  attribute :items, Types::Array.of(EventLineItem)
end
[1] pry(main)> event = InventoryPayload.new(items: [{ item_id: 1, item_value_in_cents: 1 }])
=> #<InventoryPayload items=[#<EventLineItem item_id=1 item_value_in_cents=1>]>
[2] pry(main)> InventoryPayload.new(event.to_hash)
=> #<InventoryPayload items=[#<EventLineItem item_id=1 item_value_in_cents=1>]>

Ahhh there it is! The docs didn’t show this pattern so I wasn’t aware it was an option. Thanks!