hash_deep_diff 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc574fc78f743f8b629ecbb87baebea7e75789d99401f7e54691ad02469572ec
4
- data.tar.gz: 0f378780b1f08173779e3684a8d42361c0532411c7972952a626bc3e4a0bebb0
3
+ metadata.gz: 0e7ceeb1c2e935cc4b6c9d27e6d718f63f627d3adeba269df367c37464f3e9c0
4
+ data.tar.gz: 00053a3c87a26ea7dff3513594c6fb85e1235be3c341bc58f30d4934faa3de57
5
5
  SHA512:
6
- metadata.gz: dd12f9bc333f1a8ab3d4967bcdaeee7eac9cf66a97883a122cfa3f3e831165da62cce4b4dadf0e5e3c750cc43dcd59629b20c279d215b42ebd2d6fd65ebcc497
7
- data.tar.gz: 64b6e8dbf48807f23ca32e84cfec991e14ff56ffda7bb65b71a1db3bc7d800ca0f94f00038e49dc1cd2bd30b84ad6c05496de599038d97960dece9f827cde4fb
6
+ metadata.gz: 612c398c02060fd908f449ac44893a8a81d33f3c1f13ec86bb3042d748034f18a20fbd99559c211af58e5f70d77d0b107fda0fae6cb2be3a1f94e1e0299c0f2e
7
+ data.tar.gz: c63d6e0ddad970dce331a5460981916b821805a8a3fe8b95f5ab6ea73a029942091acdf6b9681da533630fd6da38440749419d25436979e07cd15da5a2020658
data/.rubocop.yml CHANGED
@@ -10,6 +10,7 @@ AllCops:
10
10
  - '.git/**/*'
11
11
  - '.bundle/**/*'
12
12
  - 'bin/*'
13
+ TargetRubyVersion: 2.6.1
13
14
 
14
15
  Style/RedundantReturn:
15
16
  Enabled: false
@@ -18,3 +19,9 @@ Metrics/BlockLength:
18
19
  Exclude:
19
20
  - 'test/**/test_*.rb'
20
21
  - '*.gemspec'
22
+ Style/YodaCondition:
23
+ EnforcedStyle: require_for_all_comparison_operators
24
+
25
+ Minitest/AssertEmptyLiteral:
26
+ Exclude:
27
+ - 'test/unit/test_change_key.rb'
data/README.md CHANGED
@@ -7,7 +7,7 @@ Status](https://img.shields.io/github/workflow/status/bpohoriletz/hash_deep_diff
7
7
 
8
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
9
 
10
- Alternative solutions [hashdiff by liufengyun](https://github.com/liufengyun/hashdiff) and [hash_hdiff by CodingZeal](https://github.com/CodingZeal/hash_diff)
10
+ Alternative solutions [hashdiff](https://github.com/liufengyun/hashdiff) by liufengyun and [hash_diff](https://github.com/CodingZeal/hash_diff) by CodingZeal
11
11
 
12
12
  ## Installation
13
13
 
@@ -26,21 +26,93 @@ Or install it yourself as:
26
26
  $ gem install hash_deep_diff
27
27
 
28
28
  ## Usage
29
- Basic example
29
+ ### Basic
30
30
 
31
31
  ```ruby
32
32
  left = { a: :a }
33
33
  right = { a: :b }
34
34
 
35
- HashDeepDiff::Comparison.new(left, right).report
35
+ print HashDeepDiff::Comparison.new(left, right).report
36
36
  ```
37
37
  ```diff
38
38
  - left[a] = a
39
39
  + left[a] = b
40
40
  ```
41
+ ### Arrays
42
+ ```ruby
43
+ left = [1, 2, { a: :a }]
44
+ right = [2, { a: :b }, 3]
45
+
46
+ print HashDeepDiff::Comparison.new(left, right).report
47
+ ```
48
+ ```diff
49
+ -left[...] = [1]
50
+ +left[...] = [3]
51
+ -left[{}][a] = a
52
+ +left[{}][a] = b
53
+ ```
54
+ ### Nesting
55
+ ```ruby
56
+ left = { a: [1, 2, { a: :a } ], b: { c: [1, 2, { d: :e } ] } }
57
+ right = { a: [2, { a: :b }, 3], b: { c: { f: { g: :h } } } }
58
+
59
+ print HashDeepDiff::Comparison.new(left, right).report
60
+ ```
61
+ ```diff
62
+ -left[a][...] = [1]
63
+ +left[a][...] = [3]
64
+ -left[a][{}][a] = a
65
+ +left[a][{}][a] = b
66
+ +left[b][c][...][f] = {}
67
+ +left[b][c][...][f][g] = h
68
+ -left[b][c][...][f] = [1, 2]
69
+ -left[b][c][{}][d] = e
70
+ ```
71
+ ### Reporting Engines
72
+ You can choose from the default diff-like reporting engine (examples are above) and YML reporting engine
73
+
74
+ ```ruby
75
+ left = { a: [1, 2, { a: :a } ], b: { c: [1, 2, { d: :e } ] } }
76
+ right = { a: [2, { a: :b }, 3], b: { c: { f: { g: :h } } } }
77
+ ```
78
+ #### Raw Report
79
+
80
+ ```ruby
81
+ print HashDeepDiff::Comparison.new(left, right, reporting_engine: HashDeepDiff::Reports::Yml).raw_report
82
+ => {"additions"=>{:a=>[3, {:a=>:b}], :b=>{:c=>[{:f=>{:g=>:h}}]}},
83
+ "deletions"=>{:a=>[1, {:a=>:a}], :b=>{:c=>[1, 2, {:d=>:e}]}}}
84
+ ```
85
+
86
+ #### YML Report
87
+
88
+ ```ruby
89
+ print HashDeepDiff::Comparison.new(left, right, reporting_engine: HashDeepDiff::Reports::Yml).report
90
+
91
+ ---
92
+ additions:
93
+ :a:
94
+ - 3
95
+ - :a: :b
96
+ :b:
97
+ :c:
98
+ - :f:
99
+ :g: :h
100
+ deletions:
101
+ :a:
102
+ - 1
103
+ - :a: :a
104
+ :b:
105
+ :c:
106
+ - 1
107
+ - 2
108
+ - :d: :e
109
+ ```
110
+
41
111
  please see [Documentation](https://rdoc.info/gems/hash_deep_diff/HashDeepDiff/Comparison) for
42
112
  more information or [Reporting test](https://github.com/bpohoriletz/hash_deep_diff/blob/a525d239189b0310aec3741dfc4862834805252d/test/integration/locales/test_uk_ru.rb#L59)
43
113
 
114
+
115
+
44
116
  ## Customization
45
117
 
46
118
  You can implement and use your own reporting engines with the default `HashDeepDiff::Delta` objects as a source of the report. In order to do so implement your own version of the reporting engine (example can be found [here](https://github.com/bpohoriletz/hash_deep_diff/tree/main/lib/hash_deep_diff/reports)) and inject it into a `Comparison`
@@ -53,5 +53,7 @@ Gem::Specification.new do |spec|
53
53
  spec.add_development_dependency 'rake', '~> 10.5.0'
54
54
  spec.add_development_dependency 'rubocop', '~> 1.26.1'
55
55
  spec.add_development_dependency 'rubocop-minitest', '~> 0.18.0'
56
+ spec.add_development_dependency 'rubocop-performance', '~> 1.13.3'
56
57
  spec.add_development_dependency 'rubocop-rake', '~> 0.6.0'
58
+ spec.add_development_dependency 'rubocop-rspec', '~> 2.10.0'
57
59
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module HashDeepDiff
6
+ # Key for a compared value inside Array or Hash
7
+ class ChangeKey
8
+ extend Forwardable
9
+ def_delegators :to_ary, :[], :+, :map, :==, :first, :shift, :empty?
10
+ # element that indicates nested Hash
11
+ NESTED_HASH = '{}'
12
+ # element that indicates Array value
13
+ ARRAY_VALUE = '...'
14
+
15
+ # based on the first element of the key returns the initial object
16
+ # to buld the change representation
17
+ # @return [Array, Hash]
18
+ def self.initial_object(values:)
19
+ return [] if values.size.positive? && [NESTED_HASH, ARRAY_VALUE].include?(values[0][0][0])
20
+
21
+ {}
22
+ end
23
+
24
+ # set the value inside Hash based on the change_key
25
+ # @return [Array, Hash]
26
+ # TOFIX; check if @path are mutated
27
+ def set(obj, value, clone_keys = path.clone)
28
+ # 1. Fetch key
29
+ current_key = clone_keys.shift
30
+ # 2. Prepare object for further processing
31
+ init_value(current_key, obj, clone_keys)
32
+ init_nesting(current_key, obj, clone_keys)
33
+ # 3. Set value - directly or recursively
34
+ set_value(current_key, obj, value, clone_keys)
35
+ recursive_set(current_key, obj, value, clone_keys)
36
+
37
+ return obj
38
+ end
39
+
40
+ # see {#to_ary}
41
+ # @return [Array]
42
+ def to_a
43
+ to_ary
44
+ end
45
+
46
+ # array with keysused to initialize the object
47
+ # @return [Array]
48
+ def to_ary
49
+ path
50
+ end
51
+
52
+ # see {#to_str}
53
+ # @return [String]
54
+ def to_s
55
+ to_str
56
+ end
57
+
58
+ # visual representation of the change key
59
+ # @return [String]
60
+ def to_str
61
+ path.map { |key| "[#{key}]" }.join
62
+ end
63
+
64
+ private
65
+
66
+ attr_reader :path
67
+
68
+ def initialize(path:)
69
+ @path = path.to_ary
70
+ end
71
+
72
+ # prepare an object before further processing
73
+ def init_value(current_key, obj, clone_keys)
74
+ if [ARRAY_VALUE] == clone_keys || NESTED_HASH == clone_keys.first
75
+ obj[current_key] ||= []
76
+ elsif !clone_keys.empty? && ![ARRAY_VALUE, NESTED_HASH].include?(current_key)
77
+ obj[current_key] ||= {}
78
+ end
79
+ end
80
+
81
+ # prepare nesting before further processing
82
+ def init_nesting(current_key, obj, clone_keys)
83
+ element = if NESTED_HASH == current_key
84
+ obj
85
+ elsif NESTED_HASH == clone_keys.first
86
+ obj[current_key]
87
+ end
88
+ element << {} unless element.nil? || element.last.respond_to?(:to_hash)
89
+ end
90
+
91
+ # no more nesting - set value inside object
92
+ def set_value(current_key, obj, value, clone_keys)
93
+ if ARRAY_VALUE == current_key
94
+ obj.prepend(*value)
95
+ clone_keys.pop
96
+ elsif clone_keys.empty?
97
+ obj[current_key] = value
98
+ clone_keys.pop
99
+ elsif [ARRAY_VALUE] == clone_keys
100
+ obj[current_key] = value + obj[current_key]
101
+ clone_keys.pop
102
+ end
103
+ end
104
+
105
+ # recursion for deeply nested values
106
+ def recursive_set(current_key, obj, value, clone_keys)
107
+ if NESTED_HASH == current_key
108
+ set(obj.last, value, clone_keys)
109
+ elsif NESTED_HASH == clone_keys.first
110
+ set(obj[current_key].last, value, clone_keys[1..])
111
+ elsif !clone_keys.empty?
112
+ set(obj[current_key], value, clone_keys)
113
+ end
114
+ end
115
+ end
116
+ end
@@ -21,36 +21,22 @@ module HashDeepDiff
21
21
  # NO_VALUE compared to { one: { three: :c } } compared - value was added
22
22
  # i.e NO_VALUE vas replaced with :c on path [:one, :three]
23
23
  # [
24
- # #<HashDeepDiff::Delta
25
- # @delta={:two=>{:left=>:a, :right=>:b}},
26
- # @prefix=[:one],
24
+ # #<HashDeepDiff::Delta:0x00007fc7bc8a6e58
25
+ # @change_key=#<HashDeepDiff::ChangeKey:0x00007fc7bc8a6d40 @path=[:one, :two]>,
27
26
  # @value={:left=>:a, :right=>:b}>,
28
- # #<HashDeepDiff::Delta
29
- # @delta={:zero=>{:left=>:z, :right=>HashDeepDiff::NO_VALUE}},
30
- # @prefix=[:one],
27
+ # #<HashDeepDiff::Delta:0x00007fc7bc8a6b60
28
+ # @change_key=#<HashDeepDiff::ChangeKey:0x00007fc7bc8a6a48 @path=[:one, :zero]>,
31
29
  # @value={:left=>:z, :right=>HashDeepDiff::NO_VALUE}>,
32
- # #<HashDeepDiff::Delta
33
- # @delta={:three=>{:left=>HashDeepDiff::NO_VALUE, :right=>:c}},
34
- # @prefix=[:one],
35
- # @value={:left=>HashDeepDiff::NO_VALUE, :right=>:c}>
30
+ # #<HashDeepDiff::Delta:0x00007fc7bc8a6930
31
+ # @change_key=#<HashDeepDiff::ChangeKey:0x00007fc7bc8a6818 @path=[:one, :three]>,
32
+ # @value={:left=>HashDeepDiff::NO_VALUE, :right=>:c}>]
36
33
  # ]
37
34
  class Comparison
38
35
  extend Forwardable
39
- # @!attribute [r] left
40
- # @return [Hash] original version of the Hash
41
- # @!attribute [r] right
42
- # @return [Hash] Hash that the original is compared to
43
- # @!attribute [r] path
44
- # @return [Array<Object>] subset of keys from original Hashes to fetch compared values
45
- # (is empty for top-level comparison)
46
36
  attr_reader :reporting_engine, :delta_engine
47
37
 
48
38
  def_delegators :comparison_factory, :comparison
49
-
50
- # @return [String]
51
- def report
52
- diff.map { |simple_delta| reporting_engine.new(delta: simple_delta).to_s }.join
53
- end
39
+ def_delegators :report_engine_factory, :report, :raw_report
54
40
 
55
41
  # @return [Array<HashDeepDiff::Delta>]
56
42
  def diff
@@ -79,6 +65,9 @@ module HashDeepDiff
79
65
 
80
66
  private
81
67
 
68
+ # @!attribute [r] path
69
+ # @return [Array<Object>] subset of keys from original Hashes to fetch compared values
70
+ # (is empty for top-level comparison)
82
71
  attr_reader :path
83
72
 
84
73
  # @param [Object] original original version
@@ -132,18 +121,23 @@ module HashDeepDiff
132
121
  keys.uniq
133
122
  end
134
123
 
124
+ # factory function
125
+ # @return [HashDeepDiff::Delta]
126
+ def delta(key: NO_VALUE)
127
+ keys = path
128
+ keys += [key] unless key == NO_VALUE
129
+
130
+ HashDeepDiff::Delta.new(path: keys, value: { left: left(key), right: right(key) })
131
+ end
132
+
135
133
  # @return [HashDeepDiff::Factories::Comparison]
136
134
  def comparison_factory
137
135
  HashDeepDiff::Factories::Comparison.new(reporting_engine: reporting_engine)
138
136
  end
139
137
 
140
- # factory function
141
- # @return [HashDeepDiff::Delta]
142
- def delta(key: NO_VALUE)
143
- change_key = path
144
- change_key += [key] unless key == NO_VALUE
145
-
146
- HashDeepDiff::Delta.new(change_key: change_key, value: { left: left(key), right: right(key) })
138
+ # @return [HashDeepDiff::Reports::Base]
139
+ def report_engine_factory
140
+ reporting_engine.new(diff: diff)
147
141
  end
148
142
  end
149
143
  end
@@ -20,7 +20,7 @@ module HashDeepDiff
20
20
  def placebo
21
21
  placebo = simple_left? ? { left: NO_VALUE, right: placebo_elment } : { left: placebo_elment, right: NO_VALUE }
22
22
 
23
- [self.class.new(change_key: change_key, value: placebo)]
23
+ [self.class.new(path: change_key, value: placebo)]
24
24
  end
25
25
 
26
26
  # true if any value is an +Array+ with hashes
@@ -59,11 +59,25 @@ module HashDeepDiff
59
59
  simple_left? && simple_right?
60
60
  end
61
61
 
62
+ # removed element(s)
63
+ def deletion
64
+ return left unless array_with_array?
65
+
66
+ return left - right
67
+ end
68
+
62
69
  # Original value
63
70
  def left
64
71
  value[:left]
65
72
  end
66
73
 
74
+ # added element(s)
75
+ def addition
76
+ return right unless array_with_array?
77
+
78
+ return right - left
79
+ end
80
+
67
81
  # Value we compare to
68
82
  def right
69
83
  value[:right]
@@ -84,12 +98,12 @@ module HashDeepDiff
84
98
 
85
99
  attr_reader :value
86
100
 
87
- # @param [Array] change_key list of keys to fetch values we're comparing
101
+ # @param [Array] path list of keys to fetch values we're comparing
88
102
  # @param [Hash<(:left, :right), Object>] value +Hash+ object with two keys - :left and :right,
89
103
  # that represents compared original value (at :left) and value we compare to (at :right)
90
- def initialize(change_key:, value:)
104
+ def initialize(path:, value:)
91
105
  @value = value
92
- @change_key = change_key
106
+ @change_key = HashDeepDiff::ChangeKey.new(path: path)
93
107
  end
94
108
 
95
109
  # an indication of added/removed nested Hash
@@ -111,5 +125,11 @@ module HashDeepDiff
111
125
  def simple_right?
112
126
  !right.respond_to?(:to_hash) && !complex_right?
113
127
  end
128
+
129
+ # true if both left and right are arrays
130
+ # @return [TrueClass, FalseClass]
131
+ def array_with_array?
132
+ left.respond_to?(:to_ary) && right.respond_to?(:to_ary)
133
+ end
114
134
  end
115
135
  end
@@ -42,8 +42,8 @@ module HashDeepDiff
42
42
  when :change
43
43
  return [[value_left, value_right, change_key]] unless complex?
44
44
 
45
- [[value_left, value_right, change_key + ['...']],
46
- [nesting_left, nesting_right, change_key + ['{}']]]
45
+ [[value_left, value_right, change_key + [ChangeKey::ARRAY_VALUE]],
46
+ [nesting_left, nesting_right, change_key + [ChangeKey::NESTED_HASH]]]
47
47
  when :deletion
48
48
  [[value_left, NO_VALUE, change_key]]
49
49
  when :addition
@@ -54,6 +54,7 @@ module HashDeepDiff
54
54
  # original value without nested hashes
55
55
  # @return [Object]
56
56
  def value_left
57
+ return NO_VALUE if left.respond_to?(:to_hash) && right.respond_to?(:to_ary)
57
58
  return left unless left.respond_to?(:to_ary)
58
59
 
59
60
  left.reject { |el| el.respond_to?(:to_hash) }
@@ -62,6 +63,7 @@ module HashDeepDiff
62
63
  # changed value without nested hashes
63
64
  # @return [Object]
64
65
  def value_right
66
+ return NO_VALUE if right.respond_to?(:to_hash) && left.respond_to?(:to_ary)
65
67
  return right unless right.respond_to?(:to_ary)
66
68
 
67
69
  right.reject { |el| el.respond_to?(:to_hash) }
@@ -70,6 +72,7 @@ module HashDeepDiff
70
72
  # nested hashes from original value
71
73
  # @return [Array<Hash>]
72
74
  def nesting_left
75
+ return left if left.respond_to?(:to_hash)
73
76
  return NO_VALUE unless complex_left?
74
77
 
75
78
  left
@@ -80,6 +83,7 @@ module HashDeepDiff
80
83
  # nested hashes from changed value
81
84
  # @return [Array<Hash>]
82
85
  def nesting_right
86
+ return right if right.respond_to?(:to_hash)
83
87
  return NO_VALUE unless complex_right?
84
88
 
85
89
  right
@@ -5,44 +5,39 @@ module HashDeepDiff
5
5
  module Reports
6
6
  # Abstract Class
7
7
  class Base
8
+ # raw data for {#report}
9
+ def raw_report
10
+ raise AbstractMethodError
11
+ end
12
+
8
13
  # see {#to_str}
9
14
  # @return [String]
10
15
  def to_s
11
16
  to_str
12
17
  end
13
18
 
14
- # A report on additions and deletions
19
+ # see {#report}
15
20
  # @return [String]
16
21
  def to_str
17
- original + replacement
22
+ report
18
23
  end
19
24
 
20
- private
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)
29
- attr_reader :old_val, :new_val, :change_key
30
-
31
- # @param [Delta] delta diff to report
32
- def initialize(delta:)
33
- @change_key = delta.change_key.to_ary
34
- @old_val = delta.left
35
- @new_val = delta.right
36
- end
37
-
38
- # old value
39
- def original
25
+ # A report on additions and deletions
26
+ # @return [String]
27
+ def report
40
28
  raise AbstractMethodError
41
29
  end
42
30
 
43
- # new value
44
- def replacement
45
- raise AbstractMethodError
31
+ private
32
+
33
+ # @!attribute [r] diff
34
+ # @return [Array<HashDeepDiff::Delta>] set of deltas from Comparison of two objects
35
+ attr_reader :diff
36
+
37
+ # @param [Array<HashDeepDiff::Delta>] diff comparison data to report
38
+ def initialize(diff:, change_key_engine: HashDeepDiff::ChangeKey)
39
+ @diff = diff.to_ary
40
+ @change_key = change_key_engine
46
41
  end
47
42
  end
48
43
  end
@@ -7,50 +7,57 @@ module HashDeepDiff
7
7
  module Reports
8
8
  # Visual representation of the {Delta} as diff
9
9
  class Diff < Base
10
+ # additiond and deletions represented as diff
11
+ # @return [String]
12
+ def report
13
+ raw_report.map { |delta| original(delta) + replacement(delta) }.join
14
+ end
15
+
16
+ # additiond and deletions raw
17
+ # @return [Array<HashDeepDiff::Delta>]
18
+ def raw_report
19
+ diff
20
+ end
21
+
10
22
  private
11
23
 
12
24
  # line of the report with deleted value
13
25
  # @return [String]
14
- def original
15
- return '' if old_val == NO_VALUE
16
- return "#{deletion}#{path} = #{old_val}\n" unless array_to_array?
17
- return '' if array_deletion.empty?
26
+ def original(delta)
27
+ return '' if delta.left == NO_VALUE
28
+ return "#{deletion}#{delta.change_key} = #{delta.left}\n" unless array_to_array?(delta)
29
+ return '' if array_deletion(delta).empty?
18
30
 
19
- "#{deletion}#{path} = #{array_deletion}\n"
31
+ "#{deletion}#{delta.change_key} = #{array_deletion(delta)}\n"
20
32
  end
21
33
 
22
34
  # line of the report with added value
23
35
  # @return [String]
24
- def replacement
25
- return '' if new_val == NO_VALUE
26
- return "#{addition}#{path} = #{new_val}\n" unless array_to_array?
27
- return '' if array_addition.empty?
36
+ def replacement(delta)
37
+ return '' if delta.right == NO_VALUE
38
+ return "#{addition}#{delta.change_key} = #{delta.right}\n" unless array_to_array?(delta)
39
+ return '' if array_addition(delta).empty?
28
40
 
29
- "#{addition}#{path} = #{array_addition}\n"
41
+ "#{addition}#{delta.change_key} = #{array_addition(delta)}\n"
30
42
  end
31
43
 
32
44
  # returns true if original value and replacement are instances of +Array+
33
45
  # @return Bool
34
- def array_to_array?
35
- old_val.instance_of?(Array) && new_val.instance_of?(Array)
46
+ # TOFIX drop
47
+ def array_to_array?(delta)
48
+ delta.left.instance_of?(Array) && delta.right.instance_of?(Array)
36
49
  end
37
50
 
38
51
  # added elemnts of array
39
52
  # @return [Array]
40
- def array_addition
41
- new_val - old_val
53
+ def array_addition(delta)
54
+ delta.right - delta.left
42
55
  end
43
56
 
44
57
  # added elemnts of array
45
58
  # @return [Array]
46
- def array_deletion
47
- old_val - new_val
48
- end
49
-
50
- # Visual representation of keys from compared objects needed to fetch the compared values
51
- # @return [String]
52
- def path
53
- change_key.map { |key| "[#{key}]" }.join
59
+ def array_deletion(delta)
60
+ delta.left - delta.right
54
61
  end
55
62
 
56
63
  # visual indication of addition
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require 'forwardable'
5
+ require 'yaml'
6
+
7
+ module HashDeepDiff
8
+ # Different reporting enjines for {Delta}
9
+ module Reports
10
+ # Visual representation of the {Delta} as diff
11
+ class Yml < Base
12
+ extend Forwardable
13
+ def_delegators :@change_key, :initial_object, :dig_set
14
+
15
+ # additions and deletions represented as YAML
16
+ # @return [String]
17
+ def report
18
+ YAML.dump(raw_report)
19
+ end
20
+
21
+ # additions and deletiond represented as Hash
22
+ # @return [Hash]
23
+ def raw_report
24
+ @raw = { 'additions' => initial_object(values: additions), 'deletions' => initial_object(values: deletions) }
25
+
26
+ additions.each { |(change_key, addition)| change_key.set(raw['additions'], addition) }
27
+ deletions.each { |(change_key, deletion)| change_key.set(raw['deletions'], deletion) }
28
+
29
+ return raw
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :raw
35
+
36
+ # added values
37
+ # @return [Array<HashDeepDiff::Delta>]
38
+ def additions
39
+ diff.reject { |delta| delta.right == NO_VALUE }
40
+ .map { |delta| [delta.change_key, delta.addition] }
41
+ .reject { |(_, addition)| [] == addition }
42
+ end
43
+
44
+ # deleted values
45
+ # @return [Array<HashDeepDiff::Delta>]
46
+ def deletions
47
+ diff.reject { |delta| delta.left == NO_VALUE }
48
+ .map { |delta| [delta.change_key, delta.deletion] }
49
+ .reject { |(_, deletion)| [] == deletion }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module HashDeepDiff
4
4
  # Version of a gem
5
- VERSION = '0.7.0'
5
+ VERSION = '0.8.0'
6
6
  end
@@ -2,7 +2,9 @@
2
2
 
3
3
  require 'hash_deep_diff/version'
4
4
  require 'hash_deep_diff/reports/diff'
5
+ require 'hash_deep_diff/reports/yml'
5
6
  require 'hash_deep_diff/delta'
7
+ require 'hash_deep_diff/change_key'
6
8
  require 'hash_deep_diff/comparison'
7
9
 
8
10
  # Global namespace
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.7.0
4
+ version: 0.8.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-05-05 00:00:00.000000000 Z
11
+ date: 2022-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -192,6 +192,20 @@ dependencies:
192
192
  - - "~>"
193
193
  - !ruby/object:Gem::Version
194
194
  version: 0.18.0
195
+ - !ruby/object:Gem::Dependency
196
+ name: rubocop-performance
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: 1.13.3
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: 1.13.3
195
209
  - !ruby/object:Gem::Dependency
196
210
  name: rubocop-rake
197
211
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +220,20 @@ dependencies:
206
220
  - - "~>"
207
221
  - !ruby/object:Gem::Version
208
222
  version: 0.6.0
223
+ - !ruby/object:Gem::Dependency
224
+ name: rubocop-rspec
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - "~>"
228
+ - !ruby/object:Gem::Version
229
+ version: 2.10.0
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - "~>"
235
+ - !ruby/object:Gem::Version
236
+ version: 2.10.0
209
237
  description: Find the exact difference between two Hash objects
210
238
  email:
211
239
  - bohdan.pohorilets@gmail.com
@@ -237,11 +265,13 @@ files:
237
265
  - bin/yri
238
266
  - hash_deep_diff.gemspec
239
267
  - lib/hash_deep_diff.rb
268
+ - lib/hash_deep_diff/change_key.rb
240
269
  - lib/hash_deep_diff/comparison.rb
241
270
  - lib/hash_deep_diff/delta.rb
242
271
  - lib/hash_deep_diff/factories/comparison.rb
243
272
  - lib/hash_deep_diff/reports/base.rb
244
273
  - lib/hash_deep_diff/reports/diff.rb
274
+ - lib/hash_deep_diff/reports/yml.rb
245
275
  - lib/hash_deep_diff/version.rb
246
276
  homepage: https://github.com/bpohoriletz/hash_deep_diff
247
277
  licenses: