Playing with dry-struct and dry-types, and wanted to add an array with members of two types, where both types have same attribute BUT different type definition (one string, another a hash struct). Example below:
require "dry-struct"
require "dry-types"
module Types
include Dry::Types.module
end
class Answer < Dry::Struct
attribute :question_id, Types::String
class Select < self
attribute :value, Types::Hash.schema(id: Types::Int)
end
class Text < self
attribute :value, Types::String
end
end
class Survey < Dry::Struct
attribute :answers, Types::Array.member(Answer::Text | Answer::Select)
end
Survey.new(
answers: [
Answer::Select.new(question_id: 1, value: {id: 10}),
Answer::Text.new(question_id: 1, value: "foo")
]
)
#<Survey answers=[#<Answer::Text question_id=1 value={:id=>10}>, #<Answer::Text question_id=1 value="foo">]>
Problem here is, both answers came back as Answer::Text.
If I invert the order of the members to .member(Answer::Select | Answer::Text), things blow up.
The other thing I tried was to have .member(Answer) since Select and Text are subclasses of Answer. It doesn’t blow up but turns Answer::Select into an Answer, getting rid of attribute :value.
How can I achieve members of an array to accept subclasses of a type?
Appreciate if someone can shed some light for me!
require "dry-struct"
require "dry-types"
require 'byebug'
module Types
include Dry::Types.module
end
class Answer < Dry::Struct
attribute :question_id, Types::String
class Select < self
attribute :value, Types::Strict::Hash.schema(id: Types::Int)
end
class Text < self
attribute :value, Types::Strict::String
end
end
class Survey < Dry::Struct
attribute :answers, Types::Array.member(Answer::Text | Answer::Select)
end
Survey.new(
answers: [
Answer::Select.new(question_id: 1, value: {id: 10}),
Answer::Text.new(question_id: 1, value: "foo")
]
)
Notice that I changed value definitions to use strict types, otherwise there’s no way to validate which input is valid for a specific type, hence it didn’t work.
Types::String is a plain definition that is typically used for annotation purposes. ie in rom-rb it used for relation schemas, where we define attribute types with various meta-data. You should use strict types in places where you want to enforce constraints and you’d rather see an exception than an invalid value being silently passed around in your system.