structure_compare 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/structure_compare.rb +4 -1
- data/lib/structure_compare/structure_comparison.rb +43 -21
- data/lib/structure_compare/version.rb +1 -1
- data/test/structure_compare_test.rb +31 -11
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38ec4e210b5674286a4e88ea6011ae71facb4ebc
|
4
|
+
data.tar.gz: 65755bdd48627fa619460f2abe6142a179f3ac00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63c5810ee73f6325340d89ea0dfa99a1fbcf88f5c3270b413abf6d9e470597bd4e86b8497929109fe6cce157a774bfc9c3bc8df7ecd458ea503057e8338d3684
|
7
|
+
data.tar.gz: 4b0309d805073de85b67b74c6c497350fc21817e230301802b0c58eb84bfc0b3f1f27d601a661eaa4cda20e9d223160427d0fdeedda339fd499b84f26bbdbbf7
|
data/lib/structure_compare.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# TODO: doc (interface, assertions, ...)
|
2
2
|
module StructureCompare
|
3
|
+
class ArgumentError < ::ArgumentError; end
|
4
|
+
class RuntimeError < ::RuntimeError; end
|
5
|
+
|
3
6
|
class StructuresNotEqualError < RuntimeError; end
|
4
|
-
class
|
7
|
+
class IndifferentAccessError < RuntimeError; end
|
5
8
|
end
|
6
9
|
|
7
10
|
require 'structure_compare/version'
|
@@ -5,7 +5,7 @@ module StructureCompare
|
|
5
5
|
@options = {
|
6
6
|
strict_key_order: false,
|
7
7
|
check_values: false,
|
8
|
-
|
8
|
+
indifferent_access: false,
|
9
9
|
float_tolerance_factor: 0
|
10
10
|
}.merge(options)
|
11
11
|
end
|
@@ -45,7 +45,7 @@ module StructureCompare
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def check_arrays_equal!(expected, actual)
|
48
|
-
|
48
|
+
check_values_equal!(
|
49
49
|
expected.count, actual.count, "array length: #{expected.count} != #{actual.count}"
|
50
50
|
)
|
51
51
|
|
@@ -61,48 +61,52 @@ module StructureCompare
|
|
61
61
|
def check_hashes_equal!(expected, actual)
|
62
62
|
check_hash_keys_equal!(expected, actual)
|
63
63
|
|
64
|
-
|
65
|
-
|
64
|
+
expected.each do |key, expected_value|
|
65
|
+
if @options[:indifferent_access]
|
66
|
+
actual_value = with_indifferent_access!(actual, key)
|
67
|
+
else
|
68
|
+
actual_value = actual[key]
|
69
|
+
end
|
66
70
|
|
67
|
-
expected_values.each_with_index do |expected_value, index|
|
68
|
-
key = expected.keys[index]
|
69
71
|
path_segment = key.is_a?(Symbol) ? "[:#{key}]" : "[\"#{key}\"]"
|
70
72
|
@path.push(path_segment)
|
71
73
|
|
72
|
-
check_structures_equal!(expected_value,
|
74
|
+
check_structures_equal!(expected_value, actual_value)
|
73
75
|
@path.pop
|
74
76
|
end
|
75
77
|
end
|
76
78
|
|
79
|
+
# TODO safeguard in case there's a key as symbol _and_ hash
|
77
80
|
def check_hash_keys_equal!(expected, actual)
|
78
81
|
expected_keys = expected.keys
|
79
82
|
actual_keys = actual.keys
|
80
83
|
|
81
|
-
if @options[:
|
84
|
+
if @options[:indifferent_access]
|
82
85
|
# NOTE: not all hash keys are symbols/strings, only convert symbols
|
83
86
|
expected_keys.map! { |key| key.is_a?(Symbol) ? key.to_s : key }
|
84
87
|
actual_keys.map! { |key| key.is_a?(Symbol) ? key.to_s : key }
|
85
88
|
end
|
86
89
|
|
90
|
+
failure_message = "hash keys aren't equal"
|
91
|
+
|
87
92
|
if @options[:strict_key_order]
|
88
|
-
|
93
|
+
if expected_keys != actual_keys
|
94
|
+
not_equal_error!(expected_keys, actual_keys, failure_message)
|
95
|
+
end
|
89
96
|
else
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
)
|
97
|
+
# NOTE: first did this with sorting, but can't sort mixed type keys
|
98
|
+
all_keys_present = (
|
99
|
+
expected_keys.all?{ |key| actual.has_key?(key) } &&
|
100
|
+
actual_keys.all?{ |key| expected.has_key?(key) }
|
101
|
+
)
|
102
|
+
if !all_keys_present
|
103
|
+
not_equal_error!(expected_keys, actual_keys, failure_message)
|
98
104
|
end
|
99
|
-
|
100
|
-
check_equal!(expected_keys, actual_keys)
|
101
105
|
end
|
102
106
|
end
|
103
107
|
|
104
108
|
def check_leafs_equal!(expected, actual)
|
105
|
-
|
109
|
+
check_values_equal!(expected, actual) if @options[:check_values]
|
106
110
|
end
|
107
111
|
|
108
112
|
def check_kind_of!(expected, actual)
|
@@ -112,7 +116,7 @@ module StructureCompare
|
|
112
116
|
end
|
113
117
|
end
|
114
118
|
|
115
|
-
def
|
119
|
+
def check_values_equal!(expected, actual, failure_message = nil)
|
116
120
|
if expected.is_a?(Float) && actual.is_a?(Float)
|
117
121
|
is_equal = float_equal_with_tolerance_factor?(
|
118
122
|
expected, actual, @options[:float_tolerance_factor]
|
@@ -141,5 +145,23 @@ module StructureCompare
|
|
141
145
|
@error = failure_message
|
142
146
|
raise StructuresNotEqualError.new() # TODO error message, path
|
143
147
|
end
|
148
|
+
|
149
|
+
def with_indifferent_access!(actual, key)
|
150
|
+
if [Symbol, String].include?(key.class)
|
151
|
+
has_both_types = (actual.has_key?(key) && actual.has_key?(key.to_s))
|
152
|
+
duplicate_key_error!(key) if has_both_types
|
153
|
+
|
154
|
+
actual.has_key?(key.to_s) ? actual[key.to_s] : actual[key.to_sym]
|
155
|
+
else
|
156
|
+
actual[key]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def duplicate_key_error!(key)
|
161
|
+
raise StructureCompare::IndifferentAccessError.new(
|
162
|
+
"#{@path.join}: key is present as string and symbol. " \
|
163
|
+
"can not use indifferent_access option!"
|
164
|
+
)
|
165
|
+
end
|
144
166
|
end
|
145
167
|
end
|
@@ -39,6 +39,18 @@ class StructureCompareTest < MiniTest::Test
|
|
39
39
|
refute_structures_equal([1, 2], [1, 2, 3])
|
40
40
|
end
|
41
41
|
|
42
|
+
def test_with_strict_key_order_off_compares_correct_values
|
43
|
+
structure_a = { a: 1, b: 2 }
|
44
|
+
structure_b = { b: 2, a: 1 }
|
45
|
+
assert_structures_equal(
|
46
|
+
structure_a, structure_b, check_values: true, strict_key_order: false
|
47
|
+
)
|
48
|
+
|
49
|
+
structure_a = { a: { c: 1 }, b: 2 }
|
50
|
+
structure_b = { b: 2, a: { c: 1 } }
|
51
|
+
assert_structures_equal(structure_a, structure_b, strict_key_order: false)
|
52
|
+
end
|
53
|
+
|
42
54
|
def test_stores_error_with_path_in_getter
|
43
55
|
structure_a = { x: 1, a: [{ b: [1, 1, 1] }] }
|
44
56
|
structure_b = { x: 1, a: [{ b: [1, 9, 1] }] }
|
@@ -67,27 +79,35 @@ class StructureCompareTest < MiniTest::Test
|
|
67
79
|
refute_structures_equal(structure_a, structure_b, check_values: true)
|
68
80
|
end
|
69
81
|
|
70
|
-
|
82
|
+
# this was a bug where we compared keys by sorting them, resulting in
|
83
|
+
# an ArgumentError if keys weren't comparable (String vs. Fixnum)
|
84
|
+
def test_mixed_type_hash_works_if_strict_key_order_not_set
|
71
85
|
hash = { a: 1, 5 => 6 }
|
72
|
-
|
73
|
-
assert_raises StructureCompare::ArgumentError do
|
74
|
-
assert_structures_equal(hash, hash)
|
75
|
-
end
|
76
|
-
|
77
|
-
assert_structures_equal(hash, hash, strict_key_order: true)
|
86
|
+
assert_structures_equal(hash, hash, strict_key_order: false)
|
78
87
|
end
|
79
88
|
|
80
|
-
def
|
81
|
-
string_hash = {"a" => 1, "b" => 2, 5 => 6}
|
82
|
-
symbol_hash = {a: 1, b: 2, 5 => 6}
|
89
|
+
def test_indifferent_access_option_works_as_expected
|
90
|
+
string_hash = { "a" => 1, "b" => 2, 5 => 6 }
|
91
|
+
symbol_hash = { a: 1, b: 2, 5 => 6 }
|
83
92
|
|
84
93
|
refute_structures_equal(string_hash, symbol_hash, strict_key_order: true)
|
85
94
|
assert_structures_equal(
|
86
95
|
string_hash, symbol_hash,
|
87
|
-
|
96
|
+
indifferent_access: true, strict_key_order: true
|
88
97
|
)
|
89
98
|
end
|
90
99
|
|
100
|
+
def test_option_for_indifferent_access_raises_if_symbol_and_string_key_present
|
101
|
+
hash = { "a" => 1, a: 2 }
|
102
|
+
assert_structures_equal(hash, hash, strict_key_order: true)
|
103
|
+
|
104
|
+
assert_raises StructureCompare::IndifferentAccessError do
|
105
|
+
assert_structures_equal(
|
106
|
+
hash, hash, strict_key_order: true, indifferent_access: true
|
107
|
+
)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
91
111
|
def test_compares_floats_correctly
|
92
112
|
assert_structures_equal([1.0, 2.0, 3.0], [1.0, 2.0, 3.0], check_values: true)
|
93
113
|
assert_structures_equal(
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: structure_compare
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Markus Seeger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|