class NiceHash
def self.diff(expected, actual, prefix = "")
result = {}
exp_keys = expected.is_a?(Hash) ? expected.keys : (expected.is_a?(Array) ? (0...expected.size) : nil)
act_keys = actual.is_a?(Hash) ? actual.keys : (actual.is_a?(Array) ? (0...actual.size) : nil)
if expected.is_a?(Hash) && actual.is_a?(Hash)
all_keys = (expected.keys + actual.keys).uniq
all_keys.each do |k|
path = prefix.empty? ? k.to_s : "#{prefix}.#{k}"
exp_v = expected[k]
act_v = actual[k]
if !expected.key?(k)
result[path] = { expected: nil, got: act_v }
elsif !actual.key?(k)
result[path] = { expected: exp_v, got: nil }
elsif exp_v.is_a?(Hash) && act_v.is_a?(Hash)
result.merge!(diff(exp_v, act_v, path))
elsif exp_v.is_a?(Array) && act_v.is_a?(Array)
max_len = [exp_v.size, act_v.size].max
max_len.times do |i|
result.merge!(diff(exp_v[i], act_v[i], "#{path}[#{i}]"))
end
elsif exp_v != act_v
result[path] = { expected: exp_v, got: act_v }
end
end
elsif expected.is_a?(Array) && actual.is_a?(Array)
max_len = [expected.size, actual.size].max
max_len.times do |i|
result.merge!(diff(expected[i], actual[i], prefix.empty? ? "[#{i}]" : "#{prefix}[#{i}]"))
end
elsif expected != actual
result[prefix] = { expected: expected, got: actual } unless prefix.empty?
end
result
end
end