caruby-core 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. data/History.txt +4 -0
  2. data/LEGAL +5 -0
  3. data/LICENSE +22 -0
  4. data/README.md +51 -0
  5. data/doc/website/css/site.css +1 -5
  6. data/doc/website/images/avatar.png +0 -0
  7. data/doc/website/images/favicon.ico +0 -0
  8. data/doc/website/images/logo.png +0 -0
  9. data/doc/website/index.html +82 -0
  10. data/doc/website/install.html +87 -0
  11. data/doc/website/quick_start.html +87 -0
  12. data/doc/website/tissue.html +85 -0
  13. data/doc/website/uom.html +10 -0
  14. data/lib/caruby.rb +3 -0
  15. data/lib/caruby/active_support/README.txt +2 -0
  16. data/lib/caruby/active_support/core_ext/string.rb +7 -0
  17. data/lib/caruby/active_support/core_ext/string/inflections.rb +167 -0
  18. data/lib/caruby/active_support/inflections.rb +55 -0
  19. data/lib/caruby/active_support/inflector.rb +398 -0
  20. data/lib/caruby/cli/application.rb +36 -0
  21. data/lib/caruby/cli/command.rb +169 -0
  22. data/lib/caruby/csv/csv_mapper.rb +157 -0
  23. data/lib/caruby/csv/csvio.rb +185 -0
  24. data/lib/caruby/database.rb +252 -0
  25. data/lib/caruby/database/fetched_matcher.rb +66 -0
  26. data/lib/caruby/database/persistable.rb +432 -0
  27. data/lib/caruby/database/persistence_service.rb +162 -0
  28. data/lib/caruby/database/reader.rb +599 -0
  29. data/lib/caruby/database/saved_merger.rb +131 -0
  30. data/lib/caruby/database/search_template_builder.rb +59 -0
  31. data/lib/caruby/database/sql_executor.rb +75 -0
  32. data/lib/caruby/database/store_template_builder.rb +200 -0
  33. data/lib/caruby/database/writer.rb +469 -0
  34. data/lib/caruby/domain/annotatable.rb +25 -0
  35. data/lib/caruby/domain/annotation.rb +23 -0
  36. data/lib/caruby/domain/attribute_metadata.rb +447 -0
  37. data/lib/caruby/domain/java_attribute_metadata.rb +160 -0
  38. data/lib/caruby/domain/merge.rb +91 -0
  39. data/lib/caruby/domain/properties.rb +95 -0
  40. data/lib/caruby/domain/reference_visitor.rb +289 -0
  41. data/lib/caruby/domain/resource_attributes.rb +528 -0
  42. data/lib/caruby/domain/resource_dependency.rb +205 -0
  43. data/lib/caruby/domain/resource_introspection.rb +159 -0
  44. data/lib/caruby/domain/resource_metadata.rb +117 -0
  45. data/lib/caruby/domain/resource_module.rb +285 -0
  46. data/lib/caruby/domain/uniquify.rb +38 -0
  47. data/lib/caruby/import/annotatable_class.rb +28 -0
  48. data/lib/caruby/import/annotation_class.rb +27 -0
  49. data/lib/caruby/import/annotation_module.rb +67 -0
  50. data/lib/caruby/import/java.rb +338 -0
  51. data/lib/caruby/migration/migratable.rb +167 -0
  52. data/lib/caruby/migration/migrator.rb +533 -0
  53. data/lib/caruby/migration/resource.rb +8 -0
  54. data/lib/caruby/migration/resource_module.rb +11 -0
  55. data/lib/caruby/migration/uniquify.rb +20 -0
  56. data/lib/caruby/resource.rb +969 -0
  57. data/lib/caruby/util/attribute_path.rb +46 -0
  58. data/lib/caruby/util/cache.rb +53 -0
  59. data/lib/caruby/util/class.rb +99 -0
  60. data/lib/caruby/util/collection.rb +1053 -0
  61. data/lib/caruby/util/controlled_value.rb +35 -0
  62. data/lib/caruby/util/coordinate.rb +75 -0
  63. data/lib/caruby/util/domain_extent.rb +49 -0
  64. data/lib/caruby/util/file_separator.rb +65 -0
  65. data/lib/caruby/util/inflector.rb +20 -0
  66. data/lib/caruby/util/log.rb +95 -0
  67. data/lib/caruby/util/math.rb +12 -0
  68. data/lib/caruby/util/merge.rb +59 -0
  69. data/lib/caruby/util/module.rb +34 -0
  70. data/lib/caruby/util/options.rb +92 -0
  71. data/lib/caruby/util/partial_order.rb +36 -0
  72. data/lib/caruby/util/person.rb +119 -0
  73. data/lib/caruby/util/pretty_print.rb +184 -0
  74. data/lib/caruby/util/properties.rb +112 -0
  75. data/lib/caruby/util/stopwatch.rb +66 -0
  76. data/lib/caruby/util/topological_sync_enumerator.rb +53 -0
  77. data/lib/caruby/util/transitive_closure.rb +45 -0
  78. data/lib/caruby/util/tree.rb +48 -0
  79. data/lib/caruby/util/trie.rb +37 -0
  80. data/lib/caruby/util/uniquifier.rb +30 -0
  81. data/lib/caruby/util/validation.rb +48 -0
  82. data/lib/caruby/util/version.rb +56 -0
  83. data/lib/caruby/util/visitor.rb +351 -0
  84. data/lib/caruby/util/weak_hash.rb +36 -0
  85. data/lib/caruby/version.rb +3 -0
  86. metadata +186 -0
@@ -0,0 +1,46 @@
1
+ require 'caruby/util/validation'
2
+
3
+ # An AttributePath encapsulates an array of attributes that can be evaluated on a source object.
4
+ class AttributePath < Array
5
+ include Validation
6
+
7
+ # Creates an AttributePath from the path Array, String or Symbol. A path string is a period-delimited sequence
8
+ # of attributes, e.g. +person.name+.
9
+ def initialize(path)
10
+ raise ArgumentError.new("Path empty") if path.nil_or_empty?
11
+ # standardize the argument as a symbol array
12
+ attributes = case path
13
+ when Symbol then
14
+ [path]
15
+ when String then
16
+ path.split('.').map { |name| name.to_sym }
17
+ when Array then
18
+ path.map { |name| name.to_sym }
19
+ else
20
+ raise ArgumentError.new("Argument type unsupported - expected Symbol, String or Array; found #{path.class}")
21
+ end
22
+ # make the array
23
+ super(attributes)
24
+ end
25
+
26
+ # Returns the result of evaluating this evaluator's attribute path on the source object.
27
+ # If the evaluation results in a migratable object, then that object is migrated.
28
+ def evaluate(source)
29
+ # call the attribute path as far as possible
30
+ inject(source) do |current, attr|
31
+ return if current.nil?
32
+ evaluate_attribute(attr, current)
33
+ end
34
+ end
35
+
36
+ # Returns the result of evaluating attribute on the source object.
37
+ # If attr is +self+, then the source object is returned.
38
+ def evaluate_attribute(attr, source)
39
+ # call the attribute path as far as possible
40
+ attr == :self ? source : source.send(attr)
41
+ end
42
+
43
+ def to_s
44
+ join('.')
45
+ end
46
+ end
@@ -0,0 +1,53 @@
1
+ require 'caruby/util/collection'
2
+
3
+ module CaRuby
4
+ # Cache for objects held in memory and accessed by key.
5
+ class Cache
6
+
7
+ # The classes which are not cleared when {#clear} is called without the +all+ flag.
8
+ attr_reader :sticky
9
+
10
+ # Returns a new Cache whose value key is determined by calling the given
11
+ # extractor block on the cached value.
12
+ #
13
+ # If the value is not cached and there is a factory Proc, then the result of
14
+ # calling the factory on the missing value is cached with the value key.
15
+ #
16
+ # @param [Proc] optional factory Proc called with a missing value as argument
17
+ # to create a cached object
18
+ def initialize(factory=nil, &extractor)
19
+ @factory = factory
20
+ # Make the class => { key => value } hash.
21
+ # The { key => value } hash takes a value as an argument and converts
22
+ # it to the key by calling the block given to this initializer.
23
+ @hash = LazyHash.new { KeyTransformerHash.new { |value| yield value } }
24
+ @sticky = Set.new
25
+ end
26
+
27
+ # Returns the object cached with the same class and key as the given value.
28
+ # If this Cache has a factory but does not have an entry for value, then the
29
+ # factory is called on the value to create a new entry.
30
+ def [](value)
31
+ chash = @hash[value.class]
32
+ cached = chash[value] if chash
33
+ return cached unless cached.nil? and @factory
34
+ obj = @factory.call(value) || return
35
+ chash[value] = obj
36
+ end
37
+
38
+ # Adds the given value to this cache.
39
+ def add(value)
40
+ @hash[value.class][value] = value
41
+ end
42
+
43
+ # Clears the cache key => object hashes. If all is true, then every class hash
44
+ # is cleared. Otherwise, only the non-sticky classes are cleared.
45
+ def clear(all=false)
46
+ if @sticky.empty? then
47
+ @hash.clear
48
+ else
49
+ @hash.each { |klass, chash| chash.clear unless @sticky.include?(klass) }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,99 @@
1
+ require 'enumerator'
2
+
3
+ class Class
4
+ # Returns an Enumerable on this class and its ancestors.
5
+ def class_hierarchy
6
+ @hierarchy ||= Enumerable::Enumerator.new(self, :each_class_in_hierarchy)
7
+ end
8
+
9
+ # Returns this class's superclass, thereby enabling class ranges, e.g.
10
+ # class A; end
11
+ # class B < A; end
12
+ # (B..Object).to_a #=> [B, A, Object]
13
+ alias :succ :superclass
14
+
15
+ private
16
+
17
+ # Creates an alias for each accessor method of the given attribute.
18
+ #
19
+ # @example
20
+ # class Person
21
+ # attr_reader :social_security_number
22
+ # attr_accessor :postal_code
23
+ # define_attribute_alias(:ssn, :social_security_number)
24
+ # define_attribute_alias(:zip_code, :postal_code)
25
+ # end
26
+ # Person.method_defined?(:ssn) #=> true
27
+ # Person.method_defined?(:ssn=) #=> false
28
+ # Person.method_defined?(:zip_code) #=> true
29
+ # Person.method_defined?(:zip_code=) #=> true
30
+ def define_attribute_alias(aliaz, attribute)
31
+ alias_method(aliaz, attribute) if method_defined?(attribute)
32
+ writer = "#{attribute}=".to_sym
33
+ alias_method("#{aliaz}=".to_sym, writer) if method_defined?(writer)
34
+ end
35
+
36
+ # Creates new accessor methods for each _method_ => _original_ hash entry.
37
+ # The new _method_ offsets the existing Number _original_ attribute value by the given
38
+ # offset (default -1).
39
+ #
40
+ # @example
41
+ # class OneBased
42
+ # attr_accessor :index
43
+ # offset_attr_accessor :zero_based_index => :index
44
+ # end
45
+ #@param [{Symbol => Symbol}] hash the offset => original method hash
46
+ #@param [Integer, nil] offset the offset amount (default is -1)
47
+ def offset_attr_accessor(hash, offset=nil)
48
+ offset ||= -1
49
+ hash.each do |method, original|
50
+ define_method(method) { value = send(original); value + offset if value } if method_defined?(original)
51
+ original_writer = "#{original}=".to_sym
52
+ if method_defined?(original_writer) then
53
+ define_method("#{method}=".to_sym) do |value|
54
+ adjusted = value - offset if value
55
+ send(original_writer, adjusted)
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ def each_class_in_hierarchy
62
+ current = self
63
+ until current.nil?
64
+ yield current
65
+ current = current.superclass
66
+ end
67
+ end
68
+
69
+ # Redefines method using the given block. The block argument is a new alias for the old method.
70
+ # The block creates a proc which implements the new method body.
71
+ #
72
+ # @example
73
+ # redefine_method(:ssn) { |old_method| lambda { send(old_method).delete('-').to_i } }
74
+ # @return [Symbol] an alias to the old method implementation
75
+ def redefine_method(method)
76
+ # make a new alias id method__base for the existing method.
77
+ # disambiguate with a counter suffix if necessary.
78
+ counter = 2
79
+ # make a valid alias base
80
+ old, eq = /^([^=]*)(=)?$/.match(method.to_s).captures
81
+ old.tr!('|', 'or')
82
+ old.tr!('&', 'and')
83
+ old.tr!('+', 'plus')
84
+ old.tr!('*', 'mult')
85
+ old.tr!('/', 'div')
86
+ old.gsub!(/[^\w]/, 'op')
87
+ base = "redefined__#{old}"
88
+ old_id = "#{base}#{eq}".to_sym
89
+ while method_defined?(old_id)
90
+ base = "#{base}#{counter}"
91
+ old_id = "#{base}#{eq}".to_sym
92
+ counter = counter + 1
93
+ end
94
+ alias_method(old_id, method)
95
+ body = yield old_id
96
+ define_method(method, body)
97
+ old_id
98
+ end
99
+ end
@@ -0,0 +1,1053 @@
1
+ require 'set'
2
+ require 'delegate'
3
+ require 'enumerator'
4
+ require 'generator'
5
+ require 'caruby/util/class'
6
+ require 'caruby/util/validation'
7
+ require 'caruby/util/options'
8
+ require 'caruby/util/pretty_print'
9
+
10
+ class Object
11
+ # Returns whether this object is a collection capable of holding heterogenous items.
12
+ # An Object is a not a collection by default. Subclasses can override this method.
13
+ def collection?
14
+ false
15
+ end
16
+ end
17
+
18
+ module Enumerable
19
+ # Overrides {Object#collection?} to returns +true+, since an Enumerable is capable of
20
+ # holding heterogenous items by default. Subclasses can override this method.
21
+ def collection?
22
+ true
23
+ end
24
+ end
25
+
26
+ class String
27
+ # Overrides {Enumerable#collection?} to returns +false+, since a String is constrained
28
+ # to hold characters.
29
+ def collection?
30
+ false
31
+ end
32
+ end
33
+
34
+ module Enumerable
35
+ # Returns a new Hash generated from this Enumerable and an optional value generator block.
36
+ # This Enumerable contains the Hash keys. If the value generator block is given to this
37
+ # method then the block is called with each enumerated element as an argument to
38
+ # generate the associated hash value. If no block is given, then the values are nil.
39
+ #
40
+ # @example
41
+ # [1, 2, 3].hashify { |item| item.modulo(2) } #=> { 1 => 1, 2 => 0, 3 => 1 }
42
+ # [:a].hashify #=> { :a => nil }
43
+ # @return [Hash]
44
+ def hashify
45
+ hash = {}
46
+ each { |item| hash[item] = yield item if block_given? }
47
+ hash
48
+ end
49
+
50
+ # Returns a new Hash generated from this Enumerable and a required value generator block.
51
+ # This Enumerable contains the Hash keys. The block is called with each enumerated
52
+ # element as an argument to generate the associated hash value.
53
+ # Only non-nil, non-empty values are included in the hash.
54
+ #
55
+ # @example
56
+ # [1, 2, 3].to_compact_hash { |item| item.modulo(2) } #=> { 1 => 1, 2 => 0, 3 => 1 }
57
+ # [1, 2, 3].to_compact_hash { |n| n.modulo(2) unless item > 2 } #=> {1 => 1, 2 => 0}
58
+ # [1, 2, 3].to_compact_hash { |n| n > 2 } #=> {1 => false, 2 => false, 3 => true}
59
+ # [1, 2, 3].to_compact_hash { |n| Array.new(n - 1, n) } #=> {2 => [2], 3 => [2, 3]}
60
+ # @return [Hash]
61
+ # @raise [ArgumentError] if the generator block is not given
62
+ # @see #hashify
63
+ def to_compact_hash
64
+ raise ArgumentError.new("Compact hash builder is missing the value generator block") unless block_given?
65
+ to_compact_hash_with_index { |item, index| yield item }
66
+ end
67
+
68
+ # Returns a new Hash generated from this Enumerable with a block whose arguments include the enumerated item
69
+ # and its index. nil or empty values are excluded.
70
+ def to_compact_hash_with_index
71
+ hash = {}
72
+ self.each_with_index do |item, index|
73
+ next if item.nil?
74
+ value = yield(item, index)
75
+ next if value.nil_or_empty?
76
+ hash[item] = value
77
+ end
78
+ hash
79
+ end
80
+
81
+ # Returns whether this Enumerable iterates over at least one item.
82
+ #
83
+ # This method is functionally equivalent to +to_a.empty+ but is more concise and efficient.
84
+ def empty?
85
+ not any? { true }
86
+ end
87
+
88
+ # Returns the first enumerated item in this Enumerable, or nil if this Enumerable is empty.
89
+ #
90
+ # This method is functionally equivalent to +to_a.first+ but is more concise and efficient.
91
+ def first
92
+ detect { true }
93
+ end
94
+
95
+ # Returns the count of items enumerated in this Enumerable.
96
+ #
97
+ # This method is functionally equivalent to +to_a.size+ but is more concise and efficient
98
+ # for an Enumerable which does not implement the {#size} method.
99
+ def size
100
+ inject(0) { |size, item| size + 1 }
101
+ end
102
+
103
+ alias :length :size
104
+
105
+ # Prints the content of this Enumerable as a series using {Array#to_series}.
106
+ def to_series(conjunction=nil)
107
+ to_a.to_series
108
+ end
109
+
110
+ # Returns the first non-nil, non-false enumerated value resulting from a call to the block given to this method,
111
+ # or nil if no value detected.
112
+ #
113
+ # @example
114
+ # [1, 2].detect_value { |item| item / 2 if item % 2 == 0 } #=> 1
115
+ # @return [Object] the detected block result
116
+ # @see #detect_with_value
117
+ def detect_value
118
+ each do |*item|
119
+ value = yield(*item)
120
+ return value if value
121
+ end
122
+ nil
123
+ end
124
+
125
+ # Returns the first item and value for which an enumeration on the block given to this method returns
126
+ # a non-nil, non-false value.
127
+ #
128
+ # @example
129
+ # [1, 2].detect_with_value { |item| item / 2 if item % 2 == 0 } #=> [2, 1]
130
+ # @return [(Object, Object)] the detected [item, value] pair
131
+ # @see #detect_value
132
+ def detect_with_value
133
+ value = nil
134
+ match = detect do |*item|
135
+ value = yield(*item)
136
+ end
137
+ [match, value]
138
+ end
139
+
140
+ # Returns a new Enumerable that iterates over the base Enumerable items for which filter evaluates to a non-nil,
141
+ # non-false value, e.g.:
142
+ # [1, 2, 3].filter { |n| n != 2 }.to_a #=> [1, 3]
143
+ #
144
+ # Unlike select, filter reflects changes to the base Enumerable, e.g.:
145
+ # a = [1, 2, 3]
146
+ # filter = a.filter { |n| n != 2 }
147
+ # a << 4
148
+ # filter.to_a #=> [1, 3, 4]
149
+ #
150
+ # In addition, filter has a small, fixed storage requirement, making it preferable to select for large collections.
151
+ # Note, however, that unlike select, filter does not return an Array.
152
+ # The default filter block returns the passed item.
153
+ #
154
+ # @return [Enumerable] the filtered result
155
+ # @example
156
+ # [1, nil, 3].filter.to_a #=> [1, 3]
157
+ def filter(&filter) # :yields: item
158
+ Filter.new(self, &filter)
159
+ end
160
+
161
+ # @return an Enumerable which iterates over the non-nil items in this Enumerable
162
+ def compact
163
+ filter { |item| not item.nil? }
164
+ end
165
+
166
+ # Returns a new Flattener on this Enumerable, e.g.:
167
+ # {:a => {:b => :c}, :d => [:e]}.enum_values.flatten.to_a #=> [:b, :c, :e]
168
+ #
169
+ # @return [Enumerable] the flattened result
170
+ def flatten
171
+ Flattener.new(self).to_a
172
+ end
173
+
174
+ # Returns an Enumerable which iterates over items in this Enumerable and the other Enumerable in sequence, e.g.:
175
+ # [1, 2, 3] + [3, 4] #=> [1, 2, 3, 3, 4]
176
+ #
177
+ # Unlike Array#+, {#union} reflects changes to the underlying enumerators.
178
+ #
179
+ # @example
180
+ # a = [1, 2]
181
+ # b = [4, 5]
182
+ # ab = a.union(b)
183
+ # ab #=> [1, 2, 4, 5]
184
+ # a << 3
185
+ # ab #=> [1, 2, 3, 4, 5]
186
+ # @return [Enumerable] self followed by other
187
+ def union(other)
188
+ MultiEnumerator.new(self, other)
189
+ end
190
+
191
+ alias :+ :union
192
+
193
+ # @return an Enumerable which iterates over items in this Enumerable but not the other Enumerable
194
+ def difference(other)
195
+ filter { |item| not other.include?(item) }
196
+ end
197
+
198
+ alias :- :difference
199
+
200
+ # @return an Enumerable which iterates over items in this Enumerable which are also in the other Enumerable
201
+ def intersect(other)
202
+ filter { |item| other.include?(item) }
203
+ end
204
+
205
+ alias :& :intersect
206
+
207
+ # Returns a new Enumerable that iterates over the base Enumerable applying the transformer block to each item, e.g.:
208
+ # [1, 2, 3].transform { |n| n * 2 }.to_a #=> [2, 4, 6]
209
+ #
210
+ # Unlike #collect, {#wrap} reflects changes to the base Enumerable, e.g.:
211
+ # a = [2, 4, 6]
212
+ ``# transformed = a.wrap { |n| n * 2 }
213
+ # a << 4
214
+ # transformed.to_a #=> [2, 4, 6, 8]
215
+ #
216
+ # In addition, transform has a small, fixed storage requirement, making it preferable to select for large collections.
217
+ # Note, however, that unlike collect, transform does not return an Array.
218
+ def wrap(&transformer) # :yields: item
219
+ Transformer.new(self, &transformer)
220
+ end
221
+
222
+ private
223
+
224
+ class Filter
225
+ include Enumerable
226
+
227
+ def initialize(enum=[], &filter)
228
+ @base = enum
229
+ @filter = filter
230
+ end
231
+
232
+ # Calls block on each item which passes this Filter's filter test.
233
+ def each(&block)
234
+ @base.each { |item| yield(item) if @filter ? @filter.call(item) : item }
235
+ end
236
+
237
+ # Optimized for a Set base.
238
+ def include?(item)
239
+ return false if Set === @base and not @base.include?(item)
240
+ super
241
+ end
242
+
243
+ # Adds value to the base Enumerable, if the base supports it.
244
+ def <<(value)
245
+ @base << value
246
+ end
247
+
248
+ # @return a new Array consisting of this Filter's filtered content merged with the other Enumerable
249
+ def merge(other)
250
+ to_a.merge(other)
251
+ end
252
+
253
+ # Merges the other Enumerable into the base Enumerable, if the base supports it.
254
+ def merge!(other)
255
+ @base.merge!(other)
256
+ end
257
+ end
258
+
259
+ class Transformer
260
+ include Enumerable
261
+
262
+ def initialize(enum=[], &transformer)
263
+ @base = enum
264
+ @xfm = transformer
265
+ end
266
+
267
+ # Sets the base Enumerable on which this Transformer operates and returns this transformer, e.g.:
268
+ # transformer = Transformer.new { |n| n * 2 }
269
+ # transformer.on([1, 2, 3]).to_a #=> [2, 4, 6]
270
+ def on(enum)
271
+ @base = enum
272
+ self
273
+ end
274
+
275
+ # Calls block on each item after this Transformer's transformer block is applied.
276
+ def each
277
+ @base.each { |item| yield(item.nil? ? nil : @xfm.call(item)) }
278
+ end
279
+ end
280
+
281
+ # A MultiEnumerator iterates over several Enumerators in sequence. Unlike Array#+, MultiEnumerator reflects changes to the
282
+ # underlying enumerators, e.g.:
283
+ # a = [1, 2]
284
+ # b = [4, 5]
285
+ # ab = MultiEnumerator.new(a, b)
286
+ # ab.to_a #=> [1, 2, 4, 5]
287
+ # a << 3; b << 6; ab.to_a #=> [1, 2, 3, 4, 5, 6]
288
+ class MultiEnumerator
289
+ include Enumerable
290
+
291
+ # Creates a new MultiEnumerator on the given Enumerator enums.
292
+ def initialize(*enums)
293
+ super()
294
+ @enums = enums
295
+ @enums.compact!
296
+ end
297
+
298
+ # Iterates over each of this MultiEnumerator's Enumerators in sequence.
299
+ def each
300
+ @enums.each { |enum| enum.each { |item| yield item } }
301
+ end
302
+ end
303
+ end
304
+
305
+ # The Collector utility module implements the {on} method to apply a block to
306
+ # a collection transitive closure.
307
+ module Collector
308
+ # Collects the result of applying the given block to the given obj.
309
+ # If obj is a collection, then collects the result of recursively calling this Collector on the enumerated members.
310
+ # If obj is nil, then returns nil.
311
+ # Otherwise, calls block on obj and returns the result.
312
+ #
313
+ # @example
314
+ # Collector.on([1, 2, [3, 4]]) { |n| n * 2 } #=> [2, 4, [6, 8]]]
315
+ # Collector.on(nil) { |n| n * 2 } #=> nil
316
+ # Collector.on(1) { |n| n * 2 } #=> 2
317
+ def self.on(obj, &block)
318
+ obj.collection? ? obj.map { |item| on(item, &block) } : yield(obj) unless obj.nil?
319
+ end
320
+ end
321
+
322
+ class Object
323
+ # Visits this object's enumerable content as follows:
324
+ # * If this object is an Enumerable, then the block given to this method is called on each
325
+ # item in this Enumerable.
326
+ # * Otherwise, if this object is non-nil, then the the block is called on self.
327
+ # * Otherwise, this object is nil and this method is a no-op.
328
+ def enumerate(&block)
329
+ Enumerable === self ? each(&block) : yield(self) unless nil?
330
+ end
331
+
332
+ # Returns an enumerator on this Object. This default implementation returns an Enumerable::Enumerator
333
+ # on enumerate.
334
+ def to_enum
335
+ Enumerable::Enumerator.new(self, :enumerate)
336
+ end
337
+ end
338
+
339
+ module Enumerable
340
+ # @return self
341
+ def to_enum
342
+ self
343
+ end
344
+ end
345
+
346
+ # A Flattener applies a given block to flattened collection content.
347
+ class Flattener
348
+ include Enumerable
349
+
350
+ # Visits the enumerated items in the given object's flattened content.
351
+ # block is called on the base itself if the base is neither nil nor a Enumerable.
352
+ # If the base object is nil or empty, then this method is a no-op and returns nil.
353
+ def self.on(obj, &block)
354
+ obj.collection? ? obj.each { |item| on(item, &block) } : yield(obj) unless obj.nil?
355
+ end
356
+
357
+ # Creates a new Flattener on the given object. obj can be an Enumerable,
358
+ # a single non-collection object or nil.
359
+ def initialize(obj)
360
+ @base = obj
361
+ end
362
+
363
+ # Calls the the given block on this Flattener's flattened content.
364
+ # If the base object is a collection, then the block is called on the flattened content.
365
+ # If the base object is nil, then this method is a no-op.
366
+ # If the base object is neither nil nor a collection, then the block given to this method
367
+ # is called on the base object itself.
368
+ #
369
+ # @example
370
+ # Flattener.new(nil).each { |n| print n } #=>
371
+ # Flattener.new(1).each { |n| print n } #=> 1
372
+ # Flattener.new([1, [2, 3]]).each { |n| print n } #=> 123
373
+ def each(&block)
374
+ Flattener.on(@base, &block)
375
+ end
376
+ end
377
+
378
+ # ConditionalEnumerator applies a filter to another Enumerable, e.g.:
379
+ # ConditionalEnumerator.new([1, 2, 3]) { |i| i < 3 }.to_a #=> [1, 2]
380
+ #
381
+ class ConditionalEnumerator
382
+ include Enumerable
383
+
384
+ # Creates a ConditionalEnumerator which wraps the base Enumerator with a conditional filter.
385
+ def initialize(base, &filter)
386
+ @base = base
387
+ @filter = filter
388
+ end
389
+
390
+ # Applies the iterator block to each of this ConditionalEnumerator's base Enumerable items
391
+ # for which this ConditionalEnumerator's filter returns true.
392
+ def each
393
+ @base.each { |item| (yield item) if @filter.call(item) }
394
+ end
395
+ end
396
+
397
+ # Hashable is a Hash mixin that adds utility methods to a Hash.
398
+ # Hashable can be included by any class or module which implements an _each_ method
399
+ # with arguments _key_ and _value_.
400
+ module Hashable
401
+ include Enumerable
402
+
403
+ # @see Hash#each
404
+ def each_pair(&block)
405
+ each(&block)
406
+ end
407
+
408
+ # @see Hash#[]
409
+ def [](key)
410
+ detect_value { |k, v| v if k == key }
411
+ end
412
+
413
+ # @see Hash#each_key
414
+ def each_key
415
+ each { |key, value| yield key }
416
+ end
417
+
418
+ # @return [Object,nil] the key for which the block given to this method returns a non-nil, non-false value,
419
+ # or nil if none
420
+ def detect_key
421
+ each_key { |key| return key if yield key }
422
+ nil
423
+ end
424
+
425
+ # @see Hash#each_value
426
+ def each_value
427
+ each { |key, value| yield value }
428
+ end
429
+
430
+ # Returns a Hashable which composes each value in this Hashable with the key of
431
+ # the other Hashable, e.g.:
432
+ # x = {:a => :c, :b => :d}
433
+ # y = {:c => 1}
434
+ # z = x.compose(y)
435
+ # z[:a] #=> {:c => 1}
436
+ # z[:b] #=> nil
437
+ #
438
+ # The accessor reflects changes to the underlying hashes, e.g. given the above example:
439
+ # x[:b] = 2
440
+ # z[:b] #=> {:c => 1}
441
+ #
442
+ # Update operations on the result are not supported.
443
+ #
444
+ # @param [Hashable] other the Hashable to compose with this Hashable
445
+ # @return [Hashable] the composed result
446
+ def compose(other)
447
+ transform { |value| {value => other[value]} if other.has_key?(value) }
448
+ end
449
+
450
+ # Returns a Hashable which joins each value in this Hashable with the key of
451
+ # the other Hashable, e.g.:
452
+ # x = {:a => :c, :b => :d}
453
+ # y = {:c => 1}
454
+ # z = x.join(y)
455
+ # z[:a] #=> 1
456
+ # z[:b] #=> nil
457
+ #
458
+ # The accessor reflects changes to the underlying hashes, e.g. given the above example:
459
+ # x[:b] = 2
460
+ # z[:b] #=> 2
461
+ #
462
+ # Update operations on the result are not supported.
463
+ #
464
+ # @param [Hashable] other the Hashable to join with this Hashable
465
+ # @return [Hashable] the joined result
466
+ def join(other)
467
+ transform { |value| other[value] }
468
+ end
469
+
470
+ # Returns a Hashable which associates each key of both this Hashable and the other Hashable
471
+ # with the corresponding value in the first Hashable which has that key, e.g.:
472
+ # x = {:a => 1, :b => 2}
473
+ # y = {:b => 3, :c => 4}
474
+ # z = x + y
475
+ # z[:b] #=> 2
476
+ #
477
+ # The accessor reflects changes to the underlying hashes, e.g. given the above example:
478
+ # x.delete(:b)
479
+ # z[:b] #=> 3
480
+ #
481
+ # Update operations on the result are not supported.
482
+ #
483
+ # @param [Hashable] other the Hashable to form a union with this Hashable
484
+ # @return [Hashable] the union result
485
+ def union(other)
486
+ MultiHash.new(self, other)
487
+ end
488
+
489
+ alias :+ :union
490
+
491
+ # Returns a new Hashable that iterates over the base Hashable <key, value> pairs for which the block
492
+ # given to this method evaluates to a non-nil, non-false value, e.g.:
493
+ # {:a => 1, :b => 2, :c => 3}.filter { |k, v| k != :b }.to_hash #=> {:a => 1, :c => 3}
494
+ #
495
+ # The default filter block tests the value, e.g.:
496
+ # {:a => 1, :b => nil}.filter.to_hash #=> {:a => 1}
497
+ #
498
+ # @yield [key, value] the filter block
499
+ # @return [Hashable] the filtered result
500
+ def filter(&block)
501
+ Filter.new(self, &block)
502
+ end
503
+
504
+ # Optimization of {#filter} for a block that only uses the key.
505
+ #
506
+ # @example
507
+ # {:a => 1, :b => 2, :c => 3}.filter_on_key { |k| k != :b }.to_hash #=> {:a => 1, :c => 3}
508
+ #
509
+ # @yield [key] the filter block
510
+ # @yieldparam key the hash key to filter
511
+ # @return [Hashable] the filtered result
512
+ def filter_on_key(&block)
513
+ KeyFilter.new(self, &block)
514
+ end
515
+
516
+ # @return [Hashable] a {#filter} that only uses the value.
517
+ # @yield [value] the filter block
518
+ # @yieldparam value the hash value to filter
519
+ # @return [Hashable] the filtered result
520
+ def filter_on_value
521
+ filter { |key, value| yield value }
522
+ end
523
+
524
+ # @return [Hash] a {#filter} of this Hashable which excludes the entries with a null value
525
+ def compact
526
+ filter_on_value { |value| not value.nil? }
527
+ end
528
+
529
+ # Partitions this Hashable into two Hashables based on the given filter block
530
+ #
531
+ # @yield [key, value] the partition block
532
+ # @return [(Hashable, Hashable)] the partitioned result
533
+ def hash_partition(&block)
534
+ [filter(&block), filter { |k, v| not yield(k, v) }]
535
+ end
536
+
537
+ # Returns the difference between this Hashable and the other Hashable in a Hash of the form:
538
+ #
539
+ # _key_ => [_mine_, _theirs_]
540
+ #
541
+ # where:
542
+ # * _key_ is the key of association which differs
543
+ # * _mine_ is the value for _key_ in this hash
544
+ # * _theirs_ is the value for _key_ in the other hash
545
+ #
546
+ # @param [Hashable] other the Hashable to subtract
547
+ # @yield [key, v1, v2] the optional block which determines whether values differ (default is equality)
548
+ # @yieldparam key the key for which values are compared
549
+ # @yieldparam v1 the value for key from this Hashable
550
+ # @yieldparam v2 the value for key from the other Hashable
551
+ # @return [{Object => (Object,Object)}] a hash of the differences
552
+ def diff(other)
553
+ (keys.to_set + other.keys).to_compact_hash do |key|
554
+ mine = self[key]
555
+ yours = other[key]
556
+ [mine, yours] unless block_given? ? yield(key, mine, yours) : mine == yours
557
+ end
558
+ end
559
+
560
+ # @yield [key1, key2] the key sort block
561
+ # @return a Hashable whose #each and {#each_pair} enumerations are sorted by key
562
+ def sort(&sorter)
563
+ SortedHash.new(self, &sorter)
564
+ end
565
+
566
+ # Returns a hash which associates each key in this hash with the value mapped by the others.
567
+ #
568
+ # @example
569
+ # {:a => 1, :b => 2}.assoc_values({:a => 3, :c => 4}) #=> {:a => [1, 3], :b => [2, nil], :c => [nil, 4]}
570
+ # {:a => 1, :b => 2}.assoc_values({:a => 3}, {:a => 4, :b => 5}) #=> {:a => [1, 3, 4], :b => [2, nil, 5]}
571
+ #
572
+ # @param [<Hashable>] others the other Hashables to associate with this Hashable
573
+ # @return [Hash] the association hash
574
+ def assoc_values(*others)
575
+ all_keys = keys
576
+ others.each { |hash| all_keys.concat(hash.keys) }
577
+ all_keys.to_compact_hash do |key|
578
+ others.map { |other| other[key] }.unshift(self[key])
579
+ end
580
+ end
581
+
582
+ # Returns an Enumerable whose each block is called on each key which maps to a value which
583
+ # either equals the given target_value or satisfies the filter block.
584
+ #
585
+ # @param target_value the filter value
586
+ # @yield [value] the filter block
587
+ # @return [Enumerable] the filtered keys
588
+ def enum_keys_with_value(target_value=nil, &filter) # :yields: value
589
+ return enum_keys_with_value { |value| value == target_value } if target_value
590
+ filter_on_value(&filter).keys
591
+ end
592
+
593
+ # @return [Enumerable] Enumerable over this Hashable's keys
594
+ def enum_keys
595
+ Enumerable::Enumerator.new(self, :each_key)
596
+ end
597
+
598
+ # @return [Array] this Hashable's keys
599
+ def keys
600
+ enum_keys.to_a
601
+ end
602
+
603
+ # @param key search target
604
+ # @return whether this Hashable has the given key
605
+ def has_key?(key)
606
+ enum_keys.include?(key)
607
+ end
608
+
609
+ alias :include? :has_key?
610
+
611
+ # @return [Enumerable] an Enumerable over this Hashable's values
612
+ def enum_values
613
+ Enumerable::Enumerator.new(self, :each_value)
614
+ end
615
+
616
+ # @yield [key] the key selector
617
+ # @return the keys which satisfy the block given to this method
618
+ def select_keys(&block)
619
+ enum_keys.select(&block)
620
+ end
621
+
622
+ # @yield [key] the key rejector
623
+ # @return the keys which do not satisfy the block given to this method
624
+ def reject_keys(&block)
625
+ enum_keys.reject(&block)
626
+ end
627
+
628
+ # @yield [value] the value selector
629
+ # @return the values which satisfy the block given to this method
630
+ def select_values(&block)
631
+ enum_values.select(&block)
632
+ end
633
+
634
+ # @yield [value] the value rejector
635
+ # @return the values which do not satisfy the block given to this method
636
+ def reject_values(&block)
637
+ enum_values.reject(&block)
638
+ end
639
+
640
+ # @return [Array] this Enumerable's values
641
+ def values
642
+ enum_values.to_a
643
+ end
644
+
645
+ # @param value search target
646
+ # @return whether this Hashable has the given value
647
+ def has_value?(value)
648
+ enum_values.include?(value)
649
+ end
650
+
651
+ # @return [Array] a flattened Array of this Hash
652
+ # @example
653
+ # {:a => {:b => :c}, :d => :e, :f => [:g]} #=> [:a, :b, :c, :d, :e, :f, :g]
654
+ def flatten
655
+ Flattener.new(self).to_a
656
+ end
657
+
658
+ # @yield [key, value] hash splitter
659
+ # @return [Hash] two hashes split by whether calling the block on the
660
+ # entry returns a non-nil, non-false value
661
+ # @example
662
+ # {:a => 1, :b => 2}.partition { |key, value| value < 2 } #=> [{:a => 1}, {:b => 2}]
663
+ def partition(&block)
664
+ super(&block).map { |pairs| pairs.to_assoc_hash }
665
+ end
666
+
667
+ # Returns a new Hash that recursively copies this hash's values. Values of type hash are copied using copy_recursive.
668
+ # Other values are unchanged.
669
+ #
670
+ # This method is useful for preserving and restoring hash associations.
671
+ #
672
+ # @return [Hash] a deep copy of this Hashable
673
+ def copy_recursive
674
+ copy = Hash.new
675
+ keys.each do |key|
676
+ value = self[key]
677
+ copy[key] = Hash === value ? value.copy_recursive : value
678
+ end
679
+ copy
680
+ end
681
+
682
+ # @return [Hash] a new Hash that transforms each value
683
+ # @example
684
+ # {:a => 1, :b => 2}.transform { |n| n * 2 }.values #=> [2, 4]
685
+ def transform(&transformer)
686
+ ValueTransformerHash.new(self, &transformer)
687
+ end
688
+
689
+ def to_hash
690
+ inject({}) { |hash, pair| hash[pair.first] = pair.last; hash }
691
+ end
692
+
693
+ def to_set
694
+ to_a.to_set
695
+ end
696
+
697
+ def to_s
698
+ to_hash.to_s
699
+ end
700
+
701
+ def inspect
702
+ to_hash.inspect
703
+ end
704
+
705
+ def ==(other)
706
+ to_hash == other.to_hash rescue super
707
+ end
708
+
709
+ private
710
+
711
+ # @see #filter
712
+ class Filter
713
+ include Hashable
714
+
715
+ def initialize(base, &filter)
716
+ @base = base
717
+ @filter = filter
718
+ end
719
+
720
+ def each
721
+ @base.each { |k, v| yield(k, v) if @filter ? @filter.call(k, v) : v }
722
+ end
723
+ end
724
+
725
+ # @see #filter_on_key
726
+ class KeyFilter < Filter
727
+ include Hashable
728
+
729
+ def initialize(base)
730
+ super(base) { |k, v| yield(k) }
731
+ end
732
+
733
+ def [](key)
734
+ super if @filter.call(key, nil)
735
+ end
736
+ end
737
+
738
+ # @see #sort
739
+ class SortedHash
740
+ include Hashable
741
+
742
+ def initialize(base, &comparator)
743
+ @base = base
744
+ @comparator = comparator
745
+ end
746
+
747
+ def each
748
+ @base.keys.sort { |k1, k2| @comparator ? @comparator.call(k1, k2) : k1 <=> k2 }.each { |k| yield(k, @base[k]) }
749
+ end
750
+ end
751
+
752
+ # Combines hashes. See Hash#+ for details.
753
+ class MultiHash
754
+ include Hashable
755
+
756
+ def initialize(*hashes)
757
+ @hashes = hashes
758
+ end
759
+
760
+ def [](key)
761
+ @hashes.each { |hash| return hash[key] if hash.has_key?(key) }
762
+ nil
763
+ end
764
+
765
+ def has_key?(key)
766
+ @hashes.any? { |hash| hash.has_key?(key) }
767
+ end
768
+
769
+ def has_value?(value)
770
+ @hashes.any? { |hash| hash.has_value?(value) }
771
+ end
772
+
773
+ def each
774
+ @hashes.each_with_index do |hash, index|
775
+ hash.each do |key, value|
776
+ yield(key, value) unless (0...index).any? { |i| @hashes[i].has_key?(key) }
777
+ end
778
+ end
779
+ self
780
+ end
781
+ end
782
+
783
+ # The ValueTransformerHash class pipes the value from a base Hashable into a transformer block.
784
+ class ValueTransformerHash
785
+ include Hashable
786
+
787
+ # Creates a ValueTransformerHash on the base hash and value transformer block.
788
+ def initialize(base, &transformer) # :yields: value
789
+ @base = base
790
+ @xfm = transformer
791
+ end
792
+
793
+ # Returns the value at key after this ValueTransformerHash's transformer block is applied, or nil
794
+ # if this hash does not contain key.
795
+ def [](key)
796
+ @xfm.call(@base[key]) if @base.has_key?(key)
797
+ end
798
+
799
+ def each
800
+ @base.each { |key, value| yield(key, @xfm.call(value)) }
801
+ end
802
+ end
803
+ end
804
+
805
+ # The KeyTransformerHash class pipes the key access argument into a transformer block before
806
+ # accessing a base Hashable, e.g.:
807
+ # hash = KeyTransformerHash.new { |key| key % 2 }
808
+ # hash[1] = :a
809
+ # hash[3] # => :a
810
+ class KeyTransformerHash
811
+ include Hashable
812
+
813
+ # Creates a KeyTransformerHash on the optional base hash and required key transformer block.
814
+ #
815
+ # Raises ArgumentError if there is no extractor block
816
+ def initialize(base={}, &transformer) # :yields: key
817
+ raise ArgumentError.new("Missing required Accessor block") unless block_given?
818
+ @base = base
819
+ @xfm = transformer
820
+ end
821
+
822
+ # Returns the value at key after this KeyTransformerHash's transformer block is applied to the key,
823
+ # or nil if the base hash does not contain an association for the transforemd key.
824
+ def [](key)
825
+ @base[@xfm.call(key)]
826
+ end
827
+
828
+ # Sets the value at key after this KeyTransformerHash's transformer block is applied, or nil
829
+ # if this hash does not contain an association for the transformed key.
830
+ def []=(key, value)
831
+ @base[@xfm.call(key)] = value
832
+ end
833
+
834
+ # Delegates to the base hash.
835
+ # Note that this breaks the standard Hash contract, since
836
+ # all? { |k, v| self[k] }
837
+ # is not necessarily true because the key is transformed on access.
838
+ # @see Accessor for a KeyTransformerHash variant that restores this contract
839
+ def each(&block)
840
+ @base.each(&block)
841
+ end
842
+ end
843
+
844
+ class Hash
845
+ include Hashable
846
+
847
+ # The EMPTY_HASH constant is an immutable empty hash, used primarily as a default argument.
848
+ class << EMPTY_HASH = Hash.new
849
+ def []=(key, value)
850
+ raise NotImplementedError.new("Modification of the constant empty hash is not supported")
851
+ end
852
+ end
853
+ end
854
+
855
+ # Hashinator creates a Hashable from an Enumerable on [_key_, _value_] pairs.
856
+ # The Hashinator reflects changes to the underlying Enumerable.
857
+ #
858
+ # @example
859
+ # base = [[:a, 1], [:b, 2]]
860
+ # hash = Hashinator.new(base)
861
+ # hash[:a] #=> 1
862
+ # base.first[1] = 3
863
+ # hash[:a] #=> 3
864
+ class Hashinator
865
+ include Hashable
866
+
867
+ def initialize(enum)
868
+ @base = enum
869
+ end
870
+
871
+ def each
872
+ @base.each { |pair| yield(*pair) }
873
+ end
874
+ end
875
+
876
+ #
877
+ # A Hash that creates a new entry on demand.
878
+ #
879
+ class LazyHash < Hash
880
+ #
881
+ # Creates a new Hash with the specified value factory proc.
882
+ # The factory proc has one argument, the key.
883
+ # If access by key fails, then a new association is created
884
+ # from the key to the result of calling the factory proc.
885
+ #
886
+ # Example:
887
+ # hash = LazyHash.new { |key| key.to_s }
888
+ # hash[1] = "1"
889
+ # hash[1] #=> "1"
890
+ # hash[2] #=> "2"
891
+ #
892
+ # If a block is not provided, then the default association value is nil, e.g.:
893
+ # hash = LazyHash.new
894
+ # hash.has_key?(1) #=> false
895
+ # hash[1] #=> nil
896
+ # hash.has_key?(1) #=> true
897
+ #
898
+ # A nil key always returns nil. There is no hash entry for nil, e.g.:
899
+ # hash = LazyHash.new { |key| key }
900
+ # hash[nil] #=> nil
901
+ # hash.has_key?(nil) #=> false
902
+ #
903
+ # If the :compact option is set, then an entry is not created
904
+ # if the value initializer result is nil or empty, e.g.:
905
+ # hash = LazyHash.new { |n| 10.div(n) unless n.zero? }
906
+ # hash[0] #=> nil
907
+ # hash.has_key?(0) #=> false
908
+ def initialize(options=nil)
909
+ reject_flag = Options.get(:compact, options)
910
+ # Make the hash with the factory block
911
+ super() do |hash, key|
912
+ if key then
913
+ value = yield key if block_given?
914
+ hash[key] = value unless reject_flag and value.nil_or_empty?
915
+ end
916
+ end
917
+ end
918
+ end
919
+
920
+ class Array
921
+ # The EMPTY_ARRAY constant is an immutable empty array, used primarily as a default argument.
922
+ class << EMPTY_ARRAY = Array.new
923
+ def <<(value)
924
+ raise NotImplementedError.new("Modification of the constant empty array is not supported")
925
+ end
926
+ end
927
+
928
+ # Relaxes the Ruby Array methods which take an Array argument to allow collection Enumerable arguments.
929
+ [:|, :+, :-, :&].each do |meth|
930
+ redefine_method(meth) do |old_meth|
931
+ lambda { |other| send(old_meth, other.collection? ? other.to_a : other) }
932
+ end
933
+ end
934
+
935
+ redefine_method(:flatten) do |old_meth|
936
+ # if an item is a non-Array collection, then convert it into an array before recursively flattening the list
937
+ lambda { map { |item| item.collection? ? item.to_a : item }.send(old_meth) }
938
+ end
939
+
940
+ # Returns an array containing all but the first item in this Array. This method is syntactic sugar for
941
+ # +self[1..-1]+ or +last(length-1)+
942
+ def rest
943
+ self[1..-1]
944
+ end
945
+
946
+ # Prints the content of this array as a series, e.g.:
947
+ # [1, 2, 3].to_series #=> "1, 2 and 3"
948
+ # [1, 2, 3].to_series('or') #=> "1, 2 or 3"
949
+ #
950
+ # If a block is given to this method, then the block is applied before the series is formed, e.g.:
951
+ # [1, 2, 3].to_series { |n| n + 1 } #=> "2, 3 and 4"
952
+ def to_series(conjunction=nil)
953
+ conjunction ||= 'and'
954
+ return map { |item| yield item }.to_series(conjunction) if block_given?
955
+ padded_conjunction = " #{conjunction} "
956
+ # join all but the last item as a comma-separated list and append the conjunction and last item
957
+ length < 2 ? to_s : self[0...-1].join(', ') + padded_conjunction + last.to_s
958
+ end
959
+
960
+ # Returns a new Hash generated from this array of arrays by associating the first element of each
961
+ # member to the remaining elements. If there are only two elements in the member, then the first
962
+ # element is associated with the second element. If there is less than two elements in the member,
963
+ # the first element is associated with nil. An empty array is ignored.
964
+ #
965
+ # Example:
966
+ # [[:a, 1], [:b, 2, 3], [:c], []].to_assoc_hash #=> { :a => 1, :b => [2,3], :c => nil }
967
+ def to_assoc_hash
968
+ hash = Hash.new
969
+ each do |item|
970
+ raise ArgumentError.new("Array member must be an array: #{item.pp_s(:single_line)}") unless Array === item
971
+ key = item.first
972
+ if item.size < 2 then
973
+ value = nil
974
+ elsif item.size == 2 then
975
+ value = item[1]
976
+ else
977
+ value = item[1..-1]
978
+ end
979
+ hash[key] = value unless key.nil?
980
+ end
981
+ hash
982
+ end
983
+
984
+ alias :base__flatten :flatten
985
+ private :base__flatten
986
+ # Recursively flattens this array, including any collection item that implements the +to_a+ method.
987
+ def flatten
988
+ # if any item is a Set or Java Collection, then convert those into arrays before recursively flattening the list
989
+ if any? { |item| Set === item or Java::JavaUtil::Collection === item } then
990
+ return map { |item| (Set === item or Java::JavaUtil::Collection === item) ? item.to_a : item }.flatten
991
+ end
992
+ base__flatten
993
+ end
994
+
995
+ # Adds the other Enumerable to this array.
996
+ #
997
+ # Raises ArgumentError if other does not respond to the +to_a+ method.
998
+ def add_all(other)
999
+ return concat(other) if Array === other
1000
+ begin
1001
+ return add_all(other.to_a)
1002
+ rescue
1003
+ raise ArgumentError.new("Can't convert #{other.class.name} to array") unless other.respond_to?(:to_a)
1004
+ end
1005
+ end
1006
+
1007
+ alias :merge! :add_all
1008
+ end
1009
+
1010
+ # CaseInsensitiveHash accesses entries in a case-insensitive String comparison. The accessor method
1011
+ # key argument is converted to a String before look-up.
1012
+ #
1013
+ # @example
1014
+ # hash = CaseInsensitiveHash.new
1015
+ # hash[:UP] = "down"
1016
+ # hash['up'] #=> "down"
1017
+ class CaseInsensitiveHash < Hash
1018
+ def initialize
1019
+ super
1020
+ end
1021
+
1022
+ def [](key)
1023
+ # if there is lower-case key association, then convert to lower-case and return.
1024
+ # otherwise, delegate to super with the call argument unchanged. this ensures
1025
+ # that a default block passed to the constructor will be called with the correct
1026
+ # key argument.
1027
+ has_key?(key) ? super(key.to_s.downcase) : super(key)
1028
+ end
1029
+
1030
+ def []=(key, value)
1031
+ super(key.to_s.downcase, value)
1032
+ end
1033
+
1034
+ def has_key?(key)
1035
+ super(key.to_s.downcase)
1036
+ end
1037
+
1038
+ def delete(key)
1039
+ super(key.to_s.downcase)
1040
+ end
1041
+
1042
+ alias :store :[]=
1043
+ alias :include? :has_key?
1044
+ alias :key? :has_key?
1045
+ alias :member? :has_key?
1046
+ end
1047
+
1048
+ class Set
1049
+ # The standard Set {#merge} is an anomaly among Ruby collections, since merge modifies the called Set in-place rather
1050
+ # than return a new Set containing the merged contents. Preserve this unfortunate behavior, but partially address
1051
+ # the anomaly by adding the merge! alias for in-place merge.
1052
+ alias :merge! :merge
1053
+ end