dgd-tools 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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: