xqsr3 0.32.2 → 0.36.0

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