Module: HashJoiner
- Defined in:
- lib/hash-joiner/version.rb,
lib/hash-joiner.rb
Overview
Defined Under Namespace
Classes: JoinError, MergeError
Constant Summary collapse
- MERGEABLE_CLASSES =
The set of mergeable classes
[::Hash, ::Array]
- VERSION =
"0.0.7"
Class Method Summary collapse
-
.assert_hash_properties_are_mergeable(key, lhs_value, rhs_value) ⇒ nil
Asserts that
rhs_value
can be merged intolhs_value
for the property identified bykey
. -
.assert_is_hash_with_key(h, key, error_prefix) ⇒ nil
Asserts that
h
is a hash containingkey
. -
.assert_objects_are_mergeable(lhs, rhs) ⇒ nil
Asserts that
lhs
andrhs
are of the same type and can be merged. -
.assign_empty_defaults(collection, array_properties, hash_properties, string_properties) ⇒ Object
Given a collection, initialize any missing properties to empty values.
-
.deep_merge(lhs, rhs) ⇒ Hash, Array
Performs a deep merge of
Hash
andArray
structures. -
.deep_merge_hashes(lhs, rhs) ⇒ Hash
Performs a deep merge of Hash structures.
-
.join_array_data(key_field, lhs, rhs) ⇒ Array<Hash>
Joins data in
lhs
with data fromrhs
based onkey_field
. -
.join_data(category, key_field, lhs, rhs) ⇒ Hash+
Joins objects in
lhs
[category] with data fromrhs
[category]. -
.promote_array_data(collection, key) ⇒ Array
Recursively promotes data within an Array.
-
.promote_data(collection, key) ⇒ Hash, ...
Recursively promotes data within the
collection
matchingkey
to the same level askey
itself. -
.promote_hash_data(collection, key) ⇒ Hash
Recursively promotes data within a Hash.
-
.prune_empty_properties(collection) ⇒ Object
Recursively prunes all empty properties from every element of a collection.
-
.remove_data(collection, key) ⇒ Hash, ...
Recursively strips information from
collection
matchingkey
.
Class Method Details
.assert_hash_properties_are_mergeable(key, lhs_value, rhs_value) ⇒ nil
Asserts that rhs_value
can be merged into lhs_value
for the property identified by key
.
137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/hash-joiner.rb', line 137 def self.assert_hash_properties_are_mergeable(key, lhs_value, rhs_value) lhs_class = lhs_value == false ? ::TrueClass : lhs_value.class rhs_class = rhs_value == false ? ::TrueClass : rhs_value.class unless lhs_value.nil? or lhs_class == rhs_class raise MergeError.new( "LHS[#{key}] value (#{lhs_class}): #{lhs_value}\n" + "RHS[#{key}] value (#{rhs_class}): #{rhs_value}") end nil end |
.assert_is_hash_with_key(h, key, error_prefix) ⇒ nil
Asserts that h
is a hash containing key
. Used to ensure that a Hash
can be joined with another Hash
object.
216 217 218 219 220 221 222 |
# File 'lib/hash-joiner.rb', line 216 def self.assert_is_hash_with_key(h, key, error_prefix) if !h.instance_of? ::Hash raise JoinError.new("#{error_prefix} is not a Hash: #{h}") elsif !h.member? key raise JoinError.new("#{error_prefix} missing \"#{key}\": #{h}") end end |
.assert_objects_are_mergeable(lhs, rhs) ⇒ nil
Asserts that lhs
and rhs
are of the same type and can be merged.
94 95 96 97 98 99 100 101 |
# File 'lib/hash-joiner.rb', line 94 def self.assert_objects_are_mergeable(lhs, rhs) if lhs.class != rhs.class raise MergeError.new("LHS (#{lhs.class}): #{lhs}\n" + "RHS (#{rhs.class}): #{rhs}") elsif !MERGEABLE_CLASSES.include? lhs.class raise MergeError.new "Class not mergeable: #{lhs.class}" end end |
.assign_empty_defaults(collection, array_properties, hash_properties, string_properties) ⇒ Object
Given a collection, initialize any missing properties to empty values.
273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/hash-joiner.rb', line 273 def self.assign_empty_defaults(collection, array_properties, hash_properties, string_properties) if collection.instance_of? ::Hash array_properties.each {|i| collection[i] ||= Array.new} hash_properties.each {|i| collection[i] ||= Hash.new} string_properties.each {|i| collection[i] ||= String.new} elsif collection.instance_of? ::Array collection.each do |i| assign_empty_defaults(i, array_properties, hash_properties, string_properties) end end collection end |
.deep_merge(lhs, rhs) ⇒ Hash, Array
Performs a deep merge of Hash
and Array
structures. If the collections are Hashes, Hash
or Array
members of rhs
will be deep-merged with any existing members in lhs
. If the collections are Arrays, the values from rhs
will be appended to lhs
.
113 114 115 116 117 118 119 120 121 122 |
# File 'lib/hash-joiner.rb', line 113 def self.deep_merge(lhs, rhs) assert_objects_are_mergeable lhs, rhs if rhs.instance_of? ::Hash deep_merge_hashes lhs, rhs elsif rhs.instance_of? ::Array lhs.concat rhs end lhs end |
.deep_merge_hashes(lhs, rhs) ⇒ Hash
Performs a deep merge of Hash structures. Used to implement deep_merge
.
156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/hash-joiner.rb', line 156 def self.deep_merge_hashes(lhs, rhs) rhs.each do |key,rhs_value| lhs_value = lhs[key] assert_hash_properties_are_mergeable key, lhs_value, rhs_value if MERGEABLE_CLASSES.include? lhs_value.class deep_merge lhs_value, rhs_value else lhs[key] = rhs_value end end end |
.join_array_data(key_field, lhs, rhs) ⇒ Array<Hash>
Joins data in lhs
with data from rhs
based on key_field
. Both lhs
and rhs
should be of type Array<Hash>. Performs a deep_merge
on matching objects; assigns values from rhs
to lhs
if no corresponding value yet exists in lhs
.
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/hash-joiner.rb', line 238 def self.join_array_data(key_field, lhs, rhs) unless lhs.instance_of? ::Array and rhs.instance_of? ::Array raise JoinError.new("Both lhs (#{lhs.class}) and " + "rhs (#{rhs.class}) must be an Array of Hash") end lhs_index = {} lhs.each do |i| self.assert_is_hash_with_key(i, key_field, "LHS element") lhs_index[i[key_field]] = i end # TODO(mbland): Make exception-safe by splitting into two loops: one for # the assert; one to modify lhs after all the assertions have succeeded. rhs.each do |i| self.assert_is_hash_with_key(i, key_field, "RHS element") key = i[key_field] if lhs_index.member? key deep_merge lhs_index[key], i else lhs << i end end lhs end |
.join_data(category, key_field, lhs, rhs) ⇒ Hash+
Joins objects in lhs
[category] with data from rhs
[category]. If the category
objects are of type Array<Hash>, key_field
will be used as the primary key to join the objects in the two collections; otherwise key_field
is ignored.
190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/hash-joiner.rb', line 190 def self.join_data(category, key_field, lhs, rhs) rhs_data = rhs[category] return lhs unless rhs_data lhs_data = lhs[category] if !(lhs_data and [::Hash, ::Array].include? lhs_data.class) lhs[category] = rhs_data elsif lhs_data.instance_of? ::Hash self.deep_merge lhs_data, rhs_data else self.join_array_data key_field, lhs_data, rhs_data end lhs end |
.promote_array_data(collection, key) ⇒ Array
Recursively promotes data within an Array. Used to implement promote_data.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/hash-joiner.rb', line 63 def self.promote_array_data(collection, key) collection.each do |i| # If the Array entry is a hash that contains only the target key, # then that key should map to an Array to be promoted. if i.instance_of? ::Hash and i.keys == [key] data_to_promote = i[key] i.delete key deep_merge collection, data_to_promote else promote_data i, key end end collection.delete_if {|i| i.empty?} end |
.promote_data(collection, key) ⇒ Hash, ...
Recursively promotes data within the collection
matching key
to the same level as key
itself. After promotion, each key
reference will be deleted.
34 35 36 37 38 39 40 |
# File 'lib/hash-joiner.rb', line 34 def self.promote_data(collection, key) if collection.instance_of? ::Hash promote_hash_data collection, key elsif collection.instance_of? ::Array promote_array_data collection, key end end |
.promote_hash_data(collection, key) ⇒ Hash
Recursively promotes data within a Hash. Used to implement promote_data.
48 49 50 51 52 53 54 55 |
# File 'lib/hash-joiner.rb', line 48 def self.promote_hash_data(collection, key) if collection.member? key data_to_promote = collection[key] collection.delete key deep_merge collection, data_to_promote end collection.each_value {|i| promote_data i, key} end |
.prune_empty_properties(collection) ⇒ Object
Recursively prunes all empty properties from every element of a collection.
291 292 293 |
# File 'lib/hash-joiner.rb', line 291 def self.prune_empty_properties(collection) prune_empty_properties_helper(collection, {}) end |
.remove_data(collection, key) ⇒ Hash, ...
Recursively strips information from collection
matching key
.
14 15 16 17 18 19 20 21 22 |
# File 'lib/hash-joiner.rb', line 14 def self.remove_data(collection, key) if collection.instance_of? ::Hash collection.delete key collection.each_value {|i| remove_data i, key} elsif collection.instance_of? ::Array collection.each {|i| remove_data i, key} collection.delete_if {|i| i.empty?} end end |