Skip to content

Instantly share code, notes, and snippets.

@jordanorelli
Last active July 22, 2019 13:05
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jordanorelli/0fd0e92f6ae530fefbe84c272c39651c to your computer and use it in GitHub Desktop.
Save jordanorelli/0fd0e92f6ae530fefbe84c272c39651c to your computer and use it in GitHub Desktop.
diff yaml trees semantically
#!/usr/bin/env ruby
# diffs the contents of yaml files semantically. that is, ydiff will parse each
# yaml file and then diff their resulting trees, instead of attempting to diff
# the text. it's specifically for comparing directories of translations from
# Crowdin, so it's effectively only concerned with strings.
require 'pathname'
require 'rubygems'
require 'yaml'
require 'pp'
def diff_dir(left_path, right_path)
left_path.each_child do |left_child|
if left_child.directory?
right_candidate = right_path.join(left_child)
next unless right_candidate.exist?
diff_dir(left_child, right_candidate)
else
right_candidate = right_path.join(left_child)
next unless right_candidate.exist?
diff_file(left_child, right_candidate)
end
end
end
def diff_file(left_path, right_path)
left_tree = YAML.load_file(left_path)
right_tree = YAML.load_file(right_path)
puts "# ================================================================================"
puts "# < #{left_path}"
puts "# > #{right_path}"
puts "# ================================================================================"
puts ""
diff_tree(left_tree, right_tree)
end
def diff_tree(left, right, path = '')
case left
when Hash
diff_hash(left, right, path)
when String
diff_string(left, right, path)
when Array
diff_array(left, right, path)
when Symbol
diff_symbol(left, right, path)
else
STDERR.puts "SKIP #{path}: left type of #{left.class} is unhandled"
end
end
def added(item, path)
case item
when Hash
item.each_key do |key|
added(item[key], "#{path}/#{key}")
end
when String
puts "+ #{path}: #{item}"
when Array
item.each_with_index do |v, i|
added(v, "#{path}[#{i}]")
end
when Symbol
puts "+ #{path}: #{item}"
else
STDERR.puts "SKIP ADDED #{path}: dunno how to report #{item.class}: #{item}"
end
end
def diff_symbol(left, right, path = '')
if left != right
puts "- #{path} #{left}"
puts "+ #{path} #{right}"
end
end
def diff_array(left, right, path = '')
left.each_with_index do |v, i|
diff_tree(v, right[i], "#{path}[#{i}]")
end
end
def diff_string(left, right, path = '')
if left != right
puts "- #{path} #{left}"
puts "+ #{path} #{right}"
end
end
def diff_hash(left, right, path = '')
left.each_key do |key|
if right[key]
diff_tree(left[key], right[key], "#{path}/#{key}")
else
puts "- #{path}/#{key}"
end
end
right.each_key do |key|
unless left[key]
added(right[key], "#{path}/#{key}")
end
end
end
left_path = ARGV[0]
right_path = ARGV[1]
if File.directory? left_path
diff_dir(Pathname.new(left_path), Pathname.new(right_path))
else
diff_file(left_path, right_path)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment