Module: Canon::Comparison::RubyObjectComparator

Defined in:
lib/canon/comparison/ruby_object_comparator.rb

Overview

Ruby Object Comparison Utilities

Provides public comparison methods for Ruby objects (Hash, Array, primitives). This module extracts shared comparison logic that was previously accessed via send() from YamlComparator.

Class Method Summary collapse

Class Method Details

.add_difference(path, obj1, obj2, diff_code, opts, differences) ⇒ Object

Add a Ruby object difference

Parameters:

  • path (String)

    Path to the difference

  • obj1 (Object)

    First object

  • obj2 (Object)

    Second object

  • diff_code (Symbol)

    Difference code

  • opts (Hash)

    Comparison options

  • differences (Array)

    Array to append difference to



168
169
170
171
172
173
174
175
176
177
# File 'lib/canon/comparison/ruby_object_comparator.rb', line 168

def self.add_difference(path, obj1, obj2, diff_code, opts, differences)
  return unless opts[:verbose]

  differences << {
    path: path,
    value1: obj1,
    value2: obj2,
    difference: diff_code,
  }
end

.compare_arrays(arr1, arr2, opts, differences, path) ⇒ Symbol

Compare two arrays

Parameters:

  • arr1 (Array)

    First array

  • arr2 (Array)

    Second array

  • opts (Hash)

    Comparison options

  • differences (Array)

    Array to append differences to

  • path (String)

    Current path in the object structure

Returns:

  • (Symbol)

    Comparison result constant



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/canon/comparison/ruby_object_comparator.rb', line 121

def self.compare_arrays(arr1, arr2, opts, differences, path)
  unless arr1.length == arr2.length
    add_difference(path, arr1, arr2,
                   Comparison::UNEQUAL_ARRAY_LENGTHS, opts,
                   differences)
    return Comparison::UNEQUAL_ARRAY_LENGTHS
  end

  all_equivalent = true
  arr1.each_with_index do |elem1, index|
    elem2 = arr2[index]
    elem_path = "#{path}[#{index}]"
    result = compare_objects(elem1, elem2, opts, differences,
                             elem_path)
    all_equivalent = false unless result == Comparison::EQUIVALENT
  end

  all_equivalent ? Comparison::EQUIVALENT : Comparison::UNEQUAL_ARRAY_ELEMENTS
end

.compare_hashes(hash1, hash2, opts, differences, path) ⇒ Symbol

Compare two hashes

Parameters:

  • hash1 (Hash)

    First hash

  • hash2 (Hash)

    Second hash

  • opts (Hash)

    Comparison options

  • differences (Array)

    Array to append differences to

  • path (String)

    Current path in the object structure

Returns:

  • (Symbol)

    Comparison result constant



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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/canon/comparison/ruby_object_comparator.rb', line 55

def self.compare_hashes(hash1, hash2, opts, differences, path)
  keys1 = hash1.keys
  keys2 = hash2.keys

  # Sort keys if order should be ignored (based on match options)
  match_opts = opts[:match_opts]
  if match_opts && match_opts[:key_order] != :strict
    keys1 = keys1.sort_by(&:to_s)
    keys2 = keys2.sort_by(&:to_s)
  elsif match_opts && match_opts[:key_order] == :strict
    # Strict mode: key order matters
    # Check if keys are in same order
    # Keys are different or in different order
    # First check if it's just ordering (same keys, different order)
    if (keys1 != keys2) && (keys1.sort_by(&:to_s) == keys2.sort_by(&:to_s))
      # Same keys, different order - this is a key_order difference
      key_path = path.empty? ? "(key order)" : "#{path}.(key order)"
      add_difference(key_path, keys1, keys2,
                     Comparison::UNEQUAL_HASH_KEY_ORDER, opts, differences)
      return Comparison::UNEQUAL_HASH_KEY_ORDER
    end
  end

  # Check for missing keys
  missing_in_second = keys1 - keys2
  missing_in_first = keys2 - keys1

  missing_in_second.each do |key|
    key_path = path.empty? ? key.to_s : "#{path}.#{key}"
    add_difference(key_path, hash1[key], nil,
                   Comparison::MISSING_HASH_KEY, opts, differences)
  end

  missing_in_first.each do |key|
    key_path = path.empty? ? key.to_s : "#{path}.#{key}"
    add_difference(key_path, nil, hash2[key],
                   Comparison::MISSING_HASH_KEY, opts, differences)
  end

  has_missing_keys = !missing_in_first.empty? || !missing_in_second.empty?

  # Compare common keys
  common_keys = keys1 & keys2
  all_equivalent = true
  common_keys.each do |key|
    key_path = path.empty? ? key.to_s : "#{path}.#{key}"
    result = compare_objects(hash1[key], hash2[key], opts,
                             differences, key_path)
    all_equivalent = false unless result == Comparison::EQUIVALENT
  end

  # Return appropriate status
  return Comparison::MISSING_HASH_KEY if has_missing_keys && all_equivalent
  return Comparison::UNEQUAL_HASH_VALUES unless all_equivalent

  has_missing_keys ? Comparison::MISSING_HASH_KEY : Comparison::EQUIVALENT
end

.compare_objects(obj1, obj2, opts, differences, path) ⇒ Symbol

Compare Ruby objects (Hash, Array, primitives) for JSON/YAML

Parameters:

  • obj1 (Object)

    First object

  • obj2 (Object)

    Second object

  • opts (Hash)

    Comparison options

  • differences (Array)

    Array to append differences to

  • path (String)

    Current path in the object structure

Returns:

  • (Symbol)

    Comparison result constant



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/canon/comparison/ruby_object_comparator.rb', line 19

def self.compare_objects(obj1, obj2, opts, differences, path)
  # Check for type mismatch
  unless obj1.instance_of?(obj2.class)
    add_difference(path, obj1, obj2, Comparison::UNEQUAL_TYPES,
                   opts, differences)
    return Comparison::UNEQUAL_TYPES
  end

  case obj1
  when Hash
    compare_hashes(obj1, obj2, opts, differences, path)
  when Array
    compare_arrays(obj1, obj2, opts, differences, path)
  when NilClass, TrueClass, FalseClass, Numeric, String, Symbol
    compare_primitives(obj1, obj2, opts, differences, path)
  else
    # Fallback to equality comparison
    if obj1 == obj2
      Comparison::EQUIVALENT
    else
      add_difference(path, obj1, obj2,
                     Comparison::UNEQUAL_PRIMITIVES, opts,
                     differences)
      Comparison::UNEQUAL_PRIMITIVES
    end
  end
end

.compare_primitives(val1, val2, opts, differences, path) ⇒ Symbol

Compare primitive values

Parameters:

  • val1 (Object)

    First value

  • val2 (Object)

    Second value

  • opts (Hash)

    Comparison options

  • differences (Array)

    Array to append differences to

  • path (String)

    Current path in the object structure

Returns:

  • (Symbol)

    Comparison result constant



149
150
151
152
153
154
155
156
157
158
# File 'lib/canon/comparison/ruby_object_comparator.rb', line 149

def self.compare_primitives(val1, val2, opts, differences, path)
  if val1 == val2
    Comparison::EQUIVALENT
  else
    add_difference(path, val1, val2,
                   Comparison::UNEQUAL_PRIMITIVES, opts,
                   differences)
    Comparison::UNEQUAL_PRIMITIVES
  end
end