dgd-tools 0.1.6 → 0.1.7

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: 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