Module: Engram::ReservedMetadata

Defined in:
lib/engram/reserved_metadata.rb

Overview

Collision-preserving operations for schemas sharing Engram's metadata namespace.

Constant Summary collapse

KEY =
"_engram"

Class Method Summary collapse

Class Method Details

.attach(metadata, schema_key, value, namespace_description: "metadata") ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/engram/reserved_metadata.rb', line 10

def attach(, schema_key, value, namespace_description: "metadata")
   = ( || {}).dup
  reserved_values = [.delete(KEY), .delete(:_engram)].compact
  unless reserved_values.all? { |reserved| reserved.is_a?(Hash) }
    raise Engram::Error, "metadata key #{KEY.inspect} is reserved for Engram #{namespace_description}"
  end

  reserved = reserved_values.reduce({}) do |merged, reserved_value|
    siblings = reserved_value.reject { |key, _nested| key.to_s == schema_key }
    merge(merged, normalize(siblings))
  end
  .merge(KEY => reserved.merge(schema_key => normalize(value)))
end

.merge(left, right, path = []) ⇒ Object



39
40
41
42
43
# File 'lib/engram/reserved_metadata.rb', line 39

def merge(left, right, path = [])
  right.each_with_object(left.dup) do |(key, value), merged|
    merged[key] = merged.key?(key) ? merge_value(merged[key], value, path + [key]) : value
  end
end

.merge_value(left, right, path) ⇒ Object

Raises:



45
46
47
48
49
50
# File 'lib/engram/reserved_metadata.rb', line 45

def merge_value(left, right, path)
  return merge(left, right, path) if left.is_a?(Hash) && right.is_a?(Hash)
  return left if left == right

  raise Engram::Error, "conflicting reserved metadata at #{path.join(".")}"
end

.normalize(value, path = []) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/engram/reserved_metadata.rb', line 24

def normalize(value, path = [])
  return value.map { |nested| normalize(nested, path) } if value.is_a?(Array)
  return value unless value.is_a?(Hash)

  value.each_with_object({}) do |(key, nested), normalized|
    string_key = key.to_s
    normalized_value = normalize(nested, path + [string_key])
    normalized[string_key] = if normalized.key?(string_key)
      merge_value(normalized[string_key], normalized_value, path + [string_key])
    else
      normalized_value
    end
  end
end