Module: Liminal::Internal::SchemaMerge

Defined in:
lib/liminal/internal/schema_merge.rb

Overview

Deterministic schema-aware merge operations.

Class Method Summary collapse

Class Method Details

.combine(left, right) ⇒ Object

Combine two conjunctive schema fragments without weakening either one.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/liminal/internal/schema_merge.rb', line 10

def combine(left, right)
  left = deep_copy(left)
  right = deep_copy(right)
  return right if left.empty?
  return left if right.empty?
  return left if left == right

  merged = left.dup

  right.each do |key, right_value|
    unless merged.key?(key)
      merged[key] = right_value
      next
    end

    left_value = merged[key]
    merged_value = combine_value(key, left_value, right_value)
    return all_of(left, right) if merged_value.equal?(CONFLICT)

    merged[key] = merged_value
  end

  merged
end

.deep_copy(value) ⇒ Object



55
56
57
58
59
60
61
62
63
64
# File 'lib/liminal/internal/schema_merge.rb', line 55

def deep_copy(value)
  case value
  when Hash
    value.each_with_object({}) { |(key, item), copy| copy[key] = deep_copy(item) }
  when Array
    value.map { |item| deep_copy(item) }
  else
    value
  end
end

.ordered_union(left, right) ⇒ Object



66
67
68
69
70
# File 'lib/liminal/internal/schema_merge.rb', line 66

def ordered_union(left, right)
  (left + right).each_with_object([]) do |value, values|
    values << value unless values.include?(value)
  end
end

.override(generated, supplied) ⇒ Object

Apply an enrichment override. Override values win except that required arrays are unioned and nested hashes are recursively merged.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/liminal/internal/schema_merge.rb', line 37

def override(generated, supplied)
  result = generated.each_with_object({}) do |(key, value), copy|
    copy[key] = deep_copy(value)
  end

  supplied.each do |key, value|
    result[key] = if key == "required" && result[key].is_a?(Array) && value.is_a?(Array)
                    ordered_union(result[key], value)
                  elsif result[key].is_a?(Hash) && value.is_a?(Hash)
                    override(result[key], value)
                  else
                    deep_copy(value)
                  end
  end

  result
end