Module: RSpec::JsonApi::Traversal

Defined in:
lib/rspec/json_api/traversal.rb

Overview

Traversal holds the structural helpers used to compare JSON shapes: collecting the nested key structure of a Hash and sorting it into a canonical order. These were previously monkey-patched onto core Hash and Array; keeping them here means the gem no longer mutates those classes in the host application.

Class Method Summary collapse

Class Method Details

.deep_key_paths(hash) ⇒ Object

Every leaf key path of a hash, e.g. { a: { b: 1 }, c: 2 } => [[:a, :b], [:c]].



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/rspec/json_api/traversal.rb', line 23

def deep_key_paths(hash)
  stack = hash.map { |key, value| [[key], value] }
  key_map = []

  until stack.empty?
    key, value = stack.pop

    key_map << key unless value.is_a?(Hash)

    next unless value.is_a?(Hash)

    value.each { |k, v| stack.push([key.dup << k, v]) }
  end

  key_map.reverse
end

.deep_keys(hash) ⇒ Object

The nested keys of a hash, with each nested hash's keys inlined as an array, e.g. { a: 1, b: { c: 2 } } => [:a, :b, [:c]].



15
16
17
18
19
20
# File 'lib/rspec/json_api/traversal.rb', line 15

def deep_keys(hash)
  hash.each_with_object([]) do |(key, value), keys|
    keys << key
    keys << deep_keys(value) if value.respond_to?(:keys)
  end
end

.deep_sort(array) ⇒ Object

Recursively sorts an array (and any nested arrays) into a canonical order so two key structures can be compared regardless of original order.



42
43
44
45
46
# File 'lib/rspec/json_api/traversal.rb', line 42

def deep_sort(array)
  array
    .map { |element| element.is_a?(Array) ? deep_sort(element) : element }
    .sort_by { |element| element.is_a?(Array) ? element.first.to_s : element.to_s }
end

.same_key_structure?(actual, expected) ⇒ Boolean

Whether two hashes have the same keys under the same parent objects.

Returns:

  • (Boolean)


49
50
51
52
53
# File 'lib/rspec/json_api/traversal.rb', line 49

def same_key_structure?(actual, expected)
  same_keys?(actual, expected) && actual.all? do |key, actual_value|
    same_nested_key_structure?(actual_value, expected[key])
  end
end

.same_keys?(actual, expected) ⇒ Boolean

Returns:

  • (Boolean)


55
56
57
# File 'lib/rspec/json_api/traversal.rb', line 55

def same_keys?(actual, expected)
  actual.size == expected.size && actual.each_key.all? { |key| expected.key?(key) }
end

.same_nested_key_structure?(actual, expected) ⇒ Boolean

Returns:

  • (Boolean)


59
60
61
62
# File 'lib/rspec/json_api/traversal.rb', line 59

def same_nested_key_structure?(actual, expected)
  nested = actual.is_a?(Hash) || expected.is_a?(Hash)
  !nested || (actual.is_a?(Hash) && expected.is_a?(Hash) && same_key_structure?(actual, expected))
end