dgd-tools 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bf28b15e9e5aa7fe5c3d52835011e37c2b34f8ed839b59e6faea3ff9b23433d7
4
- data.tar.gz: 66d8280067875fe8dfbb056e3288dec4471eba855937ee182dd24db70f30cd9b
3
+ metadata.gz: 4bedae7ff16304641a20a9fe56eb1e6113bfe57641e6deedc05f23068303426b
4
+ data.tar.gz: 7d9956ee4dee5c1893e39d95a965e1ecc28ae5bda10cfb9b61c6cb0793174675
5
5
  SHA512:
6
- metadata.gz: 79fb4a29346b26de15f1cf4b6fe0c81ad845f45e24688b83f91f03c6aca61b691b6fb0698d64eb2fd633ae0f52d8df2bdb2226549ba524d748201b8f728adab2
7
- data.tar.gz: 1f4ffe31a23b5f0302b0d47815c74a4484847cfd9afce393d37c85515be981943447d5807eef11bca221269c2cde7c3137e5868561f306e8d83035ac5d03dd5b
6
+ metadata.gz: 81163aa4aca58ce6e5231c951244d74f9e979558d9164883ebc3ce11069c700b4ea15da3adca85cf668d053473060a721cfd56b3dc6830d22ab307ccf37d4678
7
+ data.tar.gz: c7bc8985bddaf46ca782ef9be6e84472b8f783c3edaa5477c9602975fb4b58aee8869b883582350fab613f53140bb91fd9d757354ae13d6231c127bf8c6b835d
@@ -2,11 +2,15 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  dgd-tools (0.1.2)
5
+ nokogiri (~> 1.10.5)
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
10
+ mini_portile2 (2.4.0)
9
11
  minitest (5.14.2)
12
+ nokogiri (1.10.10)
13
+ mini_portile2 (~> 2.4.0)
10
14
  rake (12.3.3)
11
15
 
12
16
  PLATFORMS
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "bundler/setup"
4
4
  require "dgd-tools/manifest"
5
+ require "dgd-tools/skotos_xml_obj"
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
@@ -23,4 +23,6 @@ Gem::Specification.new do |spec|
23
23
  spec.bindir = "exe"
24
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = ["lib"]
26
+
27
+ spec.add_runtime_dependency "nokogiri", "~>1.10.5"
26
28
  end
@@ -0,0 +1,42 @@
1
+ <object program="/base/sys/craftd">
2
+ <Base:CraftDaemon>
3
+ <Base:Craftables>
4
+ <Base:Craftable item="OBJ(Regent:Props:Fabric:blanket)"/>
5
+ <Base:Craftable item="OBJ(Regent:Staff:Evett:Items:Clothes:Top)"/>
6
+ <Base:Craftable item="OBJ(Regent:Props:Fabric:Cloth-cleaning)"/>
7
+ <Base:Craftable item="OBJ(Regent:Staff:Debbie:IC:General:orderbook)"/>
8
+ </Base:Craftables>
9
+ <Base:AllTools>
10
+ <Base:AllToolDependents tool="mop floor"/>
11
+ <Base:AllToolDependents tool="new">
12
+ <Base:OneToolDependent item="OBJ(Regent:Props:Fabric:blanket)"/>
13
+ <Base:OneToolDependent item="OBJ(Regent:Props:Fabric:Cloth-cleaning)"/>
14
+ <Base:OneToolDependent item="OBJ(Regent:Staff:Debbie:IC:General:orderbook)"/>
15
+ </Base:AllToolDependents>
16
+ <Base:AllToolDependents tool="regent:props:sewing:tools:needle"/>
17
+ <Base:AllToolDependents tool="regent:props:sewing:tools:scissors"/>
18
+ <Base:AllToolDependents tool="tool:heatable:container:watertight"/>
19
+ <Base:AllToolDependents tool="tool:heatsource:fire"/>
20
+ <Base:AllToolDependents tool="tools:needle">
21
+ <Base:OneToolDependent item="OBJ(Regent:Props:Fabric:blanket)"/>
22
+ <Base:OneToolDependent item="OBJ(Regent:Props:Fabric:Cloth-cleaning)"/>
23
+ </Base:AllToolDependents>
24
+ </Base:AllTools>
25
+ <Base:AllIngredients>
26
+ <Base:AllIngredientDependents ingredient="bolt">
27
+ <Base:OneIngredientDependent item="OBJ(Regent:Props:Fabric:blanket)"/>
28
+ <Base:OneIngredientDependent item="OBJ(Regent:Props:Fabric:Cloth-cleaning)"/>
29
+ </Base:AllIngredientDependents>
30
+ <Base:AllIngredientDependents ingredient="ingredient:candle"/>
31
+ <Base:AllIngredientDependents ingredient="ingredient:lantern-candle-empty"/>
32
+ <Base:AllIngredientDependents ingredient="ingredient:lantern:candle"/>
33
+ <Base:AllIngredientDependents ingredient="ingredients:tallow"/>
34
+ <Base:AllIngredientDependents ingredient="ingredients:wick:hemp"/>
35
+ <Base:AllIngredientDependents ingredient="new">
36
+ <Base:OneIngredientDependent item="OBJ(Regent:Props:Fabric:blanket)"/>
37
+ <Base:OneIngredientDependent item="OBJ(Regent:Staff:Evett:Items:Clothes:Top)"/>
38
+ <Base:OneIngredientDependent item="OBJ(Regent:Props:Fabric:Cloth-cleaning)"/>
39
+ </Base:AllIngredientDependents>
40
+ </Base:AllIngredients>
41
+ </Base:CraftDaemon>
42
+ </object>
@@ -0,0 +1,3 @@
1
+ <object program="/base/sys/materials">
2
+ <Base:Materials/>
3
+ </object>
@@ -0,0 +1,49 @@
1
+ <object program="/base/sys/properties">
2
+ <Base:PropertyTypes>
3
+ <Base:PropertyTypeIntList>
4
+ <Base:PropertyTypeInt property="page:allowall"/>
5
+ <Base:PropertyTypeInt property="skill:advancedefensive"/>
6
+ <Base:PropertyTypeInt property="skill:attackingdefensive"/>
7
+ <Base:PropertyTypeInt property="skill:cutoffensive"/>
8
+ <Base:PropertyTypeInt property="skill:dodgedefensive"/>
9
+ <Base:PropertyTypeInt property="skill:feintoffensive"/>
10
+ <Base:PropertyTypeInt property="skill:fumbledefensive"/>
11
+ <Base:PropertyTypeInt property="skill:guarddefensive"/>
12
+ <Base:PropertyTypeInt property="skill:idledefensive"/>
13
+ <Base:PropertyTypeInt property="skill:lang1"/>
14
+ <Base:PropertyTypeInt property="skill:lang2"/>
15
+ <Base:PropertyTypeInt property="skill:lang3"/>
16
+ <Base:PropertyTypeInt property="skill:lang4"/>
17
+ <Base:PropertyTypeInt property="skill:lang5"/>
18
+ <Base:PropertyTypeInt property="skill:lang6"/>
19
+ <Base:PropertyTypeInt property="skill:lang7"/>
20
+ <Base:PropertyTypeInt property="skill:recoverdefensive"/>
21
+ <Base:PropertyTypeInt property="skill:restdefensive"/>
22
+ <Base:PropertyTypeInt property="skill:retiredefensive"/>
23
+ <Base:PropertyTypeInt property="skill:salutedefensive"/>
24
+ <Base:PropertyTypeInt property="skill:teaching"/>
25
+ <Base:PropertyTypeInt property="skill:thrustoffensive"/>
26
+ <Base:PropertyTypeInt property="skill_study"/>
27
+ <Base:PropertyTypeInt property="skotos:currentlang"/>
28
+ </Base:PropertyTypeIntList>
29
+ <Base:PropertyTypeFloatList>
30
+ <Base:PropertyTypeFloat property="skill:fatigue"/>
31
+ </Base:PropertyTypeFloatList>
32
+ <Base:PropertyTypeStringList>
33
+ <Base:PropertyTypeString property="skotos:charname"/>
34
+ <Base:PropertyTypeString property="skotos:creator"/>
35
+ </Base:PropertyTypeStringList>
36
+ <Base:PropertyTypeObjectList>
37
+ <Base:PropertyTypeObject property="virtualhome:home"/>
38
+ <Base:PropertyTypeObject property="virtualhome:oubliette"/>
39
+ </Base:PropertyTypeObjectList>
40
+ <Base:PropertyTypeArrayList>
41
+ <Base:PropertyTypeArray property="startstory:skilllist:done"/>
42
+ <Base:PropertyTypeArray property="startstory:skilllist:left"/>
43
+ </Base:PropertyTypeArrayList>
44
+ <Base:PropertyTypeMappingList>
45
+ <Base:PropertyTypeMapping property="page:allow"/>
46
+ <Base:PropertyTypeMapping property="page:pending"/>
47
+ </Base:PropertyTypeMappingList>
48
+ </Base:PropertyTypes>
49
+ </object>
@@ -0,0 +1,41 @@
1
+ <object program="/base/obj/thing">
2
+ <Base:Thing>
3
+ <Ur:UrObject/>
4
+ <Base:Bulk immobile="false" mass="1" density="1"/>
5
+ <Base:Container flexible="false" transparent-container="false" public-container="false" tight="false" capacity="0" maxweight="0"/>
6
+ <Base:Misc gender="neuter" volition="false" weapon="false" default_stance="none" combinable="false" discrete="false" by_weight="false" tight="false" scriptrunner="false">
7
+ <Base:Edible value="false"/>
8
+ <Base:Potable value="false"/>
9
+ <Base:DrinkMessageFirst/>
10
+ <Base:DrinkMessageThird/>
11
+ <Base:Transparency value="false"/>
12
+ <Base:Unsafe value="false"/>
13
+ <Base:Safe value="false"/>
14
+ <Base:ClothesExpected value="false"/>
15
+ <Base:DieMessageFirst/>
16
+ <Base:DieMessageThird/>
17
+ </Base:Misc>
18
+ <Base:Details/>
19
+ <Base:Combat>
20
+ <Base:Strength value="1"/>
21
+ <Base:MaxFatigue value="1"/>
22
+ </Base:Combat>
23
+ <Base:Clothing>
24
+ <Base:SingleWear value="false"/>
25
+ </Base:Clothing>
26
+ <Base:Crafting see_level="0" do_level="0" time="0" attention="false" held="false">
27
+ <Base:Ingredients/>
28
+ <Base:Tools/>
29
+ <Base:CraftVerbs/>
30
+ </Base:Crafting>
31
+ <Base:InitialContents/>
32
+ <Base:InitialProperties/>
33
+ <Core:Properties>
34
+ <Core:Property property="revisions">
35
+ (\{ 1065557891, "the_exiled", "E" \})
36
+ </Core:Property>
37
+ <Core:Property property="skill:fatigue">1.0</Core:Property>
38
+ </Core:Properties>
39
+ <Notes:Notes/>
40
+ </Base:Thing>
41
+ </object>
@@ -0,0 +1,42 @@
1
+ <object program="/base/obj/thing" owner="bobo">
2
+ <Base:Thing>
3
+ <Ur:UrObject/>
4
+ <Base:Bulk immobile="false" mass="1" density="1"/>
5
+ <Base:Container flexible="false" transparent-container="false" public-container="false" tight="false" capacity="0" maxweight="0"/>
6
+ <Base:Misc gender="neuter" volition="false" weapon="false" default_stance="none" combinable="false" discrete="false" by_weight="false" tight="false" scriptrunner="false" bogusfield="nope">
7
+ <Base:Edible value="false"/>
8
+ <Base:Potable value="false"/>
9
+ <Base:DrinkMessageFirst/>
10
+ <Base:DrinkMessageThird/>
11
+ <Base:Transparency value="false"/>
12
+ <Base:Unsafe value="false"/>
13
+ <Base:Safe value="false"/>
14
+ <Base:ClothesExpected value="false"/>
15
+ <Base:DieMessageFirst/>
16
+ <Base:DieMessageThird/>
17
+ <Base:DieMessageFortySecond/>
18
+ </Base:Misc>
19
+ <Base:Details/>
20
+ <Base:Combat>
21
+ <Base:Strength value="1"/>
22
+ <Base:MaxFatigue value="1"/>
23
+ </Base:Combat>
24
+ <Base:Clothing>
25
+ <Base:SingleWear value="false"/>
26
+ </Base:Clothing>
27
+ <Base:Crafting see_level="0" do_level="0" time="0" attention="false" held="false">
28
+ <Base:Ingredients/>
29
+ <Base:Tools/>
30
+ <Base:CraftVerbs/>
31
+ </Base:Crafting>
32
+ <Base:InitialContents/>
33
+ <Base:InitialProperties/>
34
+ <Core:Properties>
35
+ <Core:Property property="revisions">
36
+ (\{ 1065557891, "the_exiled", "E" \})
37
+ </Core:Property>
38
+ <Core:Property property="skill:fatigue">1.0</Core:Property>
39
+ </Core:Properties>
40
+ <Notes:Notes/>
41
+ </Base:Thing>
42
+ </object>
@@ -14,6 +14,7 @@ when "test"
14
14
  puts "Running dgd.manifest installer..."
15
15
  repo = DGD::Manifest::Repo.new
16
16
  repo.manifest_file("dgd.manifest")
17
+ repo.precheck(".")
17
18
  puts "Verified Manifest packages: this looks likely correct."
18
19
  when "install"
19
20
  unless File.exist?("dgd.manifest")
@@ -22,7 +23,10 @@ when "install"
22
23
  puts "Running DGD Manifest installer..."
23
24
  repo = DGD::Manifest::Repo.new
24
25
  repo.manifest_file("dgd.manifest")
25
- repo.assemble_app(".")
26
+ current_dir = File.expand_path(".")
27
+ repo.precheck(current_dir)
28
+ repo.assemble_app(current_dir)
29
+ puts "Assembled DGD application into #{current_dir}"
26
30
  when "server"
27
31
  puts "Starting DGD server..."
28
32
  DGD::Manifest.system_call("~/.dgd-tools/dgd/bin/dgd dgd.config")
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Diff multiple Skotos-XML-format objects dumped from a
4
+ # SkotOS-or-similar game to find out what updates should
5
+ # be merged between them.
6
+
7
+ require "dgd-tools/skotos_xml_obj"
8
+
9
+ if ARGV.size != 2
10
+ STDERR.puts "Usage: skotos-xml-diff <obj_or_dir_1> <obj_or_dir_2>"
11
+ exit -1
12
+ end
13
+
14
+ if File.directory?(ARGV[0]) ^ File.directory?(ARGV[1])
15
+ STDERR.puts "Please give two files or two directories, not one of each!"
16
+ exit -1
17
+ end
18
+
19
+ if File.directory?(ARGV[0])
20
+ diff = SkotOS::XMLObject.diff_dirs(*ARGV)
21
+ if diff.empty?
22
+ puts "No difference."
23
+ else
24
+ puts diff.join("\n\n=====\n\n")
25
+ end
26
+ else
27
+ obj1 = SkotOS::XMLObject.from_file(ARGV[0])
28
+ obj2 = SkotOS::XMLObject.from_file(ARGV[1])
29
+ diff = SkotOS::XMLObject.diff_between(obj1, obj2)
30
+ if diff.empty?
31
+ puts "No difference."
32
+ else
33
+ puts diff
34
+ end
35
+ end
@@ -65,6 +65,43 @@ module DGD::Manifest
65
65
  @manifest_file ||= AppFile.new(self, path)
66
66
  end
67
67
 
68
+ def files_to_assemble
69
+ subdirs = []
70
+
71
+ @manifest_file.specs.each do |spec|
72
+ git_repo = spec.source
73
+ git_repo.use_details(spec.source_details)
74
+
75
+ spec.paths.each do |from, to|
76
+ from_path = "#{git_repo.local_dir}/#{from}"
77
+ if File.directory?(from_path)
78
+ files = Dir["#{from_path}/**/*"].to_a + Dir["#{from_path}/**/.*"].to_a
79
+ dirs = files.select { |file| File.directory?(file) }
80
+ non_dirs = files - dirs
81
+ subdirs << { from: from_path, to: to, dirs: dirs, non_dirs: non_dirs, source: git_repo }
82
+ elsif from_path["*"] # If from_path contains at least one asterisk
83
+ components = from.split("/")
84
+ first_wild_idx = components.index { |item| item["*"] }
85
+ no_wild_from_path = components[0..(first_wild_idx-1)].join("/")
86
+ wild_path = components[first_wild_idx..-1].join("/")
87
+
88
+ files = Dir["#{git_repo.local_dir}/#{no_wild_from_path}/#{wild_path}"].to_a
89
+ dirs = files.select { |file| File.directory?(file) }
90
+ dirs += files.map { |f| File.dirname(f) }
91
+ dirs.uniq!
92
+
93
+ non_dirs = files - dirs
94
+ subdirs << { from: "#{git_repo.local_dir}/#{no_wild_from_path}", to: to, dirs: dirs, non_dirs: non_dirs, source: git_repo }
95
+ else
96
+ # A single file
97
+ subdirs << { from: from_path, to: to, dirs: [], non_dirs: [from], source: git_repo }
98
+ end
99
+ end
100
+ end
101
+
102
+ subdirs
103
+ end
104
+
68
105
  def assemble_app(location)
69
106
  dgd_root = "#{File.expand_path(location)}/#{GENERATED_ROOT}"
70
107
  app_path = "#{File.expand_path(location)}/#{@manifest_file.app_root}"
@@ -72,26 +109,37 @@ module DGD::Manifest
72
109
  FileUtils.cp_r(app_path, dgd_root)
73
110
 
74
111
  write_config_file("#{location}/dgd.config")
75
- specs = @manifest_file.specs
112
+ FileUtils.mkdir_p("#{location}/state") # Statedir for statedumps, editor files, etc.
76
113
 
77
- copies = []
114
+ files_to_assemble.sort_by { |sd| sd[:to] }.each do |sd_hash|
115
+ to_path = "#{dgd_root}/#{sd_hash[:to]}"
78
116
 
79
- specs.each do |spec|
80
- git_repo = spec.source
81
- git_repo.use_details(spec.source_details)
117
+ # Make appropriate dirs, including empty ones
118
+ sd_hash[:dirs].each do |dir|
119
+ FileUtils.mkdir_p dir.sub(sd_hash[:from], to_path)
120
+ end
82
121
 
83
- spec.paths.each do |from, to|
84
- from_path = "#{git_repo.local_dir}/#{from}"
85
- to_path = "#{dgd_root}/#{to}"
86
- copies << [from_path, to_path]
122
+ # Copy all files
123
+ sd_hash[:non_dirs].each do |from_file|
124
+ to_file = from_file.sub(sd_hash[:from], "#{dgd_root}/#{sd_hash[:to]}")
125
+ to_dir = File.dirname(to_file)
126
+ FileUtils.mkdir_p to_dir
127
+ FileUtils.cp from_file, to_file
87
128
  end
88
129
  end
130
+ end
89
131
 
90
- copies.sort_by { |c| c[1] }.each do |from_path, to_path|
91
- to_dir = to_path.split("/")[0..-2].join("/")
92
- FileUtils.mkdir_p to_dir
93
- STDERR.puts "COPYING #{from_path.inspect} #{to_path.inspect}"
94
- FileUtils.cp_r(from_path, to_path)
132
+ def precheck(location)
133
+ app_path = "#{File.expand_path(location)}/#{@manifest_file.app_root}"
134
+ app_files = Dir["#{app_path}/**"].to_a.select { |f| !File.directory?(f) }
135
+
136
+ subdirs = files_to_assemble
137
+ all_files = subdirs.flat_map { |sd| sd[:non_dirs] } + app_files
138
+ all_files.sort!
139
+
140
+ if all_files.size != all_files.uniq.size
141
+ repeated = all_files.uniq.select { |f| all_files.count(f) > 1 }
142
+ raise "Error in dgd.manifest! Repeated files: #{repeated.inspect} / #{all_files.inspect}"
95
143
  end
96
144
  end
97
145
 
@@ -197,20 +245,20 @@ CONTENTS
197
245
  raise "Repeated (conflicting?) paths in dgd.manifest! #{repeated_paths.inspect}"
198
246
  end
199
247
 
200
- # Make sure the dgd.manifest file overrides either no kernel paths or both/all
201
- if KERNEL_PATHS.any? { |kp| output_paths.include?(kp) }
202
- unless KERNEL_PATHS.all? { |kp| output_paths.include?(kp) }
203
- raise "dgd.manifest file #{path.inspect} includes some Kernel Library paths but not all! All needed: #{KERNEL_PATHS}!"
204
- end
205
- puts "This dgd.manifest file overrides the Kernel Library with its own."
206
- else
207
- puts "This dgd.manifest needs the default Kernel Library."
208
- # This app has specified no kernellib paths -- add them
209
- git_repo = @repo.git_repo(DEFAULT_KERNELLIB_URL)
210
- klib_spec = GoodsSpec.new @repo, name: "default Kernel Library",
211
- source: git_repo, paths: KERNEL_PATH_MAP
212
- specs.unshift klib_spec
213
- end
248
+ ## Make sure the dgd.manifest file overrides either no kernel paths or both/all
249
+ #if KERNEL_PATHS.any? { |kp| output_paths.include?(kp) }
250
+ # unless KERNEL_PATHS.all? { |kp| output_paths.include?(kp) }
251
+ # raise "dgd.manifest file #{path.inspect} includes some Kernel Library paths but not all! All needed: #{KERNEL_PATHS}!"
252
+ # end
253
+ # puts "This dgd.manifest file overrides the Kernel Library with its own."
254
+ #else
255
+ # puts "This dgd.manifest needs the default Kernel Library."
256
+ # # This app has specified no kernellib paths -- add them
257
+ # git_repo = @repo.git_repo(DEFAULT_KERNELLIB_URL)
258
+ # klib_spec = GoodsSpec.new @repo, name: "default Kernel Library",
259
+ # source: git_repo, paths: KERNEL_PATH_MAP
260
+ # specs.unshift klib_spec
261
+ #end
214
262
 
215
263
  nil
216
264
  end
@@ -0,0 +1,169 @@
1
+ require "dgd-tools/version"
2
+
3
+ # Nokogiri is unusually permissive as an XML parser, which is
4
+ # good - SkotOS XML objects don't parse with most XML parsers.
5
+ require "nokogiri"
6
+
7
+ require "tempfile"
8
+
9
+ module SkotOS; end
10
+
11
+ class SkotOS::XMLObject
12
+ attr_reader :pretty
13
+
14
+ def initialize(pretty)
15
+ @pretty = pretty
16
+ end
17
+
18
+ def self.from_file(filename)
19
+ # SkotOS files often have references to undefined namespaces,
20
+ # but we can get Nokogiri to parse it.
21
+ doc = Nokogiri::XML(File.read filename)
22
+
23
+ remove_undiffed(doc)
24
+
25
+ pretty = doc.to_xml(indent:3)
26
+ #data = doc.to_hash
27
+ #prune_whitespace(data)
28
+ SkotOS::XMLObject.new pretty
29
+ end
30
+
31
+ def self.diff_between(obj1, obj2, o1_name: "Object 1", o2_name: "Object 2")
32
+ of1 = Tempfile.new("skotos_xml_diff1_")
33
+ of2 = Tempfile.new("skotos_xml_diff2_")
34
+
35
+ begin
36
+ of1.write(obj1.pretty)
37
+ of2.write(obj2.pretty)
38
+ of1.close
39
+ of2.close
40
+
41
+ # Diff 'fails' if there's a difference between the two files.
42
+ diff = system_call("diff -c #{of1.path} #{of2.path}", fail_ok: true)
43
+ diff.sub!(of1.path, o1_name)
44
+ diff.sub!(of2.path, o2_name)
45
+ ensure
46
+ of1.unlink
47
+ of2.unlink
48
+ end
49
+ diff
50
+ end
51
+
52
+ def self.diff_dirs(dir1, dir2)
53
+ entries1 = Dir.glob("*", base: dir1).to_a
54
+ entries2 = Dir.glob("*", base: dir2).to_a
55
+
56
+ only_in_1 = entries1 - entries2
57
+ only_in_2 = entries2 - entries1
58
+ in_both = entries1 & entries2
59
+
60
+ diff = []
61
+ diff << "Only in first: #{only_in_1.join(", ")}" unless only_in_1.empty?
62
+ diff << "Only in second: #{only_in_2.join(", ")}" unless only_in_2.empty?
63
+
64
+ in_both.each do |file|
65
+ in_1 = "#{dir1}/#{file}"
66
+ in_2 = "#{dir2}/#{file}"
67
+ if File.directory?(in_1) ^ File.directory?(in_2)
68
+ diff << "Only a directory in one, not both: #{dir1}/#{file}"
69
+ elsif File.directory?(in_1)
70
+ d = diff_dirs(in_1, in_2)
71
+ diff.concat(d)
72
+ else
73
+ o1 = from_file(in_1)
74
+ o2 = from_file(in_2)
75
+ diff << diff_between(o1, o2, o1_name: in_1, o2_name: in_2)
76
+ end
77
+ end
78
+ diff
79
+ end
80
+
81
+ def self.remove_undiffed(doc)
82
+ if doc.root && doc.root.element? && doc.root.attribute("owner")
83
+ doc.root.remove_attribute("owner")
84
+ end
85
+ end
86
+
87
+ def self.system_call(cmd, fail_ok: false)
88
+ f = Tempfile.new("system_call_xml_diff_")
89
+ begin
90
+ puts "Running command: #{cmd.inspect}..."
91
+ system(cmd, out: f)
92
+ unless fail_ok || $?.success?
93
+ f.rewind
94
+ out = f.read
95
+ raise "Error running command: #{cmd.inspect}!\n\nOutput:\n#{out}\n\n"
96
+ end
97
+ f.rewind
98
+ return f.read
99
+ ensure
100
+ f.close
101
+ f.unlink
102
+ end
103
+ end
104
+ end
105
+
106
+ =begin
107
+ # Abandoned approach follows
108
+ # Some code taken from: https://stackoverflow.com/a/10144623
109
+ class Nokogiri::XML::Node
110
+ TYPENAMES = {1=>'element',2=>'attribute',3=>'plaintext',4=>'cdata',8=>'comment'}
111
+ def to_hash
112
+ {kind:TYPENAMES[node_type],name:name}.tap do |h|
113
+ h.merge! nshref:namespace.href, nsprefix:namespace.prefix if namespace
114
+ h.merge! text:text
115
+ h.merge! attr:attribute_nodes.map(&:to_hash) if element?
116
+ h.merge! kids:children.map(&:to_hash) if element?
117
+ end
118
+ end
119
+ end
120
+ class Nokogiri::XML::Document
121
+ def to_hash; root.to_hash; end
122
+ end
123
+
124
+ class SkotOS::XMLObject
125
+ OBJ_FIELDS = [:kind, :name, :text, :attr, :nshref, :nsprefix]
126
+ def self.diff_between(obj1, obj2, diff = [])
127
+ single_obj1 = obj1.slice(*OBJ_FIELDS)
128
+ single_obj2 = obj2.slice(*OBJ_FIELDS)
129
+
130
+ this_diff = []
131
+ OBJ_FIELDS.each do |field|
132
+ if single_obj1[field] != single_obj2[field]
133
+ this_diff.concat ["+#{field}: #{single_obj2[field]}", "-#{field}: #{single_obj1[field]}"]
134
+ end
135
+ end
136
+
137
+ single_obj1[:kids]
138
+
139
+ diff
140
+ end
141
+
142
+ def self.prune_whitespace(data)
143
+ data[:text].gsub!(/\W+/, " ")
144
+ data[:text].strip!
145
+ new_kids = data[:kids].flat_map do |node|
146
+ if node[:kind] == "comment"
147
+ []
148
+ elsif node[:kind] == "plaintext"
149
+ new_text = node[:text].gsub(/\W+/, " ").strip
150
+ if new_text == ""
151
+ []
152
+ else
153
+ node[:text] = new_text
154
+ [node]
155
+ end
156
+ elsif node[:kind] == "element" || node[:kind] == "attribute"
157
+ node[:text].gsub!(/\W+/, " ")
158
+ node[:text].strip!
159
+ prune_whitespace(node)
160
+ [node]
161
+ else
162
+ raise "Is this illegal or did I just not anticipate it?"
163
+ end
164
+ end
165
+ data[:kids] = new_kids
166
+ nil
167
+ end
168
+ end
169
+ =end
@@ -1,3 +1,3 @@
1
1
  module DGD
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
metadata CHANGED
@@ -1,21 +1,36 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dgd-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noah Gibbs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-23 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2020-11-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.10.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.10.5
13
27
  description: dgd-tools supplies DGD Manifest and eventually perhaps other tools. DGD
14
28
  Manifest is an experimental DGD library and packaging system.
15
29
  email:
16
30
  - the.codefolio.guy@gmail.com
17
31
  executables:
18
32
  - dgd-manifest
33
+ - skotos-xml-diff
19
34
  extensions: []
20
35
  extra_rdoc_files: []
21
36
  files:
@@ -29,9 +44,16 @@ files:
29
44
  - bin/console
30
45
  - bin/setup
31
46
  - dgd-tools.gemspec
47
+ - example_xml/CraftDaemon.xml
48
+ - example_xml/Materials.xml
49
+ - example_xml/PropertyTypes.xml
50
+ - example_xml/Thing.xml
51
+ - example_xml/Thing2.xml
32
52
  - exe/dgd-manifest
53
+ - exe/skotos-xml-diff
33
54
  - goods/skotos_httpd.goods
34
55
  - lib/dgd-tools/manifest.rb
56
+ - lib/dgd-tools/skotos_xml_obj.rb
35
57
  - lib/dgd-tools/version.rb
36
58
  homepage: https://github.com/noahgibbs/dgd-tools
37
59
  licenses: