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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9b50073f973250c144bb05272510f462dda18a7f5c9a8719156065773fdcaaa
4
- data.tar.gz: 903439ed3ea22994d072201509506232fb77482506494bbb8c8419dc9fb8d546
3
+ metadata.gz: 113e55eeac757365f792e7e2554a8fbb5f16c06cfcf83d4dcf7db268637ef47e
4
+ data.tar.gz: ae222f4980bff17fa8f9fecef25cb1fa54db61503c075cc49b3ef755927ec5f1
5
5
  SHA512:
6
- metadata.gz: e1c00d4710a67688a35197197da93ac3bfabca5f5a51edc152bfbd341a16548e762ddf2290364b7fc3a20e99cdb6d8d909ff84e766468fe623eb3977afd6d90f
7
- data.tar.gz: 2d860929e99458cd89ee60ce877a87557d6bb9d52dcd25e0989f1a0a7b372d026bb646ee2b19498f6637a485f15fcd1e68cf1e1c0dbe666ad5af1792d60a893a
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
- Metrics/LineLength:
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
@@ -4,5 +4,5 @@ source 'http://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :test do
7
- gem 'rake', '< 11'
7
+ gem 'rake'
8
8
  end
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Hashdiff [![Build Status](https://secure.travis-ci.org/liufengyun/hashdiff.svg)](http://travis-ci.org/liufengyun/hashdiff) [![Gem Version](https://badge.fury.io/rb/hashdiff.svg)](http://badge.fury.io/rb/hashdiff)
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
 
@@ -94,9 +94,8 @@ Hashdiff.unpatch!(b, diff).should == a
94
94
 
95
95
  ### Options
96
96
 
97
- There are eight options available: `:delimiter`, `:similarity`,
98
- `:strict`, `:indifferent`, `:numeric_tolerance`, `:strip`, `:case_insensitive`,
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', '~> 2.0')
26
- s.add_development_dependency('rubocop', '~> 0.49.1') # last version that works with ruby 2.0
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hashdiff
4
- VERSION = '1.0.1'.freeze
4
+ VERSION = '1.1.2'.freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hashdiff
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.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: 2020-02-27 00:00:00.000000000 Z
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: '2.0'
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: '2.0'
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: 0.49.1
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: 0.49.1
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: '0'
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: '0'
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.0.6
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
@@ -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
@@ -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
@@ -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
@@ -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