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