[Dry-Struct] Array with conditional multiple members


#1

Is there a way to impose a Type based on some attribute?

For instance, I have some structs like these:


class Base < Dry::Struct
  module Types
    include Dry::Types.module
  end
end

class Vehicle < Base
  attribute :value, Types::Coercible::Float
  attribute :manufacturing_year, Types::Coercible::Integer.meta(omittable: true)
  attribute :type, Types::String.default('vehicle')
end

class RealEstate < Base
  attribute :value, Types::Coercible::Float
  attribute :address, Types::String
  attribute :type, Types::String.default('real_estate')
end

class LoanApplication < Base
  attribute :collaterals, Types::Array.of(Vehicle | RealEstate)
end

When I cannot have a vehicle and a real estate:

LoanApplication.new({
  collaterals: [
    {value: 4, manufacturing_year: 2019},
    {value: 4, address: '135 Courtney St.'}
  ]
}) # => Both collaterals are Vehicles

I can change the order dry-struct matches Vehicles or Real Estates here (and it works), but this is a simplified view of a more complex struct where I cannot simply change the order.

Is there a way to match a Struct based on a attribute value? That is, is there a way to know it is a vehicle when I use type: 'vehicle' as an attribute?

In a way that I would have something like the following:

LoanApplication.new({
  collaterals: [
    {value: 4, manufacturing_year: 2019, type: 'vehicle'}, # this would be a Vehicle because of the `type: 'vehicle'`
    {value: 4, address: '135 Courtney St.', type: 'real_estate'} # this would be a Vehicle because of the `type: 'real_estate'`
  ]
})

#2

Yes, use Type::Value

class Vehicle < Base
  attribute :value, Types::Coercible::Float
  attribute :manufacturing_year, Types::Coercible::Integer.meta(omittable: true)
  attribute :type, Types::Value('vehicle')
end

class RealEstate < Base
  attribute :value, Types::Coercible::Float
  attribute :address, Types::String
  attribute :type, Types::Value('real_estate')
end

#3

Yaaaay! It worked :smiley: Thank you very much (: