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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ddbaa8e8f12ba99330973c609c00ab710e9fa39b369797b80dd46f24aa6740e1
4
- data.tar.gz: 6a5954a960ca0876cdb9e28f3e535be687de5596ed07be61581102b75b178ab4
3
+ metadata.gz: dc574fc78f743f8b629ecbb87baebea7e75789d99401f7e54691ad02469572ec
4
+ data.tar.gz: 0f378780b1f08173779e3684a8d42361c0532411c7972952a626bc3e4a0bebb0
5
5
  SHA512:
6
- metadata.gz: af66a50e5a7eb848c96b757e8e37a4d767868de6fd96ebfd4f528085111e2fe1c9e39215b8514d534d33f94cfc4c024c5c2bba1813acad729fa906e18eee796a
7
- data.tar.gz: 358e25298ee3c42ea469e44113fe62935ec8f564c4111c52e170bd25764d99cc4b71c42d154689ad2a9c54ff21f9359dad57cfbd450677853eb60655b90aecf4
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
  ![GitHub](https://img.shields.io/github/license/bpohoriletz/hash_deep_diff)
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
 
@@ -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 << delta(key: key)
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.placebo,
111
- comparison(delta: complex_delta, modifier: :right).diff,
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
 
@@ -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, NilClass]
19
+ # @return [Array<HashDeepDiff::Delta>]
20
20
  def placebo
21
- return nil unless partial?
21
+ placebo = simple_left? ? { left: NO_VALUE, right: placebo_elment } : { left: placebo_elment, right: NO_VALUE }
22
22
 
23
- placebo = simple_left? ? { left: NO_VALUE, right: {} } : { left: {}, right: NO_VALUE }
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
- self.class.new(change_key: change_key, value: placebo)
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 [Bool]
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 [Bool]
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 [Bool]
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
- # Returns true if left value has no nested Hashes
80
- # @return [Bool]
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
- # Returns true if right value has no nested Hashes
86
- # @return [Bool]
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
- def_delegators :delta, :left, :right, :change_key
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: nil)
15
+ # @return [HashDeepDiff::Comparison]
16
+ def comparison(delta:, modifier: :change)
16
17
  @delta = delta
17
18
 
18
- case modifier
19
- when nil
20
- full_compare
21
- when :left
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
- # compare two hashes
37
- def full_compare
38
- HashDeepDiff::Comparison.new(left, right, change_key,
39
- delta_engine: delta.class,
40
- reporting_engine: reporting_engine)
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
- # compare Hash with nothing (deletion)
44
- def compare_left
45
- HashDeepDiff::Comparison.new(left, NO_VALUE, change_key,
46
- delta_engine: delta.class,
47
- reporting_engine: reporting_engine)
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
- # compare nothing with Hash (addition)
51
- def compare_right
52
- HashDeepDiff::Comparison.new(NO_VALUE, right, change_key,
53
- delta_engine: delta.class,
54
- reporting_engine: reporting_engine)
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
- # old value
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
- return "#{deletion}#{path} = #{old_val}\n"
19
+ "#{deletion}#{path} = #{array_deletion}\n"
18
20
  end
19
21
 
20
- # new value
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
- return "#{addition}#{path} = #{new_val}\n"
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module HashDeepDiff
4
4
  # Version of a gem
5
- VERSION = '0.6.0'
5
+ VERSION = '0.7.0'
6
6
  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.6.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-04-25 00:00:00.000000000 Z
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