hash_deep_diff 0.3.3 → 0.4.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 +0 -0
- data/README.md +5 -2
- data/lib/hash_deep_diff/acts_as_hash.rb +27 -0
- data/lib/hash_deep_diff/comparison.rb +11 -31
- data/lib/hash_deep_diff/delta.rb +80 -0
- data/lib/hash_deep_diff/version.rb +1 -1
- data/lib/hash_deep_diff.rb +1 -0
- metadata +5 -6
- data/lib/hash_deep_diff/delta/acts_as_delta.rb +0 -69
- data/lib/hash_deep_diff/delta/inner.rb +0 -40
- data/lib/hash_deep_diff/delta/left.rb +0 -30
- data/lib/hash_deep_diff/delta/right.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24756c15f3f48bbe108e5c2bb12c2db3e6030cef78ab91a6cf76044ca70d2dd5
|
4
|
+
data.tar.gz: a8cca8ab8a23c704f6f69c2613ded6b4d0d2c7b0e5caed34d437fe0951582a45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 731ccc90f750c6796374ae83ccf9c78741e47251c7940add8f0e9c9c34417f84f8375e74c639891c4e37e5976200bb8786e4bd2dbbe9357bb9b92e8a14b46c7b
|
7
|
+
data.tar.gz: 8dfbab0f3e5fd2666e2d5eb3458d9df858e204e49cd604348676af43c511bd855dba61720702cc69c7e3ecd0751e29606eec10bef41ee1a0e8bdd4ba33db79f7
|
data/CHANGELOG.md
ADDED
File without changes
|
data/README.md
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
Find the exact difference between two Hash objects and build a report to visualize it
|
4
4
|
|
5
|
+
[](https://badge.fury.io/rb/hash_deep_diff)
|
7
|
+
|
5
8
|
## Installation
|
6
9
|
|
7
10
|
Add this line to your application's Gemfile:
|
@@ -29,10 +32,10 @@ HashDeepDiff::Comparison.new(left, right).report
|
|
29
32
|
```
|
30
33
|
```diff
|
31
34
|
- left[a] = a
|
32
|
-
+
|
35
|
+
+ left[a] = b
|
33
36
|
```
|
34
37
|
|
35
38
|
## Contributing
|
36
39
|
|
37
40
|
Bug reports and pull requests are welcome on GitHub at [bpohoriletz](https://github.com/bpohoriletz/hash_deep_diff).
|
38
|
-
|
41
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module HashDeepDiff
|
6
|
+
# This module includes behavior that is needed to use deltas instead of Hash inside this gem
|
7
|
+
module ActsAsHash
|
8
|
+
def self.included(base)
|
9
|
+
base.include(InstanceMethods)
|
10
|
+
base.extend(Forwardable)
|
11
|
+
base.def_delegators :@delta, :==, :each_with_object, :each_key, :[],
|
12
|
+
:to_a, :empty?, :keys
|
13
|
+
end
|
14
|
+
|
15
|
+
# Assumes that the class will include method delta that will return a representation of an
|
16
|
+
# instance of a class as a Hash
|
17
|
+
module InstanceMethods
|
18
|
+
def to_h
|
19
|
+
@delta
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_hash
|
23
|
+
@delta
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,17 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'delta
|
4
|
-
require_relative 'delta/inner'
|
5
|
-
require_relative 'delta/right'
|
3
|
+
require_relative 'delta'
|
6
4
|
|
7
5
|
# :nodoc:
|
8
6
|
module HashDeepDiff
|
9
|
-
#
|
7
|
+
# An instrument to build and report the difference between two hash-like objects
|
10
8
|
class Comparison
|
11
9
|
attr_reader :left, :right, :path
|
12
10
|
|
13
|
-
def diff
|
14
|
-
|
11
|
+
def diff
|
12
|
+
deep_delta
|
15
13
|
end
|
16
14
|
|
17
15
|
def report
|
@@ -26,8 +24,8 @@ module HashDeepDiff
|
|
26
24
|
@path = path.to_ary
|
27
25
|
end
|
28
26
|
|
29
|
-
def deep_delta
|
30
|
-
delta
|
27
|
+
def deep_delta
|
28
|
+
delta.flat_map do |diff|
|
31
29
|
if diff.complex?
|
32
30
|
self.class.new(diff.left, diff.right, diff.path).diff
|
33
31
|
else
|
@@ -36,37 +34,19 @@ module HashDeepDiff
|
|
36
34
|
end
|
37
35
|
end
|
38
36
|
|
39
|
-
def
|
40
|
-
left_diff_keys.map { |key| Delta::Left.new(path: path + [key], value: left[key]) }
|
41
|
-
end
|
42
|
-
|
43
|
-
def right_delta
|
44
|
-
right_diff_keys.map { |key| Delta::Right.new(path: path + [key], value: right[key]) }
|
45
|
-
end
|
46
|
-
|
47
|
-
def delta(&block)
|
48
|
-
block ||= ->(val) { val }
|
49
|
-
|
37
|
+
def delta
|
50
38
|
common_keys.each_with_object([]) do |key, memo|
|
51
|
-
value_left =
|
52
|
-
value_right =
|
39
|
+
value_left = left[key] || NO_VALUE
|
40
|
+
value_right = right[key] || NO_VALUE
|
53
41
|
|
54
42
|
next if value_right.instance_of?(value_left.class) && (value_right == value_left)
|
55
43
|
|
56
|
-
memo << Delta
|
44
|
+
memo << Delta.new(path: path + [key], value: { left: value_left, right: value_right })
|
57
45
|
end
|
58
46
|
end
|
59
47
|
|
60
48
|
def common_keys
|
61
|
-
left.keys
|
62
|
-
end
|
63
|
-
|
64
|
-
def right_diff_keys
|
65
|
-
right.keys - left.keys
|
66
|
-
end
|
67
|
-
|
68
|
-
def left_diff_keys
|
69
|
-
left.keys - right.keys
|
49
|
+
(left.keys + right.keys).uniq
|
70
50
|
end
|
71
51
|
end
|
72
52
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'acts_as_hash'
|
4
|
+
|
5
|
+
module HashDeepDiff
|
6
|
+
# Representation of the diff of two values
|
7
|
+
# examples:
|
8
|
+
# - diff of { a: a } and {} is { a: { left: a, right: HashDeepDiff::NO_VALUE } }
|
9
|
+
# - diff of { a: a } and { a: b } is { a: { left: a, right: b } }
|
10
|
+
# - diff of {} and { a: b } is { a: { left: HashDeepDiff::NO_VALUE, right: b } }
|
11
|
+
class Delta
|
12
|
+
include ActsAsHash
|
13
|
+
|
14
|
+
def to_str
|
15
|
+
[deletion, addition].compact.join("\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
def complex?
|
19
|
+
left.respond_to?(:to_hash) && right.respond_to?(:to_hash)
|
20
|
+
end
|
21
|
+
|
22
|
+
# TOFIX poor naming
|
23
|
+
# overrides parameter in initializer
|
24
|
+
def path
|
25
|
+
@prefix + [@delta.keys.first]
|
26
|
+
end
|
27
|
+
|
28
|
+
def left
|
29
|
+
@value[:left]
|
30
|
+
end
|
31
|
+
|
32
|
+
def right
|
33
|
+
@value[:right]
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
to_str
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def initialize(path:, value:)
|
43
|
+
# TOFIX this may prohibit usage of hashes with Array keys
|
44
|
+
if path.respond_to?(:to_ary)
|
45
|
+
@delta = { path[-1] => value }
|
46
|
+
@value = value
|
47
|
+
@prefix = path[0..-2]
|
48
|
+
else
|
49
|
+
@delta = { path => value }
|
50
|
+
@value = value
|
51
|
+
@prefix = []
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def deletion
|
56
|
+
return nil if left == NO_VALUE
|
57
|
+
|
58
|
+
if left.respond_to?(:to_hash)
|
59
|
+
left.keys.map { |key| "-left#{diff_prefix}[#{key}] = #{left[key]}" }.join("\n")
|
60
|
+
else
|
61
|
+
"-left#{diff_prefix} = #{left}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def addition
|
66
|
+
return nil if right == NO_VALUE
|
67
|
+
|
68
|
+
if right.respond_to?(:to_hash)
|
69
|
+
right.keys.map { |key| "+left#{diff_prefix}[#{key}] = #{right[key]}" }.join("\n")
|
70
|
+
else
|
71
|
+
"+left#{diff_prefix} = #{right}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# TOFIX poor naming
|
76
|
+
def diff_prefix
|
77
|
+
path.map { |key| "[#{key}]" }.join
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/hash_deep_diff.rb
CHANGED
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.4.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-04-
|
11
|
+
date: 2022-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -175,6 +175,7 @@ files:
|
|
175
175
|
- ".bundle/config"
|
176
176
|
- ".gitignore"
|
177
177
|
- ".rubocop.yml"
|
178
|
+
- CHANGELOG.md
|
178
179
|
- Gemfile
|
179
180
|
- Guardfile
|
180
181
|
- README.md
|
@@ -188,11 +189,9 @@ files:
|
|
188
189
|
- bin/setup
|
189
190
|
- hash_deep_diff.gemspec
|
190
191
|
- lib/hash_deep_diff.rb
|
192
|
+
- lib/hash_deep_diff/acts_as_hash.rb
|
191
193
|
- lib/hash_deep_diff/comparison.rb
|
192
|
-
- lib/hash_deep_diff/delta
|
193
|
-
- lib/hash_deep_diff/delta/inner.rb
|
194
|
-
- lib/hash_deep_diff/delta/left.rb
|
195
|
-
- lib/hash_deep_diff/delta/right.rb
|
194
|
+
- lib/hash_deep_diff/delta.rb
|
196
195
|
- lib/hash_deep_diff/version.rb
|
197
196
|
homepage: https://github.com/bpohoriletz/hash_deep_diff
|
198
197
|
licenses:
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'forwardable'
|
4
|
-
|
5
|
-
module HashDeepDiff
|
6
|
-
module Delta
|
7
|
-
# This module includes behavior that is needed to use deltas instead of Hash inside this gem
|
8
|
-
module ActsAsDelta
|
9
|
-
def self.included(base)
|
10
|
-
base.prepend(Initialize)
|
11
|
-
base.include(InstanceMethods)
|
12
|
-
base.extend(Forwardable)
|
13
|
-
base.def_delegators :@delta, :==, :each_with_object, :each_key, :[],
|
14
|
-
:to_a, :empty?, :keys
|
15
|
-
end
|
16
|
-
|
17
|
-
# Assumes that the class will include method delta that will return a representation of an
|
18
|
-
# instance of a class as a Hash
|
19
|
-
module InstanceMethods
|
20
|
-
# TOFIX poor naming
|
21
|
-
def diff_prefix
|
22
|
-
path.map { |key| "[#{key}]" }.join
|
23
|
-
end
|
24
|
-
|
25
|
-
# TOFIX poor naming
|
26
|
-
# overrides parameter in initializer
|
27
|
-
def path
|
28
|
-
@prefix + [@delta.keys.first]
|
29
|
-
end
|
30
|
-
|
31
|
-
def to_h
|
32
|
-
@delta
|
33
|
-
end
|
34
|
-
|
35
|
-
def to_hash
|
36
|
-
@delta
|
37
|
-
end
|
38
|
-
|
39
|
-
def to_s
|
40
|
-
to_str
|
41
|
-
end
|
42
|
-
|
43
|
-
def to_str
|
44
|
-
raise NoMethodError, "expected #{self.class} to implement #to_str"
|
45
|
-
end
|
46
|
-
|
47
|
-
def complex?
|
48
|
-
raise NoMethodError, "expected #{self.class} to implement #complex?"
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
# Override #initialize method
|
53
|
-
module Initialize
|
54
|
-
def initialize(path:, value:)
|
55
|
-
# TOFIX this may prohibit usage of hashes with Array keys
|
56
|
-
if path.respond_to?(:to_ary)
|
57
|
-
@delta = { path[-1] => value }
|
58
|
-
@value = value
|
59
|
-
@prefix = path[0..-2]
|
60
|
-
else
|
61
|
-
@delta = { path => value }
|
62
|
-
@value = value
|
63
|
-
@prefix = []
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'acts_as_delta'
|
4
|
-
|
5
|
-
module HashDeepDiff
|
6
|
-
module Delta
|
7
|
-
# Representation of the pure left diff
|
8
|
-
# i.e element that are missing in the hash on the right of the comparison
|
9
|
-
# for example left diff of { a: a } and {} is { a: a }
|
10
|
-
class Inner
|
11
|
-
include Delta::ActsAsDelta
|
12
|
-
|
13
|
-
def to_str
|
14
|
-
return diff unless complex?
|
15
|
-
|
16
|
-
HashDeepDiff::Comparison.new(left, right, path).report
|
17
|
-
end
|
18
|
-
|
19
|
-
def diff
|
20
|
-
lines = <<~Q
|
21
|
-
-left#{diff_prefix} = #{left}
|
22
|
-
+right#{diff_prefix} = #{right}
|
23
|
-
Q
|
24
|
-
lines.strip
|
25
|
-
end
|
26
|
-
|
27
|
-
def complex?
|
28
|
-
left.respond_to?(:to_hash) && right.respond_to?(:to_hash)
|
29
|
-
end
|
30
|
-
|
31
|
-
def left
|
32
|
-
@value[:left]
|
33
|
-
end
|
34
|
-
|
35
|
-
def right
|
36
|
-
@value[:right]
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'acts_as_delta'
|
4
|
-
|
5
|
-
module HashDeepDiff
|
6
|
-
module Delta
|
7
|
-
# Representation of the pure left diff
|
8
|
-
# i.e element that are missing in the hash on the right of the comparison
|
9
|
-
# for example left diff of { a: a } and {} is { a: a }
|
10
|
-
class Left
|
11
|
-
include Delta::ActsAsDelta
|
12
|
-
|
13
|
-
def to_str
|
14
|
-
return "+left#{diff_prefix} = #{left}" unless left.respond_to?(:to_hash)
|
15
|
-
|
16
|
-
left.keys.map do |key|
|
17
|
-
self.class.new(path: path + [key], value: left[key])
|
18
|
-
end.join("\n").strip
|
19
|
-
end
|
20
|
-
|
21
|
-
def left
|
22
|
-
@value
|
23
|
-
end
|
24
|
-
|
25
|
-
def right
|
26
|
-
nil
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'acts_as_delta'
|
4
|
-
|
5
|
-
module HashDeepDiff
|
6
|
-
module Delta
|
7
|
-
# Representation of the pure right diff
|
8
|
-
# i.e element that are missing in the hash on the right of the comparison
|
9
|
-
# for example right diff of {} and { a: a } is { a: a }
|
10
|
-
class Right
|
11
|
-
include Delta::ActsAsDelta
|
12
|
-
|
13
|
-
def to_str
|
14
|
-
return "-left#{diff_prefix} = #{right}" unless right.respond_to?(:to_hash)
|
15
|
-
|
16
|
-
right.keys.map do |key|
|
17
|
-
self.class.new(path: path + [key], value: right[key])
|
18
|
-
end.join("\n").strip
|
19
|
-
end
|
20
|
-
|
21
|
-
def left
|
22
|
-
nil
|
23
|
-
end
|
24
|
-
|
25
|
-
def right
|
26
|
-
@value
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|