class SkotOS::XMLObject
Attributes
ignore_types[RW]
ignore_whitespace[RW]
merry_only[RW]
noko_doc[R]
pretty[R]
Public Class Methods
diff_between(obj1, obj2, o1_name: "Object 1", o2_name: "Object 2")
click to toggle source
# File lib/dgd-tools/skotos_xml_obj.rb, line 39 def self.diff_between(obj1, obj2, o1_name: "Object 1", o2_name: "Object 2") of1 = Tempfile.new("skotos_xml_diff1_") of2 = Tempfile.new("skotos_xml_diff2_") begin of1.write(obj1.pretty) of2.write(obj2.pretty) of1.close of2.close diff_opts = [ "c" ] diff_opts += [ "b", "B" ] if self.ignore_whitespace # Diff 'fails' if there's a difference between the two files. cmd = "diff -#{diff_opts.join("")} #{of1.path} #{of2.path}" #puts "Diff command: #{cmd}" diff = system_call(cmd, fail_ok: true) diff.sub!(of1.path, o1_name) diff.sub!(of2.path, o2_name) ensure of1.unlink of2.unlink end diff end
diff_dirs(dir1, dir2)
click to toggle source
# File lib/dgd-tools/skotos_xml_obj.rb, line 79 def self.diff_dirs(dir1, dir2) entries1 = skip_ignored_files(Dir.glob("*", base: dir1).to_a, dir1) entries2 = skip_ignored_files(Dir.glob("*", base: dir2).to_a, dir2) only_in_1 = entries1 - entries2 only_in_2 = entries2 - entries1 in_both = entries1 & entries2 diff = [] diff << "Only in first: #{only_in_1.map { |s| dir1 + "/" + s }.join(", ")}" unless only_in_1.empty? diff << "Only in second: #{only_in_2.map { |s| dir2 + "/" + s }.join(", ")}" unless only_in_2.empty? in_both.each do |file| in_1 = File.join dir1, file in_2 = File.join dir2, file if File.directory?(in_1) ^ File.directory?(in_2) diff << "Only a directory in one, not both: #{dir1}/#{file}" elsif File.directory?(in_1) d = diff_dirs(in_1, in_2) diff.concat(d) else o1 = from_file(in_1) o2 = from_file(in_2) this_diff = diff_between(o1, o2, o1_name: in_1, o2_name: in_2) diff << this_diff unless this_diff.strip == "" end end diff end
from_file(filename)
click to toggle source
# File lib/dgd-tools/skotos_xml_obj.rb, line 28 def self.from_file(filename) # SkotOS files often have references to undefined namespaces, # but we can get Nokogiri to parse it. doc = Nokogiri::XML(File.read filename) remove_undiffed(doc) pretty = doc.to_xml(indent:3) SkotOS::XMLObject.new pretty, noko_doc: doc end
new(pretty, noko_doc: nil)
click to toggle source
# File lib/dgd-tools/skotos_xml_obj.rb, line 23 def initialize(pretty, noko_doc: nil) @pretty = pretty @noko_doc = noko_doc end
noko_non_text(nodes)
click to toggle source
# File lib/dgd-tools/skotos_xml_obj.rb, line 171 def self.noko_non_text(nodes) nodes.select { |n| !n.is_a? Nokogiri::XML::Text } end
noko_remove(node)
click to toggle source
# File lib/dgd-tools/skotos_xml_obj.rb, line 154 def self.noko_remove(node) nn = node.next nn.remove if nn.is_a?(Nokogiri::XML::Text) node.remove end
noko_remove_non_merry_nodes(root)
click to toggle source
# File lib/dgd-tools/skotos_xml_obj.rb, line 184 def self.noko_remove_non_merry_nodes(root) root.children.each do |node| if node.name != "Core:PropertyContainer" node.remove next end node.children.each do |node2| if node2.name != "Core:PCProperties" node2.remove next end node2.children.each do |property_node| if property_node.name != "Core:Property" || property_node.attribute("property").value[0..5] != "merry:" property_node.remove next end # Leave the Merry node alone end if node2.children.size == 0 node2.remove end end if node.children.size == 0 node.remove end end end
noko_single_node(node, name, attrs: {})
click to toggle source
# File lib/dgd-tools/skotos_xml_obj.rb, line 160 def self.noko_single_node(node, name, attrs: {}) choices = noko_with_name_and_attrs(node, name, attrs) if choices.size < 1 nil elsif choices.size > 1 raise "Single-node search returned more than one node! #{name.inspect}, #{attrs.inspect}" else choices[0] end end
noko_with_name_and_attrs(node, name, attrs = {})
click to toggle source
# File lib/dgd-tools/skotos_xml_obj.rb, line 175 def self.noko_with_name_and_attrs(node, name, attrs = {}) results = node.children.flat_map { |n| noko_with_name_and_attrs(n, name, attrs) } if node.name == name && attrs.all? { |k, v| node.attribute(k).value == v } results << node end results end
remove_undiffed(doc)
click to toggle source
# File lib/dgd-tools/skotos_xml_obj.rb, line 109 def self.remove_undiffed(doc) if doc.root && doc.root.element? ignored_top_elements = ["program", "clone", "owner"] ignored_top_elements.each do |attr| if doc.root.attribute(attr) doc.root.remove_attribute(attr) end end end rev = noko_single_node(doc.root, "Core:Property", attrs: { "property" => "revisions" }) noko_remove(rev) if rev list = noko_single_node(doc.root, "Core:Property", attrs: { "property" => "#list#" }) list.remove if list properties = noko_with_name_and_attrs(doc.root, "Core:Property") properties.each do |prop_node| prop_node.remove if prop_node.attribute("property").value.start_with?("sys:sync") end if self.merry_only # Kill off all the non-Merry nodes noko_remove_non_merry_nodes(doc.root) end if self.ignore_types self.ignore_types.each do |ignored_type| skipped = noko_with_name_and_attrs(doc.root, ignored_type) skipped.each { |n| noko_remove(n) } end end base_combat = noko_single_node(doc.root, "Base:Combat") if base_combat base_strength = noko_single_node(base_combat, "Base:Strength", attrs: { "value" => "1" }) base_max_fatigue = noko_single_node(base_combat, "Base:MaxFatigue", attrs: { "value" => "1" }) if base_strength && base_max_fatigue && noko_non_text(base_combat.children).size == 2 next_text = base_combat.next base_combat.remove next_text.remove end end end
skip_ignored_files(list, base_dir)
click to toggle source
# File lib/dgd-tools/skotos_xml_obj.rb, line 65 def self.skip_ignored_files(list, base_dir) if self.merry_only list.select { |path| File.directory?(base_dir + "/" + path) || path[/.xml$/] || path[/.XML$/] } else list.select do |path| !path[/,v$/] && # Ignore files ending in comma-v !path[/-backup-\d+-\d+-\d+\.xml/] && # Ignore files ending in -backup-[DATE].xml path != ".git" && # Ignore .git directories path != "MOVED" # Ignore MOVED - it's a sort of recycle, waiting to be emptied end end end
system_call(cmd, fail_ok: false)
click to toggle source
# File lib/dgd-tools/skotos_xml_obj.rb, line 215 def self.system_call(cmd, fail_ok: false) f = Tempfile.new("system_call_xml_diff_") begin system(cmd, out: f) unless fail_ok || $?.success? f.rewind out = f.read raise "Error running command: #{cmd.inspect}!\n\nOutput:\n#{out}\n\n" end f.rewind return f.read ensure f.close f.unlink end end