hashdiff 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +4 -0
- data/README.md +17 -13
- data/changelog.md +4 -0
- data/hashdiff.gemspec +1 -1
- data/lib/hashdiff/compare_hashes.rb +14 -1
- data/lib/hashdiff/diff.rb +3 -0
- data/lib/hashdiff/util.rb +2 -1
- data/lib/hashdiff/version.rb +1 -1
- data/spec/hashdiff/diff_spec.rb +5 -0
- data/spec/hashdiff/readme_spec.rb +15 -0
- data/spec/hashdiff/util_spec.rb +21 -0
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a9b50073f973250c144bb05272510f462dda18a7f5c9a8719156065773fdcaaa
|
4
|
+
data.tar.gz: 903439ed3ea22994d072201509506232fb77482506494bbb8c8419dc9fb8d546
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1c00d4710a67688a35197197da93ac3bfabca5f5a51edc152bfbd341a16548e762ddf2290364b7fc3a20e99cdb6d8d909ff84e766468fe623eb3977afd6d90f
|
7
|
+
data.tar.gz: 2d860929e99458cd89ee60ce877a87557d6bb9d52dcd25e0989f1a0a7b372d026bb646ee2b19498f6637a485f15fcd1e68cf1e1c0dbe666ad5af1792d60a893a
|
data/.rubocop.yml
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require: rubocop-rspec
|
2
|
+
AllCops:
|
3
|
+
TargetRubyVersion: 2.0 # always the lowest version we support
|
2
4
|
Metrics/PerceivedComplexity:
|
3
5
|
Enabled: false
|
4
6
|
Metrics/CyclomaticComplexity:
|
@@ -26,3 +28,5 @@ Style/RedundantFreeze:
|
|
26
28
|
Enabled: false
|
27
29
|
RSpec/ExampleLength:
|
28
30
|
Enabled: false
|
31
|
+
RSpec/DescribeClass:
|
32
|
+
Enabled: false
|
data/README.md
CHANGED
@@ -32,7 +32,7 @@ Hashdiff answers the question above using an opinionated approach:
|
|
32
32
|
|
33
33
|
To use the gem, add the following to your Gemfile:
|
34
34
|
|
35
|
-
```
|
35
|
+
```Ruby
|
36
36
|
gem 'hashdiff'
|
37
37
|
```
|
38
38
|
|
@@ -95,8 +95,8 @@ Hashdiff.unpatch!(b, diff).should == a
|
|
95
95
|
### Options
|
96
96
|
|
97
97
|
There are eight options available: `:delimiter`, `:similarity`,
|
98
|
-
`:strict`, `:numeric_tolerance`, `:strip`, `:case_insensitive`,
|
99
|
-
and `:use_lcs`
|
98
|
+
`:strict`, `:indifferent`, `:numeric_tolerance`, `:strip`, `:case_insensitive`,
|
99
|
+
`:array_path` and `:use_lcs`
|
100
100
|
|
101
101
|
#### `:delimiter`
|
102
102
|
|
@@ -106,7 +106,7 @@ You can specify `:delimiter` to be something other than the default dot. For exa
|
|
106
106
|
a = {a:{x:2, y:3, z:4}, b:{x:3, z:45}}
|
107
107
|
b = {a:{y:3}, b:{y:3, z:30}}
|
108
108
|
|
109
|
-
diff = Hashdiff.diff(a, b, :
|
109
|
+
diff = Hashdiff.diff(a, b, delimiter: '\t')
|
110
110
|
diff.should == [['-', 'a\tx', 2], ['-', 'a\tz', 4], ['-', 'b\tx', 3], ['~', 'b\tz', 45, 30], ['+', 'b\ty', 3]]
|
111
111
|
```
|
112
112
|
|
@@ -118,6 +118,10 @@ In cases where you have similar hash objects in arrays, you can pass a custom va
|
|
118
118
|
|
119
119
|
The `:strict` option, which defaults to `true`, specifies whether numeric types are compared on type as well as value. By default, an Integer will never be equal to a Float (e.g. 4 != 4.0). Setting `:strict` to false makes the comparison looser (e.g. 4 == 4.0).
|
120
120
|
|
121
|
+
#### `:indifferent`
|
122
|
+
|
123
|
+
The `:indifferent` option, which defaults to `false`, specifies whether to treat hash keys indifferently. Setting `:indifferent` to true has the effect of ignoring differences between symbol keys (ie. {a: 1} ~= {'a' => 1})
|
124
|
+
|
121
125
|
#### `:numeric_tolerance`
|
122
126
|
|
123
127
|
The :numeric_tolerance option allows for a small numeric tolerance.
|
@@ -126,7 +130,7 @@ The :numeric_tolerance option allows for a small numeric tolerance.
|
|
126
130
|
a = {x:5, y:3.75, z:7}
|
127
131
|
b = {x:6, y:3.76, z:7}
|
128
132
|
|
129
|
-
diff = Hashdiff.diff(a, b, :
|
133
|
+
diff = Hashdiff.diff(a, b, numeric_tolerance: 0.1)
|
130
134
|
diff.should == [["~", "x", 5, 6]]
|
131
135
|
```
|
132
136
|
|
@@ -138,7 +142,7 @@ The :strip option strips all strings before comparing.
|
|
138
142
|
a = {x:5, s:'foo '}
|
139
143
|
b = {x:6, s:'foo'}
|
140
144
|
|
141
|
-
diff = Hashdiff.diff(a, b, :
|
145
|
+
diff = Hashdiff.diff(a, b, numeric_tolerance: 0.1, strip: true)
|
142
146
|
diff.should == [["~", "x", 5, 6]]
|
143
147
|
```
|
144
148
|
|
@@ -150,7 +154,7 @@ The :case_insensitive option makes string comparisons ignore case.
|
|
150
154
|
a = {x:5, s:'FooBar'}
|
151
155
|
b = {x:6, s:'foobar'}
|
152
156
|
|
153
|
-
diff = Hashdiff.diff(a, b, :
|
157
|
+
diff = Hashdiff.diff(a, b, numeric_tolerance: 0.1, case_insensitive: true)
|
154
158
|
diff.should == [["~", "x", 5, 6]]
|
155
159
|
```
|
156
160
|
|
@@ -164,7 +168,7 @@ is useful for `patch!` when used on hashes without string keys.
|
|
164
168
|
a = {x:5}
|
165
169
|
b = {'x'=>6}
|
166
170
|
|
167
|
-
diff = Hashdiff.diff(a, b, :
|
171
|
+
diff = Hashdiff.diff(a, b, array_path: true)
|
168
172
|
diff.should == [['-', [:x], 5], ['+', ['x'], 6]]
|
169
173
|
```
|
170
174
|
|
@@ -173,7 +177,7 @@ For cases where there are arrays in paths their index will be added to the path.
|
|
173
177
|
a = {x:[0,1]}
|
174
178
|
b = {x:[0,2]}
|
175
179
|
|
176
|
-
diff = Hashdiff.diff(a, b, :
|
180
|
+
diff = Hashdiff.diff(a, b, array_path: true)
|
177
181
|
diff.should == [["-", [:x, 1], 1], ["+", [:x, 1], 2]]
|
178
182
|
```
|
179
183
|
|
@@ -183,7 +187,7 @@ This shouldn't cause problems if you are comparing an array with a hash:
|
|
183
187
|
a = {x:{0=>1}}
|
184
188
|
b = {x:[1]}
|
185
189
|
|
186
|
-
diff = Hashdiff.diff(a, b, :
|
190
|
+
diff = Hashdiff.diff(a, b, array_path: true)
|
187
191
|
diff.should == [["~", [:x], {0=>1}, [1]]]
|
188
192
|
```
|
189
193
|
|
@@ -205,7 +209,7 @@ Note, currently the :similarity option has no effect when :use_lcs is false.
|
|
205
209
|
a = {x: [0, 1, 2]}
|
206
210
|
b = {x: [0, 2, 2, 3]}
|
207
211
|
|
208
|
-
diff = Hashdiff.diff(a, b, :
|
212
|
+
diff = Hashdiff.diff(a, b, use_lcs: false)
|
209
213
|
diff.should == [["~", "x[1]", 1, 2], ["+", "x[3]", 3]]
|
210
214
|
```
|
211
215
|
|
@@ -255,11 +259,11 @@ An order difference alone between two arrays can create too many diffs to be use
|
|
255
259
|
a = {a:'car', b:['boat', 'plane'] }
|
256
260
|
b = {a:'car', b:['plane', 'boat'] }
|
257
261
|
|
258
|
-
Hashdiff.diff(a, b)
|
262
|
+
Hashdiff.diff(a, b).should == [["+", "b[0]", "plane"], ["-", "b[2]", "plane"]]
|
259
263
|
|
260
264
|
b[:b].sort!
|
261
265
|
|
262
|
-
Hashdiff.diff(a, b)
|
266
|
+
Hashdiff.diff(a, b).should == []
|
263
267
|
```
|
264
268
|
|
265
269
|
## Maintainers
|
data/changelog.md
CHANGED
data/hashdiff.gemspec
CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
|
24
24
|
s.add_development_dependency('bluecloth')
|
25
25
|
s.add_development_dependency('rspec', '~> 2.0')
|
26
|
-
s.add_development_dependency('rubocop')
|
26
|
+
s.add_development_dependency('rubocop', '~> 0.49.1') # last version that works with ruby 2.0
|
27
27
|
s.add_development_dependency('rubocop-rspec')
|
28
28
|
s.add_development_dependency('yard')
|
29
29
|
|
@@ -10,6 +10,15 @@ module Hashdiff
|
|
10
10
|
|
11
11
|
obj1_keys = obj1.keys
|
12
12
|
obj2_keys = obj2.keys
|
13
|
+
obj1_lookup = {}
|
14
|
+
obj2_lookup = {}
|
15
|
+
|
16
|
+
if opts[:indifferent]
|
17
|
+
obj1_lookup = obj1_keys.each_with_object({}) { |k, h| h[k.to_s] = k }
|
18
|
+
obj2_lookup = obj2_keys.each_with_object({}) { |k, h| h[k.to_s] = k }
|
19
|
+
obj1_keys = obj1_keys.map { |k| k.is_a?(Symbol) ? k.to_s : k }
|
20
|
+
obj2_keys = obj2_keys.map { |k| k.is_a?(Symbol) ? k.to_s : k }
|
21
|
+
end
|
13
22
|
|
14
23
|
added_keys = (obj2_keys - obj1_keys).sort_by(&:to_s)
|
15
24
|
common_keys = (obj1_keys & obj2_keys).sort_by(&:to_s)
|
@@ -19,6 +28,7 @@ module Hashdiff
|
|
19
28
|
|
20
29
|
# add deleted properties
|
21
30
|
deleted_keys.each do |k|
|
31
|
+
k = opts[:indifferent] ? obj1_lookup[k] : k
|
22
32
|
change_key = Hashdiff.prefix_append_key(opts[:prefix], k, opts)
|
23
33
|
custom_result = Hashdiff.custom_compare(opts[:comparison], change_key, obj1[k], nil)
|
24
34
|
|
@@ -33,13 +43,16 @@ module Hashdiff
|
|
33
43
|
common_keys.each do |k|
|
34
44
|
prefix = Hashdiff.prefix_append_key(opts[:prefix], k, opts)
|
35
45
|
|
36
|
-
|
46
|
+
k1 = opts[:indifferent] ? obj1_lookup[k] : k
|
47
|
+
k2 = opts[:indifferent] ? obj2_lookup[k] : k
|
48
|
+
result.concat(Hashdiff.diff(obj1[k1], obj2[k2], opts.merge(prefix: prefix)))
|
37
49
|
end
|
38
50
|
|
39
51
|
# added properties
|
40
52
|
added_keys.each do |k|
|
41
53
|
change_key = Hashdiff.prefix_append_key(opts[:prefix], k, opts)
|
42
54
|
|
55
|
+
k = opts[:indifferent] ? obj2_lookup[k] : k
|
43
56
|
custom_result = Hashdiff.custom_compare(opts[:comparison], change_key, nil, obj2[k])
|
44
57
|
|
45
58
|
if custom_result
|
data/lib/hashdiff/diff.rb
CHANGED
@@ -9,6 +9,7 @@ module Hashdiff
|
|
9
9
|
# @param [Array, Hash] obj2
|
10
10
|
# @param [Hash] options the options to use when comparing
|
11
11
|
# * :strict (Boolean) [true] whether numeric values will be compared on type as well as value. Set to false to allow comparing Integer, Float, BigDecimal to each other
|
12
|
+
# * :indifferent (Boolean) [false] whether to treat hash keys indifferently. Set to true to ignore differences between symbol keys (ie. {a: 1} ~= {'a' => 1})
|
12
13
|
# * :delimiter (String) ['.'] the delimiter used when returning nested key references
|
13
14
|
# * :numeric_tolerance (Numeric) [0] should be a positive numeric value. Value by which numeric differences must be greater than. By default, numeric values are compared exactly; with the :tolerance option, the difference between numeric values must be greater than the given value.
|
14
15
|
# * :strip (Boolean) [false] whether or not to call #strip on strings before comparing
|
@@ -52,6 +53,7 @@ module Hashdiff
|
|
52
53
|
# @param [Array, Hash] obj2
|
53
54
|
# @param [Hash] options the options to use when comparing
|
54
55
|
# * :strict (Boolean) [true] whether numeric values will be compared on type as well as value. Set to false to allow comparing Integer, Float, BigDecimal to each other
|
56
|
+
# * :indifferent (Boolean) [false] whether to treat hash keys indifferently. Set to true to ignore differences between symbol keys (ie. {a: 1} ~= {'a' => 1})
|
55
57
|
# * :similarity (Numeric) [0.8] should be between (0, 1]. Meaningful if there are similar hashes in arrays. See {best_diff}.
|
56
58
|
# * :delimiter (String) ['.'] the delimiter used when returning nested key references
|
57
59
|
# * :numeric_tolerance (Numeric) [0] should be a positive numeric value. Value by which numeric differences must be greater than. By default, numeric values are compared exactly; with the :tolerance option, the difference between numeric values must be greater than the given value.
|
@@ -79,6 +81,7 @@ module Hashdiff
|
|
79
81
|
similarity: 0.8,
|
80
82
|
delimiter: '.',
|
81
83
|
strict: true,
|
84
|
+
indifferent: false,
|
82
85
|
strip: false,
|
83
86
|
numeric_tolerance: 0,
|
84
87
|
array_path: false,
|
data/lib/hashdiff/util.rb
CHANGED
@@ -16,7 +16,7 @@ module Hashdiff
|
|
16
16
|
|
17
17
|
diffs = count_diff diff(obja, objb, opts)
|
18
18
|
|
19
|
-
(1 - diffs
|
19
|
+
(1 - diffs / (count_a + count_b).to_f) >= opts[:similarity]
|
20
20
|
end
|
21
21
|
|
22
22
|
# @private
|
@@ -107,6 +107,7 @@ module Hashdiff
|
|
107
107
|
# check if objects are comparable
|
108
108
|
def self.comparable?(obj1, obj2, strict = true)
|
109
109
|
return true if (obj1.is_a?(Array) || obj1.is_a?(Hash)) && obj2.is_a?(obj1.class)
|
110
|
+
return true if (obj2.is_a?(Array) || obj2.is_a?(Hash)) && obj1.is_a?(obj2.class)
|
110
111
|
return true if !strict && obj1.is_a?(Numeric) && obj2.is_a?(Numeric)
|
111
112
|
|
112
113
|
obj1.is_a?(obj2.class) && obj2.is_a?(obj1.class)
|
data/lib/hashdiff/version.rb
CHANGED
data/spec/hashdiff/diff_spec.rb
CHANGED
@@ -49,6 +49,11 @@ describe Hashdiff do
|
|
49
49
|
diff.should == []
|
50
50
|
end
|
51
51
|
|
52
|
+
it 'ignores string vs symbol differences, when indifferent is true' do
|
53
|
+
diff = described_class.diff({ 'a' => 2, :b => 2 }, { :a => 2, 'b' => 2, :c => 3 }, indifferent: true)
|
54
|
+
diff.should == [['+', 'c', 3]]
|
55
|
+
end
|
56
|
+
|
52
57
|
it 'is able to diff changes in hash value' do
|
53
58
|
diff = described_class.diff({ 'a' => 2, 'b' => 3, 'c' => ' hello' }, 'a' => 2, 'b' => 4, 'c' => 'hello')
|
54
59
|
diff.should == [['~', 'b', 3, 4], ['~', 'c', ' hello', 'hello']]
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe 'README.md' do
|
6
|
+
it 'has correct examples' do
|
7
|
+
File.read('README.md').scan(/```ruby(.*?)```/m).flatten(1).each do |block|
|
8
|
+
begin
|
9
|
+
eval block # rubocop:disable Security/Eval
|
10
|
+
rescue Exception => e # rubocop:disable Lint/RescueException
|
11
|
+
raise "README.md code block:\n#{block}\n\nhas error:\n#{e}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/spec/hashdiff/util_spec.rb
CHANGED
@@ -68,6 +68,7 @@ describe Hashdiff do
|
|
68
68
|
it 'compares different objects without tolerance' do
|
69
69
|
expect(described_class.compare_values('hats', 'ninjas')).to be false
|
70
70
|
end
|
71
|
+
|
71
72
|
it 'compares other objects with tolerance' do
|
72
73
|
expect(described_class.compare_values('hats', 'ninjas', numeric_tolerance: 0.01)).to be false
|
73
74
|
end
|
@@ -92,4 +93,24 @@ describe Hashdiff do
|
|
92
93
|
expect(described_class.compare_values('horse', 'Horse', case_insensitive: true)).to be true
|
93
94
|
end
|
94
95
|
end
|
96
|
+
|
97
|
+
describe '.comparable?' do
|
98
|
+
it 'identifies hashes as comparable' do
|
99
|
+
expect(described_class.comparable?({}, {})).to be true
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'identifies a subclass of Hash to be comparable with a Hash' do
|
103
|
+
other = Class.new(Hash)
|
104
|
+
expect(described_class.comparable?(other.new, {})).to be true
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'identifies a Hash to be comparable with a subclass of Hash' do
|
108
|
+
other = Class.new(Hash)
|
109
|
+
expect(described_class.comparable?({}, other.new)).to be true
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'does not identify a Numeric as comparable with a Hash' do
|
113
|
+
expect(described_class.comparable?(1, {})).to be false
|
114
|
+
end
|
115
|
+
end
|
95
116
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hashdiff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Liu Fengyun
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bluecloth
|
@@ -42,16 +42,16 @@ dependencies:
|
|
42
42
|
name: rubocop
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.49.1
|
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:
|
54
|
+
version: 0.49.1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rubocop-rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,6 +114,7 @@ files:
|
|
114
114
|
- spec/hashdiff/lcs_spec.rb
|
115
115
|
- spec/hashdiff/linear_compare_array_spec.rb
|
116
116
|
- spec/hashdiff/patch_spec.rb
|
117
|
+
- spec/hashdiff/readme_spec.rb
|
117
118
|
- spec/hashdiff/util_spec.rb
|
118
119
|
- spec/spec_helper.rb
|
119
120
|
homepage: https://github.com/liufengyun/hashdiff
|
@@ -140,8 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
141
|
- !ruby/object:Gem::Version
|
141
142
|
version: '0'
|
142
143
|
requirements: []
|
143
|
-
|
144
|
-
rubygems_version: 2.5.2.3
|
144
|
+
rubygems_version: 3.0.6
|
145
145
|
signing_key:
|
146
146
|
specification_version: 4
|
147
147
|
summary: Hashdiff is a diff lib to compute the smallest difference between two hashes.
|