module Collapsium::RecursiveMerge

Provides recursive merge functions for hashes.

Public Class Methods

merged_keys(the_self, v1, v2) click to toggle source
# File lib/collapsium/recursive_merge.rb, line 52
def merged_keys(the_self, v1, v2)
  keys = (v1.keys + v2.keys).uniq
  if the_self.singleton_class.ancestors.include?(IndifferentAccess)
    # We want to preserve each Hash's key types as much as possible, but
    # IndifferentAccess doesn't care about types. We can use it to figure out
    # which unique keys only exist in v2.
    only_v2 = IndifferentAccess.unique_keys(keys) \
              - IndifferentAccess.unique_keys(v1.keys)

    # At this point, IndifferentAccess may have modified the key types
    # in only_v2. To get back the original types, we can iterate the
    # Hash and remember all keys that are indifferently contained in
    # only_v2.
    original_types = []
    v2.each do |key, _|
      unique = IndifferentAccess.unique_keys([key])
      if only_v2.include?(unique[0])
        original_types << key
      end
    end
    keys = v1.keys + original_types
  end
  return keys
end
merger(the_self, v1, v2, overwrite) click to toggle source
# File lib/collapsium/recursive_merge.rb, line 77
def merger(the_self, v1, v2, overwrite)
  if v1.is_a? Hash and v2.is_a? Hash
    v1 = ViralCapabilities.enhance_value(the_self, v1)
    v2 = ViralCapabilities.enhance_value(the_self, v2)

    # IndifferentAccess has its own idea of which keys are unique, so if
    # we use it, we must consult it.
    keys = merged_keys(the_self, v1, v2)
    new_val = ViralCapabilities.enhance_value(the_self, {})
    keys.each do |key|
      v1_inner = v1[key]
      v2_inner = v2[key]
      if not v1_inner.nil? and not v2_inner.nil?
        new_val[key] = RecursiveMerge.merger(the_self, v1_inner, v2_inner,
                                             overwrite)
      elsif not v1_inner.nil?
        # Nothing to do, we have v1[key]
        new_val[key] = v1_inner
      else
        # v2.key?(key) is true
        new_val[key] = v2_inner
      end
    end

    v1.replace(new_val)
    return v1
  elsif v1.is_a? Array and v2.is_a? Array
    return v1 + v2
  end

  if overwrite
    return v2
  end
  return v1
end

Public Instance Methods

recursive_merge(other, overwrite = true) click to toggle source

Same as `dup.recursive_merge!` @param (see recursive_merge!)

# File lib/collapsium/recursive_merge.rb, line 47
def recursive_merge(other, overwrite = true)
  return dup.recursive_merge!(other, overwrite)
end
recursive_merge!(other, overwrite = true) click to toggle source

Recursively merge `:other` into this Hash.

This starts by merging the leaf-most Hash entries. Arrays are merged by addition.

For everything that's neither Hash or Array, if the `:overwrite` parameter is true, the entry from `:other` is used. Otherwise the entry from `:self` is used.

@param other [Hash] the hash to merge into `:self` @param overwrite [Boolean] see method description.

# File lib/collapsium/recursive_merge.rb, line 32
def recursive_merge!(other, overwrite = true)
  if other.nil?
    return self
  end

  # We can't call merge! because that will only be invoked for keys that
  # are missing, and default_proc doesn't seem to be used there. So we need
  # to call a custom merge function.
  new_self = RecursiveMerge.merger(self, self, other, overwrite)
  replace(new_self)
end