cli-dispatcher 1.1.11 → 1.2.0

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/structured.rb +101 -56
  3. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 275e6d346e13d40131484394f8da7d754df96294e559ad1461628274d14755fb
4
- data.tar.gz: c7eb8d374b5eda6861b70465413574b67ed8e6fd9fa57d1b837a0df3d73c79a0
3
+ metadata.gz: 850d024672db8785ec7d329961a30baf9e8d4f1724189f6cf6250472cc17e5e8
4
+ data.tar.gz: 11e2ab38ddd9cc99d7ec2b66bf4b97d229a470ded4295ef2e7ecddca99bca74e
5
5
  SHA512:
6
- metadata.gz: 781dd82a1715c122bd8293605bd0bb2480e51128efdec7758b982bfe3cd7c02b7cf93f25427716b1d4e302664524b329bcbebaa74d9ee956ba1a46e392d9c050
7
- data.tar.gz: c3221e641bbc6b2461b500ab626de8954ab7f8e87cef2bb5bdcdebf910b2ba38bd7e0a4f069a9bd67ee2f82212cba7c2f49dacbca23f78e8439fa7a31b564b8e
6
+ metadata.gz: 3f5a3f85f1f64785b526df560efe2ef2ee94b031d0ef9bd47d43fd6500eaf4faba0b70a6230d1ccbc49db4d9a8d0681a1ba17edd847c105ab0b8d106568ccb2f
7
+ data.tar.gz: e6c3a31bd0825b3ae234dd68b6432e5a5b7f0fe4daf76a9494545018d264615630f89368b1c352d950be23a66be76e36fa5a3059bb38ad411d37f91912480f5a
data/lib/structured.rb CHANGED
@@ -172,7 +172,8 @@ module Structured
172
172
  # raises an error, but classes may override this method to use the undefined
173
173
  # elements.
174
174
  #
175
- # @param element The unknown element name, converted to a symbol.
175
+ # @param element The unknown element name. For a YAML file, this is typically
176
+ # a string.
176
177
  #
177
178
  # @param val The value associated with the unknown element.
178
179
  #
@@ -212,6 +213,7 @@ module Structured
212
213
  def reset_elements
213
214
  @elements = {}
214
215
  @default_element = nil
216
+ @default_key = nil
215
217
  @class_description = nil
216
218
  end
217
219
 
@@ -266,14 +268,32 @@ module Structured
266
268
 
267
269
  #
268
270
  # Accepts a default element for this class. The arguments are the same as
269
- # those for element_data.
271
+ # those for element_data except as noted below.
270
272
  #
271
- # **Caution**: The type argument should almost always be a single class, and
272
- # not a hash. This is because the default arguments are automatically
273
+ # If this method is called, then for any keys found in an input hash that
274
+ # have no corresponding #element declaration in the Structured class, the
275
+ # method receive_any will be invoked. The value from the input hash
276
+ # will be processed based on any type declaration, `preproc`, and `check`
277
+ # given to default_element.
278
+ #
279
+ # The default element keys can be processed based on the argument `key`,
280
+ # which should be a hash corresponding to the element_data arguments plus
281
+ # the key :type with the default key's expected type. If `key` is not given,
282
+ # then the key must be and is automatically converted to a Symbol.
283
+ #
284
+ # **Caution**: The `type` argument should almost always be a single class,
285
+ # and not a hash. This is because the default arguments are automatically
273
286
  # treated like a hash, with the otherwise-undefined element names being the
274
287
  # keys of the hash.
275
288
  #
276
289
  def default_element(*args, **params)
290
+ if (key_params = params.delete(:key))
291
+ @default_key = element_data(
292
+ key_params.delete(:type) || Object, key_params
293
+ )
294
+ else
295
+ @default_key = element_data(Symbol, preproc: proc { |s| s.to_sym })
296
+ end
277
297
  @default_element = element_data(*args, **params)
278
298
  end
279
299
 
@@ -384,47 +404,28 @@ module Structured
384
404
  input_err("Initializer is not a Hash") unless hash.is_a?(Hash)
385
405
  hash = try_read_file(hash)
386
406
 
387
- @elements.each do |elt, data|
388
- Structured.trace(elt.to_s) do
389
- val = hash[elt] || hash[elt.to_s]
390
- next if process_nil_val(obj, elt, val, data)
391
-
392
- if data[:preproc]
393
- val = try_run(data[:preproc], obj, val, "preproc")
394
- next if process_nil_val(obj, elt, val, data)
395
- end
396
-
397
- cval = convert_item(val, data[:type], obj)
398
-
399
- # Check for validity after preproc and conversion are run
400
- if data[:check] && !try_run(data[:check], obj, cval, "check")
401
- input_err "Value #{cval} failed check for #{elt}"
402
- end
403
-
404
- # Use the converted value
405
- apply_val(obj, elt, cval)
407
+ @elements.each do |key, data|
408
+ Structured.trace(key.to_s) do
409
+ val = hash[key] || hash[key.to_s]
410
+ cval = process_value(obj, val, data)
411
+ apply_val(obj, key, cval) if cval
406
412
  end
407
413
  end
408
414
 
409
415
  # Process unknown elements
410
- unknown_elts = (hash.keys.map(&:to_sym) - @elements.keys)
411
- return if unknown_elts.empty?
416
+ unknown_keys = hash.keys.reject { |k| @elements.include?(k.to_sym) }
417
+ return if unknown_keys.empty?
412
418
  unless @default_element
413
- input_err("Unexpected element(s): #{unknown_elts.join(', ')}")
419
+ input_err("Unexpected element(s): #{unknown_keys.join(', ')}")
414
420
  end
415
- unknown_elts.each do |elt|
416
- Structured.trace(elt.to_s) do
417
- de = @default_element
418
- val = hash[elt] || hash[elt.to_s]
419
- if de[:preproc]
420
- val = try_run(de[:preproc], obj, val, "default preproc")
421
- end
422
- item = convert_item(val, de[:type], obj)
423
- if de[:check] && !try_run(de[:check], obj, item, "check")
424
- input_err "Value #{item} failed default element check"
425
- end
426
- item.receive_key(elt) if item.is_a?(Structured)
427
- obj.receive_any(elt, item)
421
+ unknown_keys.each do |key|
422
+ Structured.trace(key.to_s) do
423
+ val = hash[key]
424
+ ckey = process_value(obj, key, @default_key)
425
+ cval = process_value(obj, val, @default_element)
426
+ next unless cval
427
+ cval.receive_key(ckey) if cval.is_a?(Structured)
428
+ obj.receive_any(ckey, cval)
428
429
  end
429
430
  end
430
431
  end
@@ -450,24 +451,53 @@ module Structured
450
451
  end
451
452
  end
452
453
 
453
- # Deals with a nil value (either because no value was given, or because a
454
- # preproc deleted it).
454
+
455
+ #
456
+ # Given an element value and an #element_data hash of processing tools
457
+ # element, applies those processing tools. Namely, apply any preproc, check
458
+ # the type and perform other checks, and perform any conversions. The return
459
+ # value should be usable as the received value for the corresponding
460
+ # element.
455
461
  #
456
- # * If val is non-nil, then this method returns false.
462
+ # If this method returns nil, then there is no element to process. This
463
+ # method may also raise an InputError.
464
+ #
465
+ def process_value(obj, val, data)
466
+ val, ret = process_nil_val(val, data)
467
+ return val if ret
468
+ if data[:preproc]
469
+ val = try_run(data[:preproc], obj, val, "preproc")
470
+ val, ret = process_nil_val(val, data)
471
+ return val if ret
472
+ end
473
+
474
+ cval = convert_item(val, data[:type], obj)
475
+ if data[:check] && !try_run(data[:check], obj, cval, "check")
476
+ input_err "Value #{cval} failed check"
477
+ end
478
+ return cval
479
+ end
480
+
481
+ #
482
+ # Performs processing of an element value to deal with the possibility that
483
+ # the value is nil. This method returns [ the new value, boolean of whether
484
+ # to stop processing ] according to the following rules:
485
+ #
486
+ # * If val is non-nil, then this method returns val itself, and processing
487
+ # should not stop.
457
488
  # * If val is nil and this element is non-optional, then this method raises
458
489
  # an error.
459
- # * If val is nil and the element is optional, *and* the element has a
460
- # default value, then the object has the default value applied to the
461
- # element.
462
- # * In any event, if val is nil and the element is optional, returns true
463
- # which should signal to the caller to stop further processing of the
464
- # element.
465
- #
466
- def process_nil_val(obj, elt, val, data)
467
- return false if val
468
- input_err("Missing (or preproc deleted) #{elt}") unless data[:optional]
469
- apply_val(obj, elt, data[:default]) unless data[:default].nil?
470
- return true
490
+ # * If val is nil and the element is optional, then the object's default
491
+ # value is returned, and processing should stop.
492
+ # * If there is no default value for an optional element, then nil is
493
+ # returned, and processing should also stop.
494
+ #
495
+ def process_nil_val(val, data)
496
+ return [ val, false ] if val
497
+ unless data[:optional]
498
+ input_err("Required element is missing (or was deleted by a preproc)")
499
+ end
500
+ return [ data[:default], true ]
471
501
  end
472
502
 
473
503
  # Applies a value to an element for an object, after all processing for the
@@ -542,12 +572,22 @@ module Structured
542
572
  end
543
573
  end
544
574
 
575
+ #
576
+ # Several types can be automatically converted:
577
+ #
578
+ # * Symbol into String
579
+ # * String into Regexp
580
+ #
545
581
  def try_autoconvert(type, item)
546
582
 
547
583
  if type == String && item.is_a?(Symbol)
548
584
  return item.to_s
549
585
  end
550
586
 
587
+ if type == Symbol && item.is_a?(String)
588
+ return item.to_sym
589
+ end
590
+
551
591
  # Special case in which strings will be converted to Regexps
552
592
  if type == Regexp && item.is_a?(String)
553
593
  begin
@@ -563,7 +603,11 @@ module Structured
563
603
  # Receive hash values that are to be converted to Structured objects
564
604
  def convert_structured(item, type, parent)
565
605
  unless item.is_a?(Hash)
566
- input_err("#{item.inspect} not a #{type} or Structured hash")
606
+ if type.include?(Structured)
607
+ input_err("#{item.inspect} not a Structured hash for #{type}")
608
+ else
609
+ input_err("#{item.inspect} not a #{type}")
610
+ end
567
611
  end
568
612
 
569
613
  unless type.include?(Structured) || type.include?(StructuredPolymorphic)
@@ -604,7 +648,8 @@ module Structured
604
648
 
605
649
  if @default_element
606
650
  io.puts(
607
- " All other elements: #{describe_type(@default_element[:type])}"
651
+ " All other elements: #{describe_type(@default_key[:type])} => " \
652
+ "#{describe_type(@default_element[:type])}"
608
653
  )
609
654
  if @default_element[:description]
610
655
  io.puts(TextTools.line_break(
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cli-dispatcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.11
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Duan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-13 00:00:00.000000000 Z
11
+ date: 2024-11-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Library for creating command-line programs that accept commands. Also
@@ -26,7 +26,8 @@ files:
26
26
  homepage: https://github.com/charlesduan/cli-dispatcher
27
27
  licenses:
28
28
  - MIT
29
- metadata: {}
29
+ metadata:
30
+ source_code_uri: https://github.com/charlesduan/cli-dispatcher
30
31
  post_install_message:
31
32
  rdoc_options: []
32
33
  require_paths: