hash_deep_diff 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -0
- data/README.md +4 -2
- data/hash_deep_diff.gemspec +1 -0
- data/lib/hash_deep_diff/comparison.rb +8 -9
- data/lib/hash_deep_diff/delta.rb +38 -14
- data/lib/hash_deep_diff/factories/comparison.rb +58 -25
- data/lib/hash_deep_diff/reports/base.rb +7 -0
- data/lib/hash_deep_diff/reports/diff.rb +26 -4
- data/lib/hash_deep_diff/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc574fc78f743f8b629ecbb87baebea7e75789d99401f7e54691ad02469572ec
|
4
|
+
data.tar.gz: 0f378780b1f08173779e3684a8d42361c0532411c7972952a626bc3e4a0bebb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd12f9bc333f1a8ab3d4967bcdaeee7eac9cf66a97883a122cfa3f3e831165da62cce4b4dadf0e5e3c750cc43dcd59629b20c279d215b42ebd2d6fd65ebcc497
|
7
|
+
data.tar.gz: 64b6e8dbf48807f23ca32e84cfec991e14ff56ffda7bb65b71a1db3bc7d800ca0f94f00038e49dc1cd2bd30b84ad6c05496de599038d97960dece9f827cde4fb
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
Changes will be recorded for version 1.0.0 and later
|
data/README.md
CHANGED
@@ -5,7 +5,9 @@ Status](https://img.shields.io/github/workflow/status/bpohoriletz/hash_deep_diff
|
|
5
5
|

|
6
6
|
|
7
7
|
|
8
|
-
Find the exact difference between two Hash objects and build a report to visualize it
|
8
|
+
Find the exact difference between two Hash objects and build a report to visualize it. Works for other objects too but why would you do that :/
|
9
|
+
|
10
|
+
Alternative solutions [hashdiff by liufengyun](https://github.com/liufengyun/hashdiff) and [hash_hdiff by CodingZeal](https://github.com/CodingZeal/hash_diff)
|
9
11
|
|
10
12
|
## Installation
|
11
13
|
|
@@ -37,7 +39,7 @@ HashDeepDiff::Comparison.new(left, right).report
|
|
37
39
|
+ left[a] = b
|
38
40
|
```
|
39
41
|
please see [Documentation](https://rdoc.info/gems/hash_deep_diff/HashDeepDiff/Comparison) for
|
40
|
-
more information
|
42
|
+
more information or [Reporting test](https://github.com/bpohoriletz/hash_deep_diff/blob/a525d239189b0310aec3741dfc4862834805252d/test/integration/locales/test_uk_ru.rb#L59)
|
41
43
|
|
42
44
|
## Customization
|
43
45
|
|
data/hash_deep_diff.gemspec
CHANGED
@@ -49,6 +49,7 @@ Gem::Specification.new do |spec|
|
|
49
49
|
spec.add_development_dependency 'minitest-focus', '~> 1.3.1'
|
50
50
|
spec.add_development_dependency 'minitest-reporters', '~> 1.5.0'
|
51
51
|
spec.add_development_dependency 'naught', '~> 1.1.0'
|
52
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.9.0'
|
52
53
|
spec.add_development_dependency 'rake', '~> 10.5.0'
|
53
54
|
spec.add_development_dependency 'rubocop', '~> 1.26.1'
|
54
55
|
spec.add_development_dependency 'rubocop-minitest', '~> 0.18.0'
|
@@ -63,6 +63,7 @@ module HashDeepDiff
|
|
63
63
|
def left(key = NO_VALUE)
|
64
64
|
return NO_VALUE if @left == NO_VALUE
|
65
65
|
return @left if key == NO_VALUE
|
66
|
+
return @left unless left.respond_to?(:to_hash)
|
66
67
|
|
67
68
|
@left[key] || NO_VALUE
|
68
69
|
end
|
@@ -71,6 +72,7 @@ module HashDeepDiff
|
|
71
72
|
def right(key = NO_VALUE)
|
72
73
|
return NO_VALUE if @right == NO_VALUE
|
73
74
|
return @right if key == NO_VALUE
|
75
|
+
return @right unless right.respond_to?(:to_hash)
|
74
76
|
|
75
77
|
@right[key] || NO_VALUE
|
76
78
|
end
|
@@ -98,22 +100,19 @@ module HashDeepDiff
|
|
98
100
|
common_keys.each_with_object([]) do |key, memo|
|
99
101
|
next if values_equal?(key)
|
100
102
|
|
101
|
-
memo
|
102
|
-
end
|
103
|
+
memo.append(delta(key: key))
|
104
|
+
end.flatten
|
103
105
|
end
|
104
106
|
|
105
107
|
# depending on circumstances will return necessary comparisons
|
106
108
|
# @return [Array<HashDeepDiff::Delta>]
|
107
109
|
def inward_comparison(complex_delta)
|
108
110
|
if complex_delta.partial?
|
109
|
-
|
110
|
-
complex_delta.
|
111
|
-
comparison(delta: complex_delta, modifier: :
|
112
|
-
comparison(delta: complex_delta, modifier: :left).diff
|
113
|
-
].compact.flatten
|
114
|
-
# TOFIX add test an drop flatten
|
111
|
+
complex_delta.placebo +
|
112
|
+
comparison(delta: complex_delta, modifier: :addition).map(&:diff).flatten +
|
113
|
+
comparison(delta: complex_delta, modifier: :deletion).map(&:diff).flatten
|
115
114
|
else
|
116
|
-
comparison(delta: complex_delta).diff
|
115
|
+
comparison(delta: complex_delta).map(&:diff).flatten
|
117
116
|
end
|
118
117
|
end
|
119
118
|
|
data/lib/hash_deep_diff/delta.rb
CHANGED
@@ -16,29 +16,45 @@ module HashDeepDiff
|
|
16
16
|
attr_reader :change_key
|
17
17
|
|
18
18
|
# an indication that nested Hash was deleted/added
|
19
|
-
# @return [HashDeepDiff::Delta
|
19
|
+
# @return [Array<HashDeepDiff::Delta>]
|
20
20
|
def placebo
|
21
|
-
|
21
|
+
placebo = simple_left? ? { left: NO_VALUE, right: placebo_elment } : { left: placebo_elment, right: NO_VALUE }
|
22
22
|
|
23
|
-
|
23
|
+
[self.class.new(change_key: change_key, value: placebo)]
|
24
|
+
end
|
25
|
+
|
26
|
+
# true if any value is an +Array+ with hashes
|
27
|
+
# @return [TrueClass, FalseClass]
|
28
|
+
def complex?
|
29
|
+
complex_left? || complex_right?
|
30
|
+
end
|
31
|
+
|
32
|
+
# true if right part is an +Array+ with hashes
|
33
|
+
# @return [TrueClass, FalseClass]
|
34
|
+
def complex_right?
|
35
|
+
right.respond_to?(:to_ary) && right.any? { |el| el.respond_to?(:to_hash) }
|
36
|
+
end
|
24
37
|
|
25
|
-
|
38
|
+
# true if left part is an +Array+ with hashes
|
39
|
+
# @return [TrueClass, FalseClass]
|
40
|
+
def complex_left?
|
41
|
+
left.respond_to?(:to_ary) && left.any? { |el| el.respond_to?(:to_hash) }
|
26
42
|
end
|
27
43
|
|
28
44
|
# true if at least one of the values is a Hash
|
29
|
-
# @return [
|
45
|
+
# @return [TrueClass, FalseClass]
|
30
46
|
def partial?
|
31
|
-
!composite? && !simple?
|
47
|
+
!composite? && !simple? && !complex_left? && !complex_right?
|
32
48
|
end
|
33
49
|
|
34
50
|
# true if both valus are Hashes
|
35
|
-
# @return [
|
51
|
+
# @return [TrueClass, FalseClass]
|
36
52
|
def composite?
|
37
53
|
!simple_left? && !simple_right?
|
38
54
|
end
|
39
55
|
|
40
56
|
# true if none of the values is a Hash
|
41
|
-
# @return [
|
57
|
+
# @return [TrueClass, FalseClass]
|
42
58
|
def simple?
|
43
59
|
simple_left? && simple_right?
|
44
60
|
end
|
@@ -76,16 +92,24 @@ module HashDeepDiff
|
|
76
92
|
@change_key = change_key
|
77
93
|
end
|
78
94
|
|
79
|
-
#
|
80
|
-
# @return [
|
95
|
+
# an indication of added/removed nested Hash
|
96
|
+
# @return [Array, Hash]
|
97
|
+
def placebo_elment
|
98
|
+
return [{}] if complex_left? || complex_right?
|
99
|
+
|
100
|
+
return {}
|
101
|
+
end
|
102
|
+
|
103
|
+
# true if left value has no nested Hashes
|
104
|
+
# @return [TrueClass, FalseClass]
|
81
105
|
def simple_left?
|
82
|
-
!left.respond_to?(:to_hash)
|
106
|
+
!left.respond_to?(:to_hash) && !complex_left?
|
83
107
|
end
|
84
108
|
|
85
|
-
#
|
86
|
-
# @return [
|
109
|
+
# true if right value has no nested Hashes
|
110
|
+
# @return [TrueClass, FalseClass]
|
87
111
|
def simple_right?
|
88
|
-
!right.respond_to?(:to_hash)
|
112
|
+
!right.respond_to?(:to_hash) && !complex_right?
|
89
113
|
end
|
90
114
|
end
|
91
115
|
end
|
@@ -8,50 +8,83 @@ module HashDeepDiff
|
|
8
8
|
# Factory for {HashDeepDiff::Comparison}
|
9
9
|
class Comparison
|
10
10
|
extend Forwardable
|
11
|
-
|
11
|
+
|
12
|
+
def_delegators :delta, :left, :right, :change_key, :complex?, :complex_left?, :complex_right?
|
12
13
|
|
13
14
|
# factory function
|
14
|
-
# @return [Comparison]
|
15
|
-
def comparison(delta:, modifier:
|
15
|
+
# @return [HashDeepDiff::Comparison]
|
16
|
+
def comparison(delta:, modifier: :change)
|
16
17
|
@delta = delta
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
compare_left
|
23
|
-
when :right
|
24
|
-
compare_right
|
19
|
+
fragments(modifier).map do |(left, right, change_key)|
|
20
|
+
HashDeepDiff::Comparison.new(left, right, change_key,
|
21
|
+
delta_engine: delta.class,
|
22
|
+
reporting_engine: reporting_engine)
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
28
26
|
private
|
29
27
|
|
28
|
+
# @!attribute [r] reporting_engine
|
29
|
+
# @return [HashDeepDiff::Reports::Base] descendant of
|
30
|
+
# @!attribute [r] delta
|
31
|
+
# @return [HashDeepDiff::Delta]
|
30
32
|
attr_reader :reporting_engine, :delta
|
31
33
|
|
32
34
|
def initialize(reporting_engine:)
|
33
35
|
@reporting_engine = reporting_engine
|
34
36
|
end
|
35
37
|
|
36
|
-
#
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
# entities for further comparison
|
39
|
+
# @return [Array]
|
40
|
+
def fragments(mode)
|
41
|
+
case mode
|
42
|
+
when :change
|
43
|
+
return [[value_left, value_right, change_key]] unless complex?
|
44
|
+
|
45
|
+
[[value_left, value_right, change_key + ['...']],
|
46
|
+
[nesting_left, nesting_right, change_key + ['{}']]]
|
47
|
+
when :deletion
|
48
|
+
[[value_left, NO_VALUE, change_key]]
|
49
|
+
when :addition
|
50
|
+
[[NO_VALUE, value_right, change_key]]
|
51
|
+
end
|
41
52
|
end
|
42
53
|
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
54
|
+
# original value without nested hashes
|
55
|
+
# @return [Object]
|
56
|
+
def value_left
|
57
|
+
return left unless left.respond_to?(:to_ary)
|
58
|
+
|
59
|
+
left.reject { |el| el.respond_to?(:to_hash) }
|
48
60
|
end
|
49
61
|
|
50
|
-
#
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
62
|
+
# changed value without nested hashes
|
63
|
+
# @return [Object]
|
64
|
+
def value_right
|
65
|
+
return right unless right.respond_to?(:to_ary)
|
66
|
+
|
67
|
+
right.reject { |el| el.respond_to?(:to_hash) }
|
68
|
+
end
|
69
|
+
|
70
|
+
# nested hashes from original value
|
71
|
+
# @return [Array<Hash>]
|
72
|
+
def nesting_left
|
73
|
+
return NO_VALUE unless complex_left?
|
74
|
+
|
75
|
+
left
|
76
|
+
.select { |el| el.respond_to?(:to_hash) }
|
77
|
+
.each_with_object({}) { |el, memo| memo.merge!(el) }
|
78
|
+
end
|
79
|
+
|
80
|
+
# nested hashes from changed value
|
81
|
+
# @return [Array<Hash>]
|
82
|
+
def nesting_right
|
83
|
+
return NO_VALUE unless complex_right?
|
84
|
+
|
85
|
+
right
|
86
|
+
.select { |el| el.respond_to?(:to_hash) }
|
87
|
+
.each_with_object({}) { |el, memo| memo.merge!(el) }
|
55
88
|
end
|
56
89
|
end
|
57
90
|
end
|
@@ -19,6 +19,13 @@ module HashDeepDiff
|
|
19
19
|
|
20
20
|
private
|
21
21
|
|
22
|
+
# @!attribute [r] old_val
|
23
|
+
# @return [Object] original value
|
24
|
+
# @!attribute [r] new_val
|
25
|
+
# @return [Object] replacement of the original value
|
26
|
+
# @!attribute [r] change_key
|
27
|
+
# @return [Array<Object>] subset of keys from original Hashes to fetch reported values
|
28
|
+
# (is empty for top-level comparison)
|
22
29
|
attr_reader :old_val, :new_val, :change_key
|
23
30
|
|
24
31
|
# @param [Delta] delta diff to report
|
@@ -9,20 +9,42 @@ module HashDeepDiff
|
|
9
9
|
class Diff < Base
|
10
10
|
private
|
11
11
|
|
12
|
-
#
|
12
|
+
# line of the report with deleted value
|
13
13
|
# @return [String]
|
14
14
|
def original
|
15
15
|
return '' if old_val == NO_VALUE
|
16
|
+
return "#{deletion}#{path} = #{old_val}\n" unless array_to_array?
|
17
|
+
return '' if array_deletion.empty?
|
16
18
|
|
17
|
-
|
19
|
+
"#{deletion}#{path} = #{array_deletion}\n"
|
18
20
|
end
|
19
21
|
|
20
|
-
#
|
22
|
+
# line of the report with added value
|
21
23
|
# @return [String]
|
22
24
|
def replacement
|
23
25
|
return '' if new_val == NO_VALUE
|
26
|
+
return "#{addition}#{path} = #{new_val}\n" unless array_to_array?
|
27
|
+
return '' if array_addition.empty?
|
24
28
|
|
25
|
-
|
29
|
+
"#{addition}#{path} = #{array_addition}\n"
|
30
|
+
end
|
31
|
+
|
32
|
+
# returns true if original value and replacement are instances of +Array+
|
33
|
+
# @return Bool
|
34
|
+
def array_to_array?
|
35
|
+
old_val.instance_of?(Array) && new_val.instance_of?(Array)
|
36
|
+
end
|
37
|
+
|
38
|
+
# added elemnts of array
|
39
|
+
# @return [Array]
|
40
|
+
def array_addition
|
41
|
+
new_val - old_val
|
42
|
+
end
|
43
|
+
|
44
|
+
# added elemnts of array
|
45
|
+
# @return [Array]
|
46
|
+
def array_deletion
|
47
|
+
old_val - new_val
|
26
48
|
end
|
27
49
|
|
28
50
|
# Visual representation of keys from compared objects needed to fetch the compared values
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hash_deep_diff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bohdan Pohorilets
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -136,6 +136,20 @@ dependencies:
|
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: 1.1.0
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: pry-byebug
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 3.9.0
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 3.9.0
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
154
|
name: rake
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|