Skip to content

Inconsistent behavior in nested schemas via passed in blocks  #703

@stevenhan2

Description

@stevenhan2

Describe the bug

When schemas are passed blocks, required field behavior does not always work, and there is inconsistent behavior for reaching validation rules -- although it seems to correlate with whether the required is evaluated.

To Reproduce

class TireSchema < Dry::Schema::JSON
    define do
        optional(:tread).value(:string, included_in?: %w(offroad allterrain sport road))
        optional(:size).value(:integer)
    end
end

class SportsCarSchema < Dry::Schema::JSON
    define do
        optional(:recommended_tire).hash.schema(TireSchema.new) do
            optional(:tread).value(:string, included_in?: %w(sport road))
        end
    end
end

class RaceDriverContract < Dry::Validation::Contract
    rule('car.recommended_tire.size') do
        puts "The runtime made it to the rule!"
    end

    json do
        required(:car).hash.schema(SportsCarSchema.new) do
            required(:recommended_tire).hash.schema(TireSchema.new) do
                required(:size).value(:integer)
            end
        end
    end
end

contract = RaceDriverContract.new

contract.call({ car: { recommended_tire: { tread: "invalid value" } } })
# The runtime made it to the rule!
# => #<Dry::Validation::Result{:car=>{:recommended_tire=>{:tread=>"invalid value"}}} errors={:car=>{:recommended_tire=>{:tread=>["must be one of: offroad, allterrain, sport, road"]}}}>

contract.call({ car: { recommended_tire: { tread: "sport" } } })
# => #<Dry::Validation::Result{:car=>{:recommended_tire=>{:tread=>"sport"}}} errors={:car=>{:recommended_tire=>{:size=>["is missing"]}}}>

contract.call({ car: { recommended_tire: { tread: "allterrain" } } })
# The runtime made it to the rule!
# => #<Dry::Validation::Result{:car=>{:recommended_tire=>{:tread=>"allterrain"}}} errors={:car=>{:recommended_tire=>{:tread=>["must be one of: sport, road"]}}}>

Expected behavior

I would expect every one of the above results to contain {:recommended_tire=>{:size=>["is missing"]}, and I would either expect all of them to make it to the rule or none of them to make it to the rule.

My environment

  • Affects my production application: YES
  • Ruby version: 2.7.2
  • OS: macOS 12.0.1 21A559

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions