lib/dry/schema/key_validator.rb in dry-schema-1.8.0 vs lib/dry/schema/key_validator.rb in dry-schema-1.9.0
- old
+ new
@@ -22,22 +22,12 @@
input_paths = key_paths(input)
key_paths = key_map.to_dot_notation
input_paths.each do |path|
- error_path =
- if path[INDEX_REGEX]
- key = path.gsub(INDEX_REGEX, BRACKETS)
+ error_path = validate_path(key_paths, path)
- if key_paths.none? { |key_path| key_path.include?(key) }
- arr = path.gsub(INDEX_REGEX) { |m| ".#{m[1]}" }
- arr.split(DOT).map { |s| DIGIT_REGEX.match?(s) ? s.to_i : s.to_sym }
- end
- elsif key_paths.none? { |key_path| key_path.include?(path) }
- path
- end
-
next unless error_path
result.add_error([:unexpected_key, [error_path, input]])
end
@@ -45,27 +35,58 @@
end
private
# @api private
- def key_paths(hash)
- hash.flat_map { |key, _|
- case (value = hash[key])
- when Hash
- next key.to_s if value.empty?
+ def validate_path(key_paths, path)
+ if path[INDEX_REGEX]
+ key = path.gsub(INDEX_REGEX, BRACKETS)
- [key].product(key_paths(hash[key])).map { |keys| keys.join(DOT) }
- when Array
- hashes_or_arrays = value.select { |e| (e.is_a?(Array) || e.is_a?(Hash)) && !e.empty? }
+ if key_paths.none? { paths_match?(key, _1) }
+ arr = path.gsub(INDEX_REGEX) { ".#{_1[1]}" }
+ arr.split(DOT).map { DIGIT_REGEX.match?(_1) ? Integer(_1, 10) : _1.to_sym }
+ end
+ elsif key_paths.none? { paths_match?(path, _1) }
+ path
+ end
+ end
- next key.to_s if hashes_or_arrays.empty?
+ # @api private
+ def paths_match?(input_path, key_path)
+ residue = key_path.sub(input_path, "")
+ residue.empty? || residue.start_with?(DOT, BRACKETS)
+ end
- hashes_or_arrays.flat_map.with_index { |el, idx|
- key_paths(el).map { |path| ["#{key}[#{idx}]", *path].join(DOT) }
- }
+ # @api private
+ def key_paths(hash)
+ hash.flat_map { |key, value|
+ case value
+ when ::Hash
+ if value.empty?
+ [key.to_s]
+ else
+ [key].product(key_paths(hash[key])).map { _1.join(DOT) }
+ end
+ when ::Array
+ hashes_or_arrays = hashes_or_arrays(value)
+
+ if hashes_or_arrays.empty?
+ [key.to_s]
+ else
+ hashes_or_arrays.flat_map.with_index { |el, idx|
+ key_paths(el).map { ["#{key}[#{idx}]", *_1].join(DOT) }
+ }
+ end
else
key.to_s
end
+ }
+ end
+
+ # @api private
+ def hashes_or_arrays(xs)
+ xs.select { |x|
+ (x.is_a?(::Array) || x.is_a?(::Hash)) && !x.empty?
}
end
end
end
end