xqsr3 0.39.6 → 0.39.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: 6370aea72b56e6222b5bceb189340a255b87da7c047b058cbd67d6d4eb0b4bd9
4
- data.tar.gz: db1fb7918b2737221db358636c2bdbb684d6f791a42582cee8df758b41f7ab2c
3
+ metadata.gz: 7e1df2847a524be64578ba040be65911b413eccbe2affbf7e4d3bac710a505d8
4
+ data.tar.gz: 4c2360c637d977e9cdb2f88b01455aa565ea0dc11225b09be357a35094daff22
5
5
  SHA512:
6
- metadata.gz: c9d4ba6de8d0390831931f32a0d8a41962d7fad41769d2e437aa566c0e89589a5df5bd33de774eb58ecff452695b8653d65ea49e30a4efe96a09a1f4975d8897
7
- data.tar.gz: 04f7391d0212d72ad5c09a4aa2fd4c3b33304f9132f48b81f83e851b7fdad9027be7ea9344c8514be9c400fd8b072d9123d5b1268013a3f924a6f8c2bd2c03d2
6
+ metadata.gz: 52ab95d6b63616544f08c3dd482ca1695aebc51fe70d01cf60c76ce7a737d5285fb61c32d4978da6f87a0f986cc02b279f1a5ee3680aa0c47b52a3e30e52fa6f
7
+ data.tar.gz: f774505ffcc560824ea2671624f07907d503791f53c0963a9a22cf233d39d1337f5d1044e9814977455d9e6d5f4f2079a006a36c0d5bd3a2090b6ff8ad23c414
data/CHANGES.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # xqsr3 - Changes <!-- omit in toc -->
2
2
 
3
3
 
4
+ ## 0.39.7 - 19th August 2026
5
+
6
+ * fixed **MultiMap**: no longer subclasses +Hash+ (data lived only in +@inner+, so inherited +keys+/+key?+ lied); added +keys+/+key?+/+include?+/+member?+; fixed +to_hash+; deep-copy on +dup+/+clone+; +MultiMap[]+ no longer mutates nested arrays; Hash merges splat array values consistently with +MultiMap[]+ / +to_h+;
7
+ * fixed `IO.writelines` Hash EOL lookahead (+each_with_index+ was not destructuring key/value pairs);
8
+ * aligned `xqsr3/extensions` (and thus `xqsr3/all_extensions`) with documented extension packs — now loads **integer**, **kernel**, and **string** as well as **enumerable/detect_map**;
9
+ * fixed **FrequencyMap** (+#store(0)+ removes key; +#to_h+/+#to_hash+ return copies; +#[]+ / +#each_by_key+ docs);
10
+ * fixed **ParameterChecking** (+#ignore_order+ recognised; +:types+ no longer mutates caller array) and **join_with_or** (+treat_as_option:+);
11
+ * fixed `Enumerable#detect_map` to return falsy hits (+nil+ means continue);
12
+ * fixed `Integer#to_s_grp` to honour +:separator+ for the single-group path;
13
+ * fixed `ExceptionUtilities.raise_with_options` to use +set_backtrace+ instead of deprecated 3-arg +raise+; documented **WithCause** +#cause+ shadowing;
14
+
15
+
4
16
  ## 0.39.6 - 18th August 2026
5
17
 
6
18
  * fixed **BoolParser** default matching to require whole-string `true`/`false` (case-insensitive), aligned `:default_value` / `:true_value` / `:false_value` options with documented names (legacy `:default` / `:true` / `:false` still accepted), and allowed falsy override values;
data/NEWS.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  | Date | News Item |
4
4
  | ------------------ | --------------------------- |
5
+ | 19th August 2026 | [**xqsr3** 0.39.7](https://github.com/synesissoftware/xqsr3/releases/tag/0.39.7) |
5
6
  | 18th August 2026 | [**xqsr3** 0.39.6](https://github.com/synesissoftware/xqsr3/releases/tag/0.39.6) |
6
7
  | 14th August 2026 | [**xqsr3** 0.39.5](https://github.com/synesissoftware/xqsr3/releases/tag/0.39.5) |
7
8
  | 12th August 2026 | [**xqsr3** 0.39.4.1](https://github.com/synesissoftware/xqsr3/releases/tag/0.39.4.1) |
data/README.md CHANGED
@@ -87,6 +87,7 @@ and extensions to the following standard library components:
87
87
  * Array extensions
88
88
  * Enumerable extensions
89
89
  * Hash extensions
90
+ * Integer extensions
90
91
  * IO extensions
91
92
  * Kernel extensions
92
93
  * String extensions
@@ -77,10 +77,10 @@ module ArrayUtilities
77
77
  ::Xqsr3::Quality::ParameterChecking.check_parameter ar, 'ar', type: ::Array, allow_nil: true
78
78
  ::Xqsr3::Quality::ParameterChecking.check_parameter options, 'options', type: ::Hash, allow_nil: false
79
79
 
80
- ::Xqsr3::Quality::ParameterChecking.check_parameter options[:or], ':or', type: ::String, option: true, allow_nil: true
81
- ::Xqsr3::Quality::ParameterChecking.check_parameter options[:oxford_comma], ':oxford_comma', types: [ ::FalseClass, ::TrueClass ], option: true, allow_nil: true
82
- ::Xqsr3::Quality::ParameterChecking.check_parameter options[:quote_char], ':quote_char', type: ::String, option: true, allow_nil: true
83
- ::Xqsr3::Quality::ParameterChecking.check_parameter options[:separator], ':separator', type: ::String, option: true, allow_nil: true
80
+ ::Xqsr3::Quality::ParameterChecking.check_parameter options[:or], ':or', type: ::String, treat_as_option: true, allow_nil: true
81
+ ::Xqsr3::Quality::ParameterChecking.check_parameter options[:oxford_comma], ':oxford_comma', types: [ ::FalseClass, ::TrueClass ], treat_as_option: true, allow_nil: true
82
+ ::Xqsr3::Quality::ParameterChecking.check_parameter options[:quote_char], ':quote_char', type: ::String, treat_as_option: true, allow_nil: true
83
+ ::Xqsr3::Quality::ParameterChecking.check_parameter options[:separator], ':separator', type: ::String, treat_as_option: true, allow_nil: true
84
84
 
85
85
  return '' if ar.nil?
86
86
  return '' if ar.empty?
@@ -5,13 +5,13 @@
5
5
  # Purpose: FrequencyMap container
6
6
  #
7
7
  # Created: 28th January 2005
8
- # Updated: 24th July 2024
8
+ # Updated: 15th August 2026
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
- # Copyright (c) 2019-2024, Matthew Wilson and Synesis Information Systems
14
+ # Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems
15
15
  # Copyright (c) 2005-2019, Matthew Wilson and Synesis Software
16
16
  # All rights reserved.
17
17
  #
@@ -203,7 +203,7 @@ module Containers
203
203
  false
204
204
  end
205
205
 
206
- # Obtains the count for a given key, or +nil+ if the key does not exist
206
+ # Obtains the count for a given key, or +0+ if the key does not exist
207
207
  #
208
208
  # === Signature
209
209
  #
@@ -294,7 +294,8 @@ module Containers
294
294
  # Enumerates each entry pair - element + frequency - in key order
295
295
  #
296
296
  # Note: this method is more expensive than +each+ because an array of
297
- # keys must be created and sorted from which enumeration is directed
297
+ # keys must be created and sorted from which enumeration is directed.
298
+ # Keys must be mutually comparable via +<=>+.
298
299
  def each_by_key
299
300
 
300
301
  sorted_elements = @elements.sort { |a, b| a[0] <=> b[0] }
@@ -588,7 +589,8 @@ module Containers
588
589
 
589
590
  # Causes an element with the given +key+ and +count+ to be stored. If an
590
591
  # element with the given +key+ already exists, its count will be adjusted,
591
- # as will the total count
592
+ # as will the total count. A +count+ of +0+ removes the key (consistent
593
+ # with +#push+ reducing a count to zero).
592
594
  #
593
595
  # === Return
594
596
  # +true+ if the element was inserted; +false+ if the element was
@@ -599,7 +601,13 @@ module Containers
599
601
 
600
602
  old_count = @elements[key] || 0
601
603
 
602
- @elements.store key, count
604
+ if 0 == count
605
+
606
+ @elements.delete key
607
+ else
608
+
609
+ @elements.store key, count
610
+ end
603
611
 
604
612
  @count += count - old_count
605
613
 
@@ -612,16 +620,16 @@ module Containers
612
620
  @elements.to_a
613
621
  end
614
622
 
615
- # Obtains reference to internal hash instance (which must *not* be modified)
623
+ # Obtains a +Hash+ copy of the instance contents
616
624
  def to_h
617
625
 
618
- @elements.to_h
626
+ @elements.dup
619
627
  end
620
628
 
621
629
  # Obtains equivalent hash to instance
622
630
  def to_hash
623
631
 
624
- @elements.to_hash
632
+ @elements.dup
625
633
  end
626
634
 
627
635
  # A string-form of the instance
@@ -5,13 +5,13 @@
5
5
  # Purpose: multimap container
6
6
  #
7
7
  # Created: 21st March 2007
8
- # Updated: 24th July 2024
8
+ # Updated: 15th August 2026
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
- # Copyright (c) 2019-2024, Matthew Wilson and Synesis Information Systems
14
+ # Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems
15
15
  # Copyright (c) 2007-2019, Matthew Wilson and Synesis Software
16
16
  # All rights reserved.
17
17
  #
@@ -51,8 +51,8 @@
51
51
  module Xqsr3
52
52
  module Containers
53
53
 
54
- # Hash-like class that stores as mapped values in arrays
55
- class MultiMap < ::Hash
54
+ # Hash-like class that stores mapped values in arrays
55
+ class MultiMap
56
56
 
57
57
  include Enumerable
58
58
 
@@ -102,9 +102,10 @@ module Containers
102
102
 
103
103
  raise ArgumentError, "cannot pass an empty array in array of arrays initialiser" if ar.empty?
104
104
 
105
- key = ar.shift
105
+ key = ar[0]
106
+ values = ar[1 ... ar.size]
106
107
 
107
- ar.each { |value| h[key] << value }
108
+ values.each { |value| h[key] << value }
108
109
  end
109
110
 
110
111
  return self.[](h)
@@ -136,6 +137,18 @@ module Containers
136
137
  @inner = Hash.new
137
138
  end
138
139
 
140
+ # Deep-copies +@inner+ (and each values array) after +dup+/+clone+
141
+ def initialize_copy other
142
+
143
+ @merge_is_multi = other.instance_variable_get(:@merge_is_multi)
144
+ @inner = {}
145
+
146
+ other.instance_variable_get(:@inner).each do |k, vals|
147
+
148
+ @inner[k] = vals.dup
149
+ end
150
+ end
151
+
139
152
  # Obtains the values, if any, for the given key; returns +nil+ if no
140
153
  # values are stored
141
154
  def [] key
@@ -412,6 +425,16 @@ module Containers
412
425
  @inner.has_key? key
413
426
  end
414
427
 
428
+ alias include? has_key?
429
+ alias key? has_key?
430
+ alias member? has_key?
431
+
432
+ # An array of all keys in the instance
433
+ def keys
434
+
435
+ @inner.keys
436
+ end
437
+
415
438
  # Returns +true+ if any key has the given +value+; +false+ otherwise
416
439
  #
417
440
  # === Signature
@@ -533,18 +556,25 @@ module Containers
533
556
  case other
534
557
  when self.class
535
558
 
536
- ;
559
+ other.each do |k, v|
560
+
561
+ self.push k, v
562
+ end
537
563
  when ::Hash
538
564
 
539
- ;
540
- else
565
+ other.each do |k, v|
541
566
 
542
- raise TypeError, "parameter must be an instance of #{self.class} or #{Hash}"
543
- end
567
+ if ::Array === v
568
+
569
+ self.push k, *v
570
+ else
544
571
 
545
- other.each do |k, v|
572
+ self.push k, v
573
+ end
574
+ end
575
+ else
546
576
 
547
- self.push k, v
577
+ raise TypeError, "parameter must be an instance of #{self.class} or #{Hash}"
548
578
  end
549
579
 
550
580
  self
@@ -598,7 +628,13 @@ module Containers
598
628
 
599
629
  other.each do |k, v|
600
630
 
601
- self.store k, v
631
+ if ::Array === v
632
+
633
+ self.store k, *v
634
+ else
635
+
636
+ self.store k, v
637
+ end
602
638
  end
603
639
  else
604
640
 
@@ -680,10 +716,17 @@ module Containers
680
716
  @inner.to_h
681
717
  end
682
718
 
683
- # Obtains equivalent hash to instance
719
+ # Obtains equivalent hash to instance (independent copy)
684
720
  def to_hash
685
721
 
686
- @elements.to_hash
722
+ h = {}
723
+
724
+ @inner.each do |k, vals|
725
+
726
+ h[k] = vals.dup
727
+ end
728
+
729
+ h
687
730
  end
688
731
 
689
732
  # A string-form of the instance
@@ -180,7 +180,8 @@ module Diagnostics
180
180
 
181
181
  bt = x.backtrace
182
182
  (0..called_indirectly).each { bt.shift }
183
- Kernel.raise x, x.message, bt
183
+ x.set_backtrace bt
184
+ Kernel.raise x
184
185
  end
185
186
  end
186
187
 
@@ -204,7 +205,8 @@ module Diagnostics
204
205
 
205
206
  bt = x.backtrace
206
207
  (0..called_indirectly).each { bt.shift }
207
- Kernel.raise x, x.message, bt
208
+ x.set_backtrace bt
209
+ Kernel.raise x
208
210
  end
209
211
  end
210
212
  end # module ExceptionUtilities
@@ -133,7 +133,9 @@ module Exceptions
133
133
  end
134
134
 
135
135
  # The cause / inner-exception, if any, specified to the instance
136
- # initialiser
136
+ # initialiser. This attribute shadows +Exception#cause+ (the interpreter's
137
+ # raise-chain cause); for includers, +#cause+ refers to this module's
138
+ # +@cause+.
137
139
  attr_reader :cause
138
140
 
139
141
  # The options passed to the initialiser, with +:cause+ removed, if
@@ -5,13 +5,13 @@
5
5
  # Purpose: ::Enumerable#detect_map extension
6
6
  #
7
7
  # Created: 3rd December 2017
8
- # Updated: 29th March 2024
8
+ # Updated: 15th August 2026
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
- # Copyright (c) 2019-2024, Matthew Wilson and Synesis Information Systems
14
+ # Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems
15
15
  # Copyright (c) 2017-2019, Matthew Wilson and Synesis Software
16
16
  # All rights reserved.
17
17
  #
@@ -54,13 +54,15 @@ module Enumerable
54
54
  # of a particular value in a collection. The only constraint is that you
55
55
  # get back the object unchanged.
56
56
  #
57
- # The +Enumerable+#+map+ method provides a way to transform the contents of
58
- # a collection. The only constraint is that you get back another
57
+ # The +Enumerable+#+map+ method provides a way to transform the contents
58
+ # of a collection. The only constraint is that you get back another
59
59
  # collection.
60
60
  #
61
61
  # This extension method, +Enumerable+#+detect_map+ combines the features
62
62
  # of both, in that it detects the presence of a particular value in a
63
- # collection and transform the detected value.
63
+ # collection and transform the detected value. The block should return
64
+ # +nil+ to continue searching; any other value (including +false+ or +0+)
65
+ # is treated as a hit and returned immediately.
64
66
  #
65
67
  # [ 1, 2, 3 ].detect_map { |v| -2 * v if v > 2 } # => -6
66
68
  #
@@ -75,13 +77,17 @@ module Enumerable
75
77
 
76
78
  self.each do |v|
77
79
 
78
- r = yield(v) and return r
80
+ r = yield(v)
81
+
82
+ return r unless r.nil?
79
83
  end
80
84
  when 2
81
85
 
82
86
  self.each do |k, v|
83
87
 
84
- r = yield(k, v) and return r
88
+ r = yield(k, v)
89
+
90
+ return r unless r.nil?
85
91
  end
86
92
  else
87
93
 
@@ -1,4 +1,5 @@
1
1
 
2
2
  require 'xqsr3/extensions/enumerable/collect_with_index'
3
+ require 'xqsr3/extensions/enumerable/detect_map'
3
4
  require 'xqsr3/extensions/enumerable/unique'
4
5
 
@@ -83,7 +83,7 @@ class Integer
83
83
  return self.to_s
84
84
  when 1
85
85
 
86
- return self.to_s.chars.to_a.reverse.each_slice(numbers[0]).map(&:join).join(',').reverse
86
+ return self.to_s.chars.to_a.reverse.each_slice(numbers[0]).map(&:join).join(separator).reverse
87
87
  else
88
88
 
89
89
  reversed_chars = self.to_s.chars.to_a#.reverse
@@ -4,7 +4,10 @@
4
4
  array
5
5
  enumerable
6
6
  hash
7
+ integer
7
8
  io
9
+ kernel
10
+ string
8
11
  }.each do |name|
9
12
 
10
13
  require File.join(File.dirname(__FILE__), 'extensions', name)
@@ -5,13 +5,13 @@
5
5
  # Purpose: Adds a writelines() method to the IO module
6
6
  #
7
7
  # Created: 13th April 2007
8
- # Updated: 12th April 2024
8
+ # Updated: 15th August 2026
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
12
12
  # Author: Matthew Wilson
13
13
  #
14
- # Copyright (c) 2019-2024, Matthew Wilson and Synesis Information Systems
14
+ # Copyright (c) 2019-2026, Matthew Wilson and Synesis Information Systems
15
15
  # Copyright (c) 2007-2019, Matthew Wilson and Synesis Software
16
16
  # All rights reserved.
17
17
  #
@@ -125,7 +125,7 @@ module IO
125
125
 
126
126
  if contents.instance_of? ::Hash
127
127
 
128
- contents.each_with_index do |k, v, index|
128
+ contents.each_with_index do |(k, v), index|
129
129
 
130
130
  if eol_lookahead_limit && eol_lookahead_limit == index
131
131
 
@@ -82,6 +82,7 @@ module Quality
82
82
 
83
83
  allow_nil
84
84
  ignore_case
85
+ ignore_order
85
86
  message
86
87
  nil
87
88
  nothrow
@@ -118,6 +119,7 @@ module Quality
118
119
  # - +:allow_nil+ (boolean) The +value+ must not be +nil+ unless this option is true;
119
120
  # - +:nil+ an alias for +:allow_nil+;
120
121
  # - +:ignore_case+ (boolean) When +:values+ is specified, comparisons of strings, or arrays of strings, will be carried out in a case-insensitive manner;
122
+ # - +:ignore_order+ (boolean) When +:values+ is specified and +value+ is an +Array+, comparisons may ignore element order;
121
123
  # - +:types+ (+Array+) An array of types one of which +value+ must be (or must be derived from). One of these types may be an array of types, in which case +value+ may be an array that must consist wholly of those types;
122
124
  # - +:type+ (+Class+) A single type parameter, used only if +:types+ is not specified;
123
125
  # - +:values+ (+Array+) an array of values one of which +value+ must be;
@@ -174,6 +176,7 @@ module Quality
174
176
  # - +:allow_nil+ (boolean) The +value+ must not be +nil+ unless this option is true;
175
177
  # - +:nil+ an alias for +:allow_nil+;
176
178
  # - +:ignore_case+ (boolean) When +:values+ is specified, comparisons of strings, or arrays of strings, will be carried out in a case-insensitive manner;
179
+ # - +:ignore_order+ (boolean) When +:values+ is specified and +value+ is an +Array+, comparisons may ignore element order;
177
180
  # - +:types+ (+Array+) An array of types one of which +value+ must be (or must be derived from). One of these types may be an array of types, in which case +value+ may be an array that must consist wholly of those types;
178
181
  # - +:type+ (+Class+) A single type parameter, used only if +:types+ is not specified;
179
182
  # - +:values+ (+Array+) an array of values one of which +value+ must be;
@@ -328,7 +331,7 @@ module Quality
328
331
 
329
332
  # types
330
333
 
331
- types = options[:types] || []
334
+ types = (options[:types] || []).dup
332
335
  if options.has_key? :type
333
336
 
334
337
  types << options[:type] if types.empty?
data/lib/xqsr3/version.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  # Purpose: Version for Xqsr3 library
6
6
  #
7
7
  # Created: 3rd April 2016
8
- # Updated: 18th August 2026
8
+ # Updated: 19th August 2026
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/xqsr3
11
11
  #
@@ -51,7 +51,7 @@
51
51
  module Xqsr3
52
52
 
53
53
  # Current version of the Xqsr3 library
54
- VERSION = '0.39.6'
54
+ VERSION = '0.39.7'
55
55
 
56
56
  private
57
57
  VERSION_PARTS_ = VERSION.split(/[.]/).collect { |n| n.to_i } # :nodoc:
@@ -889,6 +889,13 @@ class Test_Xqsr3_Containers_FrequencyMap < Test::Unit::TestCase
889
889
  assert_equal 2, fm.size
890
890
 
891
891
  assert_raise(::TypeError) { fm.store :ghi, :blah }
892
+
893
+ fm.store :abc, 0
894
+
895
+ assert_not fm.has_key?(:abc)
896
+ assert_equal 2, fm.count
897
+ assert_equal 1, fm.size
898
+ assert_equal 0, fm[:abc]
892
899
  end
893
900
 
894
901
  def test_to_a
@@ -918,7 +925,18 @@ class Test_Xqsr3_Containers_FrequencyMap < Test::Unit::TestCase
918
925
 
919
926
  fm << :def << :def << :def
920
927
 
921
- assert_equal ({:abc => 2, :def => 3}), fm.to_h
928
+ h = fm.to_h
929
+
930
+ assert_equal ({:abc => 2, :def => 3}), h
931
+ assert_equal h, fm.to_hash
932
+ assert_not_same fm.instance_variable_get(:@elements), h
933
+
934
+ h[:abc] = 99
935
+ h[:xyz] = 1
936
+
937
+ assert_equal 2, fm[:abc]
938
+ assert_equal 5, fm.count
939
+ assert_not fm.has_key?(:xyz)
922
940
  end
923
941
 
924
942
  def test_values
@@ -69,10 +69,12 @@ class Test_Xqsr3_Containers_MultiMap < Test::Unit::TestCase
69
69
  def test_class_operator_subscript_6
70
70
 
71
71
  ar = [ [ :abc, '1' ] ]
72
+ nested = ar[0]
72
73
 
73
74
  mm = MultiMap[ar]
74
75
 
75
76
  assert_equal 1, ar.size
77
+ assert_equal [ :abc, '1' ], nested
76
78
  assert_equal 1, mm.size
77
79
  assert_equal [ '1' ], mm[:abc]
78
80
 
@@ -81,6 +83,8 @@ class Test_Xqsr3_Containers_MultiMap < Test::Unit::TestCase
81
83
  mm = MultiMap[ar]
82
84
 
83
85
  assert_equal 2, ar.size
86
+ assert_equal [ :abc, '1' ], ar[0]
87
+ assert_equal [ :def, '2' ], ar[1]
84
88
  assert_equal 2, mm.size
85
89
  assert_equal [ '1' ], mm[:abc]
86
90
  assert_equal [ '2' ], mm[:def]
@@ -253,6 +257,18 @@ class Test_Xqsr3_Containers_MultiMap < Test::Unit::TestCase
253
257
  mm2 = mm1.dup
254
258
 
255
259
  assert_eql mm1, mm2
260
+
261
+ mm2.push :xyz, 'x1'
262
+
263
+ assert_not mm1.has_key?(:xyz)
264
+ assert mm2.has_key?(:xyz)
265
+ assert_equal 3, mm1.size
266
+ assert_equal 4, mm2.size
267
+
268
+ mm2[:abc] << 'a5'
269
+
270
+ assert_equal [ 'a1', 'a2', 'a3', 'a4' ], mm1[:abc]
271
+ assert_equal [ 'a1', 'a2', 'a3', 'a4', 'a5' ], mm2[:abc]
256
272
  end
257
273
 
258
274
  def test_each
@@ -538,16 +554,35 @@ class Test_Xqsr3_Containers_MultiMap < Test::Unit::TestCase
538
554
  mm = MultiMap.new
539
555
 
540
556
  assert_not mm.has_key? :abc
557
+ assert_not mm.key? :abc
558
+ assert_not mm.include? :abc
559
+ assert_not mm.member? :abc
541
560
 
542
561
  mm.push :abc, *[ :v1, :v2 ]
543
562
 
544
563
  assert mm.has_key? :abc
564
+ assert mm.key? :abc
565
+ assert mm.include? :abc
566
+ assert mm.member? :abc
545
567
 
546
568
  mm.delete :abc
547
569
 
548
570
  assert_not mm.has_key? :abc
549
571
  end
550
572
 
573
+ def test_keys
574
+
575
+ mm = MultiMap.new
576
+
577
+ assert_equal [], mm.keys
578
+
579
+ mm.push :def
580
+ mm.push :abc, 'a1'
581
+ mm.push :ghi, 'g1', 'g2'
582
+
583
+ assert_equal [ :abc, :def, :ghi ], mm.keys.sort
584
+ end
585
+
551
586
  def test_has_value?
552
587
 
553
588
  mm = MultiMap.new
@@ -705,6 +740,10 @@ class Test_Xqsr3_Containers_MultiMap < Test::Unit::TestCase
705
740
  mm1.multi_merge! h
706
741
 
707
742
  assert_equal [ :abc, 1, :abc, 2, :abc, 3, :abc, 4, :abc, 5, :def, 'a', :ghi, 'x' ], mm1.flatten
743
+
744
+ mm1.multi_merge!({ abc: [ 6 ], jkl: [ 'y', 'z' ] })
745
+
746
+ assert_equal [ :abc, 1, :abc, 2, :abc, 3, :abc, 4, :abc, 5, :abc, 6, :def, 'a', :ghi, 'x', :jkl, 'y', :jkl, 'z' ], mm1.flatten
708
747
  end
709
748
 
710
749
  def test_strict_merge
@@ -868,6 +907,26 @@ class Test_Xqsr3_Containers_MultiMap < Test::Unit::TestCase
868
907
  assert_equal ({ abc: [ 1, 2, 3, 4, 5 ], def: [] }), mm.to_h
869
908
  end
870
909
 
910
+ def test_to_hash
911
+
912
+ mm = MultiMap.new
913
+
914
+ mm.push :abc, 1, 2
915
+ mm.push :def, 'd'
916
+
917
+ h = mm.to_hash
918
+
919
+ assert_equal ({ abc: [ 1, 2 ], def: [ 'd' ] }), h
920
+ assert_equal h, mm.to_h
921
+ assert_not_same mm.to_h, h
922
+
923
+ h[:abc] << 3
924
+ h[:ghi] = [ 'x' ]
925
+
926
+ assert_equal [ 1, 2 ], mm[:abc]
927
+ assert_not mm.has_key?(:ghi)
928
+ end
929
+
871
930
  def test_values_unflattened
872
931
 
873
932
  mm = MultiMap.new
@@ -22,6 +22,9 @@ class Test_Enumerable_detect_map < Test::Unit::TestCase
22
22
  assert_nil [].detect_map { |v| return -2 * v }
23
23
  assert_equal(-2, [ 1, 2, 3, 4, 5 ].detect_map { |v| -2 * v })
24
24
  assert_equal(-4, [ 1, 2, 3, 4, 5 ].detect_map { |v| -2 * v if 2 == v })
25
+
26
+ assert_equal false, [ 1, 2, 3 ].detect_map { |v| false if 1 == v }
27
+ assert_equal 0, [ 1, 2, 3 ].detect_map { |v| 0 if 2 == v }
25
28
  end
26
29
 
27
30
  def test_with_hash
@@ -22,6 +22,9 @@ class Test_X_Integer_to_s_grp < Test::Unit::TestCase
22
22
  assert_equal "8,7,6,5,4,3,2,1", 87654321.to_s_grp(1)
23
23
 
24
24
  assert_equal "876,54321", 87654321.to_s_grp(5)
25
+
26
+ assert_equal "87 654 321", 87654321.to_s_grp(3, separator: ' ')
27
+ assert_equal "87_654_321", 87654321.to_s_grp(3, separator: '_')
25
28
  end
26
29
 
27
30
  def test_array_with_single_number
@@ -0,0 +1,35 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '../../../lib')
4
+
5
+
6
+ require 'xqsr3/extensions'
7
+
8
+ require 'xqsr3/extensions/test/unit'
9
+ require 'test/unit'
10
+
11
+
12
+ class Test_Xqsr3_extensions_pack < Test::Unit::TestCase
13
+
14
+ def test_extensions_pack_loads_documented_surfaces
15
+
16
+ assert [].respond_to?(:join_with_or)
17
+ assert [].respond_to?(:collect_with_index)
18
+ assert [].respond_to?(:detect_map)
19
+ assert [].respond_to?(:unique)
20
+ assert Hash.new.respond_to?(:deep_transform)
21
+ assert 1234.respond_to?(:to_s_grp)
22
+ assert ::IO.respond_to?(:writelines)
23
+ assert ''.respond_to?(:starts_with?)
24
+ assert ''.respond_to?(:truncate)
25
+ assert_equal 16, Integer('10', 16)
26
+ end
27
+
28
+ def test_all_extensions_loads_test_unit_assertions
29
+
30
+ require 'xqsr3/all_extensions'
31
+
32
+ assert self.respond_to?(:assert_true)
33
+ assert self.respond_to?(:assert_raise_with_message)
34
+ end
35
+ end
@@ -182,6 +182,30 @@ class Test_Xqsr3_IO_writelines < Test::Unit::TestCase
182
182
  assert_equal "abc\ndef\nghi\n", s.string
183
183
  end
184
184
 
185
+ def test_hash_value_with_embedded_eol
186
+
187
+ input = { 'ab' => "c\nmore", 'de' => 'f' }
188
+
189
+ s = StringIO.new
190
+
191
+ r = ::Xqsr3::IO.writelines s, input
192
+
193
+ assert_equal 2, r
194
+ assert_equal "abc\nmoredef", s.string
195
+ end
196
+
197
+ def test_hash_key_with_embedded_eol
198
+
199
+ input = { "ab\nx" => 'c', 'de' => 'f' }
200
+
201
+ s = StringIO.new
202
+
203
+ r = ::Xqsr3::IO.writelines s, input
204
+
205
+ assert_equal 2, r
206
+ assert_equal "ab\nxcdef", s.string
207
+ end
208
+
185
209
  def test_strings_with_cr_in_array_and_line_sep
186
210
 
187
211
  input = [ "abc\n", "def\n", "ghi\n" ]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xqsr3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.39.6
4
+ version: 0.39.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Wilson
@@ -136,7 +136,7 @@ files:
136
136
  - test/unit/extensions/kernel/ts_all.rb
137
137
  - test/unit/extensions/object/tc_inspect.rb
138
138
  - test/unit/extensions/object/ts_all.rb
139
- - test/unit/extensions/string/tc_bool.tb
139
+ - test/unit/extensions/string/tc_bool.rb
140
140
  - test/unit/extensions/string/tc_ends_with.rb
141
141
  - test/unit/extensions/string/tc_map_option_string.rb
142
142
  - test/unit/extensions/string/tc_nil_if_empty.rb
@@ -146,6 +146,7 @@ files:
146
146
  - test/unit/extensions/string/tc_to_symbol.rb
147
147
  - test/unit/extensions/string/tc_truncate.rb
148
148
  - test/unit/extensions/string/ts_all.rb
149
+ - test/unit/extensions/tc_extensions_pack.rb
149
150
  - test/unit/extensions/test/ts_all.rb
150
151
  - test/unit/extensions/test/unit/tc_assert_raise_with_message.rb
151
152
  - test/unit/extensions/test/unit/tc_assert_subclass_of.rb