hashdiff 1.0.1 → 1.1.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 +4 -4
- data/.github/workflows/ci.yml +28 -0
- data/.rubocop.yml +11 -1
- data/Gemfile +1 -1
- data/README.md +26 -4
- data/changelog.md +19 -0
- data/hashdiff.gemspec +4 -4
- data/lib/hashdiff/compare_hashes.rb +6 -0
- data/lib/hashdiff/diff.rb +5 -0
- data/lib/hashdiff/version.rb +1 -1
- metadata +14 -22
- data/spec/hashdiff/best_diff_spec.rb +0 -75
- data/spec/hashdiff/diff_array_spec.rb +0 -60
- data/spec/hashdiff/diff_spec.rb +0 -360
- data/spec/hashdiff/lcs_spec.rb +0 -76
- data/spec/hashdiff/linear_compare_array_spec.rb +0 -50
- data/spec/hashdiff/patch_spec.rb +0 -185
- data/spec/hashdiff/readme_spec.rb +0 -15
- data/spec/hashdiff/util_spec.rb +0 -116
- data/spec/spec_helper.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 113e55eeac757365f792e7e2554a8fbb5f16c06cfcf83d4dcf7db268637ef47e
|
4
|
+
data.tar.gz: ae222f4980bff17fa8f9fecef25cb1fa54db61503c075cc49b3ef755927ec5f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7476c18bd05f2e5c57da5b15d9bd938bbfd5fa330825b21be8d867fded3b404d6ae3405acca5f235397f816b866aede13425e0ab314ee71497bfa54010ab6e82
|
7
|
+
data.tar.gz: ce5d8d3ea4e8bca768688aa782e21bab01ae4a5ae8cd0b8f5b11927abc5c5163da3dc2a1135a069ad139998654471d11359b48e2a2eeb1d87f17bc461813745f
|
@@ -0,0 +1,28 @@
|
|
1
|
+
name: ci
|
2
|
+
|
3
|
+
on:
|
4
|
+
- pull_request
|
5
|
+
- push
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
build:
|
9
|
+
strategy:
|
10
|
+
fail-fast: false
|
11
|
+
matrix:
|
12
|
+
ruby:
|
13
|
+
- 2.7
|
14
|
+
- 3.0
|
15
|
+
- 3.1
|
16
|
+
- 3.2
|
17
|
+
- 3.3
|
18
|
+
runs-on: ubuntu-latest
|
19
|
+
steps:
|
20
|
+
- uses: actions/checkout@v4
|
21
|
+
- name: Set up Ruby
|
22
|
+
uses: ruby/setup-ruby@v1
|
23
|
+
with:
|
24
|
+
ruby-version: ${{ matrix.ruby }}
|
25
|
+
- name: Install dependencies
|
26
|
+
run: bundle install --jobs $(nproc) --retry 3
|
27
|
+
- name: Run rake
|
28
|
+
run: bundle exec rake
|
data/.rubocop.yml
CHANGED
@@ -9,7 +9,7 @@ Metrics/MethodLength:
|
|
9
9
|
Enabled: false
|
10
10
|
Metrics/AbcSize:
|
11
11
|
Enabled: false
|
12
|
-
|
12
|
+
Layout/LineLength:
|
13
13
|
Enabled: false
|
14
14
|
Metrics/ClassLength:
|
15
15
|
Enabled: false
|
@@ -17,16 +17,26 @@ Metrics/BlockLength:
|
|
17
17
|
Enabled: false
|
18
18
|
Metrics/ModuleLength:
|
19
19
|
Enabled: false
|
20
|
+
Style/CaseLikeIf:
|
21
|
+
Enabled: false
|
20
22
|
Style/Documentation:
|
21
23
|
Enabled: false
|
22
24
|
Style/FrozenStringLiteralComment:
|
23
25
|
Enabled: true
|
24
26
|
EnforcedStyle: always
|
27
|
+
Style/OptionalBooleanParameter:
|
28
|
+
Enabled: false
|
25
29
|
Style/NumericPredicate:
|
26
30
|
Enabled: false
|
27
31
|
Style/RedundantFreeze:
|
28
32
|
Enabled: false
|
33
|
+
Style/RedundantReturn:
|
34
|
+
Enabled: false
|
29
35
|
RSpec/ExampleLength:
|
30
36
|
Enabled: false
|
31
37
|
RSpec/DescribeClass:
|
32
38
|
Enabled: false
|
39
|
+
RSpec/SpecFilePathFormat:
|
40
|
+
Enabled: false
|
41
|
+
RSpec/NoExpectationExample:
|
42
|
+
Enabled: false
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Hashdiff [](https://github.com/liufengyun/hashdiff/actions?query=workflow%3Aci) [](http://badge.fury.io/rb/hashdiff)
|
2
2
|
|
3
3
|
Hashdiff is a ruby library to compute the smallest difference between two hashes.
|
4
4
|
|
@@ -94,9 +94,8 @@ Hashdiff.unpatch!(b, diff).should == a
|
|
94
94
|
|
95
95
|
### Options
|
96
96
|
|
97
|
-
|
98
|
-
`:
|
99
|
-
`:array_path` and `:use_lcs`
|
97
|
+
The following options are available: `:delimiter`, `:similarity`, `:strict`, `:ignore_keys`,
|
98
|
+
`:indifferent`, `:numeric_tolerance`, `:strip`, `:case_insensitive`, `:array_path` and `:use_lcs`
|
100
99
|
|
101
100
|
#### `:delimiter`
|
102
101
|
|
@@ -118,6 +117,29 @@ In cases where you have similar hash objects in arrays, you can pass a custom va
|
|
118
117
|
|
119
118
|
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
119
|
|
120
|
+
#### `:ignore_keys`
|
121
|
+
|
122
|
+
The `:ignore_keys` option allows you to specify one or more keys to ignore, which defaults to `[]` (none). Ignored keys are ignored at all levels in both hashes. For example:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
a = { a: 4, g: 0, b: { a: 5, c: 6, e: 1 } }
|
126
|
+
b = { b: { a: 7, c: 3, f: 1 }, d: 8 }
|
127
|
+
diff = Hashdiff.diff(a, b, ignore_keys: %i[a f])
|
128
|
+
diff.should == [['-', 'g', 0], ['-', 'b.e', 1], ['~', 'b.c', 6, 3], ['+', 'd', 8]]
|
129
|
+
```
|
130
|
+
If you wish instead to ignore keys at a particlar level you should
|
131
|
+
use a [custom comparison method](https://github.com/liufengyun/hashdiff#specifying-a-custom-comparison-method) instead. For example to diff only at the 2nd level of both hashes:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
a = { a: 4, g: 0, b: { a: 5, c: 6, e: 1 } }
|
135
|
+
b = { b: { a: 7, c: 3, f: 1 }, d: 8 }
|
136
|
+
diff = Hashdiff.diff(a, b) do |path, _e, _a|
|
137
|
+
arr = path.split('.')
|
138
|
+
true if %w[a f].include?(arr.last) && arr.size == 2 # note '.' is the default delimiter
|
139
|
+
end
|
140
|
+
diff.should == [['-', 'a', 4], ['-', 'g', 0], ['-', 'b.e', 1], ['~', 'b.c', 6, 3], ['+', 'd', 8]]
|
141
|
+
```
|
142
|
+
|
121
143
|
#### `:indifferent`
|
122
144
|
|
123
145
|
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})
|
data/changelog.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## v1.1.2 2024-11-12
|
4
|
+
|
5
|
+
* Fix bundler cache #96 (@olleolleolle)
|
6
|
+
* Quote the '3.0' in YAML #95 (@olleolleolle)
|
7
|
+
* Fix version in source code #97 (@liufengyun)
|
8
|
+
|
9
|
+
## v1.1.1 2024-08-02
|
10
|
+
|
11
|
+
* Fix bug in ignore_keys option #88 (@Matzfan)
|
12
|
+
* Exclude spec files from gem package #94 (@amatsuda)
|
13
|
+
|
14
|
+
## v1.1.0 2023-12-14
|
15
|
+
|
16
|
+
* Add ignore_keys option (#86 @Matzfan)
|
17
|
+
* Remove pinned version of rake < 11
|
18
|
+
* Bump rspec dep ~> 3.5
|
19
|
+
* Bump rubocop dep >= 1.52.1
|
20
|
+
* Bump rubocop-rspec dep > 1.16.0
|
21
|
+
|
3
22
|
## v1.0.1 2020-02-25
|
4
23
|
|
5
24
|
* Add indifferent option
|
data/hashdiff.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.summary = ' Hashdiff is a diff lib to compute the smallest difference between two hashes. '
|
11
11
|
s.description = ' Hashdiff is a diff lib to compute the smallest difference between two hashes. '
|
12
12
|
|
13
|
-
s.files = `git ls-files`.split("\n")
|
13
|
+
s.files = `git ls-files`.split("\n").grep_v(%r{^spec/})
|
14
14
|
s.test_files = `git ls-files -- Appraisals {spec}/*`.split("\n")
|
15
15
|
|
16
16
|
s.require_paths = ['lib']
|
@@ -22,9 +22,9 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.homepage = 'https://github.com/liufengyun/hashdiff'
|
23
23
|
|
24
24
|
s.add_development_dependency('bluecloth')
|
25
|
-
s.add_development_dependency('rspec', '~>
|
26
|
-
s.add_development_dependency('rubocop', '
|
27
|
-
s.add_development_dependency('rubocop-rspec')
|
25
|
+
s.add_development_dependency('rspec', '~> 3.5')
|
26
|
+
s.add_development_dependency('rubocop', '>= 1.52.1') # earliest version that works with Ruby 3.3
|
27
|
+
s.add_development_dependency('rubocop-rspec', '> 1.16.0') # https://github.com/rubocop/rubocop-rspec/issues/461
|
28
28
|
s.add_development_dependency('yard')
|
29
29
|
|
30
30
|
if s.respond_to?(:metadata)
|
@@ -26,6 +26,12 @@ module Hashdiff
|
|
26
26
|
|
27
27
|
result = []
|
28
28
|
|
29
|
+
opts[:ignore_keys].each do |k|
|
30
|
+
added_keys.delete k
|
31
|
+
common_keys.delete k
|
32
|
+
deleted_keys.delete k
|
33
|
+
end
|
34
|
+
|
29
35
|
# add deleted properties
|
30
36
|
deleted_keys.each do |k|
|
31
37
|
k = opts[:indifferent] ? obj1_lookup[k] : k
|
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
|
+
# * :ignore_keys (Symbol, String or Array) [[]] a list of keys to ignore. No comparison is made for the specified key(s) in either hash
|
12
13
|
# * :indifferent (Boolean) [false] whether to treat hash keys indifferently. Set to true to ignore differences between symbol keys (ie. {a: 1} ~= {'a' => 1})
|
13
14
|
# * :delimiter (String) ['.'] the delimiter used when returning nested key references
|
14
15
|
# * :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.
|
@@ -53,6 +54,7 @@ module Hashdiff
|
|
53
54
|
# @param [Array, Hash] obj2
|
54
55
|
# @param [Hash] options the options to use when comparing
|
55
56
|
# * :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
|
57
|
+
# * :ignore_keys (Symbol, String or Array) [[]] a list of keys to ignore. No comparison is made for the specified key(s) in either hash
|
56
58
|
# * :indifferent (Boolean) [false] whether to treat hash keys indifferently. Set to true to ignore differences between symbol keys (ie. {a: 1} ~= {'a' => 1})
|
57
59
|
# * :similarity (Numeric) [0.8] should be between (0, 1]. Meaningful if there are similar hashes in arrays. See {best_diff}.
|
58
60
|
# * :delimiter (String) ['.'] the delimiter used when returning nested key references
|
@@ -81,6 +83,7 @@ module Hashdiff
|
|
81
83
|
similarity: 0.8,
|
82
84
|
delimiter: '.',
|
83
85
|
strict: true,
|
86
|
+
ignore_keys: [],
|
84
87
|
indifferent: false,
|
85
88
|
strip: false,
|
86
89
|
numeric_tolerance: 0,
|
@@ -90,6 +93,8 @@ module Hashdiff
|
|
90
93
|
|
91
94
|
opts[:prefix] = [] if opts[:array_path] && opts[:prefix] == ''
|
92
95
|
|
96
|
+
opts[:ignore_keys] = [*opts[:ignore_keys]]
|
97
|
+
|
93
98
|
opts[:comparison] = block if block_given?
|
94
99
|
|
95
100
|
# prefer to compare with provided block
|
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.
|
4
|
+
version: 1.1.2
|
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: 2024-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bluecloth
|
@@ -30,42 +30,42 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '3.5'
|
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: '
|
40
|
+
version: '3.5'
|
41
41
|
- !ruby/object:Gem::Dependency
|
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: 1.52.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: 1.52.1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rubocop-rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 1.16.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:
|
68
|
+
version: 1.16.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: yard
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -88,6 +88,7 @@ executables: []
|
|
88
88
|
extensions: []
|
89
89
|
extra_rdoc_files: []
|
90
90
|
files:
|
91
|
+
- ".github/workflows/ci.yml"
|
91
92
|
- ".gitignore"
|
92
93
|
- ".rspec"
|
93
94
|
- ".rubocop.yml"
|
@@ -108,15 +109,6 @@ files:
|
|
108
109
|
- lib/hashdiff/patch.rb
|
109
110
|
- lib/hashdiff/util.rb
|
110
111
|
- lib/hashdiff/version.rb
|
111
|
-
- spec/hashdiff/best_diff_spec.rb
|
112
|
-
- spec/hashdiff/diff_array_spec.rb
|
113
|
-
- spec/hashdiff/diff_spec.rb
|
114
|
-
- spec/hashdiff/lcs_spec.rb
|
115
|
-
- spec/hashdiff/linear_compare_array_spec.rb
|
116
|
-
- spec/hashdiff/patch_spec.rb
|
117
|
-
- spec/hashdiff/readme_spec.rb
|
118
|
-
- spec/hashdiff/util_spec.rb
|
119
|
-
- spec/spec_helper.rb
|
120
112
|
homepage: https://github.com/liufengyun/hashdiff
|
121
113
|
licenses:
|
122
114
|
- MIT
|
@@ -141,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
133
|
- !ruby/object:Gem::Version
|
142
134
|
version: '0'
|
143
135
|
requirements: []
|
144
|
-
rubygems_version: 3.
|
136
|
+
rubygems_version: 3.3.5
|
145
137
|
signing_key:
|
146
138
|
specification_version: 4
|
147
139
|
summary: Hashdiff is a diff lib to compute the smallest difference between two hashes.
|
@@ -1,75 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Hashdiff do
|
6
|
-
it 'is able to best diff' do
|
7
|
-
a = { 'x' => [{ 'a' => 1, 'c' => 3, 'e' => 5 }, { 'y' => 3 }] }
|
8
|
-
b = { 'x' => [{ 'a' => 1, 'b' => 2, 'e' => 5 }] }
|
9
|
-
|
10
|
-
diff = described_class.best_diff(a, b)
|
11
|
-
diff.should == [['-', 'x[0].c', 3], ['+', 'x[0].b', 2], ['-', 'x[1]', { 'y' => 3 }]]
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'uses custom delimiter when provided' do
|
15
|
-
a = { 'x' => [{ 'a' => 1, 'c' => 3, 'e' => 5 }, { 'y' => 3 }] }
|
16
|
-
b = { 'x' => [{ 'a' => 1, 'b' => 2, 'e' => 5 }] }
|
17
|
-
|
18
|
-
diff = described_class.best_diff(a, b, delimiter: "\t")
|
19
|
-
diff.should == [['-', "x[0]\tc", 3], ['+', "x[0]\tb", 2], ['-', 'x[1]', { 'y' => 3 }]]
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'uses custom comparison when provided' do
|
23
|
-
a = { 'x' => [{ 'a' => 'foo', 'c' => 'goat', 'e' => 'snake' }, { 'y' => 'baz' }] }
|
24
|
-
b = { 'x' => [{ 'a' => 'bar', 'b' => 'cow', 'e' => 'puppy' }] }
|
25
|
-
|
26
|
-
diff = described_class.best_diff(a, b) do |path, obj1, obj2|
|
27
|
-
case path
|
28
|
-
when /^x\[.\]\..$/
|
29
|
-
obj1.length == obj2.length if obj1 && obj2
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
diff.should == [['-', 'x[0].c', 'goat'], ['+', 'x[0].b', 'cow'], ['-', 'x[1]', { 'y' => 'baz' }]]
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'is able to best diff array in hash' do
|
37
|
-
a = { 'menu' => {
|
38
|
-
'id' => 'file',
|
39
|
-
'value' => 'File',
|
40
|
-
'popup' => {
|
41
|
-
'menuitem' => [
|
42
|
-
{ 'value' => 'New', 'onclick' => 'CreateNewDoc()' },
|
43
|
-
{ 'value' => 'Close', 'onclick' => 'CloseDoc()' }
|
44
|
-
]
|
45
|
-
}
|
46
|
-
} }
|
47
|
-
|
48
|
-
b = { 'menu' => {
|
49
|
-
'id' => 'file 2',
|
50
|
-
'value' => 'File',
|
51
|
-
'popup' => {
|
52
|
-
'menuitem' => [
|
53
|
-
{ 'value' => 'New1', 'onclick' => 'CreateNewDoc()' },
|
54
|
-
{ 'value' => 'Open', 'onclick' => 'OpenDoc()' },
|
55
|
-
{ 'value' => 'Close', 'onclick' => 'CloseDoc()' }
|
56
|
-
]
|
57
|
-
}
|
58
|
-
} }
|
59
|
-
|
60
|
-
diff = described_class.best_diff(a, b)
|
61
|
-
diff.should == [
|
62
|
-
['~', 'menu.id', 'file', 'file 2'],
|
63
|
-
['~', 'menu.popup.menuitem[0].value', 'New', 'New1'],
|
64
|
-
['+', 'menu.popup.menuitem[1]', { 'value' => 'Open', 'onclick' => 'OpenDoc()' }]
|
65
|
-
]
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'is able to have an array_path specified' do
|
69
|
-
a = { 'x' => [{ 'a' => 1, 'c' => 3, 'e' => 5 }, { 'y' => 3 }] }
|
70
|
-
b = { 'x' => [{ 'a' => 1, 'b' => 2, 'e' => 5 }] }
|
71
|
-
|
72
|
-
diff = described_class.best_diff(a, b, array_path: true)
|
73
|
-
diff.should == [['-', ['x', 0, 'c'], 3], ['+', ['x', 0, 'b'], 2], ['-', ['x', 1], { 'y' => 3 }]]
|
74
|
-
end
|
75
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Hashdiff do
|
6
|
-
it 'is able to diff two equal array' do
|
7
|
-
a = [1, 2, 3]
|
8
|
-
b = [1, 2, 3]
|
9
|
-
|
10
|
-
diff = described_class.diff_array_lcs(a, b)
|
11
|
-
diff.should == []
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'is able to diff two arrays with one element in common' do
|
15
|
-
a = [1, 2, 3]
|
16
|
-
b = [1, 8, 7]
|
17
|
-
|
18
|
-
diff = described_class.diff_array_lcs(a, b)
|
19
|
-
diff.should == [['-', 2, 3], ['-', 1, 2], ['+', 1, 8], ['+', 2, 7]]
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'is able to diff two arrays with nothing in common' do
|
23
|
-
a = [1, 2]
|
24
|
-
b = []
|
25
|
-
|
26
|
-
diff = described_class.diff_array_lcs(a, b)
|
27
|
-
diff.should == [['-', 1, 2], ['-', 0, 1]]
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'is able to diff an empty array with an non-empty array' do
|
31
|
-
a = []
|
32
|
-
b = [1, 2]
|
33
|
-
|
34
|
-
diff = described_class.diff_array_lcs(a, b)
|
35
|
-
diff.should == [['+', 0, 1], ['+', 1, 2]]
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'is able to diff two arrays with two elements in common' do
|
39
|
-
a = [1, 3, 5, 7]
|
40
|
-
b = [2, 3, 7, 5]
|
41
|
-
|
42
|
-
diff = described_class.diff_array_lcs(a, b)
|
43
|
-
diff.should == [['-', 0, 1], ['+', 0, 2], ['+', 2, 7], ['-', 4, 7]]
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'is able to test two arrays with two common elements in different order' do
|
47
|
-
a = [1, 3, 4, 7]
|
48
|
-
b = [2, 3, 7, 5]
|
49
|
-
|
50
|
-
diff = described_class.diff_array_lcs(a, b)
|
51
|
-
diff.should == [['-', 0, 1], ['+', 0, 2], ['-', 2, 4], ['+', 3, 5]]
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'is able to diff two arrays with similar elements' do
|
55
|
-
a = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }, 3]
|
56
|
-
b = [1, { 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5 }]
|
57
|
-
diff = described_class.diff_array_lcs(a, b)
|
58
|
-
diff.should == [['+', 0, 1], ['-', 2, 3]]
|
59
|
-
end
|
60
|
-
end
|
data/spec/hashdiff/diff_spec.rb
DELETED
@@ -1,360 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Hashdiff do
|
6
|
-
it 'is able to diff two empty hashes' do
|
7
|
-
diff = described_class.diff({}, {})
|
8
|
-
diff.should == []
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'is able to diff an hash with an empty hash' do
|
12
|
-
a = { 'a' => 3, 'b' => 2 }
|
13
|
-
b = {}
|
14
|
-
|
15
|
-
diff = described_class.diff(a, b)
|
16
|
-
expect(diff).to eq([['-', 'a', 3], ['-', 'b', 2]])
|
17
|
-
|
18
|
-
diff = described_class.diff(b, a)
|
19
|
-
diff.should == [['+', 'a', 3], ['+', 'b', 2]]
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'is able to diff two equal hashes' do
|
23
|
-
diff = described_class.diff({ 'a' => 2, 'b' => 2 }, 'a' => 2, 'b' => 2)
|
24
|
-
diff.should == []
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'is able to diff two equal hashes with mixed key types' do
|
28
|
-
a = { 'a' => 1, :b => 1 }
|
29
|
-
diff = described_class.diff(a, a)
|
30
|
-
diff.should == []
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'is able to diff if mixed key types are removed' do
|
34
|
-
a = { 'a' => 1, :b => 1 }
|
35
|
-
b = {}
|
36
|
-
diff = described_class.diff(a, b)
|
37
|
-
diff.should == [['-', 'a', 1], ['-', 'b', 1]]
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'is able to diff if mixed key types are added' do
|
41
|
-
a = { 'a' => 1, :b => 1 }
|
42
|
-
b = {}
|
43
|
-
diff = described_class.diff(b, a)
|
44
|
-
diff.should == [['+', 'a', 1], ['+', 'b', 1]]
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'is able to diff two hashes with equivalent numerics, when strict is false' do
|
48
|
-
diff = described_class.diff({ 'a' => 2.0, 'b' => 2 }, { 'a' => 2, 'b' => 2.0 }, strict: false)
|
49
|
-
diff.should == []
|
50
|
-
end
|
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
|
-
|
57
|
-
it 'is able to diff changes in hash value' do
|
58
|
-
diff = described_class.diff({ 'a' => 2, 'b' => 3, 'c' => ' hello' }, 'a' => 2, 'b' => 4, 'c' => 'hello')
|
59
|
-
diff.should == [['~', 'b', 3, 4], ['~', 'c', ' hello', 'hello']]
|
60
|
-
end
|
61
|
-
|
62
|
-
it 'is able to diff changes in hash value which is array' do
|
63
|
-
diff = described_class.diff({ 'a' => 2, 'b' => [1, 2, 3] }, 'a' => 2, 'b' => [1, 3, 4])
|
64
|
-
diff.should == [['-', 'b[1]', 2], ['+', 'b[2]', 4]]
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'is able to diff changes in hash value which is hash' do
|
68
|
-
diff = described_class.diff({ 'a' => { 'x' => 2, 'y' => 3, 'z' => 4 }, 'b' => { 'x' => 3, 'z' => 45 } },
|
69
|
-
'a' => { 'y' => 3 }, 'b' => { 'y' => 3, 'z' => 30 })
|
70
|
-
diff.should == [['-', 'a.x', 2], ['-', 'a.z', 4], ['-', 'b.x', 3], ['~', 'b.z', 45, 30], ['+', 'b.y', 3]]
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'is able to best diff similar objects in array' do
|
74
|
-
diff = described_class.best_diff({ 'a' => [{ 'x' => 2, 'y' => 3, 'z' => 4 }, { 'x' => 11, 'y' => 22, 'z' => 33 }], 'b' => { 'x' => 3, 'z' => 45 } },
|
75
|
-
'a' => [{ 'y' => 3 }, { 'x' => 11, 'z' => 33 }], 'b' => { 'y' => 22 })
|
76
|
-
diff.should == [['-', 'a[0].x', 2], ['-', 'a[0].z', 4], ['-', 'a[1].y', 22], ['-', 'b.x', 3], ['-', 'b.z', 45], ['+', 'b.y', 22]]
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'is able to diff addition of key value pair' do
|
80
|
-
a = { 'a' => 3, 'c' => 11, 'd' => 45, 'e' => 100, 'f' => 200 }
|
81
|
-
b = { 'a' => 3, 'c' => 11, 'd' => 45, 'e' => 100, 'f' => 200, 'g' => 300 }
|
82
|
-
|
83
|
-
diff = described_class.diff(a, b)
|
84
|
-
expect(diff).to eq([['+', 'g', 300]])
|
85
|
-
|
86
|
-
diff = described_class.diff(b, a)
|
87
|
-
diff.should == [['-', 'g', 300]]
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'is able to diff value type changes' do
|
91
|
-
a = { 'a' => 3 }
|
92
|
-
b = { 'a' => { 'a1' => 1, 'a2' => 2 } }
|
93
|
-
|
94
|
-
diff = described_class.diff(a, b)
|
95
|
-
expect(diff).to eq([['~', 'a', 3, { 'a1' => 1, 'a2' => 2 }]])
|
96
|
-
|
97
|
-
diff = described_class.diff(b, a)
|
98
|
-
diff.should == [['~', 'a', { 'a1' => 1, 'a2' => 2 }, 3]]
|
99
|
-
end
|
100
|
-
|
101
|
-
it 'is able to diff value changes: array <=> []' do
|
102
|
-
a = { 'a' => 1, 'b' => [1, 2] }
|
103
|
-
b = { 'a' => 1, 'b' => [] }
|
104
|
-
|
105
|
-
diff = described_class.diff(a, b)
|
106
|
-
diff.should == [['-', 'b[1]', 2], ['-', 'b[0]', 1]]
|
107
|
-
end
|
108
|
-
|
109
|
-
it 'is able to diff value changes: array <=> nil' do
|
110
|
-
a = { 'a' => 1, 'b' => [1, 2] }
|
111
|
-
b = { 'a' => 1, 'b' => nil }
|
112
|
-
|
113
|
-
diff = described_class.diff(a, b)
|
114
|
-
diff.should == [['~', 'b', [1, 2], nil]]
|
115
|
-
end
|
116
|
-
|
117
|
-
it 'is able to diff value chagnes: remove array completely' do
|
118
|
-
a = { 'a' => 1, 'b' => [1, 2] }
|
119
|
-
b = { 'a' => 1 }
|
120
|
-
|
121
|
-
diff = described_class.diff(a, b)
|
122
|
-
diff.should == [['-', 'b', [1, 2]]]
|
123
|
-
end
|
124
|
-
|
125
|
-
it 'is able to diff value changes: remove whole hash' do
|
126
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
127
|
-
b = { 'a' => 1 }
|
128
|
-
|
129
|
-
diff = described_class.diff(a, b)
|
130
|
-
diff.should == [['-', 'b', { 'b1' => 1, 'b2' => 2 }]]
|
131
|
-
end
|
132
|
-
|
133
|
-
it 'is able to diff value changes: hash <=> {}' do
|
134
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
135
|
-
b = { 'a' => 1, 'b' => {} }
|
136
|
-
|
137
|
-
diff = described_class.diff(a, b)
|
138
|
-
diff.should == [['-', 'b.b1', 1], ['-', 'b.b2', 2]]
|
139
|
-
end
|
140
|
-
|
141
|
-
it 'is able to diff value changes: hash <=> nil' do
|
142
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
143
|
-
b = { 'a' => 1, 'b' => nil }
|
144
|
-
|
145
|
-
diff = described_class.diff(a, b)
|
146
|
-
diff.should == [['~', 'b', { 'b1' => 1, 'b2' => 2 }, nil]]
|
147
|
-
end
|
148
|
-
|
149
|
-
it 'is able to diff similar objects in array' do
|
150
|
-
a = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }, 3]
|
151
|
-
b = [1, { 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5 }]
|
152
|
-
|
153
|
-
diff = described_class.diff(a, b)
|
154
|
-
diff.should == [['-', '[0].d', 4], ['+', '[0]', 1], ['-', '[2]', 3]]
|
155
|
-
end
|
156
|
-
|
157
|
-
it 'is able to diff similar & equal objects in array' do
|
158
|
-
a = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }, { 'x' => 5, 'y' => 6, 'z' => 3 }, 3]
|
159
|
-
b = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5 }, 3]
|
160
|
-
|
161
|
-
diff = described_class.diff(a, b)
|
162
|
-
diff.should == [['-', '[0].d', 4], ['-', '[1]', { 'x' => 5, 'y' => 6, 'z' => 3 }]]
|
163
|
-
end
|
164
|
-
|
165
|
-
it 'uses custom delimiter when provided' do
|
166
|
-
a = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }, { 'x' => 5, 'y' => 6, 'z' => 3 }, 3]
|
167
|
-
b = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5 }, 3]
|
168
|
-
|
169
|
-
diff = described_class.diff(a, b, similarity: 0.8, delimiter: "\t")
|
170
|
-
diff.should == [['-', "[0]\td", 4], ['-', '[1]', { 'x' => 5, 'y' => 6, 'z' => 3 }]]
|
171
|
-
end
|
172
|
-
|
173
|
-
context 'when :numeric_tolerance requested' do
|
174
|
-
it 'is able to diff changes in hash value' do
|
175
|
-
a = { 'a' => 0.558, 'b' => 0.0, 'c' => 0.65, 'd' => 'fin' }
|
176
|
-
b = { 'a' => 0.557, 'b' => 'hats', 'c' => 0.67, 'd' => 'fin' }
|
177
|
-
|
178
|
-
diff = described_class.diff(a, b, numeric_tolerance: 0.01)
|
179
|
-
expect(diff).to eq([['~', 'b', 0.0, 'hats'], ['~', 'c', 0.65, 0.67]])
|
180
|
-
|
181
|
-
diff = described_class.diff(b, a, numeric_tolerance: 0.01)
|
182
|
-
diff.should == [['~', 'b', 'hats', 0.0], ['~', 'c', 0.67, 0.65]]
|
183
|
-
end
|
184
|
-
|
185
|
-
it 'is able to diff changes in nested values' do
|
186
|
-
a = { 'a' => { 'x' => 0.4, 'y' => 0.338 }, 'b' => [13, 68.03] }
|
187
|
-
b = { 'a' => { 'x' => 0.6, 'y' => 0.341 }, 'b' => [14, 68.025] }
|
188
|
-
|
189
|
-
diff = described_class.diff(a, b, numeric_tolerance: 0.01)
|
190
|
-
expect(diff).to eq([['~', 'a.x', 0.4, 0.6], ['-', 'b[0]', 13], ['+', 'b[0]', 14]])
|
191
|
-
|
192
|
-
diff = described_class.diff(b, a, numeric_tolerance: 0.01)
|
193
|
-
diff.should == [['~', 'a.x', 0.6, 0.4], ['-', 'b[0]', 14], ['+', 'b[0]', 13]]
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
context 'when :strip requested' do
|
198
|
-
it 'strips strings before comparing' do
|
199
|
-
a = { 'a' => ' foo', 'b' => 'fizz buzz' }
|
200
|
-
b = { 'a' => 'foo', 'b' => 'fizzbuzz' }
|
201
|
-
diff = described_class.diff(a, b, strip: true)
|
202
|
-
diff.should == [['~', 'b', 'fizz buzz', 'fizzbuzz']]
|
203
|
-
end
|
204
|
-
|
205
|
-
it 'strips nested strings before comparing' do
|
206
|
-
a = { 'a' => { 'x' => ' foo' }, 'b' => ['fizz buzz', 'nerf'] }
|
207
|
-
b = { 'a' => { 'x' => 'foo' }, 'b' => %w[fizzbuzz nerf] }
|
208
|
-
diff = described_class.diff(a, b, strip: true)
|
209
|
-
diff.should == [['-', 'b[0]', 'fizz buzz'], ['+', 'b[0]', 'fizzbuzz']]
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
context 'when :case_insensitive requested' do
|
214
|
-
it 'strips strings before comparing' do
|
215
|
-
a = { 'a' => 'Foo', 'b' => 'fizz buzz' }
|
216
|
-
b = { 'a' => 'foo', 'b' => 'fizzBuzz' }
|
217
|
-
diff = described_class.diff(a, b, case_insensitive: true)
|
218
|
-
diff.should == [['~', 'b', 'fizz buzz', 'fizzBuzz']]
|
219
|
-
end
|
220
|
-
|
221
|
-
it 'ignores case on nested strings before comparing' do
|
222
|
-
a = { 'a' => { 'x' => 'Foo' }, 'b' => ['fizz buzz', 'nerf'] }
|
223
|
-
b = { 'a' => { 'x' => 'foo' }, 'b' => %w[fizzbuzz nerf] }
|
224
|
-
diff = described_class.diff(a, b, case_insensitive: true)
|
225
|
-
diff.should == [['-', 'b[0]', 'fizz buzz'], ['+', 'b[0]', 'fizzbuzz']]
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
context 'when both :strip and :numeric_tolerance requested' do
|
230
|
-
it 'applies filters to proper object types' do
|
231
|
-
a = { 'a' => ' foo', 'b' => 35, 'c' => 'bar', 'd' => 'baz' }
|
232
|
-
b = { 'a' => 'foo', 'b' => 35.005, 'c' => 'bar', 'd' => 18.5 }
|
233
|
-
diff = described_class.diff(a, b, strict: false, numeric_tolerance: 0.01, strip: true)
|
234
|
-
diff.should == [['~', 'd', 'baz', 18.5]]
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
context 'when both :strip and :case_insensitive requested' do
|
239
|
-
it 'applies both filters to strings' do
|
240
|
-
a = { 'a' => ' Foo', 'b' => 'fizz buzz' }
|
241
|
-
b = { 'a' => 'foo', 'b' => 'fizzBuzz' }
|
242
|
-
diff = described_class.diff(a, b, case_insensitive: true, strip: true)
|
243
|
-
diff.should == [['~', 'b', 'fizz buzz', 'fizzBuzz']]
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
context 'with custom comparison' do
|
248
|
-
let(:a) { { 'a' => 'car', 'b' => 'boat', 'c' => 'plane' } }
|
249
|
-
let(:b) { { 'a' => 'bus', 'b' => 'truck', 'c' => ' plan' } }
|
250
|
-
|
251
|
-
it 'compares using proc specified in block' do
|
252
|
-
diff = described_class.diff(a, b) do |prefix, obj1, obj2|
|
253
|
-
case prefix
|
254
|
-
when /a|b|c/
|
255
|
-
obj1.length == obj2.length
|
256
|
-
end
|
257
|
-
end
|
258
|
-
diff.should == [['~', 'b', 'boat', 'truck']]
|
259
|
-
end
|
260
|
-
|
261
|
-
it 'yields added keys' do
|
262
|
-
x = { 'a' => 'car', 'b' => 'boat' }
|
263
|
-
y = { 'a' => 'car' }
|
264
|
-
|
265
|
-
diff = described_class.diff(x, y) do |prefix, _obj1, _obj2|
|
266
|
-
case prefix
|
267
|
-
when /b/
|
268
|
-
true
|
269
|
-
end
|
270
|
-
end
|
271
|
-
diff.should == []
|
272
|
-
end
|
273
|
-
|
274
|
-
it 'compares with both proc and :strip when both provided' do
|
275
|
-
diff = described_class.diff(a, b, strip: true) do |prefix, obj1, obj2|
|
276
|
-
case prefix
|
277
|
-
when 'a'
|
278
|
-
obj1.length == obj2.length
|
279
|
-
end
|
280
|
-
end
|
281
|
-
diff.should == [['~', 'b', 'boat', 'truck'], ['~', 'c', 'plane', ' plan']]
|
282
|
-
end
|
283
|
-
|
284
|
-
it 'compares nested arrays using proc specified in block' do
|
285
|
-
a = { a: 'car', b: %w[boat plane] }
|
286
|
-
b = { a: 'bus', b: ['truck', ' plan'] }
|
287
|
-
|
288
|
-
diff = described_class.diff(a, b) do |path, obj1, obj2|
|
289
|
-
case path
|
290
|
-
when 'b[*]'
|
291
|
-
obj1.length == obj2.length
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
expect(diff).to eq [['~', 'a', 'car', 'bus'], ['~', 'b[1]', 'plane', ' plan'], ['-', 'b[0]', 'boat'], ['+', 'b[0]', 'truck']]
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
context 'when :array_path is true' do
|
300
|
-
it 'returns the diff path in an array rather than a string' do
|
301
|
-
x = { 'a' => 'foo' }
|
302
|
-
y = { 'a' => 'bar' }
|
303
|
-
diff = described_class.diff(x, y, array_path: true)
|
304
|
-
|
305
|
-
diff.should == [['~', ['a'], 'foo', 'bar']]
|
306
|
-
end
|
307
|
-
|
308
|
-
it 'shows array indexes in paths' do
|
309
|
-
x = { 'a' => [0, 1, 2] }
|
310
|
-
y = { 'a' => [0, 1, 2, 3] }
|
311
|
-
|
312
|
-
diff = described_class.diff(x, y, array_path: true)
|
313
|
-
|
314
|
-
diff.should == [['+', ['a', 3], 3]]
|
315
|
-
end
|
316
|
-
|
317
|
-
it 'shows differences with string and symbol keys' do
|
318
|
-
x = { 'a' => 'foo' }
|
319
|
-
y = { a: 'bar' }
|
320
|
-
|
321
|
-
diff = described_class.diff(x, y, array_path: true)
|
322
|
-
diff.should == [['-', ['a'], 'foo'], ['+', [:a], 'bar']]
|
323
|
-
end
|
324
|
-
|
325
|
-
it 'supports other key types' do
|
326
|
-
time = Time.now
|
327
|
-
x = { time => 'foo' }
|
328
|
-
y = { 0 => 'bar' }
|
329
|
-
|
330
|
-
diff = described_class.diff(x, y, array_path: true)
|
331
|
-
diff.should == [['-', [time], 'foo'], ['+', [0], 'bar']]
|
332
|
-
end
|
333
|
-
end
|
334
|
-
|
335
|
-
context 'when :use_lcs is false' do
|
336
|
-
it 'shows items in an array as changed' do
|
337
|
-
x = %i[a b]
|
338
|
-
y = %i[c d]
|
339
|
-
diff = described_class.diff(x, y, use_lcs: false)
|
340
|
-
|
341
|
-
diff.should == [['~', '[0]', :a, :c], ['~', '[1]', :b, :d]]
|
342
|
-
end
|
343
|
-
|
344
|
-
it 'shows additions to arrays' do
|
345
|
-
x = { a: [0] }
|
346
|
-
y = { a: [0, 1] }
|
347
|
-
diff = described_class.diff(x, y, use_lcs: false)
|
348
|
-
|
349
|
-
diff.should == [['+', 'a[1]', 1]]
|
350
|
-
end
|
351
|
-
|
352
|
-
it 'shows changes to nested arrays' do
|
353
|
-
x = { a: [[0, 1]] }
|
354
|
-
y = { a: [[1, 2]] }
|
355
|
-
diff = described_class.diff(x, y, use_lcs: false)
|
356
|
-
|
357
|
-
diff.should == [['~', 'a[0][0]', 0, 1], ['~', 'a[0][1]', 1, 2]]
|
358
|
-
end
|
359
|
-
end
|
360
|
-
end
|
data/spec/hashdiff/lcs_spec.rb
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Hashdiff do
|
6
|
-
it 'is able to find LCS between two equal array' do
|
7
|
-
a = [1, 2, 3]
|
8
|
-
b = [1, 2, 3]
|
9
|
-
|
10
|
-
lcs = described_class.lcs(a, b)
|
11
|
-
lcs.should == [[0, 0], [1, 1], [2, 2]]
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'is able to find LCS between two close arrays' do
|
15
|
-
a = [1.05, 2, 3.25]
|
16
|
-
b = [1.06, 2, 3.24]
|
17
|
-
|
18
|
-
lcs = described_class.lcs(a, b, numeric_tolerance: 0.1)
|
19
|
-
lcs.should == [[0, 0], [1, 1], [2, 2]]
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'strips strings when finding LCS if requested' do
|
23
|
-
a = %w[foo bar baz]
|
24
|
-
b = [' foo', 'bar', 'zab']
|
25
|
-
|
26
|
-
lcs = described_class.lcs(a, b, strip: true)
|
27
|
-
lcs.should == [[0, 0], [1, 1]]
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'is able to find LCS with one common elements' do
|
31
|
-
a = [1, 2, 3]
|
32
|
-
b = [1, 8, 7]
|
33
|
-
|
34
|
-
lcs = described_class.lcs(a, b)
|
35
|
-
lcs.should == [[0, 0]]
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'is able to find LCS with two common elements' do
|
39
|
-
a = [1, 3, 5, 7]
|
40
|
-
b = [2, 3, 7, 5]
|
41
|
-
|
42
|
-
lcs = described_class.lcs(a, b)
|
43
|
-
lcs.should == [[1, 1], [2, 3]]
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'is able to find LCS with two close elements' do
|
47
|
-
a = [1, 3.05, 5, 7]
|
48
|
-
b = [2, 3.06, 7, 5]
|
49
|
-
|
50
|
-
lcs = described_class.lcs(a, b, numeric_tolerance: 0.1)
|
51
|
-
lcs.should == [[1, 1], [2, 3]]
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'is able to find LCS with two common elements in different ordering' do
|
55
|
-
a = [1, 3, 4, 7]
|
56
|
-
b = [2, 3, 7, 5]
|
57
|
-
|
58
|
-
lcs = described_class.lcs(a, b)
|
59
|
-
lcs.should == [[1, 1], [3, 2]]
|
60
|
-
end
|
61
|
-
|
62
|
-
it 'is able to find LCS with a similarity value' do
|
63
|
-
a = [
|
64
|
-
{ 'value' => 'New', 'onclick' => 'CreateNewDoc()' },
|
65
|
-
{ 'value' => 'Close', 'onclick' => 'CloseDoc()' }
|
66
|
-
]
|
67
|
-
b = [
|
68
|
-
{ 'value' => 'New1', 'onclick' => 'CreateNewDoc()' },
|
69
|
-
{ 'value' => 'Open', 'onclick' => 'OpenDoc()' },
|
70
|
-
{ 'value' => 'Close', 'onclick' => 'CloseDoc()' }
|
71
|
-
]
|
72
|
-
|
73
|
-
lcs = described_class.lcs(a, b, similarity: 0.5)
|
74
|
-
lcs.should == [[0, 0], [1, 2]]
|
75
|
-
end
|
76
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Hashdiff::LinearCompareArray do
|
6
|
-
it 'finds no differences between two empty arrays' do
|
7
|
-
difference = described_class.call([], [])
|
8
|
-
difference.should == []
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'finds added items when the old array is empty' do
|
12
|
-
difference = described_class.call([], %i[a b])
|
13
|
-
difference.should == [['+', '[0]', :a], ['+', '[1]', :b]]
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'finds removed items when the new array is empty' do
|
17
|
-
difference = described_class.call(%i[a b], [])
|
18
|
-
difference.should == [['-', '[1]', :b], ['-', '[0]', :a]]
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'finds no differences between identical arrays' do
|
22
|
-
difference = described_class.call(%i[a b], %i[a b])
|
23
|
-
difference.should == []
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'finds added items in an array' do
|
27
|
-
difference = described_class.call(%i[a d], %i[a b c d])
|
28
|
-
difference.should == [['+', '[1]', :b], ['+', '[2]', :c]]
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'finds removed items in an array' do
|
32
|
-
difference = described_class.call(%i[a b c d e f], %i[a d f])
|
33
|
-
difference.should == [['-', '[4]', :e], ['-', '[2]', :c], ['-', '[1]', :b]]
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'shows additions and deletions as changed items' do
|
37
|
-
difference = described_class.call(%i[a b c], %i[c b a])
|
38
|
-
difference.should == [['~', '[0]', :a, :c], ['~', '[2]', :c, :a]]
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'shows changed items in a hash' do
|
42
|
-
difference = described_class.call([{ a: :b }], [{ a: :c }])
|
43
|
-
difference.should == [['~', '[0].a', :b, :c]]
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'shows changed items and added items' do
|
47
|
-
difference = described_class.call([{ a: 1, b: 2 }], [{ a: 2, b: 2 }, :item])
|
48
|
-
difference.should == [['~', '[0].a', 1, 2], ['+', '[1]', :item]]
|
49
|
-
end
|
50
|
-
end
|
data/spec/hashdiff/patch_spec.rb
DELETED
@@ -1,185 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Hashdiff do
|
6
|
-
it 'is able to patch key addition' do
|
7
|
-
a = { 'a' => 3, 'c' => 11, 'd' => 45, 'e' => 100, 'f' => 200 }
|
8
|
-
b = { 'a' => 3, 'c' => 11, 'd' => 45, 'e' => 100, 'f' => 200, 'g' => 300 }
|
9
|
-
diff = described_class.diff(a, b)
|
10
|
-
|
11
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
12
|
-
|
13
|
-
a = { 'a' => 3, 'c' => 11, 'd' => 45, 'e' => 100, 'f' => 200 }
|
14
|
-
b = { 'a' => 3, 'c' => 11, 'd' => 45, 'e' => 100, 'f' => 200, 'g' => 300 }
|
15
|
-
described_class.unpatch!(b, diff).should == a
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'is able to patch value type changes' do
|
19
|
-
a = { 'a' => 3 }
|
20
|
-
b = { 'a' => { 'a1' => 1, 'a2' => 2 } }
|
21
|
-
diff = described_class.diff(a, b)
|
22
|
-
|
23
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
24
|
-
|
25
|
-
a = { 'a' => 3 }
|
26
|
-
b = { 'a' => { 'a1' => 1, 'a2' => 2 } }
|
27
|
-
described_class.unpatch!(b, diff).should == a
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'is able to patch value array <=> []' do
|
31
|
-
a = { 'a' => 1, 'b' => [1, 2] }
|
32
|
-
b = { 'a' => 1, 'b' => [] }
|
33
|
-
diff = described_class.diff(a, b)
|
34
|
-
|
35
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
36
|
-
|
37
|
-
a = { 'a' => 1, 'b' => [1, 2] }
|
38
|
-
b = { 'a' => 1, 'b' => [] }
|
39
|
-
described_class.unpatch!(b, diff).should == a
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'is able to patch value array <=> nil' do
|
43
|
-
a = { 'a' => 1, 'b' => [1, 2] }
|
44
|
-
b = { 'a' => 1, 'b' => nil }
|
45
|
-
diff = described_class.diff(a, b)
|
46
|
-
|
47
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
48
|
-
|
49
|
-
a = { 'a' => 1, 'b' => [1, 2] }
|
50
|
-
b = { 'a' => 1, 'b' => nil }
|
51
|
-
described_class.unpatch!(b, diff).should == a
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'is able to patch array value removal' do
|
55
|
-
a = { 'a' => 1, 'b' => [1, 2] }
|
56
|
-
b = { 'a' => 1 }
|
57
|
-
diff = described_class.diff(a, b)
|
58
|
-
|
59
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
60
|
-
|
61
|
-
a = { 'a' => 1, 'b' => [1, 2] }
|
62
|
-
b = { 'a' => 1 }
|
63
|
-
described_class.unpatch!(b, diff).should == a
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'is able to patch array under hash key with non-word characters' do
|
67
|
-
a = { 'a' => 1, 'b-b' => [1, 2] }
|
68
|
-
b = { 'a' => 1, 'b-b' => [2, 1] }
|
69
|
-
diff = described_class.diff(a, b)
|
70
|
-
|
71
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
72
|
-
|
73
|
-
a = { 'a' => 1, 'b-b' => [1, 2] }
|
74
|
-
b = { 'a' => 1, 'b-b' => [2, 1] }
|
75
|
-
described_class.unpatch!(b, diff).should == a
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'is able to patch hash value removal' do
|
79
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
80
|
-
b = { 'a' => 1 }
|
81
|
-
diff = described_class.diff(a, b)
|
82
|
-
|
83
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
84
|
-
|
85
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
86
|
-
b = { 'a' => 1 }
|
87
|
-
described_class.unpatch!(b, diff).should == a
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'is able to patch value hash <=> {}' do
|
91
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
92
|
-
b = { 'a' => 1, 'b' => {} }
|
93
|
-
diff = described_class.diff(a, b)
|
94
|
-
|
95
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
96
|
-
|
97
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
98
|
-
b = { 'a' => 1, 'b' => {} }
|
99
|
-
described_class.unpatch!(b, diff).should == a
|
100
|
-
end
|
101
|
-
|
102
|
-
it 'is able to patch value hash <=> nil' do
|
103
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
104
|
-
b = { 'a' => 1, 'b' => nil }
|
105
|
-
diff = described_class.diff(a, b)
|
106
|
-
|
107
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
108
|
-
|
109
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
110
|
-
b = { 'a' => 1, 'b' => nil }
|
111
|
-
described_class.unpatch!(b, diff).should == a
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'is able to patch value nil removal' do
|
115
|
-
a = { 'a' => 1, 'b' => nil }
|
116
|
-
b = { 'a' => 1 }
|
117
|
-
diff = described_class.diff(a, b)
|
118
|
-
|
119
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
120
|
-
|
121
|
-
a = { 'a' => 1, 'b' => nil }
|
122
|
-
b = { 'a' => 1 }
|
123
|
-
described_class.unpatch!(b, diff).should == a
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'is able to patch similar objects between arrays' do
|
127
|
-
a = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }, 3]
|
128
|
-
b = [1, { 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5 }]
|
129
|
-
|
130
|
-
diff = described_class.diff(a, b)
|
131
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
132
|
-
|
133
|
-
a = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }, 3]
|
134
|
-
b = [1, { 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5 }]
|
135
|
-
described_class.unpatch!(b, diff).should == a
|
136
|
-
end
|
137
|
-
|
138
|
-
it 'is able to patch similar & equal objects between arrays' do
|
139
|
-
a = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }, { 'x' => 5, 'y' => 6, 'z' => 3 }, 1]
|
140
|
-
b = [1, { 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5 }]
|
141
|
-
|
142
|
-
diff = described_class.diff(a, b)
|
143
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
144
|
-
|
145
|
-
a = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }, { 'x' => 5, 'y' => 6, 'z' => 3 }, 1]
|
146
|
-
b = [1, { 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5 }]
|
147
|
-
described_class.unpatch!(b, diff).should == a
|
148
|
-
end
|
149
|
-
|
150
|
-
it 'is able to patch hash value removal with custom delimiter' do
|
151
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
152
|
-
b = { 'a' => 1, 'b' => { 'b1' => 3 } }
|
153
|
-
diff = described_class.diff(a, b, delimiter: "\n")
|
154
|
-
|
155
|
-
expect(described_class.patch!(a, diff, delimiter: "\n")).to eq(b)
|
156
|
-
|
157
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
158
|
-
b = { 'a' => 1, 'b' => { 'b1' => 3 } }
|
159
|
-
described_class.unpatch!(b, diff, delimiter: "\n").should == a
|
160
|
-
end
|
161
|
-
|
162
|
-
it 'is able to patch when the diff is generated with an array_path' do
|
163
|
-
a = { 'a' => 1, 'b' => 1 }
|
164
|
-
b = { 'a' => 1, 'b' => 2 }
|
165
|
-
diff = described_class.diff(a, b, array_path: true)
|
166
|
-
|
167
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
168
|
-
|
169
|
-
a = { 'a' => 1, 'b' => 1 }
|
170
|
-
b = { 'a' => 1, 'b' => 2 }
|
171
|
-
described_class.unpatch!(b, diff).should == a
|
172
|
-
end
|
173
|
-
|
174
|
-
it 'is able to use non string keys when diff is generated with an array_path' do
|
175
|
-
a = { 'a' => 1, :a => 2, 0 => 3 }
|
176
|
-
b = { 'a' => 5, :a => 6, 0 => 7 }
|
177
|
-
diff = described_class.diff(a, b, array_path: true)
|
178
|
-
|
179
|
-
expect(described_class.patch!(a, diff)).to eq(b)
|
180
|
-
|
181
|
-
a = { 'a' => 1, :a => 2, 0 => 3 }
|
182
|
-
b = { 'a' => 5, :a => 6, 0 => 7 }
|
183
|
-
described_class.unpatch!(b, diff).should == a
|
184
|
-
end
|
185
|
-
end
|
@@ -1,15 +0,0 @@
|
|
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
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
describe Hashdiff do
|
6
|
-
it 'is able to decode property path' do
|
7
|
-
decoded = described_class.send(:decode_property_path, 'a.b[0].c.city[5]')
|
8
|
-
decoded.should == ['a', 'b', 0, 'c', 'city', 5]
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'is able to decode property path with custom delimiter' do
|
12
|
-
decoded = described_class.send(:decode_property_path, "a\tb[0]\tc\tcity[5]", "\t")
|
13
|
-
decoded.should == ['a', 'b', 0, 'c', 'city', 5]
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'is able to tell similiar hash' do
|
17
|
-
a = { 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }
|
18
|
-
b = { 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5 }
|
19
|
-
described_class.similar?(a, b).should be true
|
20
|
-
described_class.similar?(a, b, similarity: 1).should be false
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'is able to tell similiar empty hash' do
|
24
|
-
described_class.similar?({}, {}, similarity: 1).should be true
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'is able to tell similiar empty array' do
|
28
|
-
described_class.similar?([], [], similarity: 1).should be true
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'is able to tell similiar hash with values within tolerance' do
|
32
|
-
a = { 'a' => 1.5, 'b' => 2.25, 'c' => 3, 'd' => 4, 'e' => 5 }
|
33
|
-
b = { 'a' => 1.503, 'b' => 2.22, 'c' => 3, 'e' => 5 }
|
34
|
-
described_class.similar?(a, b, numeric_tolerance: 0.05).should be true
|
35
|
-
described_class.similar?(a, b).should be false
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'is able to tell numbers and strings' do
|
39
|
-
described_class.similar?(1, 2).should be false
|
40
|
-
described_class.similar?('a', 'b').should be false
|
41
|
-
described_class.similar?('a', [1, 2, 3]).should be false
|
42
|
-
described_class.similar?(1, 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5).should be false
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'is able to tell true when similarity == 0.5' do
|
46
|
-
a = { 'value' => 'New1', 'onclick' => 'CreateNewDoc()' }
|
47
|
-
b = { 'value' => 'New', 'onclick' => 'CreateNewDoc()' }
|
48
|
-
|
49
|
-
described_class.similar?(a, b, similarity: 0.5).should be true
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'is able to tell false when similarity == 0.5' do
|
53
|
-
a = { 'value' => 'New1', 'onclick' => 'open()' }
|
54
|
-
b = { 'value' => 'New', 'onclick' => 'CreateNewDoc()' }
|
55
|
-
|
56
|
-
described_class.similar?(a, b, similarity: 0.5).should be false
|
57
|
-
end
|
58
|
-
|
59
|
-
describe '.compare_values' do
|
60
|
-
it 'compares numeric values exactly when no tolerance' do
|
61
|
-
expect(described_class.compare_values(10.004, 10.003)).to be false
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'allows tolerance with numeric values' do
|
65
|
-
expect(described_class.compare_values(10.004, 10.003, numeric_tolerance: 0.01)).to be true
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'compares different objects without tolerance' do
|
69
|
-
expect(described_class.compare_values('hats', 'ninjas')).to be false
|
70
|
-
end
|
71
|
-
|
72
|
-
it 'compares other objects with tolerance' do
|
73
|
-
expect(described_class.compare_values('hats', 'ninjas', numeric_tolerance: 0.01)).to be false
|
74
|
-
end
|
75
|
-
|
76
|
-
it 'compares same objects without tolerance' do
|
77
|
-
expect(described_class.compare_values('horse', 'horse')).to be true
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'compares strings for spaces exactly by default' do
|
81
|
-
expect(described_class.compare_values(' horse', 'horse')).to be false
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'compares strings for capitalization exactly by default' do
|
85
|
-
expect(described_class.compare_values('horse', 'Horse')).to be false
|
86
|
-
end
|
87
|
-
|
88
|
-
it 'strips strings before comparing when requested' do
|
89
|
-
expect(described_class.compare_values(' horse', 'horse', strip: true)).to be true
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'ignores string case when requested' do
|
93
|
-
expect(described_class.compare_values('horse', 'Horse', case_insensitive: true)).to be true
|
94
|
-
end
|
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
|
116
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
-
|
5
|
-
require 'rubygems'
|
6
|
-
require 'rspec'
|
7
|
-
require 'rspec/autorun'
|
8
|
-
|
9
|
-
require 'hashdiff'
|
10
|
-
|
11
|
-
RSpec.configure do |config|
|
12
|
-
config.mock_framework = :rspec
|
13
|
-
|
14
|
-
config.include RSpec::Matchers
|
15
|
-
end
|