hashdiff 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +28 -0
- data/.rubocop.yml +4 -2
- data/README.md +16 -13
- data/changelog.md +6 -1
- data/hashdiff.gemspec +1 -1
- data/lib/hashdiff/compare_hashes.rb +5 -1
- data/lib/hashdiff/diff.rb +3 -3
- data/lib/hashdiff/version.rb +1 -1
- metadata +7 -15
- 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 -375
- 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: c563bf8c756695c81b5c7ab98a90e59ebdff506544fff2b53c3d93eafff7dc44
|
4
|
+
data.tar.gz: 188a825aaf4f09fb2055fc8ccd34a1904171f0b10cb3e3b2cfd55e9cc096fdc3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be6a233462744f5b7eecea33d0487455ecc4a1b178ea361187ef023bb49d4f609c991689fdb48309694bb5582475d06f21530842a728dc9ab720aa6a0352b54b
|
7
|
+
data.tar.gz: 1126790d1eb87fe3c314bc2360f92f224e88412acf09ed10d964902466e3218c50c68a66972b63d6d92f7963b62001b336b3b5b362eb3ad9a2e50472c66f45b1
|
@@ -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
|
@@ -36,5 +36,7 @@ RSpec/ExampleLength:
|
|
36
36
|
Enabled: false
|
37
37
|
RSpec/DescribeClass:
|
38
38
|
Enabled: false
|
39
|
-
RSpec/
|
39
|
+
RSpec/SpecFilePathFormat:
|
40
|
+
Enabled: false
|
41
|
+
RSpec/NoExpectationExample:
|
40
42
|
Enabled: false
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Hashdiff [![Build Status](https://
|
1
|
+
# Hashdiff [![Build Status](https://github.com/liufengyun/hashdiff/workflows/ci/badge.svg)](https://github.com/liufengyun/hashdiff/actions?query=workflow%3Aci) [![Gem Version](https://badge.fury.io/rb/hashdiff.svg)](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
|
|
@@ -119,23 +119,26 @@ The `:strict` option, which defaults to `true`, specifies whether numeric types
|
|
119
119
|
|
120
120
|
#### `:ignore_keys`
|
121
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. For example:
|
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
123
|
|
124
124
|
```ruby
|
125
|
-
a = { a:
|
126
|
-
b = { a:
|
127
|
-
diff = Hashdiff.diff(a, b, ignore_keys:
|
128
|
-
diff.should == [['~', 'c',
|
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
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:
|
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
132
|
|
133
133
|
```ruby
|
134
|
-
a = { a:
|
135
|
-
b = { a:
|
136
|
-
diff = Hashdiff.diff(a, b)
|
137
|
-
|
138
|
-
|
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
|
+
```
|
139
142
|
|
140
143
|
#### `:indifferent`
|
141
144
|
|
data/changelog.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## v1.1.
|
3
|
+
## v1.1.1 2024-08-02
|
4
|
+
|
5
|
+
* Fix bug in ignore_keys option #88 (@Matzfan)
|
6
|
+
* Exclude spec files from gem package #94 (@amatsuda)
|
7
|
+
|
8
|
+
## v1.1.0 2023-12-14
|
4
9
|
|
5
10
|
* Add ignore_keys option (#86 @Matzfan)
|
6
11
|
* Remove pinned version of rake < 11
|
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']
|
@@ -26,7 +26,11 @@ module Hashdiff
|
|
26
26
|
|
27
27
|
result = []
|
28
28
|
|
29
|
-
opts[:ignore_keys].each
|
29
|
+
opts[:ignore_keys].each do |k|
|
30
|
+
added_keys.delete k
|
31
|
+
common_keys.delete k
|
32
|
+
deleted_keys.delete k
|
33
|
+
end
|
30
34
|
|
31
35
|
# add deleted properties
|
32
36
|
deleted_keys.each do |k|
|
data/lib/hashdiff/diff.rb
CHANGED
@@ -9,7 +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)
|
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
|
13
13
|
# * :indifferent (Boolean) [false] whether to treat hash keys indifferently. Set to true to ignore differences between symbol keys (ie. {a: 1} ~= {'a' => 1})
|
14
14
|
# * :delimiter (String) ['.'] the delimiter used when returning nested key references
|
15
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.
|
@@ -54,7 +54,7 @@ module Hashdiff
|
|
54
54
|
# @param [Array, Hash] obj2
|
55
55
|
# @param [Hash] options the options to use when comparing
|
56
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)
|
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
|
58
58
|
# * :indifferent (Boolean) [false] whether to treat hash keys indifferently. Set to true to ignore differences between symbol keys (ie. {a: 1} ~= {'a' => 1})
|
59
59
|
# * :similarity (Numeric) [0.8] should be between (0, 1]. Meaningful if there are similar hashes in arrays. See {best_diff}.
|
60
60
|
# * :delimiter (String) ['.'] the delimiter used when returning nested key references
|
@@ -93,7 +93,7 @@ module Hashdiff
|
|
93
93
|
|
94
94
|
opts[:prefix] = [] if opts[:array_path] && opts[:prefix] == ''
|
95
95
|
|
96
|
-
opts[:ignore_keys] = [*opts[:ignore_keys]]
|
96
|
+
opts[:ignore_keys] = [*opts[:ignore_keys]]
|
97
97
|
|
98
98
|
opts[:comparison] = block if block_given?
|
99
99
|
|
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.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Liu Fengyun
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bluecloth
|
@@ -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
|
@@ -126,7 +118,7 @@ metadata:
|
|
126
118
|
documentation_uri: https://www.rubydoc.info/gems/hashdiff
|
127
119
|
homepage_uri: https://github.com/liufengyun/hashdiff
|
128
120
|
source_code_uri: https://github.com/liufengyun/hashdiff
|
129
|
-
post_install_message:
|
121
|
+
post_install_message:
|
130
122
|
rdoc_options: []
|
131
123
|
require_paths:
|
132
124
|
- lib
|
@@ -141,8 +133,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
141
133
|
- !ruby/object:Gem::Version
|
142
134
|
version: '0'
|
143
135
|
requirements: []
|
144
|
-
rubygems_version: 3.
|
145
|
-
signing_key:
|
136
|
+
rubygems_version: 3.3.5
|
137
|
+
signing_key:
|
146
138
|
specification_version: 4
|
147
139
|
summary: Hashdiff is a diff lib to compute the smallest difference between two hashes.
|
148
140
|
test_files: []
|
@@ -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,375 +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
|
-
context 'with the ignore_keys option' do
|
53
|
-
a = { a: 1, b: { d: 2, a: 3 }, c: 4 }
|
54
|
-
b = { a: 2, b: { d: 2, a: 7 }, c: 5 }
|
55
|
-
|
56
|
-
it 'ignores a single key' do
|
57
|
-
diff = described_class.diff(a, b, ignore_keys: :a)
|
58
|
-
diff.should == [['~', 'c', 4, 5]]
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'ignores an array of keys' do
|
62
|
-
diff = described_class.diff(a, b, ignore_keys: %i[a c])
|
63
|
-
diff.should == []
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'ignores string vs symbol differences, when indifferent is true' do
|
68
|
-
diff = described_class.diff({ 'a' => 2, :b => 2 }, { :a => 2, 'b' => 2, :c => 3 }, indifferent: true)
|
69
|
-
diff.should == [['+', 'c', 3]]
|
70
|
-
end
|
71
|
-
|
72
|
-
it 'is able to diff changes in hash value' do
|
73
|
-
diff = described_class.diff({ 'a' => 2, 'b' => 3, 'c' => ' hello' }, 'a' => 2, 'b' => 4, 'c' => 'hello')
|
74
|
-
diff.should == [['~', 'b', 3, 4], ['~', 'c', ' hello', 'hello']]
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'is able to diff changes in hash value which is array' do
|
78
|
-
diff = described_class.diff({ 'a' => 2, 'b' => [1, 2, 3] }, 'a' => 2, 'b' => [1, 3, 4])
|
79
|
-
diff.should == [['-', 'b[1]', 2], ['+', 'b[2]', 4]]
|
80
|
-
end
|
81
|
-
|
82
|
-
it 'is able to diff changes in hash value which is hash' do
|
83
|
-
diff = described_class.diff({ 'a' => { 'x' => 2, 'y' => 3, 'z' => 4 }, 'b' => { 'x' => 3, 'z' => 45 } },
|
84
|
-
'a' => { 'y' => 3 }, 'b' => { 'y' => 3, 'z' => 30 })
|
85
|
-
diff.should == [['-', 'a.x', 2], ['-', 'a.z', 4], ['-', 'b.x', 3], ['~', 'b.z', 45, 30], ['+', 'b.y', 3]]
|
86
|
-
end
|
87
|
-
|
88
|
-
it 'is able to best diff similar objects in array' do
|
89
|
-
diff = described_class.best_diff({ 'a' => [{ 'x' => 2, 'y' => 3, 'z' => 4 }, { 'x' => 11, 'y' => 22, 'z' => 33 }], 'b' => { 'x' => 3, 'z' => 45 } },
|
90
|
-
'a' => [{ 'y' => 3 }, { 'x' => 11, 'z' => 33 }], 'b' => { 'y' => 22 })
|
91
|
-
diff.should == [['-', 'a[0].x', 2], ['-', 'a[0].z', 4], ['-', 'a[1].y', 22], ['-', 'b.x', 3], ['-', 'b.z', 45], ['+', 'b.y', 22]]
|
92
|
-
end
|
93
|
-
|
94
|
-
it 'is able to diff addition of key value pair' do
|
95
|
-
a = { 'a' => 3, 'c' => 11, 'd' => 45, 'e' => 100, 'f' => 200 }
|
96
|
-
b = { 'a' => 3, 'c' => 11, 'd' => 45, 'e' => 100, 'f' => 200, 'g' => 300 }
|
97
|
-
|
98
|
-
diff = described_class.diff(a, b)
|
99
|
-
expect(diff).to eq([['+', 'g', 300]])
|
100
|
-
|
101
|
-
diff = described_class.diff(b, a)
|
102
|
-
diff.should == [['-', 'g', 300]]
|
103
|
-
end
|
104
|
-
|
105
|
-
it 'is able to diff value type changes' do
|
106
|
-
a = { 'a' => 3 }
|
107
|
-
b = { 'a' => { 'a1' => 1, 'a2' => 2 } }
|
108
|
-
|
109
|
-
diff = described_class.diff(a, b)
|
110
|
-
expect(diff).to eq([['~', 'a', 3, { 'a1' => 1, 'a2' => 2 }]])
|
111
|
-
|
112
|
-
diff = described_class.diff(b, a)
|
113
|
-
diff.should == [['~', 'a', { 'a1' => 1, 'a2' => 2 }, 3]]
|
114
|
-
end
|
115
|
-
|
116
|
-
it 'is able to diff value changes: array <=> []' do
|
117
|
-
a = { 'a' => 1, 'b' => [1, 2] }
|
118
|
-
b = { 'a' => 1, 'b' => [] }
|
119
|
-
|
120
|
-
diff = described_class.diff(a, b)
|
121
|
-
diff.should == [['-', 'b[1]', 2], ['-', 'b[0]', 1]]
|
122
|
-
end
|
123
|
-
|
124
|
-
it 'is able to diff value changes: array <=> nil' do
|
125
|
-
a = { 'a' => 1, 'b' => [1, 2] }
|
126
|
-
b = { 'a' => 1, 'b' => nil }
|
127
|
-
|
128
|
-
diff = described_class.diff(a, b)
|
129
|
-
diff.should == [['~', 'b', [1, 2], nil]]
|
130
|
-
end
|
131
|
-
|
132
|
-
it 'is able to diff value chagnes: remove array completely' do
|
133
|
-
a = { 'a' => 1, 'b' => [1, 2] }
|
134
|
-
b = { 'a' => 1 }
|
135
|
-
|
136
|
-
diff = described_class.diff(a, b)
|
137
|
-
diff.should == [['-', 'b', [1, 2]]]
|
138
|
-
end
|
139
|
-
|
140
|
-
it 'is able to diff value changes: remove whole hash' do
|
141
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
142
|
-
b = { 'a' => 1 }
|
143
|
-
|
144
|
-
diff = described_class.diff(a, b)
|
145
|
-
diff.should == [['-', 'b', { 'b1' => 1, 'b2' => 2 }]]
|
146
|
-
end
|
147
|
-
|
148
|
-
it 'is able to diff value changes: hash <=> {}' do
|
149
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
150
|
-
b = { 'a' => 1, 'b' => {} }
|
151
|
-
|
152
|
-
diff = described_class.diff(a, b)
|
153
|
-
diff.should == [['-', 'b.b1', 1], ['-', 'b.b2', 2]]
|
154
|
-
end
|
155
|
-
|
156
|
-
it 'is able to diff value changes: hash <=> nil' do
|
157
|
-
a = { 'a' => 1, 'b' => { 'b1' => 1, 'b2' => 2 } }
|
158
|
-
b = { 'a' => 1, 'b' => nil }
|
159
|
-
|
160
|
-
diff = described_class.diff(a, b)
|
161
|
-
diff.should == [['~', 'b', { 'b1' => 1, 'b2' => 2 }, nil]]
|
162
|
-
end
|
163
|
-
|
164
|
-
it 'is able to diff similar objects in array' do
|
165
|
-
a = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }, 3]
|
166
|
-
b = [1, { 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5 }]
|
167
|
-
|
168
|
-
diff = described_class.diff(a, b)
|
169
|
-
diff.should == [['-', '[0].d', 4], ['+', '[0]', 1], ['-', '[2]', 3]]
|
170
|
-
end
|
171
|
-
|
172
|
-
it 'is able to diff similar & equal objects in array' do
|
173
|
-
a = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }, { 'x' => 5, 'y' => 6, 'z' => 3 }, 3]
|
174
|
-
b = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5 }, 3]
|
175
|
-
|
176
|
-
diff = described_class.diff(a, b)
|
177
|
-
diff.should == [['-', '[0].d', 4], ['-', '[1]', { 'x' => 5, 'y' => 6, 'z' => 3 }]]
|
178
|
-
end
|
179
|
-
|
180
|
-
it 'uses custom delimiter when provided' do
|
181
|
-
a = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5 }, { 'x' => 5, 'y' => 6, 'z' => 3 }, 3]
|
182
|
-
b = [{ 'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5 }, 3]
|
183
|
-
|
184
|
-
diff = described_class.diff(a, b, similarity: 0.8, delimiter: "\t")
|
185
|
-
diff.should == [['-', "[0]\td", 4], ['-', '[1]', { 'x' => 5, 'y' => 6, 'z' => 3 }]]
|
186
|
-
end
|
187
|
-
|
188
|
-
context 'when :numeric_tolerance requested' do
|
189
|
-
it 'is able to diff changes in hash value' do
|
190
|
-
a = { 'a' => 0.558, 'b' => 0.0, 'c' => 0.65, 'd' => 'fin' }
|
191
|
-
b = { 'a' => 0.557, 'b' => 'hats', 'c' => 0.67, 'd' => 'fin' }
|
192
|
-
|
193
|
-
diff = described_class.diff(a, b, numeric_tolerance: 0.01)
|
194
|
-
expect(diff).to eq([['~', 'b', 0.0, 'hats'], ['~', 'c', 0.65, 0.67]])
|
195
|
-
|
196
|
-
diff = described_class.diff(b, a, numeric_tolerance: 0.01)
|
197
|
-
diff.should == [['~', 'b', 'hats', 0.0], ['~', 'c', 0.67, 0.65]]
|
198
|
-
end
|
199
|
-
|
200
|
-
it 'is able to diff changes in nested values' do
|
201
|
-
a = { 'a' => { 'x' => 0.4, 'y' => 0.338 }, 'b' => [13, 68.03] }
|
202
|
-
b = { 'a' => { 'x' => 0.6, 'y' => 0.341 }, 'b' => [14, 68.025] }
|
203
|
-
|
204
|
-
diff = described_class.diff(a, b, numeric_tolerance: 0.01)
|
205
|
-
expect(diff).to eq([['~', 'a.x', 0.4, 0.6], ['-', 'b[0]', 13], ['+', 'b[0]', 14]])
|
206
|
-
|
207
|
-
diff = described_class.diff(b, a, numeric_tolerance: 0.01)
|
208
|
-
diff.should == [['~', 'a.x', 0.6, 0.4], ['-', 'b[0]', 14], ['+', 'b[0]', 13]]
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
context 'when :strip requested' do
|
213
|
-
it 'strips strings before comparing' do
|
214
|
-
a = { 'a' => ' foo', 'b' => 'fizz buzz' }
|
215
|
-
b = { 'a' => 'foo', 'b' => 'fizzbuzz' }
|
216
|
-
diff = described_class.diff(a, b, strip: true)
|
217
|
-
diff.should == [['~', 'b', 'fizz buzz', 'fizzbuzz']]
|
218
|
-
end
|
219
|
-
|
220
|
-
it 'strips nested strings before comparing' do
|
221
|
-
a = { 'a' => { 'x' => ' foo' }, 'b' => ['fizz buzz', 'nerf'] }
|
222
|
-
b = { 'a' => { 'x' => 'foo' }, 'b' => %w[fizzbuzz nerf] }
|
223
|
-
diff = described_class.diff(a, b, strip: true)
|
224
|
-
diff.should == [['-', 'b[0]', 'fizz buzz'], ['+', 'b[0]', 'fizzbuzz']]
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
context 'when :case_insensitive requested' do
|
229
|
-
it 'strips strings before comparing' do
|
230
|
-
a = { 'a' => 'Foo', 'b' => 'fizz buzz' }
|
231
|
-
b = { 'a' => 'foo', 'b' => 'fizzBuzz' }
|
232
|
-
diff = described_class.diff(a, b, case_insensitive: true)
|
233
|
-
diff.should == [['~', 'b', 'fizz buzz', 'fizzBuzz']]
|
234
|
-
end
|
235
|
-
|
236
|
-
it 'ignores case on nested strings before comparing' do
|
237
|
-
a = { 'a' => { 'x' => 'Foo' }, 'b' => ['fizz buzz', 'nerf'] }
|
238
|
-
b = { 'a' => { 'x' => 'foo' }, 'b' => %w[fizzbuzz nerf] }
|
239
|
-
diff = described_class.diff(a, b, case_insensitive: true)
|
240
|
-
diff.should == [['-', 'b[0]', 'fizz buzz'], ['+', 'b[0]', 'fizzbuzz']]
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
context 'when both :strip and :numeric_tolerance requested' do
|
245
|
-
it 'applies filters to proper object types' do
|
246
|
-
a = { 'a' => ' foo', 'b' => 35, 'c' => 'bar', 'd' => 'baz' }
|
247
|
-
b = { 'a' => 'foo', 'b' => 35.005, 'c' => 'bar', 'd' => 18.5 }
|
248
|
-
diff = described_class.diff(a, b, strict: false, numeric_tolerance: 0.01, strip: true)
|
249
|
-
diff.should == [['~', 'd', 'baz', 18.5]]
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
context 'when both :strip and :case_insensitive requested' do
|
254
|
-
it 'applies both filters to strings' do
|
255
|
-
a = { 'a' => ' Foo', 'b' => 'fizz buzz' }
|
256
|
-
b = { 'a' => 'foo', 'b' => 'fizzBuzz' }
|
257
|
-
diff = described_class.diff(a, b, case_insensitive: true, strip: true)
|
258
|
-
diff.should == [['~', 'b', 'fizz buzz', 'fizzBuzz']]
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
context 'with custom comparison' do
|
263
|
-
let(:a) { { 'a' => 'car', 'b' => 'boat', 'c' => 'plane' } }
|
264
|
-
let(:b) { { 'a' => 'bus', 'b' => 'truck', 'c' => ' plan' } }
|
265
|
-
|
266
|
-
it 'compares using proc specified in block' do
|
267
|
-
diff = described_class.diff(a, b) do |prefix, obj1, obj2|
|
268
|
-
case prefix
|
269
|
-
when /a|b|c/
|
270
|
-
obj1.length == obj2.length
|
271
|
-
end
|
272
|
-
end
|
273
|
-
diff.should == [['~', 'b', 'boat', 'truck']]
|
274
|
-
end
|
275
|
-
|
276
|
-
it 'yields added keys' do
|
277
|
-
x = { 'a' => 'car', 'b' => 'boat' }
|
278
|
-
y = { 'a' => 'car' }
|
279
|
-
|
280
|
-
diff = described_class.diff(x, y) do |prefix, _obj1, _obj2|
|
281
|
-
case prefix
|
282
|
-
when /b/
|
283
|
-
true
|
284
|
-
end
|
285
|
-
end
|
286
|
-
diff.should == []
|
287
|
-
end
|
288
|
-
|
289
|
-
it 'compares with both proc and :strip when both provided' do
|
290
|
-
diff = described_class.diff(a, b, strip: true) do |prefix, obj1, obj2|
|
291
|
-
case prefix
|
292
|
-
when 'a'
|
293
|
-
obj1.length == obj2.length
|
294
|
-
end
|
295
|
-
end
|
296
|
-
diff.should == [['~', 'b', 'boat', 'truck'], ['~', 'c', 'plane', ' plan']]
|
297
|
-
end
|
298
|
-
|
299
|
-
it 'compares nested arrays using proc specified in block' do
|
300
|
-
a = { a: 'car', b: %w[boat plane] }
|
301
|
-
b = { a: 'bus', b: ['truck', ' plan'] }
|
302
|
-
|
303
|
-
diff = described_class.diff(a, b) do |path, obj1, obj2|
|
304
|
-
case path
|
305
|
-
when 'b[*]'
|
306
|
-
obj1.length == obj2.length
|
307
|
-
end
|
308
|
-
end
|
309
|
-
|
310
|
-
expect(diff).to eq [['~', 'a', 'car', 'bus'], ['~', 'b[1]', 'plane', ' plan'], ['-', 'b[0]', 'boat'], ['+', 'b[0]', 'truck']]
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
context 'when :array_path is true' do
|
315
|
-
it 'returns the diff path in an array rather than a string' do
|
316
|
-
x = { 'a' => 'foo' }
|
317
|
-
y = { 'a' => 'bar' }
|
318
|
-
diff = described_class.diff(x, y, array_path: true)
|
319
|
-
|
320
|
-
diff.should == [['~', ['a'], 'foo', 'bar']]
|
321
|
-
end
|
322
|
-
|
323
|
-
it 'shows array indexes in paths' do
|
324
|
-
x = { 'a' => [0, 1, 2] }
|
325
|
-
y = { 'a' => [0, 1, 2, 3] }
|
326
|
-
|
327
|
-
diff = described_class.diff(x, y, array_path: true)
|
328
|
-
|
329
|
-
diff.should == [['+', ['a', 3], 3]]
|
330
|
-
end
|
331
|
-
|
332
|
-
it 'shows differences with string and symbol keys' do
|
333
|
-
x = { 'a' => 'foo' }
|
334
|
-
y = { a: 'bar' }
|
335
|
-
|
336
|
-
diff = described_class.diff(x, y, array_path: true)
|
337
|
-
diff.should == [['-', ['a'], 'foo'], ['+', [:a], 'bar']]
|
338
|
-
end
|
339
|
-
|
340
|
-
it 'supports other key types' do
|
341
|
-
time = Time.now
|
342
|
-
x = { time => 'foo' }
|
343
|
-
y = { 0 => 'bar' }
|
344
|
-
|
345
|
-
diff = described_class.diff(x, y, array_path: true)
|
346
|
-
diff.should == [['-', [time], 'foo'], ['+', [0], 'bar']]
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
context 'when :use_lcs is false' do
|
351
|
-
it 'shows items in an array as changed' do
|
352
|
-
x = %i[a b]
|
353
|
-
y = %i[c d]
|
354
|
-
diff = described_class.diff(x, y, use_lcs: false)
|
355
|
-
|
356
|
-
diff.should == [['~', '[0]', :a, :c], ['~', '[1]', :b, :d]]
|
357
|
-
end
|
358
|
-
|
359
|
-
it 'shows additions to arrays' do
|
360
|
-
x = { a: [0] }
|
361
|
-
y = { a: [0, 1] }
|
362
|
-
diff = described_class.diff(x, y, use_lcs: false)
|
363
|
-
|
364
|
-
diff.should == [['+', 'a[1]', 1]]
|
365
|
-
end
|
366
|
-
|
367
|
-
it 'shows changes to nested arrays' do
|
368
|
-
x = { a: [[0, 1]] }
|
369
|
-
y = { a: [[1, 2]] }
|
370
|
-
diff = described_class.diff(x, y, use_lcs: false)
|
371
|
-
|
372
|
-
diff.should == [['~', 'a[0][0]', 0, 1], ['~', 'a[0][1]', 1, 2]]
|
373
|
-
end
|
374
|
-
end
|
375
|
-
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
|