xqsr3 0.32.3 → 0.37.0

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