Module: Ace::Support::Config::Atoms::DeepMerger

Defined in:
lib/ace/support/config/atoms/deep_merger.rb

Overview

Pure deep merge functions for hashes

Class Method Summary collapse

Class Method Details

.coerce_to_array(value) ⇒ Array?

Coerce value to array if not nil

Parameters:

  • value (Object)

    Value to coerce

Returns:

  • (Array, nil)

    Array or nil if value was nil



111
112
113
114
# File 'lib/ace/support/config/atoms/deep_merger.rb', line 111

def coerce_to_array(value)
  return nil if value.nil?
  value.is_a?(Array) ? value : [value]
end

.merge(base, other, options = {}) ⇒ Hash

Note:

Uses shallow dup at top level (standard Ruby pattern). Nested hashes are recursively merged into new objects, so mutation risk is minimal. For a completely isolated deep copy, use: ‘merge({}, original_hash)`

Deep merge two hashes

Parameters:

  • base (Hash)

    Base hash

  • other (Hash)

    Hash to merge into base

  • options (Hash) (defaults to: {})

    Merge options

Options Hash (options):

  • :array_strategy (Symbol)

    How to handle arrays :replace - Replace base array with overlay (default) :concat - Concatenate arrays :union - Set union (dedupe by value) :coerce_union - Coerce scalars to arrays, union, filter blanks

Returns:

  • (Hash)

    Merged hash (new object)



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/ace/support/config/atoms/deep_merger.rb', line 26

def merge(base, other, options = {})
  return other.dup if base.nil?
  return base.dup if other.nil?

  array_strategy = options[:array_strategy] || :replace

  result = base.dup

  other.each do |key, other_value|
    base_value = result[key]

    result[key] = if base_value.is_a?(Hash) && other_value.is_a?(Hash)
      merge(base_value, other_value, options)
    elsif array_strategy == :coerce_union
      merge_with_coercion(base_value, other_value)
    elsif base_value.is_a?(Array) && other_value.is_a?(Array)
      merge_arrays(base_value, other_value, array_strategy)
    else
      other_value
    end
  end

  result
end

.merge_all(*hashes, **options) ⇒ Hash

Deep merge multiple hashes in order

Parameters:

  • hashes (Array<Hash>)

    Hashes to merge

  • options (Hash)

    Merge options

Returns:

  • (Hash)

    Merged result



55
56
57
58
59
60
61
62
# File 'lib/ace/support/config/atoms/deep_merger.rb', line 55

def merge_all(*hashes, **options)
  hashes = hashes.flatten.compact
  return {} if hashes.empty?

  hashes.reduce({}) do |result, hash|
    merge(result, hash, options)
  end
end

.merge_arrays(base_array, other_array, strategy) ⇒ Array

Merge two arrays based on strategy

Parameters:

  • base_array (Array)

    Base array

  • other_array (Array)

    Array to merge

  • strategy (Symbol)

    Merge strategy

Returns:

  • (Array)

    Merged array



76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/ace/support/config/atoms/deep_merger.rb', line 76

def merge_arrays(base_array, other_array, strategy)
  case strategy
  when :concat
    base_array + other_array
  when :union
    base_array | other_array
  when :replace
    other_array
  else
    raise MergeStrategyError, "Unknown array merge strategy: #{strategy}"
  end
end

.merge_with_coercion(base_value, other_value) ⇒ Object

Merge values with scalar-to-array coercion for :coerce_union strategy

Parameters:

  • base_value (Object)

    Base value (may be array, scalar, or nil)

  • other_value (Object)

    Overlay value

Returns:

  • (Object)

    Merged result



93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/ace/support/config/atoms/deep_merger.rb', line 93

def merge_with_coercion(base_value, other_value)
  base_arr = coerce_to_array(base_value)
  other_arr = coerce_to_array(other_value)

  # New key with scalar: keep as scalar
  return other_value if base_arr.nil? && !other_value.is_a?(Array)
  # New key with array: normalize
  return normalize_array(other_arr) if base_arr.nil?
  # Existing key, new value nil: keep existing normalized
  return normalize_array(base_arr) if other_arr.nil?

  # Both have values: union and normalize
  normalize_array(base_arr | other_arr)
end

.mergeable?(value) ⇒ Boolean

Check if value is mergeable

Parameters:

  • value (Object)

    Value to check

Returns:

  • (Boolean)

    true if value can be deep merged



67
68
69
# File 'lib/ace/support/config/atoms/deep_merger.rb', line 67

def mergeable?(value)
  value.is_a?(Hash) || value.is_a?(Array)
end

.normalize_array(arr) ⇒ Array

Normalize array: remove nil/empty, deduplicate

Parameters:

  • arr (Array)

    Array to normalize

Returns:

  • (Array)

    Normalized array



119
120
121
# File 'lib/ace/support/config/atoms/deep_merger.rb', line 119

def normalize_array(arr)
  arr.reject { |v| v.nil? || (v.respond_to?(:empty?) && v.empty?) }.uniq
end