Choosing a schema based on the value of a property


#1

Hi,

My use case is that I want to validate an input that requires different validations based on one key of that input.
Example: I accept a URL object as input and I want to perform different validations for HTTP and FTP schemes.

I thought about branching before running the schema validations, but that disallows using each to validate arrays in one go. I tried using custom validation blocks (when combined with and) but then I lost built-in error messages and hints. Can I achieve such branching in any way?

  HttpSchema = Dry::Validation.Schema do
    required(:method).filled?(:str?)
  end

  FtpSchema = Dry::Validation.Schema do
    required(:user).filled?(:str?)
  end

  UrlSchema = Dry::Validation.Schema do
    required(:scheme).filled(:str?).when(format?(/^http/)) do
      schema(HttpSchema) # pseudo-code
    end

    required(:scheme).filled(:str?).when(format?(/^ftp/)) do
      schema(FtpSchema)  # pseudo-code
    end
  end

  UrlsSchema = Dry::Validation.Schema do
    required(:urls).filled { each { schema(UrlSchema) } }
  end

  it 'is valid when method provided with http scheme' do
    expect(UrlsSchema.call(urls: [{scheme: 'http', method: 'foo'}]).success?).to eq(true)
  end

  it 'is valid when user provided with ftp scheme' do
    expect(UrlsSchema.call(urls: [{scheme: 'ftp', user: 'bar'}]).success?).to eq(true)
  end

Edit:
Note that in reality HttpSchema and FtpSchema equivalents are much more complex so using custom validation blocks is a problem.