hash_deep_diff 0.2.0 → 0.3.2

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: 2ba7f377f6c2d53dd22518acfe0a285ed4d4614f9bf03c1c32f1775358a01188
4
- data.tar.gz: 827fbbd1133ff72fd11475d0c5001894c27a6e71c125ccc5da891c159538beae
3
+ metadata.gz: 184fe8aaacd6b6d10635f8ff8f0986381c8e2d907b14a80d86050aea75896136
4
+ data.tar.gz: e4e33cc8c4ce40d84b0fbbe78fa380d1d62f65805f3ce5dc01e2049100db5b16
5
5
  SHA512:
6
- metadata.gz: 12eb5aa92de1f7c6d43473bf6858278ffafca368537e7e9f4fd9d2dd945493c4760c9daaee03724e5699c7acd067b06e6bf3162beb38923fe9f218addbae2742
7
- data.tar.gz: f61820a61aeac40a63431a392a3352f79ae278016b2e0238e16b690f8c0a72ea26fbc0c1351face859a71683e61dab720c0cccc55953eac6759101dc4822edc3
6
+ metadata.gz: b299b5217a613175c47e5d56da723e0691cf7e2344abc9bbf9fab9525c96c776616971636365f0c2067f05ec8d22c205c98d896df4b1b17ee73f3541bb4e5736
7
+ data.tar.gz: 159ffbeb7cdef03c130ed356f965379db05ebf8adec8b7e5186e1a45b4cbcc68489ce17b92a7e2bc38e58cdd2fafcb31c573703f566f348b0efdc0ef8c504d4e
data/Guardfile CHANGED
@@ -26,6 +26,8 @@ end
26
26
  guard :minitest do
27
27
  # with Minitest::Unit
28
28
  watch(%r{^test/(.*)/?test_(.*)\.rb$})
29
- watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
29
+ # watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
30
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { 'test' }
30
31
  watch(%r{^test/test_helper\.rb$}) { 'test' }
32
+ watch(%r{^test/support/.*$}) { 'test' }
31
33
  end
data/README.md CHANGED
@@ -1,10 +1,3 @@
1
- Ideas
2
- - if not equal use sets to diff keys
3
- - use to_ary to_hash for values to find if further recursion needed
4
- - use to_a.flatten to figure out if any unacceptable classes present
5
- - do not use respond_to?(:each) in first version
6
-
7
-
8
1
  # HashDeepDiff
9
2
 
10
3
  Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/hash_deep_diff`. To experiment with that code, run `bin/console` for an interactive prompt.
@@ -39,14 +39,15 @@ Gem::Specification.new do |spec|
39
39
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
40
40
  spec.require_paths = ['lib']
41
41
 
42
- spec.add_development_dependency 'bundler'
43
- spec.add_development_dependency 'guard'
44
- spec.add_development_dependency 'guard-minitest'
45
- spec.add_development_dependency 'guard-rubocop'
46
- spec.add_development_dependency 'minitest'
47
- spec.add_development_dependency 'minitest-reporters'
48
- spec.add_development_dependency 'rake'
49
- spec.add_development_dependency 'rubocop'
50
- spec.add_development_dependency 'rubocop-minitest'
51
- spec.add_development_dependency 'rubocop-rake'
42
+ spec.add_development_dependency 'bundler', '~> 1.17.2'
43
+ spec.add_development_dependency 'guard', '~> 2.18.0'
44
+ spec.add_development_dependency 'guard-minitest', '~> 2.4.6'
45
+ spec.add_development_dependency 'guard-rubocop', '~> 1.5.0'
46
+ spec.add_development_dependency 'minitest', '~> 5.15.0'
47
+ spec.add_development_dependency 'minitest-focus', '~> 1.3.1'
48
+ spec.add_development_dependency 'minitest-reporters', '~> 1.5.0'
49
+ spec.add_development_dependency 'rake', '~> 10.5.0'
50
+ spec.add_development_dependency 'rubocop', '~> 1.26.1'
51
+ spec.add_development_dependency 'rubocop-minitest', '~> 0.18.0'
52
+ spec.add_development_dependency 'rubocop-rake', '~> 0.6.0'
52
53
  end
@@ -1,77 +1,62 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'delta/left'
4
+ require_relative 'delta/inner'
5
+ require_relative 'delta/right'
6
+
3
7
  # :nodoc:
4
8
  module HashDeepDiff
5
9
  # :nodoc:
6
10
  class Comparison
7
- attr_reader :left, :right
11
+ attr_reader :left, :right, :path
8
12
 
9
13
  def diff(&block)
10
- return [{}, {}, {}] if left == right # this is order-sensitive comparison
11
- return [left, {}, {}] if right.empty?
12
- return [{}, {}, right] if left.empty?
13
- return first_level_delta(&block) if one_level_deep?
14
+ left_delta + deep_delta(&block) + right_delta
15
+ end
14
16
 
15
- return [left_delta, deep_delta(&block), right_delta]
17
+ def report
18
+ diff.join("\n")
16
19
  end
17
20
 
18
21
  private
19
22
 
20
- def initialize(left, right)
23
+ def initialize(left, right, path = [])
21
24
  @left = left.to_hash
22
25
  @right = right.to_hash
26
+ @path = path.to_ary
23
27
  end
24
28
 
25
29
  def deep_delta(&block)
26
- result = delta(&block)
27
- result.keys.each_with_object({}) do |key, memo|
28
- memo[key] = result[key] && next unless left[key].instance_of?(Hash)
29
-
30
- memo[key] = self.class.new(left[key], right[key]).diff
30
+ delta(&block).flat_map do |diff|
31
+ if diff.complex?
32
+ self.class.new(diff.left, diff.right, diff.path).diff
33
+ else
34
+ diff
35
+ end
31
36
  end
32
37
  end
33
38
 
34
- def first_level_delta(&block)
35
- [
36
- left_delta,
37
- delta(&block),
38
- right_delta
39
- ]
39
+ def left_delta
40
+ left_diff_keys.map { |key| Delta::Left.new(path: path + [key], value: left[key]) }
40
41
  end
41
42
 
42
43
  def right_delta
43
- right_diff_keys.each_with_object({}) do |key, memo|
44
- memo[key] = right[key]
45
- end
44
+ right_diff_keys.map { |key| Delta::Right.new(path: path + [key], value: right[key]) }
46
45
  end
47
46
 
48
47
  def delta(&block)
49
48
  block ||= ->(val) { val }
50
49
 
51
- common_keys.each_with_object({}) do |key, memo|
50
+ common_keys.each_with_object([]) do |key, memo|
52
51
  value_left = block.call(left[key])
53
52
  value_right = block.call(right[key])
54
53
 
55
54
  next if value_right.instance_of?(value_left.class) && (value_right == value_left)
56
55
 
57
- memo[key] = {
58
- left: value_left,
59
- right: value_right
60
- }
56
+ memo << Delta::Inner.new(path: path + [key], value: { left: value_left, right: value_right })
61
57
  end
62
58
  end
63
59
 
64
- def left_delta
65
- left_diff_keys.each_with_object({}) do |key, memo|
66
- memo[key] = left[key]
67
- end
68
- end
69
-
70
- def one_level_deep?
71
- left.values.none? { |value| value.respond_to?(:to_hash) } &&
72
- right.values.none? { |value| value.respond_to?(:to_hash) }
73
- end
74
-
75
60
  def common_keys
76
61
  left.keys & right.keys
77
62
  end
@@ -0,0 +1,66 @@
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
+ def path
27
+ @prefix + [@delta.keys.first]
28
+ end
29
+
30
+ def to_h
31
+ @delta
32
+ end
33
+
34
+ def to_hash
35
+ @delta
36
+ end
37
+
38
+ def to_s
39
+ to_str
40
+ end
41
+
42
+ def to_str
43
+ raise NoMethodError, "expected #{self.class} to implement #to_str"
44
+ end
45
+
46
+ def complex?
47
+ raise NoMethodError, "expected #{self.class} to implement #complex?"
48
+ end
49
+ end
50
+
51
+ # Override #initialize method
52
+ module Initialize
53
+ def initialize(path:, value:)
54
+ # TOFIX this may prohibit usage of hashes with Array keys
55
+ if path.respond_to?(:to_ary)
56
+ @delta = { path[-1] => value }
57
+ @prefix = path[0..-2]
58
+ else
59
+ @delta = { path => value }
60
+ @prefix = []
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,52 @@
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
+ if @delta.values.first.respond_to?(:to_hash)
15
+ if @delta.values.first.keys == %i[left right]
16
+ if complex?
17
+ HashDeepDiff::Comparison.new(left, right, path).report
18
+ else
19
+ lines = <<~Q
20
+ -left#{diff_prefix} = #{left}
21
+ +right#{diff_prefix} = #{right}
22
+ Q
23
+ lines.strip
24
+ end
25
+ else
26
+ @delta.values.first.keys.map do |key|
27
+ self.class.new(path: path + [key], value: @delta.values.first[key])
28
+ end.join("\n").strip
29
+ end
30
+ else
31
+ lines = <<~Q
32
+ -left#{diff_prefix} = #{left}
33
+ +right#{diff_prefix} = #{right}
34
+ Q
35
+ lines.strip
36
+ end
37
+ end
38
+
39
+ def complex?
40
+ left.respond_to?(:to_hash) && right.respond_to?(:to_hash)
41
+ end
42
+
43
+ def left
44
+ @delta.values.first[:left]
45
+ end
46
+
47
+ def right
48
+ @delta.values.first[:right]
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,24 @@
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
+ if @delta.values.first.respond_to?(:to_hash)
15
+ @delta.values.first.keys.map do |key|
16
+ self.class.new(path: path + [key], value: @delta.values.first[key])
17
+ end.join("\n").strip
18
+ else
19
+ "+left#{diff_prefix} = #{@delta.values.first}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
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
+ if @delta.values.first.respond_to?(:to_hash)
15
+ @delta.values.first.keys.map do |key|
16
+ self.class.new(path: path + [key], value: @delta.values.first[key])
17
+ end.join("\n").strip
18
+ else
19
+ "-left#{diff_prefix} = #{@delta.values.first}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HashDeepDiff
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.2'
5
5
  end
@@ -5,5 +5,4 @@ require 'hash_deep_diff/comparison'
5
5
 
6
6
  module HashDeepDiff
7
7
  class Error < StandardError; end
8
- # Your code goes here...
9
8
  end
metadata CHANGED
@@ -1,155 +1,169 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hash_deep_diff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.2
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-01 00:00:00.000000000 Z
11
+ date: 2022-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 1.17.2
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 1.17.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: guard
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 2.18.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 2.18.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: guard-minitest
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: 2.4.6
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: 2.4.6
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: guard-rubocop
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: 1.5.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: 1.5.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: minitest
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ">="
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0'
75
+ version: 5.15.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0'
82
+ version: 5.15.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest-focus
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.3.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.3.1
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: minitest-reporters
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
- - - ">="
101
+ - - "~>"
88
102
  - !ruby/object:Gem::Version
89
- version: '0'
103
+ version: 1.5.0
90
104
  type: :development
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
- - - ">="
108
+ - - "~>"
95
109
  - !ruby/object:Gem::Version
96
- version: '0'
110
+ version: 1.5.0
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: rake
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
- - - ">="
115
+ - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: '0'
117
+ version: 10.5.0
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
- - - ">="
122
+ - - "~>"
109
123
  - !ruby/object:Gem::Version
110
- version: '0'
124
+ version: 10.5.0
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rubocop
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
- - - ">="
129
+ - - "~>"
116
130
  - !ruby/object:Gem::Version
117
- version: '0'
131
+ version: 1.26.1
118
132
  type: :development
119
133
  prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
135
  requirements:
122
- - - ">="
136
+ - - "~>"
123
137
  - !ruby/object:Gem::Version
124
- version: '0'
138
+ version: 1.26.1
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: rubocop-minitest
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
- - - ">="
143
+ - - "~>"
130
144
  - !ruby/object:Gem::Version
131
- version: '0'
145
+ version: 0.18.0
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
- - - ">="
150
+ - - "~>"
137
151
  - !ruby/object:Gem::Version
138
- version: '0'
152
+ version: 0.18.0
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: rubocop-rake
141
155
  requirement: !ruby/object:Gem::Requirement
142
156
  requirements:
143
- - - ">="
157
+ - - "~>"
144
158
  - !ruby/object:Gem::Version
145
- version: '0'
159
+ version: 0.6.0
146
160
  type: :development
147
161
  prerelease: false
148
162
  version_requirements: !ruby/object:Gem::Requirement
149
163
  requirements:
150
- - - ">="
164
+ - - "~>"
151
165
  - !ruby/object:Gem::Version
152
- version: '0'
166
+ version: 0.6.0
153
167
  description: Find the exact difference between two Hash objects
154
168
  email:
155
169
  - bohdan.pohorilets@gmail.com
@@ -175,6 +189,10 @@ files:
175
189
  - hash_deep_diff.gemspec
176
190
  - lib/hash_deep_diff.rb
177
191
  - 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
178
196
  - lib/hash_deep_diff/version.rb
179
197
  homepage: https://github.com/bpohoriletz/hash_deep_diff
180
198
  licenses: