Module: EasyTalk::JsonSchemaEquality

Defined in:
lib/easy_talk/json_schema_equality.rb

Overview

Implements JSON Schema equality semantics for comparing values.

Per JSON Schema specification:

  • Objects with same keys/values in different order are equal
  • Numbers that are mathematically equal are equal (1 == 1.0)
  • Type matters for non-numbers (true != 1, false != 0)

Constant Summary collapse

MAX_DEPTH =

Maximum nesting depth to prevent SystemStackError on deeply nested structures

100

Class Method Summary collapse

Class Method Details

.duplicates?(array) ⇒ Boolean

Check if an array contains duplicate values using JSON Schema equality. Uses a Set for O(n) performance and early termination on first duplicate.

Returns:

  • (Boolean)


17
18
19
20
# File 'lib/easy_talk/json_schema_equality.rb', line 17

def duplicates?(array)
  seen = Set.new
  array.any? { |item| !seen.add?(normalize(item)) }
end

.normalize(value, depth = 0) ⇒ Object

Normalize a value for JSON Schema equality comparison

Parameters:

  • value (Object)

    The value to normalize

  • depth (Integer) (defaults to: 0)

    Current recursion depth (for stack overflow protection)

Raises:

  • (ArgumentError)

    if nesting depth exceeds MAX_DEPTH



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/easy_talk/json_schema_equality.rb', line 26

def normalize(value, depth = 0)
  raise ArgumentError, "Nesting depth exceeds maximum of #{MAX_DEPTH}" if depth > MAX_DEPTH

  case value
  when Hash
    # Convert keys to strings before sorting to handle mixed key types (Symbol/String)
    # and ensure consistent, order-independent comparison (JSON only has string keys)
    value.map { |k, v| [k.to_s, normalize(v, depth + 1)] }.sort
  when Array
    value.map { |item| normalize(item, depth + 1) }
  when Integer, Float
    # Normalize numbers to a canonical form for mathematical equality
    value.to_r
  else
    # Booleans, strings, nil - preserve as-is (type matters)
    value
  end
end