xqsr3 0.32.2 → 0.36.0

This diff has not been reviewed by any users.
Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +5 -5
  2. data/lib/xqsr3/all_extensions.rb +6 -0
  3. data/lib/xqsr3/array_utilities.rb +10 -0
  4. data/lib/xqsr3/array_utilities/join_with_or.rb +7 -14
  5. data/lib/xqsr3/command_line_utilities.rb +10 -0
  6. data/lib/xqsr3/command_line_utilities/map_option_string.rb +21 -8
  7. data/lib/xqsr3/containers.rb +11 -0
  8. data/lib/xqsr3/containers/frequency_map.rb +19 -2
  9. data/lib/xqsr3/containers/multi_map.rb +316 -27
  10. data/lib/xqsr3/conversion.rb +11 -0
  11. data/lib/xqsr3/conversion/bool_parser.rb +11 -14
  12. data/lib/xqsr3/conversion/integer_parser.rb +10 -16
  13. data/lib/xqsr3/diagnostics.rb +11 -0
  14. data/lib/xqsr3/diagnostics/exception_utilities.rb +2 -2
  15. data/lib/xqsr3/diagnostics/exceptions/with_cause.rb +15 -7
  16. data/lib/xqsr3/diagnostics/inspect_builder.rb +16 -16
  17. data/lib/xqsr3/doc_.rb +138 -9
  18. data/lib/xqsr3/extensions.rb +13 -0
  19. data/lib/xqsr3/extensions/array.rb +3 -0
  20. data/lib/xqsr3/extensions/array/join_with_or.rb +6 -0
  21. data/lib/xqsr3/extensions/enumerable/collect_with_index.rb +5 -4
  22. data/lib/xqsr3/extensions/enumerable/detect_map.rb +6 -7
  23. data/lib/xqsr3/extensions/hash.rb +5 -0
  24. data/lib/xqsr3/extensions/hash/has_match.rb +7 -0
  25. data/lib/xqsr3/extensions/hash/match.rb +7 -0
  26. data/lib/xqsr3/extensions/hash/slice.rb +22 -0
  27. data/lib/xqsr3/extensions/io/writelines.rb +38 -6
  28. data/lib/xqsr3/extensions/kernel/integer.rb +6 -13
  29. data/lib/xqsr3/extensions/string/to_bool.rb +4 -0
  30. data/lib/xqsr3/extensions/test/unit/assert_eql.rb +1 -0
  31. data/lib/xqsr3/extensions/test/unit/assert_false.rb +1 -0
  32. data/lib/xqsr3/extensions/test/unit/assert_not.rb +1 -0
  33. data/lib/xqsr3/extensions/test/unit/assert_not_eql.rb +1 -0
  34. data/lib/xqsr3/extensions/test/unit/assert_raise_with_message.rb +25 -4
  35. data/lib/xqsr3/extensions/test/unit/assert_subclass_of.rb +1 -0
  36. data/lib/xqsr3/extensions/test/unit/assert_superclass_of.rb +1 -0
  37. data/lib/xqsr3/extensions/test/unit/assert_true.rb +1 -0
  38. data/lib/xqsr3/extensions/test/unit/assert_type_has_instance_methods.rb +3 -12
  39. data/lib/xqsr3/hash_utilities.rb +11 -0
  40. data/lib/xqsr3/hash_utilities/deep_transform.rb +2 -2
  41. data/lib/xqsr3/hash_utilities/key_matching.rb +6 -4
  42. data/lib/xqsr3/internal_/test_unit_version_.rb +30 -4
  43. data/lib/xqsr3/io/writelines.rb +55 -19
  44. data/lib/xqsr3/quality.rb +8 -1
  45. data/lib/xqsr3/quality/parameter_checking.rb +52 -78
  46. data/lib/xqsr3/string_utilities.rb +16 -0
  47. data/lib/xqsr3/string_utilities/ends_with.rb +16 -7
  48. data/lib/xqsr3/string_utilities/nil_if_empty.rb +8 -4
  49. data/lib/xqsr3/string_utilities/nil_if_whitespace.rb +9 -4
  50. data/lib/xqsr3/string_utilities/quote_if.rb +12 -14
  51. data/lib/xqsr3/string_utilities/starts_with.rb +23 -5
  52. data/lib/xqsr3/string_utilities/to_symbol.rb +24 -5
  53. data/lib/xqsr3/string_utilities/truncate.rb +20 -4
  54. data/lib/xqsr3/version.rb +3 -2
  55. data/test/unit/containers/tc_multi_map.rb +174 -16
  56. data/test/unit/extensions/hash/tc_hash.rb +6 -0
  57. data/test/unit/extensions/hash/tc_slice.rb +31 -0
  58. data/test/unit/extensions/io/tc_writelines.rb +36 -0
  59. metadata +16 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: d08b7ea53bfd21226d79bb36506890a7868ba93b
4
- data.tar.gz: c4d23e493010555d8c3cfb27c515b469a47dd29f
2
+ SHA256:
3
+ metadata.gz: 4e3f5e422b5b6d6f88ee0c0eb620f38ce468ef54e1cf9d55ab41f24e909c897b
4
+ data.tar.gz: 80f3675582a96f52a108687c4a77911691972dd0a2167138ffa0c9992c13e3ed
5
5
  SHA512:
6
- metadata.gz: 34a1d293c2de8559801ec22e038a4e7fc33a7d6da5901411d84f1aa6ca67cd22295f690ecc501b166e9aa8777f5b00408473b467782ff129ebade29e0a0e15e0
7
- data.tar.gz: 9e9a153905629a84437e69ea297ca7bbc843542a3ab90b35997d1e79d816ded0d90d2c2e8381c9db13ca6ffe6a4a8ae0691c2db5a7b4478a58efef4dc2e52ee4
6
+ metadata.gz: 41b2ac34f613f74b684223073ff61c3350b8433bda18520bb0af25db2464a22ed1be8fe53859eaf37f60d366d7053d8cab77d653b45b33a57ebd1a619b6186c6
7
+ data.tar.gz: 28d271b3501677420540ec6a55b1bf69bedf398dc422362ff7ca5e50e1d827d4f4fccc45c73eece170949bab755a47472d78f207a0b7c992cb942b13d368be76
@@ -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
+
@@ -56,6 +56,7 @@ require 'xqsr3/quality/parameter_checking'
56
56
  module Xqsr3
57
57
  module ArrayUtilities
58
58
 
59
+ # +include+-able module that provides sequence-joining functionality
59
60
  module JoinWithOr
60
61
 
61
62
  extend self
@@ -65,22 +66,14 @@ module JoinWithOr
65
66
  # === Signature
66
67
  #
67
68
  # * *Parameters:*
68
- #
69
- # * *Required parameters*:
70
- # - +ar+:: [Array] The array whose contents are to be joined
71
- #
72
- # * *Options parameters*:
73
- # - +options+:: [Hash] Options that control the behaviour of the
74
- # method
69
+ # - +ar+ (Array) The array whose contents are to be joined
70
+ # - +options+ (Hash) Options that control the behaviour of the method
75
71
  #
76
72
  # * *Options:*
77
- #
78
- # - +:or+:: [String] A string that is used instead of 'or'
79
- # - +:oxford_comma+:: [boolean] Determines whether an Oxford comma
80
- # will be used. Default is +true+
81
- # - +:quote_char+ [String] The quote character. Default is empty
82
- # string
83
- # - +:separator+ [String] The separator character. Default is +','+
73
+ # - +:or+ (String) A string that is used instead of 'or'
74
+ # - +:oxford_comma+ (boolean) Determines whether an Oxford comma will be used. Default is +true+
75
+ # - +:quote_char+ (String) The quote character. Default is empty string ''
76
+ # - +:separator+ (String) The separator character. Default is ','
84
77
  def join_with_or ar, **options
85
78
 
86
79
  ::Xqsr3::Quality::ParameterChecking.check_parameter ar, 'ar', type: ::Array, allow_nil: true
@@ -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,13 +6,13 @@
6
6
  # ::Xqsr3::CommandLineUtilities::MapOptionString module
7
7
  #
8
8
  # Created: 15th April 2016
9
- # Updated: 2nd August 2017
9
+ # Updated: 15th April 2019
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/xqsr3
12
12
  #
13
13
  # Author: Matthew Wilson
14
14
  #
15
- # Copyright (c) 2016-2017, Matthew Wilson and Synesis Software
15
+ # Copyright (c) 2016-2019, Matthew Wilson and Synesis Software
16
16
  # All rights reserved.
17
17
  #
18
18
  # Redistribution and use in source and binary forms, with or without
@@ -56,21 +56,21 @@ require 'xqsr3/string_utilities/to_symbol'
56
56
  module Xqsr3
57
57
  module CommandLineUtilities
58
58
 
59
- # Facilities for mapping strings to options
59
+ # +include+-able module providing facilities for mapping strings to options
60
60
  #
61
61
  # === Components of interest
62
- # * ::Xqsr3::CommandLineUtilities::MapOptionString.map_option_string
62
+ # * ::Xqsr3::CommandLineUtilities::MapOptionString.map_option_string_from_string
63
+ # * ::Xqsr3::CommandLineUtilities::MapOptionString#map_option_string
63
64
  module MapOptionString
64
65
 
65
- # :nodoc:
66
- def self.included includer
66
+ def self.included includer # :nodoc:
67
67
 
68
68
  raise TypeError, "module #{self} cannot be included into #{includer} because it does not respond to to_str" unless includer.method_defined? :to_str
69
69
  end
70
70
 
71
71
  private
72
- # :nodoc:
73
- module MapOptionString_Helper_ # :nodoc:
72
+ # @!visibility private
73
+ module MapOptionString_Helper_ # :nodoc: all
74
74
 
75
75
  def self.map_option_string_with_options_ s, option_strings, options
76
76
 
@@ -117,6 +117,13 @@ module MapOptionString
117
117
 
118
118
  # Attempts to translate the value of a given string according
119
119
  # to a collection of options strings
120
+ #
121
+ # === Signature
122
+ #
123
+ # * *Parameters:*
124
+ # - +s+ (::String) The string to be mapped
125
+ # - +option_strings+ ([::String]) An array of strings against which the mapping will be performed
126
+ # - +options+ (Hash) Options that control the behaviour of the method
120
127
  def self.map_option_string_from_string s, option_strings, options = {}
121
128
 
122
129
  MapOptionString_Helper_.map_option_string_with_options_ s, option_strings, options
@@ -124,6 +131,11 @@ module MapOptionString
124
131
 
125
132
  # Attempts to translate the (string) value of the receiver according
126
133
  # to a collection of options strings
134
+ #
135
+ # === Signature
136
+ #
137
+ # * *Parameters:*
138
+ # - +option_strings+ ([::String]) An array of strings against which the mapping will be performed
127
139
  def map_option_string option_strings, options = {}
128
140
 
129
141
  s = self.kind_of?(::String) ? self : self.to_str
@@ -137,3 +149,4 @@ end # module Xqsr3
137
149
 
138
150
  # ############################## end of file ############################# #
139
151
 
152
+
@@ -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
@@ -163,7 +164,7 @@ class FrequencyMap
163
164
  # Pushes an element into the map, assigning it an initial count of 1
164
165
  #
165
166
  # * *Parameters:*
166
- # - +key+:: The element to insert
167
+ # - +key+ The element to insert
167
168
  def << key
168
169
 
169
170
  push key, 1
@@ -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
  #
@@ -53,10 +53,12 @@
53
53
  module Xqsr3
54
54
  module Containers
55
55
 
56
+ # Hash-like class that stores as mapped values in arrays
56
57
  class MultiMap < ::Hash
57
58
 
58
59
  include Enumerable
59
60
 
61
+ # Creates an instance from the given arguments
60
62
  def self.[] *args
61
63
 
62
64
  return self.new if 0 == args.length
@@ -71,16 +73,16 @@ class MultiMap < ::Hash
71
73
  return self.new
72
74
  when ::Hash
73
75
 
74
- fm = self.new
76
+ mm = self.new
75
77
 
76
78
  arg.each do |k, v|
77
79
 
78
80
  raise ArgumentError, "mapped elements in hashes must be arrays, #{v.class} given" unless v.kind_of? ::Array
79
81
 
80
- fm.store k, *v
82
+ mm.store k, *v
81
83
  end
82
84
 
83
- return fm
85
+ return mm
84
86
  when ::Array
85
87
 
86
88
  # accepted forms:
@@ -128,16 +130,31 @@ class MultiMap < ::Hash
128
130
  end
129
131
  end
130
132
 
133
+ # Initialises an instance
131
134
  def initialize
132
135
 
136
+ @merge_is_multi = true
137
+
133
138
  @inner = Hash.new
134
139
  end
135
140
 
141
+ # Obtains the values, if any, for the given key; returns +nil+ if no
142
+ # values are stored
136
143
  def [] key
137
144
 
138
145
  return @inner[key]
139
146
  end
140
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
141
158
  def []= key, values
142
159
 
143
160
  values = [] if values.nil?
@@ -147,6 +164,13 @@ class MultiMap < ::Hash
147
164
  store key, *values
148
165
  end
149
166
 
167
+ # Compares the instance for equality against +rhs+
168
+ #
169
+ # * *Parameters:*
170
+ # - +rhs+ (+nil+, +::Hash+, +MultiMap+) The instance to compare against
171
+ #
172
+ # * *Exceptions:*
173
+ # - +::TypeError+ if +rhs+ is not of the required type(s)
150
174
  def == rhs
151
175
 
152
176
  case rhs
@@ -163,29 +187,45 @@ class MultiMap < ::Hash
163
187
  false
164
188
  end
165
189
 
190
+ # Searches the instance comparing each element with +key+, returning the
191
+ # mapped values array if found, or +nil+ if not
166
192
  def assoc key
167
193
 
168
194
  @inner.assoc key
169
195
  end
170
196
 
197
+ # Removes all elements from the instance
171
198
  def clear
172
199
 
173
200
  @inner.clear
174
201
  end
175
202
 
203
+ # The total number of instances recorded
176
204
  def count
177
205
 
178
206
  @inner.each_value.map { |ar| ar.size}.inject(0, :+)
179
207
  end
180
208
 
209
+ # Deletes all values mapped with the given +key+
210
+ #
211
+ # * *Parameters:*
212
+ # - +key+ The key to delete
181
213
  def delete key
182
214
 
183
215
  @inner.delete key
184
216
  end
185
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
186
225
  def each *defaults
187
226
 
188
227
  raise ArgumentError, "may only supply 0 or 1 defaults" if defaults.size > 1
228
+ raise ArgumentError, 'block is required' unless block_given?
189
229
 
190
230
  @inner.each do |key, values|
191
231
 
@@ -200,6 +240,8 @@ class MultiMap < ::Hash
200
240
  end
201
241
  end
202
242
 
243
+ # Calls _block_ once for each key in the instance, passing the key. If no
244
+ # block is provided, an enumerator is returned
203
245
  def each_key
204
246
 
205
247
  return @inner.each_key unless block_given?
@@ -207,26 +249,46 @@ class MultiMap < ::Hash
207
249
  @inner.each_key { |key| yield key }
208
250
  end
209
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
210
254
  def each_unflattened
211
255
 
256
+ return @inner.each unless block_given?
257
+
212
258
  @inner.each { |key, value| yield key, value }
213
259
  end
214
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
215
264
  def each_unflattened_with_index
216
265
 
266
+ return @inner.each_with_index unless block_given?
267
+
217
268
  @inner.each_with_index { |kv, index| yield kv, index }
218
269
  end
219
270
 
271
+ # Calls _block_ once for each value in the instance, passing the value.
272
+ # If no block is provided, an enumerator is returned
220
273
  def each_value
221
274
 
275
+ return @inner.each_value unless block_given?
276
+
222
277
  @inner.each do |key, values|
223
278
 
224
279
  values.each { |value| yield value }
225
280
  end
226
281
  end
227
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
228
288
  def each_with_index
229
289
 
290
+ raise ArgumentError, 'block is required' unless block_given?
291
+
230
292
  index = 0
231
293
  self.each do |key, value|
232
294
 
@@ -236,11 +298,14 @@ class MultiMap < ::Hash
236
298
  end
237
299
  end
238
300
 
301
+ # Returns +true+ if instance contains no elements; +false+ otherwise
239
302
  def empty?
240
303
 
241
304
  @inner.empty?
242
305
  end
243
306
 
307
+ # Returns +true+ if +rhs+ is an instance of +MultiMap+ and contains
308
+ # the same elements and their counts; +false+ otherwise
244
309
  def eql? rhs
245
310
 
246
311
  case rhs
@@ -251,18 +316,26 @@ class MultiMap < ::Hash
251
316
  end
252
317
  end
253
318
 
254
- 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
255
325
 
256
- case default
257
- when ::NilClass, ::Array
258
- ;
259
- else
260
- 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
261
334
  end
262
335
 
263
336
  unless @inner.has_key? key
264
337
 
265
- return default unless default.nil?
338
+ return default unless default_parameter_defaulted_
266
339
 
267
340
  if block_given?
268
341
 
@@ -295,6 +368,7 @@ class MultiMap < ::Hash
295
368
  @inner.fetch key
296
369
  end
297
370
 
371
+ # Returns the equivalent flattened form of the instance
298
372
  def flatten
299
373
 
300
374
  r = []
@@ -316,36 +390,134 @@ class MultiMap < ::Hash
316
390
  r
317
391
  end
318
392
 
393
+ # Returns +true+ if an element with the given +key+ is in the map; +false+
394
+ # otherwise
319
395
  def has_key? key
320
396
 
321
397
  @inner.has_key? key
322
398
  end
323
399
 
400
+ # Returns +true+ if any key has the given +value+; +false+ otherwise
401
+ #
402
+ # * *Parameters:*
403
+ # - +value+ The value for which to search
324
404
  def has_value? value
325
405
 
326
- @inner.has_value? value
406
+ @inner.each do |k, vals|
407
+
408
+ return true if vals.include? value
409
+ end
410
+
411
+ false
327
412
  end
328
413
 
329
- def key value
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
330
422
 
331
- @inner.key value
423
+ raise TypeError, "'values' parameter must be of type #{::Array}" unless Array === values
424
+
425
+ @inner.has_value? values
332
426
  end
333
427
 
334
- def merge fm
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
335
444
 
336
- raise TypeError, "parameter must be an instance of type #{self.class}" unless fm.instance_of? self.class
445
+ i = nil
337
446
 
338
- fm_new = self.class.new
447
+ @inner.each do |k, vals|
339
448
 
340
- fm_new.merge! self
341
- fm_new.merge! fm
449
+ return k if vals == values
342
450
 
343
- fm_new
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
464
+
465
+ return nil
466
+ end
344
467
  end
345
468
 
346
- def merge! fm
469
+ # The number of elements in the map
470
+ def length
347
471
 
348
- fm.each do |k, v|
472
+ @inner.size
473
+ end
474
+
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
487
+
488
+ mm = self.class.new
489
+
490
+ mm.merge! self
491
+ mm.merge! other
492
+
493
+ mm
494
+ end
495
+
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
513
+
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|
349
521
 
350
522
  self.push k, v
351
523
  end
@@ -353,6 +525,96 @@ class MultiMap < ::Hash
353
525
  self
354
526
  end
355
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+
356
618
  def push key, *values
357
619
 
358
620
  @inner[key] = [] unless @inner.has_key? key
@@ -360,33 +622,60 @@ class MultiMap < ::Hash
360
622
  @inner[key].push(*values)
361
623
  end
362
624
 
625
+ # Removes a key-value pair from the instance and return as a two-item
626
+ # array
363
627
  def shift
364
628
 
365
629
  @inner.shift
366
630
  end
367
631
 
368
- def size
369
-
370
- @inner.size
371
- end
632
+ alias size length
372
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
373
637
  def store key, *values
374
638
 
375
639
  @inner[key] = values
376
640
  end
377
641
 
642
+ # Converts instance to an array of +[key,value]+ pairs
378
643
  def to_a
379
644
 
380
645
  self.flatten
381
646
  end
382
647
 
648
+ # Obtains reference to internal hash instance (which must *not* be modified)
383
649
  def to_h
384
650
 
385
- @inner.dup
651
+ @inner.to_h
386
652
  end
387
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
388
667
  def values
389
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
+
390
679
  @inner.values
391
680
  end
392
681
  end # class MultiMap