json-deep-compare 0.1.0 → 0.2.0
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.
data/lib/json-deep-compare.rb
CHANGED
@@ -1,202 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(lval, rval, options = {})
|
6
|
-
if exclusions = options[:exclusions]
|
7
|
-
options[:exclusions] = [exclusions] unless exclusions.is_a?(Array)
|
8
|
-
else
|
9
|
-
options[:exclusions] = []
|
10
|
-
end
|
11
|
-
@root_comparisons = []
|
12
|
-
@root_comparisons << NodeComparison.new(lval, rval, ":root", options)
|
13
|
-
@root_comparisons << NodeComparison.new(rval, lval, ":root", options)
|
14
|
-
end
|
15
|
-
|
16
|
-
def difference_messages
|
17
|
-
ldiffs = @root_comparisons.first.differences
|
18
|
-
rdiffs = @root_comparisons.last.differences
|
19
|
-
differences = ldiffs.dup
|
20
|
-
ldiff_selectors = ldiffs.map { |ldiff| ldiff.selector }
|
21
|
-
rdiffs.each do |rdiff|
|
22
|
-
unless ldiff_selectors.include?(rdiff.selector)
|
23
|
-
differences << rdiff.reverse
|
24
|
-
end
|
25
|
-
end
|
26
|
-
differences = differences.sort_by &:selector
|
27
|
-
differences.map(&:message).join("\n")
|
28
|
-
end
|
29
|
-
|
30
|
-
def equal?
|
31
|
-
@root_comparisons.all?(&:equal?)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
class NodeComparison
|
36
|
-
ExcerptPadding = 15
|
37
|
-
attr_reader :lval, :rval, :selector
|
38
|
-
|
39
|
-
def initialize(lval, rval, selector, options = {})
|
40
|
-
@lval, @rval, @selector, @options = lval, rval, selector, options
|
41
|
-
@children = []
|
42
|
-
if lval.is_a?(Hash)
|
43
|
-
if rval.is_a?(Hash)
|
44
|
-
lval.each do |key, left_sub_value|
|
45
|
-
@children << NodeComparison.new(
|
46
|
-
left_sub_value,
|
47
|
-
rval[key],
|
48
|
-
"#{selector} > .#{key}",
|
49
|
-
options
|
50
|
-
)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
elsif lval.is_a?(Array)
|
54
|
-
if rval.is_a?(Array)
|
55
|
-
lval.each_with_index do |left_sub_value, i|
|
56
|
-
@children << NodeComparison.new(
|
57
|
-
left_sub_value,
|
58
|
-
rval[i],
|
59
|
-
"#{selector} :nth-child(#{i+1})",
|
60
|
-
options
|
61
|
-
)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def blank?(value)
|
68
|
-
value.respond_to?(:empty?) ? value.empty? : !value
|
69
|
-
end
|
70
|
-
|
71
|
-
def blank_equality?
|
72
|
-
@options[:blank_equality]
|
73
|
-
end
|
74
|
-
|
75
|
-
def differences
|
76
|
-
if equal?
|
77
|
-
[]
|
78
|
-
else
|
79
|
-
if leaf?
|
80
|
-
if excerptable_difference?
|
81
|
-
[excerpted_difference]
|
82
|
-
else
|
83
|
-
[Difference.new(
|
84
|
-
@selector, "expected to be :lval but was :rval",
|
85
|
-
lval: value_inspect(@lval), rval: value_inspect(@rval)
|
86
|
-
)]
|
87
|
-
end
|
88
|
-
else
|
89
|
-
@children.map(&:differences).compact.flatten
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def equal?
|
95
|
-
if leaf?
|
96
|
-
if selector_excluded?
|
97
|
-
true
|
98
|
-
elsif equality_proc
|
99
|
-
equality_proc.call(@lval, @rval)
|
100
|
-
else
|
101
|
-
@lval == @rval || (blank_equality? && blank?(@lval) && blank?(@rval))
|
102
|
-
end
|
103
|
-
else
|
104
|
-
@children.all?(&:equal?)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def equality_proc
|
109
|
-
@options[:equality]
|
110
|
-
end
|
1
|
+
require 'json-deep-compare/assertions'
|
2
|
+
require 'json-deep-compare/document_comparison'
|
3
|
+
require 'json-deep-compare/node_comparison'
|
4
|
+
require 'json-deep-compare/version'
|
111
5
|
|
112
|
-
|
113
|
-
@lval.is_a?(String) and @rval.is_a?(String) && (
|
114
|
-
@lval.size > ExcerptPadding * 2 || @rval.size > ExcerptPadding * 2
|
115
|
-
)
|
116
|
-
end
|
117
|
-
|
118
|
-
def excerpted_difference
|
119
|
-
difference_start = (0..@lval.length).detect { |i| @lval[i] != @rval[i] }
|
120
|
-
range_start = if difference_start > ExcerptPadding
|
121
|
-
difference_start - ExcerptPadding
|
122
|
-
else
|
123
|
-
0
|
124
|
-
end
|
125
|
-
left_excerpt = @lval[range_start..difference_start+ExcerptPadding]
|
126
|
-
right_excerpt = @rval[range_start..difference_start+ExcerptPadding]
|
127
|
-
if difference_start - ExcerptPadding > 0
|
128
|
-
left_excerpt = "..." + left_excerpt
|
129
|
-
right_excerpt = "..." + right_excerpt
|
130
|
-
end
|
131
|
-
if difference_start + ExcerptPadding < @lval.length
|
132
|
-
left_excerpt = left_excerpt + '...'
|
133
|
-
end
|
134
|
-
if difference_start + ExcerptPadding < @rval.length
|
135
|
-
right_excerpt = right_excerpt + '...'
|
136
|
-
end
|
137
|
-
Difference.new(
|
138
|
-
@selector,
|
139
|
-
"differs starting at char :difference_start: :lval differs from :rval",
|
140
|
-
difference_start: difference_start.to_s,
|
141
|
-
lval: left_excerpt.inspect, rval: right_excerpt.inspect
|
142
|
-
)
|
143
|
-
end
|
144
|
-
|
145
|
-
def leaf?
|
146
|
-
@children.empty?
|
147
|
-
end
|
148
|
-
|
149
|
-
def selector_excluded?
|
150
|
-
@options[:exclusions].any? { |exclusion|
|
151
|
-
if exclusion.is_a?(String)
|
152
|
-
exclusion == @selector
|
153
|
-
else
|
154
|
-
@selector =~ exclusion
|
155
|
-
end
|
156
|
-
}
|
157
|
-
end
|
158
|
-
|
159
|
-
def value_inspect(value)
|
160
|
-
str = value.inspect
|
161
|
-
if str.length >= 40
|
162
|
-
"#{value.class.name} #{str[0..37]}..."
|
163
|
-
else
|
164
|
-
str
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
class Difference
|
169
|
-
attr_reader :selector
|
170
|
-
|
171
|
-
def initialize(selector, msg_template, variables)
|
172
|
-
@selector, @msg_template, @variables =
|
173
|
-
selector, msg_template, variables
|
174
|
-
end
|
175
|
-
|
176
|
-
def message
|
177
|
-
msg = @msg_template
|
178
|
-
@variables.each do |name, value|
|
179
|
-
msg = msg.gsub(/#{name.inspect}/, value)
|
180
|
-
end
|
181
|
-
"#{@selector.inspect} #{msg}"
|
182
|
-
end
|
183
|
-
|
184
|
-
def reverse
|
185
|
-
reversed_variables = {lval: @variables[:rval], rval: @variables[:lval]}
|
186
|
-
(@variables.keys - [:lval, :rval]).each do |other_var|
|
187
|
-
reversed_variables[other_var] = @variables[other_var]
|
188
|
-
end
|
189
|
-
Difference.new(@selector, @msg_template, reversed_variables)
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
module Assertions
|
195
|
-
def assert_json_equal(expected, actual, exclusions = nil)
|
196
|
-
comparison = DocumentComparison.new(expected, actual, exclusions: exclusions)
|
197
|
-
unless comparison.equal?
|
198
|
-
fail comparison.difference_messages
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
6
|
+
module JsonDeepCompare
|
202
7
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module JsonDeepCompare
|
2
|
+
class DocumentComparison
|
3
|
+
def initialize(lval, rval, options = {})
|
4
|
+
if exclusions = options[:exclusions]
|
5
|
+
options[:exclusions] = [exclusions] unless exclusions.is_a?(Array)
|
6
|
+
else
|
7
|
+
options[:exclusions] = []
|
8
|
+
end
|
9
|
+
@root_comparisons = []
|
10
|
+
@root_comparisons << NodeComparison.new(
|
11
|
+
lval, rval, ":root", options.merge(direction: :left)
|
12
|
+
)
|
13
|
+
@root_comparisons << NodeComparison.new(
|
14
|
+
rval, lval, ":root", options.merge(direction: :right)
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def difference_messages
|
19
|
+
ldiffs = @root_comparisons.first.differences
|
20
|
+
rdiffs = @root_comparisons.last.differences
|
21
|
+
differences = ldiffs.dup
|
22
|
+
ldiff_selectors = ldiffs.map { |ldiff| ldiff.selector }
|
23
|
+
rdiffs.each do |rdiff|
|
24
|
+
unless ldiff_selectors.include?(rdiff.selector)
|
25
|
+
differences << rdiff.reverse
|
26
|
+
end
|
27
|
+
end
|
28
|
+
differences = differences.sort_by &:selector
|
29
|
+
differences.map(&:message).join("\n")
|
30
|
+
end
|
31
|
+
|
32
|
+
def equal?
|
33
|
+
@root_comparisons.all?(&:equal?)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
module JsonDeepCompare
|
2
|
+
class NodeComparison
|
3
|
+
ExcerptPadding = 15
|
4
|
+
attr_reader :lval, :rval, :selector
|
5
|
+
|
6
|
+
def initialize(lval, rval, selector, options = {})
|
7
|
+
@lval, @rval, @selector, @options = lval, rval, selector, options
|
8
|
+
@children = []
|
9
|
+
if lval.is_a?(Hash)
|
10
|
+
if rval.is_a?(Hash)
|
11
|
+
lval.each do |key, left_sub_value|
|
12
|
+
@children << NodeComparison.new(
|
13
|
+
left_sub_value,
|
14
|
+
rval[key],
|
15
|
+
"#{selector} > .#{key}",
|
16
|
+
options
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
elsif lval.is_a?(Array)
|
21
|
+
if rval.is_a?(Array)
|
22
|
+
lval.each_with_index do |left_sub_value, i|
|
23
|
+
@children << NodeComparison.new(
|
24
|
+
left_sub_value,
|
25
|
+
rval[i],
|
26
|
+
"#{selector} :nth-child(#{i+1})",
|
27
|
+
options
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def blank?(value)
|
35
|
+
value.respond_to?(:empty?) ? value.empty? : !value
|
36
|
+
end
|
37
|
+
|
38
|
+
def blank_equality?
|
39
|
+
@options[:blank_equality]
|
40
|
+
end
|
41
|
+
|
42
|
+
def differences
|
43
|
+
if equal?
|
44
|
+
[]
|
45
|
+
else
|
46
|
+
if leaf?
|
47
|
+
if excerptable_difference?
|
48
|
+
[excerpted_difference]
|
49
|
+
else
|
50
|
+
[Difference.new(
|
51
|
+
@selector, "expected to be :lval but was :rval",
|
52
|
+
lval: value_inspect(@lval), rval: value_inspect(@rval)
|
53
|
+
)]
|
54
|
+
end
|
55
|
+
else
|
56
|
+
@children.map(&:differences).compact.flatten
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def equal?
|
62
|
+
if leaf?
|
63
|
+
if selector_excluded?
|
64
|
+
true
|
65
|
+
elsif equality_proc
|
66
|
+
equality_proc.call(lval_for_equality, rval_for_equality)
|
67
|
+
elsif blank_equality? && blank?(lval_for_equality) && blank?(rval_for_equality)
|
68
|
+
true
|
69
|
+
else
|
70
|
+
lval_for_equality == rval_for_equality
|
71
|
+
end
|
72
|
+
else
|
73
|
+
@children.all?(&:equal?)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def equality_proc
|
78
|
+
@options[:equality]
|
79
|
+
end
|
80
|
+
|
81
|
+
def excerptable_difference?
|
82
|
+
@lval.is_a?(String) and @rval.is_a?(String) && (
|
83
|
+
@lval.size > ExcerptPadding * 2 || @rval.size > ExcerptPadding * 2
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
def excerpted_difference
|
88
|
+
difference_start = (0..@lval.length).detect { |i| @lval[i] != @rval[i] }
|
89
|
+
range_start = if difference_start > ExcerptPadding
|
90
|
+
difference_start - ExcerptPadding
|
91
|
+
else
|
92
|
+
0
|
93
|
+
end
|
94
|
+
left_excerpt = @lval[range_start..difference_start+ExcerptPadding]
|
95
|
+
right_excerpt = @rval[range_start..difference_start+ExcerptPadding]
|
96
|
+
if difference_start - ExcerptPadding > 0
|
97
|
+
left_excerpt = "..." + left_excerpt
|
98
|
+
right_excerpt = "..." + right_excerpt
|
99
|
+
end
|
100
|
+
if difference_start + ExcerptPadding < @lval.length
|
101
|
+
left_excerpt = left_excerpt + '...'
|
102
|
+
end
|
103
|
+
if difference_start + ExcerptPadding < @rval.length
|
104
|
+
right_excerpt = right_excerpt + '...'
|
105
|
+
end
|
106
|
+
Difference.new(
|
107
|
+
@selector,
|
108
|
+
"differs starting at char :difference_start: :lval differs from :rval",
|
109
|
+
difference_start: difference_start.to_s,
|
110
|
+
lval: left_excerpt.inspect, rval: right_excerpt.inspect
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
def leaf?
|
115
|
+
@children.empty?
|
116
|
+
end
|
117
|
+
|
118
|
+
def left_to_right?
|
119
|
+
@options[:direction] == :left
|
120
|
+
end
|
121
|
+
|
122
|
+
def lval_for_equality
|
123
|
+
@_lval_for_equality ||= begin
|
124
|
+
if left_to_right? && substitution
|
125
|
+
substitution
|
126
|
+
else
|
127
|
+
lval
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def right_to_left?
|
133
|
+
@options[:direction] == :right
|
134
|
+
end
|
135
|
+
|
136
|
+
def rval_for_equality
|
137
|
+
@_rval_for_equality ||= begin
|
138
|
+
if right_to_left? && substitution
|
139
|
+
substitution
|
140
|
+
else
|
141
|
+
rval
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def selector_excluded?
|
147
|
+
@options[:exclusions].any? { |exclusion|
|
148
|
+
if exclusion.is_a?(String)
|
149
|
+
exclusion == @selector
|
150
|
+
else
|
151
|
+
@selector =~ exclusion
|
152
|
+
end
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
def substitution
|
157
|
+
@options[:substitutions] && @options[:substitutions][selector]
|
158
|
+
end
|
159
|
+
|
160
|
+
def value_inspect(value)
|
161
|
+
str = value.inspect
|
162
|
+
if str.length >= 40
|
163
|
+
"#{value.class.name} #{str[0..37]}..."
|
164
|
+
else
|
165
|
+
str
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class Difference
|
170
|
+
attr_reader :selector
|
171
|
+
|
172
|
+
def initialize(selector, msg_template, variables)
|
173
|
+
@selector, @msg_template, @variables =
|
174
|
+
selector, msg_template, variables
|
175
|
+
end
|
176
|
+
|
177
|
+
def message
|
178
|
+
msg = @msg_template
|
179
|
+
@variables.each do |name, value|
|
180
|
+
msg = msg.gsub(/#{name.inspect}/, value)
|
181
|
+
end
|
182
|
+
"#{@selector.inspect} #{msg}"
|
183
|
+
end
|
184
|
+
|
185
|
+
def reverse
|
186
|
+
reversed_variables = {lval: @variables[:rval], rval: @variables[:lval]}
|
187
|
+
(@variables.keys - [:lval, :rval]).each do |other_var|
|
188
|
+
reversed_variables[other_var] = @variables[other_var]
|
189
|
+
end
|
190
|
+
Difference.new(@selector, @msg_template, reversed_variables)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
$: << '
|
3
|
-
require '
|
2
|
+
$: << 'lib'
|
3
|
+
require 'json-deep-compare'
|
4
4
|
|
5
5
|
class DocumentComparisonTestCase < Test::Unit::TestCase
|
6
6
|
def assert_symmetrically_different(
|
@@ -198,4 +198,13 @@ class DocumentComparisonTestCase < Test::Unit::TestCase
|
|
198
198
|
)
|
199
199
|
assert comparison.equal?
|
200
200
|
end
|
201
|
+
|
202
|
+
def test_substitutions
|
203
|
+
lval = {'one' => 'two', 'three' => 'four'}
|
204
|
+
rval = {'one' => 'two', 'three' => 'THREE'}
|
205
|
+
comparison = JsonDeepCompare::DocumentComparison.new(
|
206
|
+
lval, rval, substitutions: {":root > .three" => 'THREE'}
|
207
|
+
)
|
208
|
+
assert comparison.equal?
|
209
|
+
end
|
201
210
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json-deep-compare
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-02-
|
12
|
+
date: 2014-02-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -57,6 +57,10 @@ files:
|
|
57
57
|
- Rakefile
|
58
58
|
- json-deep-compare.gemspec
|
59
59
|
- lib/json-deep-compare.rb
|
60
|
+
- lib/json-deep-compare/assertions.rb
|
61
|
+
- lib/json-deep-compare/document_comparison.rb
|
62
|
+
- lib/json-deep-compare/node_comparison.rb
|
63
|
+
- lib/json-deep-compare/version.rb
|
60
64
|
- test/json_deep_compare_test.rb
|
61
65
|
homepage: ''
|
62
66
|
licenses:
|
@@ -73,7 +77,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
73
77
|
version: '0'
|
74
78
|
segments:
|
75
79
|
- 0
|
76
|
-
hash:
|
80
|
+
hash: -2030263147861406758
|
77
81
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
82
|
none: false
|
79
83
|
requirements:
|
@@ -82,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
86
|
version: '0'
|
83
87
|
segments:
|
84
88
|
- 0
|
85
|
-
hash:
|
89
|
+
hash: -2030263147861406758
|
86
90
|
requirements: []
|
87
91
|
rubyforge_project:
|
88
92
|
rubygems_version: 1.8.23
|