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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe3724394c10b36b7537f35bce5c7f079cae0ee3344062a78bdfe7256363624b
4
- data.tar.gz: e9baa112b03013b91f75ebea9f243a0a6d51ca746ca8c8e66ea6fa38803198fd
3
+ metadata.gz: 24756c15f3f48bbe108e5c2bb12c2db3e6030cef78ab91a6cf76044ca70d2dd5
4
+ data.tar.gz: a8cca8ab8a23c704f6f69c2613ded6b4d0d2c7b0e5caed34d437fe0951582a45
5
5
  SHA512:
6
- metadata.gz: b7393bb437eddfb8e0ed66d4eba3b4135e221c7c2262df14aa4509dba98345c4766b598bd3c3f11f311c84b25b4f9f0b2bca3dfc5083c023c87c582fa607761a
7
- data.tar.gz: 57708513050ac1e63c0ce64b9650e0ae0e3128127cbb4dfc5b248cc3caca3267cbd26bed7b939be4413c11f1a7038f5503aef143119a09e336cc113324ae182a
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
+ [![Gem
6
+ Version](https://badge.fury.io/rb/hash_deep_diff.svg)](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
- + right[a] = b
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/left'
4
- require_relative 'delta/inner'
5
- require_relative 'delta/right'
3
+ require_relative 'delta'
6
4
 
7
5
  # :nodoc:
8
6
  module HashDeepDiff
9
- # :nodoc:
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(&block)
14
- left_delta + deep_delta(&block) + right_delta
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(&block)
30
- delta(&block).flat_map do |diff|
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 left_delta
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 = block.call(left[key])
52
- value_right = block.call(right[key])
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::Inner.new(path: path + [key], value: { left: value_left, right: value_right })
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 & right.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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HashDeepDiff
4
- VERSION = '0.3.3'
4
+ VERSION = '0.4.0'
5
5
  end
@@ -4,5 +4,6 @@ require 'hash_deep_diff/version'
4
4
  require 'hash_deep_diff/comparison'
5
5
 
6
6
  module HashDeepDiff
7
+ NO_VALUE = Class.new(NilClass)
7
8
  class Error < StandardError; end
8
9
  end
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.3.3
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-13 00:00:00.000000000 Z
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/acts_as_delta.rb
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