Hey all!
I was using dry-validation and dry-types and ran into some odd behavior that feels somewhat unexpected. The situation came from using a Constructor type for a class that was, itself, using a dry type inside the initalizer.
Basically, on calling the contract a CoercionError bubbles all the way up (instead of being nested in the errors as I’d expect). I’m guessing this has to do with how Constructor types are “constrained” in dry types, but my expectation is that filled on a Contract would first reject any nil input.
Here’s a reproduction script to showcase what I mean.
module Types
  include Dry.Types()
end
# Later
class Thing
  CleanString = Types::Strict::String.constructor(&:strip).constructor(&:downcase)
  def initialize(input_string)
    @thing = CleanString[input_string]
  end
end
module Types
  Thing = self.Constructor(::Thing)
end
class SomeContract < Dry::Validation::Contract
  json do
    required(:thing).filled(Types::Thing)
  end
end
SomeContract.new.call(thing: nil)
# => Dry::Types::CoercionError: undefined method 'strip' for nil:NilClass
# With "Thing" using .try - bad result :(
class Thing
  CleanString = Types::Strict::String.constructor(&:strip).constructor(&:downcase)
  def initialize(input_string)
    @thing = CleanString.try(input_string)
  end
end
# Input looks "valid"
SomeContract.new.call(thing: nil)
#<Dry::Validation::Result{:thing=>#<Thing:0x00007f93b6ae2698 @thing=#<Dry::Types::Result::Failure input=nil error=#<Dry::Types::CoercionError: undefined method `strip' for nil:NilClass>>>} errors={}>
# With "Thing" without using Dry::Types at all
class Thing
  def initialize(input_string)
    @thing = input_string.strip.downcase
  end
end
SomeContract.new.call(thing: nil)
# => #<Dry::Validation::Result{:thing=>nil} errors={:thing=>["must be Thing"]}>
– edit –
meant to also say that I did some prior reading here: Contract ignores ConstraintError thrown by Dry::Types[Constructor]
Which is close, but not exactly what I meant. I don’t expect some custom constraint message, at most I just expected that there wasn’t an outright error bubbled up.