hashdiff 1.1.2 → 1.2.1
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 +4 -4
- data/.github/workflows/ci.yml +2 -3
- data/Gemfile +1 -1
- data/README.md +26 -1
- data/changelog.md +31 -23
- data/lib/hashdiff/compare_hashes.rb +56 -30
- data/lib/hashdiff/diff.rb +4 -1
- data/lib/hashdiff/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99e198ac76f687f33cb2537c32bd81ccdd45798502cba565a624d1107a7e85ca
|
4
|
+
data.tar.gz: 7eba9f454570311e7b784d3da51db6827331f6e9c73d98d113b407d57bc60626
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8bf11d2563db1d99348a17bd6074e51cfd35dc95968bf43eca5f115b7af7547c5a08457b031b880442e4d8111343d3e096c8d5075c83a63c858fcd8627d4140
|
7
|
+
data.tar.gz: 6b318cd78d7f8dac01d1fea139fbc81d4724dc2c9a7f4cb0b21d5c54b38da5384908e6ba91b40503d7c501d596d4f91ecaf348122679f62fb91f0a2dc6e8e30a
|
data/.github/workflows/ci.yml
CHANGED
@@ -11,7 +11,7 @@ jobs:
|
|
11
11
|
matrix:
|
12
12
|
ruby:
|
13
13
|
- 2.7
|
14
|
-
- 3.0
|
14
|
+
- '3.0'
|
15
15
|
- 3.1
|
16
16
|
- 3.2
|
17
17
|
- 3.3
|
@@ -22,7 +22,6 @@ jobs:
|
|
22
22
|
uses: ruby/setup-ruby@v1
|
23
23
|
with:
|
24
24
|
ruby-version: ${{ matrix.ruby }}
|
25
|
-
|
26
|
-
run: bundle install --jobs $(nproc) --retry 3
|
25
|
+
bundler-cache: true # 'bundle install' and cache gems
|
27
26
|
- name: Run rake
|
28
27
|
run: bundle exec rake
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -95,7 +95,8 @@ Hashdiff.unpatch!(b, diff).should == a
|
|
95
95
|
### Options
|
96
96
|
|
97
97
|
The following options are available: `:delimiter`, `:similarity`, `:strict`, `:ignore_keys`,
|
98
|
-
`:indifferent`, `:numeric_tolerance`, `:strip`, `:case_insensitive`, `:array_path
|
98
|
+
`:indifferent`, `:numeric_tolerance`, `:strip`, `:case_insensitive`, `:array_path`,
|
99
|
+
`:use_lcs`, and `:preserve_key_order`
|
99
100
|
|
100
101
|
#### `:delimiter`
|
101
102
|
|
@@ -235,6 +236,30 @@ diff = Hashdiff.diff(a, b, use_lcs: false)
|
|
235
236
|
diff.should == [["~", "x[1]", 1, 2], ["+", "x[3]", 3]]
|
236
237
|
```
|
237
238
|
|
239
|
+
#### `:preserve_key_order`
|
240
|
+
|
241
|
+
By default, the change set is ordered by operation type: deletions (-) first, then updates (~), and finally additions (+).
|
242
|
+
Within each operation group, keys are sorted alphabetically:
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
a = {d: 1, c: 1, a: 1}
|
246
|
+
b = {d: 2, b: 2, a: 2}
|
247
|
+
|
248
|
+
diff = Hashdiff.diff(a, b)
|
249
|
+
diff.should == [["-", "c", 1], ["~", "a", 1, 2], ["~", "d", 1, 2], ["+", "b", 2]]
|
250
|
+
```
|
251
|
+
|
252
|
+
Setting :preserve_key_order to true processes keys in the order they appear in the first hash.
|
253
|
+
Keys that only exist in the second hash are appended in their original order:
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
a = {d: 1, c: 1, a: 1}
|
257
|
+
b = {d: 2, b: 2, a: 2}
|
258
|
+
|
259
|
+
diff = Hashdiff.diff(a, b, preserve_key_order: true)
|
260
|
+
diff.should == [["~", "d", 1, 2], ["-", "c", 1], ["~", "a", 1, 2], ["+", "b", 2]]
|
261
|
+
```
|
262
|
+
|
238
263
|
#### Specifying a custom comparison method
|
239
264
|
|
240
265
|
It's possible to specify how the values of a key should be compared.
|
data/changelog.md
CHANGED
@@ -1,19 +1,27 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## v1.2.1 2025-09-06
|
4
|
+
|
5
|
+
* Use HTTPS for the source in the Gemfile [#101](https://github.com/liufengyun/hashdiff/issues/101) ([@krzysiek1507](https://github.com/krzysiek1507))
|
6
|
+
|
7
|
+
## v1.2.0 2025-5-20
|
8
|
+
|
9
|
+
* Added :preserve_key_order option to maintain original hash key order [#99](https://github.com/liufengyun/hashdiff/issues/99) ([@robkiessling](https://github.com/robkiessling))
|
10
|
+
|
3
11
|
## v1.1.2 2024-11-12
|
4
12
|
|
5
|
-
* Fix bundler cache #96 (@olleolleolle)
|
6
|
-
* Quote the '3.0' in YAML #95 (@olleolleolle)
|
7
|
-
* Fix version in source code #97 (@liufengyun)
|
13
|
+
* Fix bundler cache [#96](https://github.com/liufengyun/hashdiff/issues/96) ([@olleolleolle](https://github.com/olleolleolle))
|
14
|
+
* Quote the '3.0' in YAML [#95](https://github.com/liufengyun/hashdiff/issues/95) ([@olleolleolle](https://github.com/olleolleolle))
|
15
|
+
* Fix version in source code [#97](https://github.com/liufengyun/hashdiff/issues/97) ([@liufengyun](https://github.com/liufengyun))
|
8
16
|
|
9
17
|
## v1.1.1 2024-08-02
|
10
18
|
|
11
|
-
* Fix bug in ignore_keys option #88 (@Matzfan)
|
12
|
-
* Exclude spec files from gem package #94 (@amatsuda)
|
19
|
+
* Fix bug in ignore_keys option [#88](https://github.com/liufengyun/hashdiff/issues/88) ([@Matzfan](https://github.com/Matzfan))
|
20
|
+
* Exclude spec files from gem package [#94](https://github.com/liufengyun/hashdiff/issues/94) ([@amatsuda](https://github.com/amatsuda))
|
13
21
|
|
14
22
|
## v1.1.0 2023-12-14
|
15
23
|
|
16
|
-
* Add ignore_keys option (#86 @Matzfan)
|
24
|
+
* Add ignore_keys option ([#86](https://github.com/liufengyun/hashdiff/issues/86) [@Matzfan](https://github.com/Matzfan))
|
17
25
|
* Remove pinned version of rake < 11
|
18
26
|
* Bump rspec dep ~> 3.5
|
19
27
|
* Bump rubocop dep >= 1.52.1
|
@@ -25,52 +33,52 @@
|
|
25
33
|
|
26
34
|
## v1.0.0 2019-06-06
|
27
35
|
|
28
|
-
* Fix typo in readme (#72 @koic)
|
29
|
-
* Fix Rubocop offence (#73 @koic)
|
30
|
-
* Bumps version to v1.0.0 (#74 @jfelchner)
|
36
|
+
* Fix typo in readme ([#72](https://github.com/liufengyun/hashdiff/issues/72) [@koic](https://github.com/koic))
|
37
|
+
* Fix Rubocop offence ([#73](https://github.com/liufengyun/hashdiff/issues/73) [@koic](https://github.com/koic))
|
38
|
+
* Bumps version to v1.0.0 ([#74](https://github.com/liufengyun/hashdiff/issues/74) [@jfelchner](https://github.com/jfelchner))
|
31
39
|
|
32
40
|
## v1.0.0.beta1 2019-06-06
|
33
41
|
|
34
|
-
* fix warnings in ci (#69 @y-yagi)
|
35
|
-
* drop warnings of the constant change (#70 @jfelchner)
|
42
|
+
* fix warnings in ci ([#69](https://github.com/liufengyun/hashdiff/issues/69) [@y-yagi](https://github.com/y-yagi))
|
43
|
+
* drop warnings of the constant change ([#70](https://github.com/liufengyun/hashdiff/issues/70) [@jfelchner](https://github.com/jfelchner))
|
36
44
|
|
37
45
|
## v0.4.0 2019-05-28
|
38
46
|
|
39
|
-
* refactoring (#56 #57 #59 #61 krzysiek1507)
|
40
|
-
* fix typo in README (#64 @pboling)
|
41
|
-
* change HashDiff to Hashdiff (#65 @jfelchner)
|
47
|
+
* refactoring ([#56](https://github.com/liufengyun/hashdiff/issues/56) [#57](https://github.com/liufengyun/hashdiff/issues/57) [#59](https://github.com/liufengyun/hashdiff/issues/59) [#61](https://github.com/liufengyun/hashdiff/issues/61) [@krzysiek1507](https://github.com/krzysiek1507))
|
48
|
+
* fix typo in README ([#64](https://github.com/liufengyun/hashdiff/issues/64) [@pboling](https://github.com/pboling))
|
49
|
+
* change HashDiff to Hashdiff ([#65](https://github.com/liufengyun/hashdiff/issues/65) [@jfelchner](https://github.com/jfelchner))
|
42
50
|
|
43
51
|
## v0.3.9 2019-04-22
|
44
52
|
|
45
|
-
* Performance tweak (thanks @krzysiek1507: #51 #52 #53)
|
53
|
+
* Performance tweak (thanks [@krzysiek1507](https://github.com/krzysiek1507): [#51](https://github.com/liufengyun/hashdiff/issues/51) [#52](https://github.com/liufengyun/hashdiff/issues/52) [#53](https://github.com/liufengyun/hashdiff/issues/53))
|
46
54
|
|
47
55
|
## v0.3.8 2018-12-30
|
48
56
|
|
49
|
-
* Add Rubocop and drops Ruby 1.9 support #47
|
57
|
+
* Add Rubocop and drops Ruby 1.9 support [#47](https://github.com/liufengyun/hashdiff/issues/47)
|
50
58
|
|
51
59
|
## v0.3.7 2017-10-08
|
52
60
|
|
53
|
-
* remove 1.8.7 support from gemspec #39
|
61
|
+
* remove 1.8.7 support from gemspec [#39](https://github.com/liufengyun/hashdiff/issues/39)
|
54
62
|
|
55
63
|
## v0.3.6 2017-08-22
|
56
64
|
|
57
|
-
* add option `use_lcs` #35
|
65
|
+
* add option `use_lcs` [#35](https://github.com/liufengyun/hashdiff/issues/35)
|
58
66
|
|
59
67
|
## v0.3.5 2017-08-06
|
60
68
|
|
61
|
-
* add option `array_path` #34
|
69
|
+
* add option `array_path` [#34](https://github.com/liufengyun/hashdiff/issues/34)
|
62
70
|
|
63
71
|
## v0.3.4 2017-05-01
|
64
72
|
|
65
|
-
* performance improvement of `#similar?` #31
|
73
|
+
* performance improvement of `#similar?` [#31](https://github.com/liufengyun/hashdiff/issues/31)
|
66
74
|
|
67
75
|
## v0.3.2 2016-12-27
|
68
76
|
|
69
|
-
* replace `Fixnum` by `Integer` #28
|
77
|
+
* replace `Fixnum` by `Integer` [#28](https://github.com/liufengyun/hashdiff/issues/28)
|
70
78
|
|
71
79
|
## v0.3.1 2016-11-24
|
72
80
|
|
73
|
-
* fix an error when a hash has mixed types #26
|
81
|
+
* fix an error when a hash has mixed types [#26](https://github.com/liufengyun/hashdiff/issues/26)
|
74
82
|
|
75
83
|
## v0.3.0 2016-2-11
|
76
84
|
|
@@ -78,7 +86,7 @@
|
|
78
86
|
|
79
87
|
## v0.2.3 2015-11-5
|
80
88
|
|
81
|
-
* improve performance of LCS algorithm #12
|
89
|
+
* improve performance of LCS algorithm [#12](https://github.com/liufengyun/hashdiff/issues/12)
|
82
90
|
|
83
91
|
## v0.2.2 2014-10-6
|
84
92
|
|
@@ -20,9 +20,9 @@ module Hashdiff
|
|
20
20
|
obj2_keys = obj2_keys.map { |k| k.is_a?(Symbol) ? k.to_s : k }
|
21
21
|
end
|
22
22
|
|
23
|
-
added_keys =
|
24
|
-
common_keys =
|
25
|
-
deleted_keys =
|
23
|
+
added_keys = obj2_keys - obj1_keys
|
24
|
+
common_keys = obj1_keys & obj2_keys
|
25
|
+
deleted_keys = obj1_keys - obj2_keys
|
26
26
|
|
27
27
|
result = []
|
28
28
|
|
@@ -32,40 +32,66 @@ module Hashdiff
|
|
32
32
|
deleted_keys.delete k
|
33
33
|
end
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
35
|
+
handle_key = lambda do |k, type|
|
36
|
+
case type
|
37
|
+
when :deleted
|
38
|
+
# add deleted properties
|
39
|
+
k = opts[:indifferent] ? obj1_lookup[k] : k
|
40
|
+
change_key = Hashdiff.prefix_append_key(opts[:prefix], k, opts)
|
41
|
+
custom_result = Hashdiff.custom_compare(opts[:comparison], change_key, obj1[k], nil)
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
if custom_result
|
44
|
+
result.concat(custom_result)
|
45
|
+
else
|
46
|
+
result << ['-', change_key, obj1[k]]
|
47
|
+
end
|
48
|
+
when :common
|
49
|
+
# recursive comparison for common keys
|
50
|
+
prefix = Hashdiff.prefix_append_key(opts[:prefix], k, opts)
|
47
51
|
|
48
|
-
|
49
|
-
|
50
|
-
|
52
|
+
k1 = opts[:indifferent] ? obj1_lookup[k] : k
|
53
|
+
k2 = opts[:indifferent] ? obj2_lookup[k] : k
|
54
|
+
result.concat(Hashdiff.diff(obj1[k1], obj2[k2], opts.merge(prefix: prefix)))
|
55
|
+
when :added
|
56
|
+
# added properties
|
57
|
+
change_key = Hashdiff.prefix_append_key(opts[:prefix], k, opts)
|
51
58
|
|
52
|
-
|
53
|
-
|
54
|
-
result.concat(Hashdiff.diff(obj1[k1], obj2[k2], opts.merge(prefix: prefix)))
|
55
|
-
end
|
59
|
+
k = opts[:indifferent] ? obj2_lookup[k] : k
|
60
|
+
custom_result = Hashdiff.custom_compare(opts[:comparison], change_key, nil, obj2[k])
|
56
61
|
|
57
|
-
|
58
|
-
|
59
|
-
|
62
|
+
if custom_result
|
63
|
+
result.concat(custom_result)
|
64
|
+
else
|
65
|
+
result << ['+', change_key, obj2[k]]
|
66
|
+
end
|
67
|
+
else
|
68
|
+
raise "Invalid type: #{type}"
|
69
|
+
end
|
70
|
+
end
|
60
71
|
|
61
|
-
|
62
|
-
|
72
|
+
if opts[:preserve_key_order]
|
73
|
+
# Building lookups to speed up key classification
|
74
|
+
added_keys_lookup = added_keys.each_with_object({}) { |k, h| h[k] = true }
|
75
|
+
common_keys_lookup = common_keys.each_with_object({}) { |k, h| h[k] = true }
|
76
|
+
deleted_keys_lookup = deleted_keys.each_with_object({}) { |k, h| h[k] = true }
|
63
77
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
78
|
+
# Iterate through all keys, preserving obj1's key order and appending any new keys from obj2. Shared keys
|
79
|
+
# (found in both obj1 and obj2) follow obj1's order since uniq only keeps the first occurrence.
|
80
|
+
(obj1_keys + obj2_keys).uniq.each do |k|
|
81
|
+
if added_keys_lookup[k]
|
82
|
+
handle_key.call(k, :added)
|
83
|
+
elsif common_keys_lookup[k]
|
84
|
+
handle_key.call(k, :common)
|
85
|
+
elsif deleted_keys_lookup[k]
|
86
|
+
handle_key.call(k, :deleted)
|
87
|
+
end
|
68
88
|
end
|
89
|
+
else
|
90
|
+
# Keys are first grouped by operation type (deletions first, then changes, then additions), and then sorted
|
91
|
+
# alphabetically within each group.
|
92
|
+
deleted_keys.sort_by(&:to_s).each { |k| handle_key.call(k, :deleted) }
|
93
|
+
common_keys.sort_by(&:to_s).each { |k| handle_key.call(k, :common) }
|
94
|
+
added_keys.sort_by(&:to_s).each { |k| handle_key.call(k, :added) }
|
69
95
|
end
|
70
96
|
|
71
97
|
result
|
data/lib/hashdiff/diff.rb
CHANGED
@@ -16,6 +16,7 @@ module Hashdiff
|
|
16
16
|
# * :strip (Boolean) [false] whether or not to call #strip on strings before comparing
|
17
17
|
# * :array_path (Boolean) [false] whether to return the path references for nested values in an array, can be used for patch compatibility with non string keys.
|
18
18
|
# * :use_lcs (Boolean) [true] whether or not to use an implementation of the Longest common subsequence algorithm for comparing arrays, produces better diffs but is slower.
|
19
|
+
# * :preserve_key_order (Boolean) [false] If false, operations are grouped by type (-, ~, then +) then by hash key alphabetically. If true, preserves the original key order from the first hash and appends new keys from the second hash in order.
|
19
20
|
#
|
20
21
|
# @yield [path, value1, value2] Optional block is used to compare each value, instead of default #==. If the block returns value other than true of false, then other specified comparison options will be used to do the comparison.
|
21
22
|
#
|
@@ -62,6 +63,7 @@ module Hashdiff
|
|
62
63
|
# * :strip (Boolean) [false] whether or not to call #strip on strings before comparing
|
63
64
|
# * :array_path (Boolean) [false] whether to return the path references for nested values in an array, can be used for patch compatibility with non string keys.
|
64
65
|
# * :use_lcs (Boolean) [true] whether or not to use an implementation of the Longest common subsequence algorithm for comparing arrays, produces better diffs but is slower.
|
66
|
+
# * :preserve_key_order (Boolean) [false] If false, operations are grouped by type (-, ~, then +) then by hash key alphabetically. If true, preserves the original key order from the first hash and appends new keys from the second hash in order.
|
65
67
|
#
|
66
68
|
#
|
67
69
|
# @yield [path, value1, value2] Optional block is used to compare each value, instead of default #==. If the block returns value other than true of false, then other specified comparison options will be used to do the comparison.
|
@@ -88,7 +90,8 @@ module Hashdiff
|
|
88
90
|
strip: false,
|
89
91
|
numeric_tolerance: 0,
|
90
92
|
array_path: false,
|
91
|
-
use_lcs: true
|
93
|
+
use_lcs: true,
|
94
|
+
preserve_key_order: false
|
92
95
|
}.merge!(options)
|
93
96
|
|
94
97
|
opts[:prefix] = [] if opts[:array_path] && opts[:prefix] == ''
|
data/lib/hashdiff/version.rb
CHANGED
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.1
|
4
|
+
version: 1.2.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: 2025-09-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bluecloth
|