dgd-tools 0.1.6 → 0.1.7

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: f64aa1b29bc2fb6a0c10292a25246ce4fbe5a43b7fa23dc19a84ab01abc58013
4
- data.tar.gz: 60fb058d6f25ca80348e5236450ed3c5fe812137d04e0fce237d36b6b252dfca
3
+ metadata.gz: 2b3639fa674b53796ffe654f0e7e0deb9506868eece4550b99b4f28b98940e97
4
+ data.tar.gz: b0f115747b8f790ba079a9789d18e5bbe024de6e9b1c6cc1c476d21066a1b48a
5
5
  SHA512:
6
- metadata.gz: 30b54b93e1b4e5da44c7cf0bc6e526dd489d74301df20cd4d9644c69aab509416d3082d2646dd6394331f81643a92cbc24933141fdca024057ed6e003866b37a
7
- data.tar.gz: 525305f71a39842d01f7ce99f3bce1d85d7dc0e5493411e09759cf003a8632609130281f40ba8ff25bf7edb8ffa73de5e9b53d3c3022c4143a8b99be70a9c4f9
6
+ metadata.gz: c739260975e503e32a44bfeaf4775d6693e606f35a247f6d432008d0b9e813ac7b29208d1db641dee46be7aec8bbb86e92c1f8fe7e9c7b1c2aadb2ce93e4fb8e
7
+ data.tar.gz: 50cc3537969a52ca379857a8936b900212e18567bdc10c1cb1152cffb7901f88db53b75a04126b8848e0edb3151d74a4514cc3fe02d7c1ae1f0ece0cbbe7d6f3
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dgd-tools (0.1.5)
4
+ dgd-tools (0.1.7)
5
5
  nokogiri (~> 1.10.5)
6
+ optimist (~> 3.0.1)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
@@ -11,6 +12,7 @@ GEM
11
12
  minitest (5.14.2)
12
13
  nokogiri (1.10.10)
13
14
  mini_portile2 (~> 2.4.0)
15
+ optimist (3.0.1)
14
16
  rake (12.3.3)
15
17
 
16
18
  PLATFORMS
data/README.md CHANGED
@@ -14,6 +14,18 @@ DGD Manifest is a simple initial library system. I'm sure I'll figure more out a
14
14
 
15
15
  This work has grown out of [SkotOS and ChatTheatre](https://github.com/ChatTheatre) tasks.
16
16
 
17
+ You can find example DGD manifest files under the "test" directory and also in [various](https://github.com/noahgibbs/prototype_vRWOT) [SkotOS-based games](https://github.com/ChatTheatre/gables_game) that use the DGD Manifest system.
18
+
19
+ You can find example "goods" (library) files under the "goods" subdirectory of this repo.
20
+
21
+ ## WOE Objects and skotos-xml-diff
22
+
23
+ SkotOS-based games use an XML format for in-game objects called WOE, which is [documented in SkotOS-Doc](https://ChatTheatre.github.io/SkotOS-Doc). The skotos-xml-diff utility will diff between WOE objects or directories of WOE objects.
24
+
25
+ See SkotOS-Doc for more detail about how this can be used with a SkotOS game.
26
+
27
+ Run "skotos-xml-diff --help" for a list of options. You can tell it to ignore whitespace, to diff only the Merry (script) contents of the objects, and to ignore certain XML node types.
28
+
17
29
  ## Installation
18
30
 
19
31
  You would normally install DGDTools directly: `gem install dgd-tools`.
@@ -26,6 +38,8 @@ If you have a DGD application that uses DGD Manifest, run `dgd-manifest install`
26
38
 
27
39
  That fully-assembled DGD directory is named ".root". To run your dgd server, type "dgd-manifest server".
28
40
 
41
+ If you update files in your root and want to update files under the generated root directory, use "dgd-manifest update".
42
+
29
43
  ## Using DGD Manifest with your DGD Application
30
44
 
31
45
  Your app will need a dgd.manifest file, which is a lot like NPM's package.json file.
data/dgd-tools.gemspec CHANGED
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.require_paths = ["lib"]
26
26
 
27
27
  spec.add_runtime_dependency "nokogiri", "~>1.10.5"
28
+ spec.add_runtime_dependency "optimist", "~>3.0.1"
28
29
  end
data/exe/dgd-manifest CHANGED
@@ -51,6 +51,17 @@ when "install"
51
51
  repo.precheck(current_dir)
52
52
  repo.assemble_app(current_dir)
53
53
  puts "Assembled DGD application into #{current_dir}"
54
+ when "update"
55
+ unless File.exist?("dgd.manifest")
56
+ raise "I don't see a dgd.manifest file in this directory!"
57
+ end
58
+ puts "Running DGD Manifest installer..."
59
+ repo = DGD::Manifest::Repo.new
60
+ repo.manifest_file("dgd.manifest")
61
+ current_dir = File.expand_path(".")
62
+ repo.precheck(current_dir)
63
+ repo.update_app(current_dir)
64
+ puts "Updated DGD application in #{current_dir}"
54
65
  when "server"
55
66
  puts "Starting DGD server..."
56
67
  DGD::Manifest.system_call("~/.dgd-tools/dgd/bin/dgd dgd.config")
data/exe/skotos-xml-diff CHANGED
@@ -4,28 +4,27 @@
4
4
  # SkotOS-or-similar game to find out what updates should
5
5
  # be merged between them.
6
6
 
7
+ require 'optimist'
7
8
  require "dgd-tools/skotos_xml_obj"
8
9
 
9
- if ARGV.size == 1 && ["-h", "--help"].include?(ARGV[0])
10
- print <<HELP_INFO
11
- skotos-xml-diff [file1] [file2]
12
-
13
- OR
14
-
15
- skotos-xml-diff [dir1] [dir2]
16
- HELP_INFO
17
- exit
10
+ OPTS = Optimist::options do
11
+ version "DGD-tools version #{DGD::VERSION}"
12
+ banner <<BANNER
13
+ Usage:
14
+ skotos-xml-diff [options] <file_or_dir_1> <file_or_dir_2>
15
+ where [options] are:
16
+ BANNER
17
+ opt :merry_only, "Only diff Merry scripts"
18
+ opt :ignore_whitespace, "Ignore whitespace in final diff"
19
+ opt :ignore_types, "Comma-separated list of XML node types to ignore (e.g. Combat:Base,Base:Exit)", type: :string
18
20
  end
19
21
 
20
- if ARGV.size != 2
21
- STDERR.puts "Usage: skotos-xml-diff <obj_or_dir_1> <obj_or_dir_2>"
22
- exit -1
23
- end
22
+ Optimist::die "Supply exactly two files or directories -- you supplied #{ARGV.size}!" unless ARGV.size == 2
23
+ Optimist::die "Supply both files or both directories, not one of each!" if File.directory?(ARGV[0]) ^ File.directory?(ARGV[1])
24
24
 
25
- if File.directory?(ARGV[0]) ^ File.directory?(ARGV[1])
26
- STDERR.puts "Please give two files or two directories, not one of each!"
27
- exit -1
28
- end
25
+ SkotOS::XMLObject.merry_only = true if OPTS[:merry_only]
26
+ SkotOS::XMLObject.ignore_whitespace = true if OPTS[:ignore_whitespace]
27
+ SkotOS::XMLObject.ignore_types = OPTS[:ignore_types].split(",") if OPTS[:ignore_types]
29
28
 
30
29
  if File.directory?(ARGV[0])
31
30
  diff = SkotOS::XMLObject.diff_dirs(*ARGV)
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "ChatTheatre Kernellib",
3
+ "git": {
4
+ "url": "https://github.com/ChatTheatre/kernellib.git",
5
+ "branch": "master"
6
+ },
7
+ "paths": {
8
+ "src/include/kernel": "include/kernel",
9
+ "src/include/*.h": "include",
10
+ "src/kernel": "kernel"
11
+ }
12
+ }
@@ -16,7 +16,6 @@ module DGD::Manifest
16
16
  }
17
17
  KERNEL_PATHS = KERNEL_PATH_MAP.values
18
18
  DEFAULT_KERNELLIB_URL = "https://github.com/ChatTheatre/kernellib"
19
-
20
19
  GENERATED_ROOT = ".root"
21
20
 
22
21
  def self.system_call(cmd)
@@ -78,12 +77,12 @@ module DGD::Manifest
78
77
  raise("No manifest file!") if @no_manifest_file
79
78
 
80
79
  @manifest_file.specs.each do |spec|
81
- git_repo = spec.source
82
- git_repo.use_details(spec.source_details) # This sets things like checked-out branch
80
+ spec_git_repo = spec.source
81
+ spec_git_repo.use_details(spec.source_details) # This sets things like checked-out branch
83
82
 
84
83
  spec.paths.each do |from, to|
85
- # Note: git_repo.local_dir is an absolute path.
86
- from_path = "#{git_repo.local_dir}/#{from}"
84
+ # Note: spec_git_repo.local_dir is an absolute path.
85
+ from_path = "#{spec_git_repo.local_dir}/#{from}"
87
86
  if File.directory?(from_path)
88
87
  files = Dir["#{from_path}/**/*"].to_a + Dir["#{from_path}/**/.*"].to_a
89
88
  dirs = files.select { |file| File.directory?(file) }
@@ -95,13 +94,13 @@ module DGD::Manifest
95
94
  no_wild_from_path = components[0..(first_wild_idx-1)].join("/")
96
95
  wild_path = components[first_wild_idx..-1].join("/")
97
96
 
98
- files = Dir["#{git_repo.local_dir}/#{no_wild_from_path}/#{wild_path}"].to_a
97
+ files = Dir["#{spec_git_repo.local_dir}/#{no_wild_from_path}/#{wild_path}"].to_a
99
98
  dirs = files.select { |file| File.directory?(file) }
100
99
  dirs += files.map { |f| File.dirname(f) }
101
100
  dirs.uniq!
102
101
 
103
102
  non_dirs = files - dirs
104
- operations << { cmd: "cp", from: "#{git_repo.local_dir}/#{no_wild_from_path}", to: to, dirs: dirs, non_dirs: non_dirs, comment: :path_wildcard }
103
+ operations << { cmd: "cp", from: "#{spec_git_repo.local_dir}/#{no_wild_from_path}", to: to, dirs: dirs, non_dirs: non_dirs, comment: :path_wildcard }
105
104
  else
106
105
  # A single file
107
106
  operations << { cmd: "cp", from: from_path, to: to, dirs: [], non_dirs: [from_path], comment: :single_file }
@@ -122,16 +121,29 @@ module DGD::Manifest
122
121
 
123
122
  public
124
123
 
124
+ def dgd_root(location)
125
+ "#{File.expand_path(location)}/#{GENERATED_ROOT}"
126
+ end
127
+
125
128
  def assemble_app(location)
126
- dgd_root = "#{File.expand_path(location)}/#{GENERATED_ROOT}"
127
- FileUtils.rm_rf(dgd_root)
129
+ Dir[File.join(dgd_root(location), "*")].each { |dir| FileUtils.rm_rf dir }
130
+
131
+ write_app_files(location)
132
+ end
133
+
134
+ def update_app(location)
135
+ write_app_files(location) # TODO: maybe check files dates? Some way to do update-only
136
+ end
128
137
 
138
+ protected
139
+
140
+ def write_app_files(location)
129
141
  Dir.chdir(location) do
130
142
  write_config_file("#{location}/dgd.config")
131
143
  FileUtils.mkdir_p("#{location}/state") # Statedir for statedumps, editor files, etc.
132
144
 
133
145
  assembly_operations(location).each do |sd_hash|
134
- to_path = "#{dgd_root}/#{sd_hash[:to]}"
146
+ to_path = "#{dgd_root(location)}/#{sd_hash[:to]}"
135
147
 
136
148
  # Make appropriate dirs, including empty ones
137
149
  sd_hash[:dirs].each do |dir|
@@ -140,7 +152,7 @@ module DGD::Manifest
140
152
 
141
153
  # Copy all files
142
154
  sd_hash[:non_dirs].each do |from_file|
143
- to_file = from_file.sub(sd_hash[:from], "#{dgd_root}/#{sd_hash[:to]}")
155
+ to_file = from_file.sub(sd_hash[:from], "#{dgd_root(location)}/#{sd_hash[:to]}")
144
156
  to_dir = File.dirname(to_file)
145
157
  FileUtils.mkdir_p to_dir
146
158
  FileUtils.cp from_file, to_file
@@ -149,6 +161,8 @@ module DGD::Manifest
149
161
  end
150
162
  end
151
163
 
164
+ public
165
+
152
166
  def precheck(location)
153
167
  all_files = assembly_operations(location).flat_map { |sd| sd[:non_dirs] }
154
168
 
@@ -160,40 +174,7 @@ module DGD::Manifest
160
174
 
161
175
  def write_config_file(path)
162
176
  File.open(path, "wb") do |f|
163
- f.write <<CONTENTS
164
- /* These are SkotOS limits. They are larger than you are likely to need. They should
165
- be configurable but they are not yet. */
166
- telnet_port = ([
167
- "*":50100 /* telnet port number */
168
- ]);
169
- binary_port = ([
170
- "*":50110 /* Failsafe */
171
- ]); /* binary ports */
172
- directory = "./#{GENERATED_ROOT}";
173
-
174
- users = 100; /* max # of users */
175
- editors = 40; /* max # of editor sessions */
176
- ed_tmpfile = "../state/ed"; /* proto editor tmpfile */
177
- swap_file = "../state/swap"; /* swap file */
178
- swap_size = 1048576; /* # sectors in swap file */
179
- sector_size = 512; /* swap sector size */
180
- swap_fragment = 4096; /* fragment to swap out */
181
- static_chunk = 64512; /* static memory chunk */
182
- dynamic_chunk = 261120; /* dynamic memory chunk */
183
- dump_file = "../state/dump"; /* dump file */
184
- dump_interval = 3600; /* dump interval */
185
-
186
- typechecking = 2; /* highest level of typechecking */
187
- include_file = "/include/std.h"; /* standard include file */
188
- include_dirs = ({ "/include", "~/include" }); /* directories to search */
189
- auto_object = "/kernel/lib/auto"; /* auto inherited object */
190
- driver_object = "/kernel/sys/driver"; /* driver object */
191
- create = "_F_create"; /* name of create function */
192
-
193
- array_size = 16384; /* max array size */
194
- objects = 262144; /* max # of objects */
195
- call_outs = 16384; /* max # of call_outs */
196
- CONTENTS
177
+ f.write @manifest_file.dgd_config.as_file
197
178
  end
198
179
  end
199
180
  end
@@ -226,11 +207,11 @@ CONTENTS
226
207
  def use_details(details)
227
208
  if details["branch"]
228
209
  Dir.chdir(@local_dir) do
229
- DGD::Manifest.system_call("git checkout #{details["branch"]}")
210
+ DGD::Manifest.system_call("git checkout #{details["branch"]} && git pull")
230
211
  end
231
212
  else
232
213
  Dir.chdir(@local_dir) do
233
- DGD::Manifest.system_call("git checkout #{default_branch}")
214
+ DGD::Manifest.system_call("git checkout #{default_branch} && git pull")
234
215
  end
235
216
  end
236
217
  end
@@ -240,7 +221,7 @@ CONTENTS
240
221
  attr_reader :path
241
222
  attr_reader :repo
242
223
  attr_reader :specs
243
- attr_reader :app_root
224
+ attr_reader :dgd_config
244
225
 
245
226
  def initialize(repo, path)
246
227
  @path = path
@@ -250,8 +231,6 @@ CONTENTS
250
231
 
251
232
  read_manifest_file(contents)
252
233
 
253
- @app_root = contents["app_root"] || "app"
254
-
255
234
  output_paths = @specs.flat_map { |s| s.paths.values }
256
235
  unless output_paths == output_paths.uniq
257
236
  repeated_paths = output_paths.select { |p| output_paths.count(p) > 1 }
@@ -267,9 +246,9 @@ CONTENTS
267
246
  #else
268
247
  # puts "This dgd.manifest needs the default Kernel Library."
269
248
  # # This app has specified no kernellib paths -- add them
270
- # git_repo = @repo.git_repo(DEFAULT_KERNELLIB_URL)
249
+ # spec_git_repo = @repo.git_repo(DEFAULT_KERNELLIB_URL)
271
250
  # klib_spec = GoodsSpec.new @repo, name: "default Kernel Library",
272
- # source: git_repo, paths: KERNEL_PATH_MAP
251
+ # source: spec_git_repo, paths: KERNEL_PATH_MAP
273
252
  # specs.unshift klib_spec
274
253
  #end
275
254
 
@@ -298,6 +277,8 @@ CONTENTS
298
277
 
299
278
  @specs = []
300
279
 
280
+ @dgd_config = DGDRuntimeConfig.new (contents["config"] || {})
281
+
301
282
  if contents["unbundled_goods"]
302
283
  raise "Unbundled_goods must be an array!" unless contents["unbundled_goods"].is_a?(Array)
303
284
 
@@ -319,9 +300,15 @@ CONTENTS
319
300
  end
320
301
  end
321
302
 
303
+ def app_root
304
+ @dgd_config.app_root
305
+ end
306
+
322
307
  def unbundled_json_to_spec(fields)
323
308
  source = nil
324
309
  source_details = nil
310
+ dependencies = []
311
+
325
312
  if fields["git"]
326
313
  raise "A git source requires a git url: #{fields.inspect}!" unless fields["git"]["url"]
327
314
  source = @repo.git_repo(fields["git"]["url"])
@@ -334,7 +321,23 @@ CONTENTS
334
321
  raise "Paths in Goods files must map strings to strings! #{fields["paths"].inspect}"
335
322
  end
336
323
 
337
- spec = GoodsSpec.new(@repo, name: fields["name"], source: source, source_details: source_details, paths: fields["paths"])
324
+ if fields["dependencies"]
325
+ # For now, permit a single string as a dependency.
326
+ fields["dependencies"] = [ fields["dependencies"] ] if fields["dependencies"].is_a?(String)
327
+
328
+ fields["dependencies"].each do |dep|
329
+ if dep.is_a?(String)
330
+ dependencies.push "url" => dep
331
+ elsif dep.is_a?(Hash)
332
+ raise "Currently only URL-based dependencies on Goods files are supported!" unless dep["url"]
333
+ dependencies.push dep
334
+ else
335
+ raise "Unexpected dependency type #{dep.class} when parsing DGD Manifest specs, item: #{dep.inspect}"
336
+ end
337
+ end
338
+ end
339
+
340
+ spec = GoodsSpec.new(@repo, name: fields["name"], source: source, source_details: source_details, paths: fields["paths"], dependencies: dependencies)
338
341
  return spec
339
342
  end
340
343
  end
@@ -345,8 +348,9 @@ CONTENTS
345
348
  attr_reader :source
346
349
  attr_reader :source_details
347
350
  attr_reader :paths
351
+ attr_reader :dependencies
348
352
 
349
- def initialize(repo, name:, source:, source_details: {}, paths:)
353
+ def initialize(repo, name:, source:, source_details: {}, paths:, dependencies:)
350
354
  @repo = repo
351
355
  @name = name
352
356
  @source = source
@@ -359,6 +363,7 @@ CONTENTS
359
363
  end
360
364
 
361
365
  @paths = cleaned_paths
366
+ @dependencies = dependencies
362
367
  end
363
368
  end
364
369
 
@@ -455,4 +460,114 @@ FILE_CONTENTS
455
460
  puts "Successfully created project at #{@location}."
456
461
  end
457
462
  end
463
+
464
+ class DGDRuntimeConfig
465
+ attr_reader :app_root
466
+
467
+ DEFAULT_CONFIG = {
468
+ users: 100,
469
+ editors: 40,
470
+ swap_size: 1048576,
471
+ sector_size: 512,
472
+ swap_fragment: 4096,
473
+ static_chunk: 64512,
474
+ dynamic_chunk: 261120,
475
+ dump_interval: 3600,
476
+ typechecking: 2,
477
+ include_file: "/include/std.h",
478
+ include_dirs: ["/include", "~/include"],
479
+ auto_object: "/kernel/lib/auto",
480
+ driver_object: "/kernel/sys/driver",
481
+ create: "_F_create",
482
+ array_size: 16384,
483
+ objects: 262144,
484
+ call_outs: 16384,
485
+ }
486
+ CONFIG_KEYS = DEFAULT_CONFIG.keys.map(&:to_s) + [ "app_root", "ports", "telnet_ports", "dump_file", "statedir" ]
487
+
488
+ def initialize(config_data)
489
+ @app_root = config_data["app_root"] || "app"
490
+ @ports = {
491
+ "*" => 50100,
492
+ }
493
+ @telnet_ports = {
494
+ "*" => 50110,
495
+ }
496
+ @statedir = config_data["statedir"] || "state"
497
+ @dump_file = if config_data["dump_file"]
498
+ "../" + config_data["dump_file"]
499
+ else
500
+ "../#{@statedir}/dump"
501
+ end
502
+ @config = DEFAULT_CONFIG.dup
503
+
504
+ @raw_data = config_data
505
+ @config.keys.each do |prop|
506
+ # For now, assume and require that JSON data is the correct type if present
507
+ @config[prop] = config_data[prop.to_s] if config_data[prop.to_s]
508
+ end
509
+ unexpected_config_keys = config_data.keys - CONFIG_KEYS
510
+ unless unexpected_config_keys.empty?
511
+ raise "Unexpected key names in DGD configuration: #{unexpected_config_keys.inspect}!"
512
+ end
513
+
514
+ if config_data["telnet_ports"]
515
+ @telnet_ports = config_to_ports(config_data["telnet_ports"])
516
+ end
517
+ if config_data["ports"]
518
+ @ports = config_to_ports(config_data["ports"])
519
+ end
520
+ end
521
+
522
+ def config_to_ports(data)
523
+ if data.is_a?(Hash)
524
+ # TODO: verify that keys are IP addr strings and values are legal port numbers
525
+ return data
526
+ elsif data.is_a?(Array)
527
+ # TODO: verify that data is an array of legal integer port numbers
528
+ ports = {}
529
+ data.each { |p| ports["*"] = p }
530
+ return ports
531
+ elsif data.is_a?(Integer)
532
+ return { "*": data }
533
+ else
534
+ raise "dgd-manifest: not sure how to get port data from a #{data.class.name} -- #{data.inspect}!"
535
+ end
536
+ end
537
+
538
+ def as_file
539
+ return <<DGD_CONFIG
540
+ telnet_port = ([
541
+ #{@telnet_ports.map { |ip, p| "#{ip.inspect}:#{p}" }.join(",\n ") }
542
+ ]); /* legacy telnet ports */
543
+ binary_port = ([
544
+ #{@ports.map { |ip, p| "#{ip.inspect}:#{p}" }.join(",\n ") }
545
+ ]); /* binary ports */
546
+ directory = "./#{GENERATED_ROOT}";
547
+
548
+ users = #{@config[:users]}; /* max # of connections */
549
+ editors = #{@config[:editors]}; /* max # of built-in-editor sessions */
550
+ ed_tmpfile = "../#{@statedir}/ed"; /* proto editor tmpfile */
551
+ swap_file = "../#{@statedir}/swap"; /* swap file */
552
+ swap_size = #{@config[:swap_size]}; /* # sectors in swap file */
553
+ sector_size = #{@config[:sector_size]}; /* swap sector size */
554
+ swap_fragment = #{@config[:swap_fragment]}; /* fragment to swap out */
555
+ static_chunk = #{@config[:static_chunk]}; /* static memory chunk */
556
+ dynamic_chunk = #{@config[:dynamic_chunk]}; /* dynamic memory chunk */
557
+ dump_file = #{@dump_file.inspect}; /* dump file */
558
+ dump_interval = #{@config[:dump_interval]}; /* expected statedump interval in seconds */
559
+
560
+ typechecking = #{@config[:typechecking]}; /* level of typechecking (2 is highest) */
561
+ include_file = #{@config[:include_file].inspect}; /* standard include file */
562
+ include_dirs = ({ #{@config[:include_dirs].map(&:inspect).join(", ")} }); /* directories to search */
563
+ auto_object = #{@config[:auto_object].inspect}; /* auto inherited object */
564
+ driver_object = #{@config[:driver_object].inspect}; /* driver object */
565
+ create = #{@config[:create].inspect}; /* name of create function */
566
+
567
+ array_size = #{@config[:array_size]}; /* max array size */
568
+ objects = #{@config[:objects]}; /* max # of objects */
569
+ call_outs = #{@config[:call_outs]}; /* max # of callouts */
570
+ DGD_CONFIG
571
+ end
572
+ end
458
573
  end
@@ -8,13 +8,21 @@ require "tempfile"
8
8
 
9
9
  module SkotOS; end
10
10
 
11
- # TODO: remove <Core:Property property="revisions"> from anywhere in the XML tree
11
+ class SkotOS::XMLObject; end
12
+
13
+ class << SkotOS::XMLObject
14
+ attr_accessor :merry_only
15
+ attr_accessor :ignore_whitespace
16
+ attr_accessor :ignore_types
17
+ end
12
18
 
13
19
  class SkotOS::XMLObject
14
20
  attr_reader :pretty
21
+ attr_reader :noko_doc
15
22
 
16
- def initialize(pretty)
23
+ def initialize(pretty, noko_doc: nil)
17
24
  @pretty = pretty
25
+ @noko_doc = noko_doc
18
26
  end
19
27
 
20
28
  def self.from_file(filename)
@@ -25,9 +33,7 @@ class SkotOS::XMLObject
25
33
  remove_undiffed(doc)
26
34
 
27
35
  pretty = doc.to_xml(indent:3)
28
- #data = doc.to_hash
29
- #prune_whitespace(data)
30
- SkotOS::XMLObject.new pretty
36
+ SkotOS::XMLObject.new pretty, noko_doc: doc
31
37
  end
32
38
 
33
39
  def self.diff_between(obj1, obj2, o1_name: "Object 1", o2_name: "Object 2")
@@ -40,8 +46,11 @@ class SkotOS::XMLObject
40
46
  of1.close
41
47
  of2.close
42
48
 
49
+ diff_opts = [ "-c" ]
50
+ diff_opts += [ "-w", "-B" ] if self.ignore_whitespace
51
+
43
52
  # Diff 'fails' if there's a difference between the two files.
44
- diff = system_call("diff -c #{of1.path} #{of2.path}", fail_ok: true)
53
+ diff = system_call("diff #{diff_opts.join(" ")} #{of1.path} #{of2.path}", fail_ok: true)
45
54
  diff.sub!(of1.path, o1_name)
46
55
  diff.sub!(of2.path, o2_name)
47
56
  ensure
@@ -51,28 +60,35 @@ class SkotOS::XMLObject
51
60
  diff
52
61
  end
53
62
 
54
- def self.skip_ignored_files(list)
55
- list.select do |path|
56
- !path[/,v$/] && # Ignore files ending in comma-v
57
- !path[/-backup-\d+-\d+-\d+\.xml/] # Ignore files ending in -backup-[DATE].xml
63
+ def self.skip_ignored_files(list, base_dir)
64
+ if self.merry_only
65
+ list.select { |path| File.directory?(base_dir + "/" + path) ||
66
+ path[/.xml$/] || path[/.XML$/] }
67
+ else
68
+ list.select do |path|
69
+ !path[/,v$/] && # Ignore files ending in comma-v
70
+ !path[/-backup-\d+-\d+-\d+\.xml/] && # Ignore files ending in -backup-[DATE].xml
71
+ path != ".git" && # Ignore .git directories
72
+ path != "MOVED" # Ignore MOVED - it's a sort of recycle, waiting to be emptied
73
+ end
58
74
  end
59
75
  end
60
76
 
61
77
  def self.diff_dirs(dir1, dir2)
62
- entries1 = skip_ignored_files(Dir.glob("*", base: dir1).to_a)
63
- entries2 = skip_ignored_files(Dir.glob("*", base: dir2).to_a)
78
+ entries1 = skip_ignored_files(Dir.glob("*", base: dir1).to_a, dir1)
79
+ entries2 = skip_ignored_files(Dir.glob("*", base: dir2).to_a, dir2)
64
80
 
65
81
  only_in_1 = entries1 - entries2
66
82
  only_in_2 = entries2 - entries1
67
83
  in_both = entries1 & entries2
68
84
 
69
85
  diff = []
70
- diff << "Only in first: #{only_in_1.join(", ")}" unless only_in_1.empty?
71
- diff << "Only in second: #{only_in_2.join(", ")}" unless only_in_2.empty?
86
+ diff << "Only in first: #{only_in_1.map { |s| dir1 + "/" + s }.join(", ")}" unless only_in_1.empty?
87
+ diff << "Only in second: #{only_in_2.map { |s| dir2 + "/" + s }.join(", ")}" unless only_in_2.empty?
72
88
 
73
89
  in_both.each do |file|
74
- in_1 = "#{dir1}/#{file}"
75
- in_2 = "#{dir2}/#{file}"
90
+ in_1 = File.join dir1, file
91
+ in_2 = File.join dir2, file
76
92
  if File.directory?(in_1) ^ File.directory?(in_2)
77
93
  diff << "Only a directory in one, not both: #{dir1}/#{file}"
78
94
  elsif File.directory?(in_1)
@@ -97,6 +113,90 @@ class SkotOS::XMLObject
97
113
  end
98
114
  end
99
115
  end
116
+
117
+ rev = noko_single_node(doc.root, "Core:Property", attrs: { "property" => "revisions" })
118
+ rev.remove if rev
119
+
120
+ list = noko_single_node(doc.root, "Core:Property", attrs: { "property" => "#list#" })
121
+ list.remove if list
122
+
123
+ if self.merry_only
124
+ # Kill off all the non-Merry nodes
125
+ noko_remove_non_merry_nodes(doc.root)
126
+ end
127
+
128
+ if self.ignore_types
129
+ self.ignore_types.each do |ignored_type|
130
+ skipped = noko_with_name_and_attrs(doc.root, ignored_type)
131
+ skipped.each { |n| n.remove }
132
+ end
133
+ end
134
+
135
+ base_combat = noko_single_node(doc.root, "Base:Combat")
136
+ if base_combat
137
+ base_strength = noko_single_node(base_combat, "Base:Strength", attrs: { "value" => "1" })
138
+ base_max_fatigue = noko_single_node(base_combat, "Base:MaxFatigue", attrs: { "value" => "1" })
139
+ if base_strength && base_max_fatigue && noko_non_text(base_combat.children).size == 2
140
+ next_text = base_combat.next
141
+ base_combat.remove
142
+ next_text.remove
143
+ end
144
+ end
145
+ end
146
+
147
+ def self.noko_single_node(node, name, attrs: {})
148
+ choices = noko_with_name_and_attrs(node, name, attrs)
149
+ if choices.size < 1
150
+ nil
151
+ elsif choices.size > 1
152
+ raise "Single-node search returned more than one node! #{name.inspect}, #{attrs.inspect}"
153
+ else
154
+ choices[0]
155
+ end
156
+ end
157
+
158
+ def self.noko_non_text(nodes)
159
+ nodes.select { |n| !n.is_a? Nokogiri::XML::Text }
160
+ end
161
+
162
+ def self.noko_with_name_and_attrs(node, name, attrs = {})
163
+ results = node.children.flat_map { |n| noko_with_name_and_attrs(n, name, attrs) }
164
+ if node.name == name &&
165
+ attrs.all? { |k, v| node.attribute(k).value == v }
166
+ results << node
167
+ end
168
+ results
169
+ end
170
+
171
+ def self.noko_remove_non_merry_nodes(root)
172
+ root.children.each do |node|
173
+ if node.name != "Core:PropertyContainer"
174
+ node.remove
175
+ next
176
+ end
177
+
178
+ node.children.each do |node2|
179
+ if node2.name != "Core:PCProperties"
180
+ node2.remove
181
+ next
182
+ end
183
+
184
+ node2.children.each do |property_node|
185
+ if property_node.name != "Core:Property" || property_node.attribute("property").value[0..5] != "merry:"
186
+ property_node.remove
187
+ next
188
+ end
189
+ # Leave the Merry node alone
190
+ end
191
+
192
+ if node2.children.size == 0
193
+ node2.remove
194
+ end
195
+ end
196
+ if node.children.size == 0
197
+ node.remove
198
+ end
199
+ end
100
200
  end
101
201
 
102
202
  def self.system_call(cmd, fail_ok: false)
@@ -1,3 +1,3 @@
1
1
  module DGD
2
- VERSION = "0.1.6"
2
+ VERSION = "0.1.7"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dgd-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noah Gibbs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-04 00:00:00.000000000 Z
11
+ date: 2021-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.10.5
27
+ - !ruby/object:Gem::Dependency
28
+ name: optimist
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.0.1
27
41
  description: dgd-tools supplies DGD Manifest and eventually perhaps other tools. DGD
28
42
  Manifest is an experimental DGD library and packaging system.
29
43
  email:
@@ -53,6 +67,7 @@ files:
53
67
  - example_xml/t2/Thing.xml
54
68
  - exe/dgd-manifest
55
69
  - exe/skotos-xml-diff
70
+ - goods/chattheatre_kernellib.goods
56
71
  - goods/skotos_httpd.goods
57
72
  - lib/dgd-tools/manifest.rb
58
73
  - lib/dgd-tools/skotos_xml_obj.rb