Nested strings are not correctly stripped

Hi all!

This is the code as it currently stands:

module Types
  include Dry::Types()

  StrippedString = Types::String.constructor(&:strip)
end

class ManuallyRegisterPersonContract < ApplicationContract
  params do
    required(:first_name).maybe(Types::StrippedString, min_size?: 1)
    required(:last_name).maybe(Types::StrippedString, min_size?: 1)
    required(:date_of_birth).maybe(:date)
    required(:email_locators).maybe(:array).each do
      hash do
        required(:name).filled(Types::StrippedString, min_size?: 1)
        required(:email).filled(Types::StrippedString, min_size?: 1)
      end
    end

    optional(:metadata).hash(MetadataContract)
  end
end

RSpec.describe ManuallyRegisterPersonContract do
  context "a valid contract where values have extraneous spaces" do
    it "returns stripped values after validation" do
      params = {
        first_name: "  Lord Robert Stephenson Smyth ",
        last_name: "\tBaden Powell of Gylwell\n",
        date_of_birth: "\n2020-01-01\t ",
        email_locators: [
          {
            name: "personal\t",
            email: "  baden@example.test "
          }
        ]
      }

      expect(subject.call(params).to_h).to eq(
        first_name: "Lord Robert Stephenson Smyth",
        last_name: "Baden Powell of Gylwell",
        date_of_birth: Date.new(2020, 1, 1),
        email_locators: [
          {
            name: "personal",
            email: "baden@example.test"
          }
        ]
      )
    end
  end
end

The above spec fails like this:

1) ManuallyRegisterPersonContract a valid contract where values have extraneous spaces returns stripped values after validation
Failure/Error:
	expect(subject.call(params).to_h).to eq(
		first_name: "Lord Robert Stephenson Smyth",
		last_name: "Baden Powell of Gylwell",
		date_of_birth: Date.new(2020, 1, 1),
		email_locators: [
			{
				name: "personal",
				email: "baden@example.test"
			}
		]

	expected: {:date_of_birth=>Wed, 01 Jan 2020, :email_locators=>[{:email=>"baden@example.test", :name=>"personal"}], :first_name=>"Lord Robert Stephenson Smyth", :last_name=>"Baden Powell of Gylwell"}
		 got: {:date_of_birth=>Wed, 01 Jan 2020, :email_locators=>[{:email=>"  baden@example.test ", :name=>"personal\t"}], :first_name=>"Lord Robert Stephenson Smyth", :last_name=>"Baden Powell of Gylwell"}

	(compared using ==)

	Diff:
	@@ -1,5 +1,5 @@
	:date_of_birth => Wed, 01 Jan 2020,
	-:email_locators => [{:email=>"baden@example.test", :name=>"personal"}],
	+:email_locators => [{:email=>"  baden@example.test ", :name=>"personal\t"}],
	:first_name => "Lord Robert Stephenson Smyth",
	:last_name => "Baden Powell of Gylwell",

Notice the email locator values are not stripped of extra spaces. I don’t understand the difference between having Types::StrippedString at the top-level or within a nested hash.

Can someone explain what I am doing wrong?

Thanks a bunch!
François

  • Ruby 2.7.2
  • dry-validation (1.5.6 9b03b9e)
  • dry-schema (1.5.5 670859a)
  • dry-types (1.4.0)

This is a known issue where custom types are “lost” in some situations. I’ll investigate and get back to you.