Ensure that an key is present in tandem with an other keys depending on its value

Hey,

I have two optional keys key1 and key2 in my schema.

If this keys are present, both have to occur as a combination of either two integer or two nil values.

Supplying only one key is not valid.

How to ensure this with dry-validation?

I tried it with the schema blow … without success.

schema = Dry::Validation.Schema do
  optional(:key1).maybe(:int?)
  optional(:key2).maybe(:int?)
  rule(keys_occur_together: [:key1, :key2]) do |k1, k2|
    (k1.none? & k2.none?) | (k1.filled? & k2.filled?)
  end
end

# ++ OK because both values are nil
schema.call({key1: nil, key2: nil})
# ++ OK because both values are integers
schema.call({key1: 123, key2: 1234})

#  Should raise an error because key2 is missing - but it doesn't work
schema.call({key1: nil})
#  Should raise an error because key1 is missing - but it doesn't work
schema.call({key2: nil})

I’m currently struggling with the nil values.

If one of the keys it explicitly set to nil and the other one is not present, both are considered to be nil, which is not true.

none? is a check for nil, which both key1 and key2 are in your last two test cases. We can see they are both nil here:

    h = {key1: nil}
     => {:key1 => nil} 
    h[:key1]
     => nil 
    h[:key2]
     => nil 
    h[:key1] == h[:key2]
     => true 
    h[:key1].equal?(h[:key2])
     => true

Supplying either value produces an error as expected:

schema.call({key1: 1})
 => #<Dry::Validation::Result output={:key1=>1} errors={:keys_occur_together=>["cannot be defined or must be filled"]}> 
schema.call({key2: 2})
 => #<Dry::Validation::Result output={:key2=>2} errors={:keys_occur_together=>["cannot be defined or must be filled"]}>