xqsr3 0.32.3 → 0.37.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/xqsr3/all_extensions.rb +6 -0
  3. data/lib/xqsr3/array_utilities.rb +10 -0
  4. data/lib/xqsr3/command_line_utilities.rb +10 -0
  5. data/lib/xqsr3/command_line_utilities/map_option_string.rb +3 -2
  6. data/lib/xqsr3/containers.rb +11 -0
  7. data/lib/xqsr3/containers/frequency_map.rb +18 -1
  8. data/lib/xqsr3/containers/multi_map.rb +290 -29
  9. data/lib/xqsr3/conversion.rb +11 -0
  10. data/lib/xqsr3/conversion/integer_parser.rb +3 -2
  11. data/lib/xqsr3/diagnostics.rb +11 -0
  12. data/lib/xqsr3/diagnostics/inspect_builder.rb +5 -1
  13. data/lib/xqsr3/extensions.rb +13 -0
  14. data/lib/xqsr3/extensions/array.rb +3 -0
  15. data/lib/xqsr3/extensions/hash.rb +6 -0
  16. data/lib/xqsr3/extensions/hash/except.rb +26 -0
  17. data/lib/xqsr3/extensions/hash/slice.rb +22 -0
  18. data/lib/xqsr3/extensions/io/writelines.rb +38 -6
  19. data/lib/xqsr3/extensions/test/unit/assert_raise_with_message.rb +22 -2
  20. data/lib/xqsr3/hash_utilities.rb +11 -0
  21. data/lib/xqsr3/hash_utilities/key_matching.rb +6 -4
  22. data/lib/xqsr3/internal_/test_unit_version_.rb +26 -0
  23. data/lib/xqsr3/io/writelines.rb +49 -13
  24. data/lib/xqsr3/quality.rb +8 -1
  25. data/lib/xqsr3/quality/parameter_checking.rb +3 -2
  26. data/lib/xqsr3/string_utilities.rb +16 -0
  27. data/lib/xqsr3/string_utilities/ends_with.rb +3 -2
  28. data/lib/xqsr3/string_utilities/nil_if_empty.rb +3 -2
  29. data/lib/xqsr3/string_utilities/nil_if_whitespace.rb +3 -2
  30. data/lib/xqsr3/string_utilities/quote_if.rb +7 -2
  31. data/lib/xqsr3/string_utilities/starts_with.rb +3 -2
  32. data/lib/xqsr3/string_utilities/to_symbol.rb +3 -2
  33. data/lib/xqsr3/string_utilities/truncate.rb +3 -2
  34. data/lib/xqsr3/version.rb +3 -2
  35. data/test/unit/containers/tc_multi_map.rb +174 -16
  36. data/test/unit/diagnostics/tc_exception_utilities.rb +7 -3
  37. data/test/unit/extensions/hash/tc_deep_transform.rb +0 -1
  38. data/test/unit/extensions/hash/tc_except.rb +67 -0
  39. data/test/unit/extensions/hash/tc_hash.rb +6 -0
  40. data/test/unit/extensions/hash/tc_slice.rb +31 -0
  41. data/test/unit/extensions/io/tc_writelines.rb +36 -0
  42. data/test/unit/extensions/kernel/tc_raise_with_options.rb +6 -3
  43. data/test/unit/tc_version.rb +1 -1
  44. metadata +22 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5362a61770df81001025713c2465c5ed9bea6a49
4
- data.tar.gz: 70f643bccc5e09b76acf3a4944e02dbe0e763d6a
3
+ metadata.gz: '048dfbb7beeff5b7e90df73f59be96e686611f91'
4
+ data.tar.gz: 815facf131a846b49eec30be5e45bf369383d52d
5
5
  SHA512:
6
- metadata.gz: 8cc83e03876a54224a4f4a28ea3ebf4ff289b2a4f8b11d1c727c41716a3ef68e670d10dd0f796d5b3273124b9308a0e4701fae02a030856f1183a2dafae3b2cb
7
- data.tar.gz: 3b12d249383fddfde861575e9ed42a329220aba5b47f22204afdf0b5a3317290460004071e3c2d3a4c9590231b5eb61a01c38e1ca6f093ed2cd696ea7095469f
6
+ metadata.gz: e77733d055c75114bcd38811e5af5f89726c563976d49b8d197246309ac6a2c92ade6cf66e812f81a02b17f682f530b870441fc9f8436b499179dee2165d539c
7
+ data.tar.gz: 38743b47b07c1b1876d680a759e6c9ec3e9aac7ef6482305a2dbe452fa5d1f28e0bb3b5921653778feb94b76a8f884f90eb03dbae7bad9dd6d5815fad9902308
@@ -0,0 +1,6 @@
1
+
2
+ require File.join(File.dirname(__FILE__), 'extensions')
3
+
4
+ require File.join(File.dirname(__FILE__), 'extensions', 'test', 'unit')
5
+
6
+
@@ -0,0 +1,10 @@
1
+
2
+ %w{
3
+
4
+ join_with_or
5
+ }.each do |name|
6
+
7
+ require File.join(File.dirname(__FILE__), 'array_utilities', name)
8
+ end
9
+
10
+
@@ -0,0 +1,10 @@
1
+
2
+ %w{
3
+
4
+ map_option_string
5
+ }.each do |name|
6
+
7
+ require File.join(File.dirname(__FILE__), 'command_line_utilities', name)
8
+ end
9
+
10
+
@@ -6,7 +6,7 @@
6
6
  # ::Xqsr3::CommandLineUtilities::MapOptionString module
7
7
  #
8
8
  # Created: 15th April 2016
9
- # Updated: 12th April 2019
9
+ # Updated: 15th April 2019
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/xqsr3
12
12
  #
@@ -69,7 +69,8 @@ module MapOptionString
69
69
  end
70
70
 
71
71
  private
72
- module MapOptionString_Helper_ # :nodoc:
72
+ # @!visibility private
73
+ module MapOptionString_Helper_ # :nodoc: all
73
74
 
74
75
  def self.map_option_string_with_options_ s, option_strings, options
75
76
 
@@ -0,0 +1,11 @@
1
+
2
+ %w{
3
+
4
+ frequency_map
5
+ multi_map
6
+ }.each do |name|
7
+
8
+ require File.join(File.dirname(__FILE__), 'containers', name)
9
+ end
10
+
11
+
@@ -5,7 +5,7 @@
5
5
  # Purpose: FrequencyMap container
6
6
  #
7
7
  # Created: 28th January 2005
8
- # Updated: 12th April 2019
8
+ # Updated: 15th April 2019
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
@@ -77,6 +77,7 @@ class FrequencyMap
77
77
  # fm[:z] # => 0
78
78
  ByElement = Class.new do
79
79
 
80
+ # Create an instance of Xqsr3::FrequencyMap from an array
80
81
  def self.[] *args
81
82
 
82
83
  fm = FrequencyMap.new
@@ -180,12 +181,16 @@ class FrequencyMap
180
181
 
181
182
  case rhs
182
183
  when ::NilClass
184
+
183
185
  return false
184
186
  when ::Hash
187
+
185
188
  return rhs.size == @elements.size && rhs == @elements
186
189
  when self.class
190
+
187
191
  return rhs.count == self.count && rhs == @elements
188
192
  else
193
+
189
194
  raise TypeError, "can compare #{self.class} only to instances of #{self.class} and #{::Hash}, but #{rhs.class} given"
190
195
  end
191
196
 
@@ -407,6 +412,12 @@ class FrequencyMap
407
412
 
408
413
  # Returns +true+ if an element with a count of the given +value+ is in the
409
414
  # map; +false+ otherwise
415
+ #
416
+ # * *Parameters:*
417
+ # - +value+ (Integer) The value of the count for which to search
418
+ #
419
+ # * *Exceptions:*
420
+ # - +::TypeError+ if +value+ is not an Integer
410
421
  def has_value? value
411
422
 
412
423
  case value
@@ -437,6 +448,8 @@ class FrequencyMap
437
448
  #
438
449
  # @elements.keep_if
439
450
  # end
451
+ =begin
452
+ =end
440
453
 
441
454
  # Returns the element that has the given count, or +nil+ if none found
442
455
  #
@@ -540,6 +553,8 @@ class FrequencyMap
540
553
  self
541
554
  end
542
555
 
556
+ # Removes a key-value pair from the instance and return as a two-item
557
+ # array
543
558
  def shift
544
559
 
545
560
  r = @elements.shift
@@ -589,11 +604,13 @@ class FrequencyMap
589
604
  @elements.to_hash
590
605
  end
591
606
 
607
+ # A string-form of the instance
592
608
  def to_s
593
609
 
594
610
  @elements.to_s
595
611
  end
596
612
 
613
+ # An array of all frequencies (without element keys) in the instance
597
614
  def values
598
615
 
599
616
  @elements.values
@@ -5,7 +5,7 @@
5
5
  # Purpose: multimap container
6
6
  #
7
7
  # Created: 21st March 2007
8
- # Updated: 12th April 2019
8
+ # Updated: 15th April 2019
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
@@ -73,16 +73,16 @@ class MultiMap < ::Hash
73
73
  return self.new
74
74
  when ::Hash
75
75
 
76
- fm = self.new
76
+ mm = self.new
77
77
 
78
78
  arg.each do |k, v|
79
79
 
80
80
  raise ArgumentError, "mapped elements in hashes must be arrays, #{v.class} given" unless v.kind_of? ::Array
81
81
 
82
- fm.store k, *v
82
+ mm.store k, *v
83
83
  end
84
84
 
85
- return fm
85
+ return mm
86
86
  when ::Array
87
87
 
88
88
  # accepted forms:
@@ -133,6 +133,8 @@ class MultiMap < ::Hash
133
133
  # Initialises an instance
134
134
  def initialize
135
135
 
136
+ @merge_is_multi = true
137
+
136
138
  @inner = Hash.new
137
139
  end
138
140
 
@@ -143,6 +145,16 @@ class MultiMap < ::Hash
143
145
  return @inner[key]
144
146
  end
145
147
 
148
+ # Adds/assigns a new key+values pair. Equivalent to
149
+ #
150
+ # store(key, *values)
151
+ #
152
+ # * *Parameters:*
153
+ # - +key+ The element key
154
+ # - +values+ (Array) The values to be associated with the key
155
+ #
156
+ # * *Exceptions:*
157
+ # - +::TypeError+ if +values+ is not an array
146
158
  def []= key, values
147
159
 
148
160
  values = [] if values.nil?
@@ -203,9 +215,17 @@ class MultiMap < ::Hash
203
215
  @inner.delete key
204
216
  end
205
217
 
218
+ # Calls _block_ once for each key-value pair, passing the key and each
219
+ # of its values in turn. If the values for a given key are empty and
220
+ # +defaults+ is not empty, the block is invoked for that key (with
221
+ # +defaults[0]+) once
222
+ #
223
+ # * *Exceptions:*
224
+ # - +ArgumentError+ if more than 1 +defaults+ is provided, or no block is given
206
225
  def each *defaults
207
226
 
208
227
  raise ArgumentError, "may only supply 0 or 1 defaults" if defaults.size > 1
228
+ raise ArgumentError, 'block is required' unless block_given?
209
229
 
210
230
  @inner.each do |key, values|
211
231
 
@@ -220,8 +240,8 @@ class MultiMap < ::Hash
220
240
  end
221
241
  end
222
242
 
223
- # Calls _block_ once for each element in the instance, passing the
224
- # key. If no block is provided, an enumerator is returned
243
+ # Calls _block_ once for each key in the instance, passing the key. If no
244
+ # block is provided, an enumerator is returned
225
245
  def each_key
226
246
 
227
247
  return @inner.each_key unless block_given?
@@ -229,26 +249,46 @@ class MultiMap < ::Hash
229
249
  @inner.each_key { |key| yield key }
230
250
  end
231
251
 
252
+ # Calls _block_ once for each key-values pair, passing the key and its
253
+ # values array. If no block is provided, an enumerator is returned
232
254
  def each_unflattened
233
255
 
256
+ return @inner.each unless block_given?
257
+
234
258
  @inner.each { |key, value| yield key, value }
235
259
  end
236
260
 
261
+ # Calls _block_ once for each key-values pair, passing the key and its
262
+ # values array and a key index. If no block is provided, an enumerator
263
+ # is returned
237
264
  def each_unflattened_with_index
238
265
 
266
+ return @inner.each_with_index unless block_given?
267
+
239
268
  @inner.each_with_index { |kv, index| yield kv, index }
240
269
  end
241
270
 
271
+ # Calls _block_ once for each value in the instance, passing the value.
272
+ # If no block is provided, an enumerator is returned
242
273
  def each_value
243
274
 
275
+ return @inner.each_value unless block_given?
276
+
244
277
  @inner.each do |key, values|
245
278
 
246
279
  values.each { |value| yield value }
247
280
  end
248
281
  end
249
282
 
283
+ # Calls _block_ once for each key-values, passing the key and each of its
284
+ # values and a value index
285
+ #
286
+ # * *Exceptions:*
287
+ # - +ArgumentError+ if no block is given
250
288
  def each_with_index
251
289
 
290
+ raise ArgumentError, 'block is required' unless block_given?
291
+
252
292
  index = 0
253
293
  self.each do |key, value|
254
294
 
@@ -276,18 +316,26 @@ class MultiMap < ::Hash
276
316
  end
277
317
  end
278
318
 
279
- def fetch key, default = nil, &block
319
+ # Returns the values associated with the given key
320
+ #
321
+ # * *Parameters:*
322
+ # - +key+ The key
323
+ # - +default+ The default value
324
+ def fetch key, default = (default_parameter_defaulted_ = true; nil), &block
280
325
 
281
- case default
282
- when ::NilClass, ::Array
283
- ;
284
- else
285
- raise TypeError, "default parameter ('#{default}') must be of type #{::Array}, but was of type #{default.class}"
326
+ unless default_parameter_defaulted_
327
+
328
+ case default
329
+ when ::NilClass, ::Array
330
+ ;
331
+ else
332
+ raise TypeError, "default parameter ('#{default}') must be of type #{::Array}, but was of type #{default.class}"
333
+ end
286
334
  end
287
335
 
288
336
  unless @inner.has_key? key
289
337
 
290
- return default unless default.nil?
338
+ return default unless default_parameter_defaulted_
291
339
 
292
340
  if block_given?
293
341
 
@@ -349,31 +397,127 @@ class MultiMap < ::Hash
349
397
  @inner.has_key? key
350
398
  end
351
399
 
400
+ # Returns +true+ if any key has the given +value+; +false+ otherwise
401
+ #
402
+ # * *Parameters:*
403
+ # - +value+ The value for which to search
352
404
  def has_value? value
353
405
 
354
- @inner.has_value? value
406
+ @inner.each do |k, vals|
407
+
408
+ return true if vals.include? value
409
+ end
410
+
411
+ false
412
+ end
413
+
414
+ # Returns +true+ if any key has the given +values+; +false+ otherwise
415
+ #
416
+ # * *Parameters:*
417
+ # - +values+ (Array) The values for which to search
418
+ #
419
+ # * *Exceptions:*
420
+ # - +::TypeError+ if +value+ is not an Array
421
+ def has_values? values
422
+
423
+ raise TypeError, "'values' parameter must be of type #{::Array}" unless Array === values
424
+
425
+ @inner.has_value? values
355
426
  end
356
427
 
357
- def key value
428
+ # Returns the key for the given value(s)
429
+ #
430
+ # * *Parameters:*
431
+ # - +values+ (Array) The value(s) for which to search
432
+ #
433
+ # If a single value is specified, the entries in the instance are
434
+ # searched first for an exact match to all (1) value(s); if that fails,
435
+ # then the first key with a values containing the given value is
436
+ # returned
437
+ def key *values
438
+
439
+ case values.size
440
+ when 0
441
+
442
+ return nil
443
+ when 1
444
+
445
+ i = nil
446
+
447
+ @inner.each do |k, vals|
448
+
449
+ return k if vals == values
450
+
451
+ if i.nil?
452
+
453
+ i = k if vals.include? values[0]
454
+ end
455
+ end
456
+
457
+ return i
458
+ else
459
+
460
+ @inner.each do |key, vals|
461
+
462
+ return key if vals == values
463
+ end
358
464
 
359
- @inner.key value
465
+ return nil
466
+ end
360
467
  end
361
468
 
362
- def merge fm
469
+ # The number of elements in the map
470
+ def length
363
471
 
364
- raise TypeError, "parameter must be an instance of type #{self.class}" unless fm.instance_of? self.class
472
+ @inner.size
473
+ end
365
474
 
366
- fm_new = self.class.new
475
+ # Returns a new instance containing a merging of the current instance and
476
+ # the +other+ instance
477
+ #
478
+ # NOTE: where any key is found in both merging instances the values
479
+ # resulting will be a concatenation of the sets of values
480
+ #
481
+ # * *Parameters:*
482
+ # - +other+ (MultiMap, Hash) The instance from which to merge
483
+ #
484
+ # * *Exceptions:*
485
+ # - +TypeError+ Raised if +other+ is not a MultiMap or a Hash
486
+ def multi_merge other
367
487
 
368
- fm_new.merge! self
369
- fm_new.merge! fm
488
+ mm = self.class.new
370
489
 
371
- fm_new
490
+ mm.merge! self
491
+ mm.merge! other
492
+
493
+ mm
372
494
  end
373
495
 
374
- def merge! fm
496
+ # Merges the contents of +other+ into the current instance
497
+ #
498
+ # NOTE: where any key is found in both merging instances the values
499
+ # resulting will be a concatenation of the sets of values
500
+ #
501
+ # * *Parameters:*
502
+ # - +other+ (MultiMap, Hash) The instance from which to merge
503
+ #
504
+ # * *Exceptions:*
505
+ # - +TypeError+ Raised if +other+ is not a MultiMap or a Hash
506
+ def multi_merge! other
507
+
508
+ case other
509
+ when self.class
510
+
511
+ ;
512
+ when ::Hash
375
513
 
376
- fm.each do |k, v|
514
+ ;
515
+ else
516
+
517
+ raise TypeError, "parameter must be an instance of #{self.class} or #{Hash}"
518
+ end
519
+
520
+ other.each do |k, v|
377
521
 
378
522
  self.push k, v
379
523
  end
@@ -381,6 +525,96 @@ class MultiMap < ::Hash
381
525
  self
382
526
  end
383
527
 
528
+ # Returns a new instance containing a merging of the current instance and
529
+ # the +other+ instance
530
+ #
531
+ # NOTE: where any key is found in both merging instances the values from
532
+ # +other+ will be used
533
+ #
534
+ # * *Parameters:*
535
+ # - +other+ (MultiMap, Hash) The instance from which to merge
536
+ #
537
+ # * *Exceptions:*
538
+ # - +TypeError+ Raised if +other+ is not a MultiMap or a Hash
539
+ def strict_merge other
540
+
541
+ mm = self.class.new
542
+
543
+ mm.strict_merge! self
544
+ mm.strict_merge! other
545
+
546
+ mm
547
+ end
548
+
549
+ # Merges the contents of +other+ into the current instance
550
+ #
551
+ # NOTE: where any key is found in both merging instances the values from
552
+ # +other+ will be used
553
+ #
554
+ # * *Parameters:*
555
+ # - +other+ (MultiMap, Hash) The instance from which to merge
556
+ #
557
+ # * *Exceptions:*
558
+ # - +TypeError+ Raised if +other+ is not a MultiMap or a Hash
559
+ def strict_merge! other
560
+
561
+ case other
562
+ when self.class
563
+
564
+ other.each_unflattened do |k, vals|
565
+
566
+ self.store k, *vals
567
+ end
568
+ when ::Hash
569
+
570
+ other.each do |k, v|
571
+
572
+ self.store k, v
573
+ end
574
+ else
575
+
576
+ raise TypeError, "parameter must be an instance of #{self.class} or #{Hash}"
577
+ end
578
+
579
+ self
580
+ end
581
+
582
+ # See #merge
583
+ def merge other
584
+
585
+ if @merge_is_multi
586
+
587
+ multi_merge other
588
+ else
589
+
590
+ strict_merge other
591
+ end
592
+ end
593
+
594
+ # See #merge!
595
+ def merge! other
596
+
597
+ if @merge_is_multi
598
+
599
+ multi_merge! other
600
+ else
601
+
602
+ strict_merge! other
603
+ end
604
+ end
605
+
606
+ # Pushes the given +key+ and +values+. If the +key+ is already in the
607
+ # map then the +values+ will be concatenated with those already present
608
+ #
609
+ # === Signature
610
+ #
611
+ # * *Parameters:*
612
+ # - +key+ The element key
613
+ # - +values+ (*Array) The value(s) to be pushed
614
+ #
615
+ # === Exceptions
616
+ # - +::RangeError+ raised if the value of +count+ results in a negative count for the given element
617
+ # - +::TypeError+ if +count+ is not an +::Integer+
384
618
  def push key, *values
385
619
 
386
620
  @inner[key] = [] unless @inner.has_key? key
@@ -388,33 +622,60 @@ class MultiMap < ::Hash
388
622
  @inner[key].push(*values)
389
623
  end
390
624
 
625
+ # Removes a key-value pair from the instance and return as a two-item
626
+ # array
391
627
  def shift
392
628
 
393
629
  @inner.shift
394
630
  end
395
631
 
396
- def size
397
-
398
- @inner.size
399
- end
632
+ alias size length
400
633
 
634
+ # Causes an element with the given +key+ and +values+ to be stored. If an
635
+ # element with the given +key+ already exists, its values will b
636
+ # replaced
401
637
  def store key, *values
402
638
 
403
639
  @inner[key] = values
404
640
  end
405
641
 
642
+ # Converts instance to an array of +[key,value]+ pairs
406
643
  def to_a
407
644
 
408
645
  self.flatten
409
646
  end
410
647
 
648
+ # Obtains reference to internal hash instance (which must *not* be modified)
411
649
  def to_h
412
650
 
413
- @inner.dup
651
+ @inner.to_h
414
652
  end
415
653
 
654
+ # Obtains equivalent hash to instance
655
+ def to_hash
656
+
657
+ @elements.to_hash
658
+ end
659
+
660
+ # A string-form of the instance
661
+ def to_s
662
+
663
+ @inner.to_s
664
+ end
665
+
666
+ # An array of all values in the instance
416
667
  def values
417
668
 
669
+ r = []
670
+
671
+ @inner.values.each { |vals| r += vals }
672
+
673
+ r
674
+ end
675
+
676
+ # An array of all sets of values in the instance
677
+ def values_unflattened
678
+
418
679
  @inner.values
419
680
  end
420
681
  end # class MultiMap