Module: Chef::Mixin::DeepMerge

Extended by:
DeepMerge
Included in:
DeepMerge
Defined in:
lib/chef/mixin/deep_merge.rb

Overview

== Chef::Mixin::DeepMerge Implements a deep merging algorithm for nested data structures. ==== Notice: This code was originally imported from deep_merge by Steve Midgley. deep_merge is available under the MIT license from http://trac.misuse.org/science/wiki/DeepMerge

Defined Under Namespace

Classes: InvalidParameter

Instance Method Summary collapse

Instance Method Details

#deep_merge(source, dest) ⇒ Object


134
135
136
# File 'lib/chef/mixin/deep_merge.rb', line 134

def deep_merge(source, dest)
  deep_merge!(safe_dup(source), safe_dup(dest))
end

#deep_merge!(source, dest) ⇒ Object

Deep Merge core documentation. deep_merge! method permits merging of arbitrary child elements. The two top level elements must be hashes. These hashes can contain unlimited (to stack limit) levels of child elements. These child elements to not have to be of the same types. Where child elements are of the same type, deep_merge will attempt to merge them together. Where child elements are not of the same type, deep_merge will skip or optionally overwrite the destination element with the contents of the source element at that level. So if you have two hashes like this: source = => [1,2,3], :y => 2 dest = => [4,5,'6'], :y => [7,8,9] dest.deep_merge!(source) Results: => [1,2,3,4,5,'6'], :y => 2 By default, "deep_merge!" will overwrite any unmergeables and merge everything else. To avoid this, use "deep_merge" (no bang/exclamation mark)


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/chef/mixin/deep_merge.rb', line 55

def deep_merge!(source, dest)
  # if dest doesn't exist, then simply copy source to it
  if dest.nil?
    dest = source; return dest
  end

  case source
  when nil
    dest
  when Hash
    if dest.kind_of?(Hash)
      source.each do |src_key, src_value|
        if dest[src_key]
          dest[src_key] = deep_merge!(src_value, dest[src_key])
        else # dest[src_key] doesn't exist so we take whatever source has
          dest[src_key] = src_value
        end
      end
    else # dest isn't a hash, so we overwrite it completely
      dest = source
    end
  when Array
    if dest.kind_of?(Array)
      dest |= source
    else
      dest = source
    end
  when String
    dest = source
  else # src_hash is not an array or hash, so we'll have to overwrite dest
    dest = source
  end
  dest
end

#hash_only_merge(merge_onto, merge_with) ⇒ Object

deep_merge!


90
91
92
# File 'lib/chef/mixin/deep_merge.rb', line 90

def hash_only_merge(merge_onto, merge_with)
  hash_only_merge!(safe_dup(merge_onto), safe_dup(merge_with))
end

#hash_only_merge!(merge_onto, merge_with) ⇒ Object

Deep merge without Array merge. merge_onto is the object that will "lose" in case of conflict. merge_with is the object whose values will replace merge_ontos values when there is a conflict.


104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/chef/mixin/deep_merge.rb', line 104

def hash_only_merge!(merge_onto, merge_with)
  # If there are two Hashes, recursively merge.
  if merge_onto.kind_of?(Hash) && merge_with.kind_of?(Hash)
    merge_with.each do |key, merge_with_value|
      value =
        if merge_onto.has_key?(key)
          hash_only_merge(merge_onto[key], merge_with_value)
        else
          merge_with_value
        end

      if merge_onto.respond_to?(:public_method_that_only_deep_merge_should_use)
        # we can't call ImmutableMash#[]= because its immutable, but we need to mutate it to build it in-place
        merge_onto.public_method_that_only_deep_merge_should_use(key, value)
      else
        merge_onto[key] = value
      end
    end
    merge_onto

  # If merge_with is nil, don't replace merge_onto
  elsif merge_with.nil?
    merge_onto

  # In all other cases, replace merge_onto with merge_with
  else
    merge_with
  end
end

#merge(first, second) ⇒ Object


32
33
34
35
36
37
# File 'lib/chef/mixin/deep_merge.rb', line 32

def merge(first, second)
  first  = Mash.new(first)  unless first.kind_of?(Mash)
  second = Mash.new(second) unless second.kind_of?(Mash)

  DeepMerge.deep_merge(second, first)
end

#safe_dup(thing) ⇒ Object


94
95
96
97
98
# File 'lib/chef/mixin/deep_merge.rb', line 94

def safe_dup(thing)
  thing.dup
rescue TypeError
  thing
end