Polymorphic schema for a sub-hash in dry-validation

I’m trying to support a “polymorphic” schema for sub-hashes using dry-validation.

I couldn’t find any documentation on this capability, and I ended up hacking something together that seems… ugly.

There has got to be a better way to do this.

require 'dry-validation'

class ParentContract < Dry::Validation::Contract
  json do
    optional(:child).hash
  end

  rule :child do
    case value[:type]
    when 'special'
      child_contract = SpecialChildContract.new
    else
      child_contract = ChildContract.new
    end

    child_contract.call(value).errors.each do |message|
      body = message.meta.empty? ? message.text : {text: message.text, meta: message.meta}
      key([:child] + message.path).failure(body)
    end
  end
end

class ChildContract < Dry::Validation::Contract
  json do
    required(:type).value(:string)
    required(:name).value(:string)
    optional(:description).value(:string)
  end
end

class SpecialChildContract < ChildContract
  json do
    required(:foobar).value(:string)
  end
end

c1 = ChildContract.new
p c1.call(type: 'generic', name: 'abc').errors.to_h

c2 = SpecialChildContract.new
p c2.call(type: 'special', name: 'abc', description: 1234).errors.to_h

h = ParentContract.new
p h.call(child: { type: 'special', name: 'abc', description: 1234}).errors.to_h

Inheritance is not fully supported yet, especially when it comes to rails-specific concepts like STI and its type attribute. We’ll get there eventually but for now you need to stick to quick’n’dirty hacks like yours.