Module: RSpec::JsonApi::SchemaMatch
- Defined in:
- lib/rspec/json_api/schema_match.rb
Overview
SchemaMatch compares parsed JSON (a Hash, an Array, or a scalar) against an expected schema. It is the single entry point behind the match_json_schema matcher: callers hand it the actual and expected values and it dispatches on shape internally, so the matcher does not need to know whether it is looking at an object or a collection.
Class Method Summary collapse
- .compare(actual, expected) ⇒ Object
- .compare_array(actual_value, expected_value) ⇒ Object
- .compare_class(actual_value, expected_value) ⇒ Object
-
.compare_exact_array(actual_value, expected_value) ⇒ Object
Any other array => element-by-element match, sizes must be equal.
-
.compare_interface_array(actual_value, expected_value) ⇒ Object
- { …interface… }
-
> every element must match the single interface.
- .compare_key_paths_and_values(keys, actual, expected) ⇒ Object
- .compare_proc(actual_value, expected_value) ⇒ Object
- .compare_regexp(actual_value, expected_value) ⇒ Object
- .compare_simple_value(actual_value, expected_value) ⇒ Object
-
.compare_typed_array(actual_value, expected_value) ⇒ Object
- SomeClass
-
> every element must be an instance of SomeClass.
- .compare_values(actual_value, expected_value) ⇒ Object
-
.dig_path(data, key_path) ⇒ Object
Digs a key path without raising when an intermediate value is not a Hash.
- .interface?(expected_value) ⇒ Boolean
-
.match(actual, expected) ⇒ Object
Top-level comparison.
- .same_key_structure?(actual, expected) ⇒ Boolean
- .simple_type?(expected_value) ⇒ Boolean
Class Method Details
.compare(actual, expected) ⇒ Object
35 36 37 38 39 40 41 |
# File 'lib/rspec/json_api/schema_match.rb', line 35 def compare(actual, expected) return false if actual.blank? && expected.present? keys = Traversal.deep_key_paths(expected) | Traversal.deep_key_paths(actual) compare_key_paths_and_values(keys, actual, expected) end |
.compare_array(actual_value, expected_value) ⇒ Object
86 87 88 89 90 91 92 93 94 |
# File 'lib/rspec/json_api/schema_match.rb', line 86 def compare_array(actual_value, expected_value) if simple_type?(expected_value) compare_typed_array(actual_value, expected_value) elsif interface?(expected_value) compare_interface_array(actual_value, expected_value) else compare_exact_array(actual_value, expected_value) end end |
.compare_class(actual_value, expected_value) ⇒ Object
74 75 76 |
# File 'lib/rspec/json_api/schema_match.rb', line 74 def compare_class(actual_value, expected_value) actual_value.instance_of?(expected_value) end |
.compare_exact_array(actual_value, expected_value) ⇒ Object
Any other array => element-by-element match, sizes must be equal.
114 115 116 117 118 119 120 |
# File 'lib/rspec/json_api/schema_match.rb', line 114 def compare_exact_array(actual_value, expected_value) return false if actual_value&.size != expected_value&.size expected_value.each_with_index.all? do |elem, index| elem.is_a?(Hash) ? compare(actual_value[index], elem) : compare_simple_value(actual_value[index], elem) end end |
.compare_interface_array(actual_value, expected_value) ⇒ Object
- { …interface… }
-
> every element must match the single interface.
Elements go through match (not compare) so each one is held to the same key-structure guard as a top-level object; otherwise an element with an extra null-valued key would slip through (nil == nil).
107 108 109 110 111 |
# File 'lib/rspec/json_api/schema_match.rb', line 107 def compare_interface_array(actual_value, expected_value) interface = expected_value[0] actual_value.all? { |elem| match(elem, interface) } end |
.compare_key_paths_and_values(keys, actual, expected) ⇒ Object
43 44 45 46 47 48 49 50 |
# File 'lib/rspec/json_api/schema_match.rb', line 43 def compare_key_paths_and_values(keys, actual, expected) keys.all? do |key_path| actual_value = dig_path(actual, key_path) expected_value = dig_path(expected, key_path) compare_values(actual_value, expected_value) end end |
.compare_proc(actual_value, expected_value) ⇒ Object
82 83 84 |
# File 'lib/rspec/json_api/schema_match.rb', line 82 def compare_proc(actual_value, expected_value) Constraints.match(actual_value, expected_value.call) end |
.compare_regexp(actual_value, expected_value) ⇒ Object
78 79 80 |
# File 'lib/rspec/json_api/schema_match.rb', line 78 def compare_regexp(actual_value, expected_value) expected_value.match?(actual_value.to_s) end |
.compare_simple_value(actual_value, expected_value) ⇒ Object
122 123 124 |
# File 'lib/rspec/json_api/schema_match.rb', line 122 def compare_simple_value(actual_value, expected_value) actual_value == expected_value end |
.compare_typed_array(actual_value, expected_value) ⇒ Object
- SomeClass
-
> every element must be an instance of SomeClass.
97 98 99 100 101 |
# File 'lib/rspec/json_api/schema_match.rb', line 97 def compare_typed_array(actual_value, expected_value) type = expected_value[0] actual_value.all? { |elem| compare_class(elem, type) } end |
.compare_values(actual_value, expected_value) ⇒ Object
64 65 66 67 68 69 70 71 72 |
# File 'lib/rspec/json_api/schema_match.rb', line 64 def compare_values(actual_value, expected_value) case expected_value when Class then compare_class(actual_value, expected_value) when Regexp then compare_regexp(actual_value, expected_value) when Proc then compare_proc(actual_value, expected_value) when Array then compare_array(actual_value, expected_value) else compare_simple_value(actual_value, expected_value) end end |
.dig_path(data, key_path) ⇒ Object
Digs a key path without raising when an intermediate value is not a Hash. Plain Hash#dig raises TypeError if it walks into a scalar (e.g. a schema expects a nested object but the actual value is a String), so a mismatch would crash instead of failing the match.
56 57 58 59 60 61 62 |
# File 'lib/rspec/json_api/schema_match.rb', line 56 def dig_path(data, key_path) key_path.reduce(data) do |value, key| break nil unless value.is_a?(Hash) value[key] end end |
.interface?(expected_value) ⇒ Boolean
130 131 132 |
# File 'lib/rspec/json_api/schema_match.rb', line 130 def interface?(expected_value) expected_value.size == 1 && expected_value[0].is_a?(Hash) end |
.match(actual, expected) ⇒ Object
Top-level comparison. Applies the shape guards (class equality and, for objects, key-set equality) before recursing.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/rspec/json_api/schema_match.rb', line 15 def match(actual, expected) return false unless actual.instance_of?(expected.class) case expected when Array compare_array(actual, expected) when Hash return false unless same_key_structure?(actual, expected) compare(actual, expected) else compare_simple_value(actual, expected) end end |
.same_key_structure?(actual, expected) ⇒ Boolean
30 31 32 33 |
# File 'lib/rspec/json_api/schema_match.rb', line 30 def same_key_structure?(actual, expected) Traversal.deep_sort(Traversal.deep_keys(actual)) == Traversal.deep_sort(Traversal.deep_keys(expected)) end |
.simple_type?(expected_value) ⇒ Boolean
126 127 128 |
# File 'lib/rspec/json_api/schema_match.rb', line 126 def simple_type?(expected_value) expected_value.size == 1 && expected_value[0].instance_of?(Class) end |