hash_deep_diff 0.3.3 → 0.5.0

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: fe3724394c10b36b7537f35bce5c7f079cae0ee3344062a78bdfe7256363624b
4
- data.tar.gz: e9baa112b03013b91f75ebea9f243a0a6d51ca746ca8c8e66ea6fa38803198fd
3
+ metadata.gz: 3aab07a4c397dbc84845d5f0faff052bbae89eb8be4bf24a5ed9df0d5129c3da
4
+ data.tar.gz: 2ea89b301d808655625e0fa6a9c5028b9d194bba3c4dc54cf1f5c6859c59fc6f
5
5
  SHA512:
6
- metadata.gz: b7393bb437eddfb8e0ed66d4eba3b4135e221c7c2262df14aa4509dba98345c4766b598bd3c3f11f311c84b25b4f9f0b2bca3dfc5083c023c87c582fa607761a
7
- data.tar.gz: 57708513050ac1e63c0ce64b9650e0ae0e3128127cbb4dfc5b248cc3caca3267cbd26bed7b939be4413c11f1a7038f5503aef143119a09e336cc113324ae182a
6
+ metadata.gz: f0a05ee023dbbd4d8551b5065a57a65b645ca3b0a864eace0de91b10ec08da77b0a15df6422af0f5ce0afd4459ca141e227398f9560198f6d350963fac1a99f0
7
+ data.tar.gz: 279185ca8464992a65f2fff626b6ee90f44ac7b7e98706e34443390ad24457961b642e5b2ebee10c4c8e3e5c72b7bfee454dd644f8f7ef68db5872c426137dc7
@@ -0,0 +1,39 @@
1
+ name: CI
2
+ on:
3
+ pull_request:
4
+
5
+ jobs:
6
+ minitest:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - name: Checkout Repo
10
+ uses: actions/checkout@v2
11
+
12
+ - name: Install Ruby
13
+ uses: ruby/setup-ruby@v1
14
+ with:
15
+ ruby-version: 2.6
16
+ bundler-cache: false
17
+
18
+ - name: Install Gems
19
+ run: bin/bundle
20
+
21
+ - name: Run Minitest
22
+ run: bin/rake test
23
+ rubocop:
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - name: Checkout Repo
27
+ uses: actions/checkout@v2
28
+
29
+ - name: Install Ruby
30
+ uses: ruby/setup-ruby@v1
31
+ with:
32
+ ruby-version: 2.6
33
+ bundler-cache: false
34
+
35
+ - name: Install Gems
36
+ run: bin/bundle
37
+
38
+ - name: Run Rubocop
39
+ run: bin/rubocop
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --private
data/CHANGELOG.md ADDED
File without changes
data/Guardfile CHANGED
@@ -20,7 +20,6 @@
20
20
  guard :rubocop do
21
21
  watch(/.+\.rb$/)
22
22
  watch(/.+\.gemspec$/)
23
- watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
24
23
  end
25
24
 
26
25
  guard :minitest do
@@ -31,3 +30,7 @@ guard :minitest do
31
30
  watch(%r{^test/test_helper\.rb$}) { 'test' }
32
31
  watch(%r{^test/support/.*$}) { 'test' }
33
32
  end
33
+
34
+ guard 'yard' do
35
+ watch(%r{lib/.+\.rb})
36
+ end
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 - 2022 Bohdan Pohorilets
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,4 +1,9 @@
1
1
  # HashDeepDiff
2
+ [![Gem
3
+ Version](https://badge.fury.io/rb/hash_deep_diff.svg)](https://badge.fury.io/rb/hash_deep_diff) ![GitHub Workflow
4
+ Status](https://img.shields.io/github/workflow/status/bpohoriletz/hash_deep_diff/CI)
5
+ ![GitHub](https://img.shields.io/github/license/bpohoriletz/hash_deep_diff)
6
+
2
7
 
3
8
  Find the exact difference between two Hash objects and build a report to visualize it
4
9
 
@@ -29,10 +34,12 @@ HashDeepDiff::Comparison.new(left, right).report
29
34
  ```
30
35
  ```diff
31
36
  - left[a] = a
32
- + right[a] = b
37
+ + left[a] = b
33
38
  ```
39
+ please see [Documentation](https://rdoc.info/gems/hash_deep_diff) for
40
+ more info
34
41
 
35
42
  ## Contributing
36
43
 
37
44
  Bug reports and pull requests are welcome on GitHub at [bpohoriletz](https://github.com/bpohoriletz/hash_deep_diff).
38
-
45
+
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rake/testtask'
5
5
 
6
6
  Rake::TestTask.new do |t|
7
7
  t.libs << 'test'
8
- t.test_files = FileList['test/**/*_spec.rb']
8
+ t.test_files = FileList['test/hash_deep_diff/test_*.rb']
9
9
  t.verbose = true
10
10
  end
11
11
 
data/bin/yard ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'yard' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("yard", "yard")
data/bin/yardoc ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'yardoc' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("yard", "yardoc")
data/bin/yri ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'yri' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
23
+
24
+ require "rubygems"
25
+ require "bundler/setup"
26
+
27
+ load Gem.bin_path("yard", "yri")
@@ -39,10 +39,11 @@ Gem::Specification.new do |spec|
39
39
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
40
40
  spec.require_paths = ['lib']
41
41
 
42
- spec.add_development_dependency 'bundler', '~> 1.17.2'
42
+ spec.add_development_dependency 'bundler', '~> 2.3.11'
43
43
  spec.add_development_dependency 'guard', '~> 2.18.0'
44
44
  spec.add_development_dependency 'guard-minitest', '~> 2.4.6'
45
45
  spec.add_development_dependency 'guard-rubocop', '~> 1.5.0'
46
+ spec.add_development_dependency 'guard-yard', '~> 2.2.1'
46
47
  spec.add_development_dependency 'minitest', '~> 5.15.0'
47
48
  spec.add_development_dependency 'minitest-focus', '~> 1.3.1'
48
49
  spec.add_development_dependency 'minitest-reporters', '~> 1.5.0'
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module HashDeepDiff
6
+ # This module includes behavior that is needed to use instances of Delta instead of Hash
7
+ # in this gem
8
+ module ActsAsHash
9
+ # @param [Object] base a hook that is invoked when module is included in a class
10
+ def self.included(base)
11
+ base.extend Forwardable
12
+ base.def_delegators :@delta, :==, :each_with_object, :each_key, :[],
13
+ :to_a, :empty?, :keys
14
+ base.include InstanceMethods
15
+ end
16
+
17
+ # We assume that the class will initialize instance variable +@delta+ that will return
18
+ # a representation of an instance of a class as a +Hash+ object
19
+ module InstanceMethods
20
+ # a +Hash+ representation of an object
21
+ def to_h
22
+ to_hash
23
+ end
24
+
25
+ # a +Hash+ representation of an object
26
+ def to_hash
27
+ @delta
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,72 +1,104 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'delta/left'
4
- require_relative 'delta/inner'
5
- require_relative 'delta/right'
3
+ require_relative 'delta'
6
4
 
7
- # :nodoc:
8
5
  module HashDeepDiff
9
- # :nodoc:
6
+ # Representation of the recursive difference between two hashes
7
+ # main parts are
8
+ # * path - empty for original hashes, otherwise path to values being compared
9
+ # * left - basically left.dig(path), left value of two being compared
10
+ # * right - basically right.dig(path), right value of two being compared
11
+ #
12
+ # Examples:
13
+ # - { one: :a } compared with { one: :b } does not have nesting so we compare keys and values
14
+ # - { one: { two: :a, zero: z } } compared with { one: { two: :b, three: :c } } has nesting, so is represented as
15
+ # - { two: :a } compared with { two: :b, three: :c }, as there is no more nesting we compare keys and values
16
+ # and have the following comparisons
17
+ # { one: { two: :a } } compared to { one: { two: :b } } - value was changed
18
+ # i.e :a vas replaced with :b on path [:one, :two]
19
+ # { one: { zero: z } } compared to NO_VALUE - value was deleted
20
+ # i.e :z vas replaced with NO_VALUE on path [:one, :zero]
21
+ # NO_VALUE compared to { one: { three: :c } } compared - value was added
22
+ # i.e NO_VALUE vas replaced with :c on path [:one, :three]
23
+ # [
24
+ # #<HashDeepDiff::Delta
25
+ # @delta={:two=>{:left=>:a, :right=>:b}},
26
+ # @prefix=[:one],
27
+ # @value={:left=>:a, :right=>:b}>,
28
+ # #<HashDeepDiff::Delta
29
+ # @delta={:zero=>{:left=>:z, :right=>HashDeepDiff::NO_VALUE}},
30
+ # @prefix=[:one],
31
+ # @value={:left=>:z, :right=>HashDeepDiff::NO_VALUE}>,
32
+ # #<HashDeepDiff::Delta
33
+ # @delta={:three=>{:left=>HashDeepDiff::NO_VALUE, :right=>:c}},
34
+ # @prefix=[:one],
35
+ # @value={:left=>HashDeepDiff::NO_VALUE, :right=>:c}>
36
+ # ]
10
37
  class Comparison
38
+ # @!attribute [r] left
39
+ # @return [Hash] original version of the Hash
40
+ # @!attribute [r] right
41
+ # @return [Hash] Hash that the original is compared to
42
+ # @!attribute [r] path
43
+ # @return [Array<Object>] to a compared Hashes (is empty for top-level comparison)
11
44
  attr_reader :left, :right, :path
12
45
 
13
- def diff(&block)
14
- left_delta + deep_delta(&block) + right_delta
15
- end
16
-
46
+ # @return [String]
17
47
  def report
18
48
  diff.join("\n")
19
49
  end
20
50
 
51
+ # @return [Array<HashDeepDiff::Delta>]
52
+ def diff
53
+ comparison.flat_map do |delta|
54
+ # if there are nested hashes we need to compare them furter
55
+ # if no we return difference between values (HashDeepDiff::Delta)
56
+ delta.complex? ? self.class.new(delta.left, delta.right, delta.path).diff : delta
57
+ end
58
+ end
59
+
21
60
  private
22
61
 
23
- def initialize(left, right, path = [])
62
+ # @param [Hash] left original version of the hash
63
+ # @param [Hash] right new version of the hash
64
+ # @param [Array] prefix keys to fetch current comparison (not empty for nested comparisons)
65
+ def initialize(left, right, prefix = [])
24
66
  @left = left.to_hash
25
67
  @right = right.to_hash
26
- @path = path.to_ary
68
+ @path = prefix.to_ary
27
69
  end
28
70
 
29
- def deep_delta(&block)
30
- delta(&block).flat_map do |diff|
31
- if diff.complex?
32
- self.class.new(diff.left, diff.right, diff.path).diff
33
- else
34
- diff
35
- end
71
+ # @return [Array<HashDeepDiff::Delta>]
72
+ def comparison
73
+ common_keys.each_with_object([]) do |key, memo|
74
+ next if values_equal?(key)
75
+
76
+ memo << Delta.new(path: path + [key], value: { left: value_left(key), right: value_right(key) })
36
77
  end
37
78
  end
38
79
 
39
- def left_delta
40
- left_diff_keys.map { |key| Delta::Left.new(path: path + [key], value: left[key]) }
80
+ # @param [Object] key the key which value we're currently comparing
81
+ # @return [Bool]
82
+ def values_equal?(key)
83
+ value_right(key).instance_of?(value_left(key).class) && (value_right(key) == value_left(key))
41
84
  end
42
85
 
43
- def right_delta
44
- right_diff_keys.map { |key| Delta::Right.new(path: path + [key], value: right[key]) }
86
+ # Original value
87
+ # @param [Object] key the key which value we're currently comparing
88
+ def value_left(key)
89
+ left[key] || NO_VALUE
45
90
  end
46
91
 
47
- def delta(&block)
48
- block ||= ->(val) { val }
49
-
50
- common_keys.each_with_object([]) do |key, memo|
51
- value_left = block.call(left[key])
52
- value_right = block.call(right[key])
53
-
54
- next if value_right.instance_of?(value_left.class) && (value_right == value_left)
55
-
56
- memo << Delta::Inner.new(path: path + [key], value: { left: value_left, right: value_right })
57
- end
92
+ # Value we compare to
93
+ # @param [Object] key the key which value we're currently comparing
94
+ def value_right(key)
95
+ right[key] || NO_VALUE
58
96
  end
59
97
 
98
+ # All keys from both original and compared objects
99
+ # @return [Array]
60
100
  def common_keys
61
- left.keys & right.keys
62
- end
63
-
64
- def right_diff_keys
65
- right.keys - left.keys
66
- end
67
-
68
- def left_diff_keys
69
- left.keys - right.keys
101
+ (left.keys + right.keys).uniq
70
102
  end
71
103
  end
72
104
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'acts_as_hash'
4
+ require_relative 'report'
5
+
6
+ module HashDeepDiff
7
+ # Representation of the diff of two values
8
+ # examples:
9
+ # - diff of { a: a } and {} is { a: { left: a, right: HashDeepDiff::NO_VALUE } }
10
+ # - diff of { a: a } and { a: b } is { a: { left: a, right: b } }
11
+ # - diff of {} and { a: b } is { a: { left: HashDeepDiff::NO_VALUE, right: b } }
12
+ class Delta
13
+ include ActsAsHash
14
+
15
+ # Visual representation of additions and deletiond at given +path+
16
+ # @return [String]
17
+ def to_str
18
+ [deletion, addition].compact.join("\n")
19
+ end
20
+
21
+ # Returns true if we have nested Hashes
22
+ # @return [Bool]
23
+ def complex?
24
+ left.respond_to?(:to_hash) && right.respond_to?(:to_hash)
25
+ end
26
+
27
+ # Keys needed to fetch values that we're comparing
28
+ # @return [Array]
29
+ def path
30
+ @prefix + [@delta.keys.first]
31
+ end
32
+
33
+ # Original value
34
+ def left
35
+ @value[:left]
36
+ end
37
+
38
+ # Value we compare to
39
+ def right
40
+ @value[:right]
41
+ end
42
+
43
+ # See {#to_str}
44
+ def to_s
45
+ to_str
46
+ end
47
+
48
+ private
49
+
50
+ # @param [Array, Object] path list of keys to fetch values we're comparing
51
+ # @param [Hash<(:left, :right), Object>] value +Hash+ object with two keys - :left and :right,
52
+ # that represents compared original value (at :left) and value we compare to (at :right)
53
+ def initialize(path:, value:)
54
+ # TOFIX this may prohibit usage of hashes with Array keys
55
+ # TOFIX extract path to a separate object
56
+ if path.respond_to?(:to_ary)
57
+ @delta = { path[-1] => value }
58
+ @value = value
59
+ @prefix = path[0..-2]
60
+ else
61
+ @delta = { path => value }
62
+ @value = value
63
+ @prefix = []
64
+ end
65
+ end
66
+
67
+ # Visual representation of additions
68
+ # @return [NillClass, String]
69
+ def deletion
70
+ return nil if left == NO_VALUE
71
+
72
+ Report.new(path: path, value: left, mode: Report::Mode::DELETION)
73
+ end
74
+
75
+ # Visual representation of deletions
76
+ # @return [NillClass, String]
77
+ def addition
78
+ return nil if right == NO_VALUE
79
+
80
+ Report.new(path: path, value: right)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HashDeepDiff
4
+ # Visual representation of the difference between two values
5
+ class Report
6
+ # We have two cases
7
+ # * added - when value on the left is missing
8
+ # * deleted - when the value on the right is missing
9
+ module Mode
10
+ # for additions
11
+ ADDITION = '+left'
12
+ # for deletions
13
+ DELETION = '-left'
14
+ end
15
+
16
+ # A report with all additions and deletions
17
+ # @return [String]
18
+ def to_str
19
+ if @value.respond_to?(:to_hash) && !@value.empty?
20
+ [@mode, diff_prefix, ' = ', "{}\n"].join +
21
+ @value.keys.map do |key|
22
+ Report.new(path: @path + [key], value: @value[key], mode: @mode)
23
+ end.join("\n")
24
+ else
25
+ [@mode, diff_prefix, ' = ', @value.to_s].join
26
+ end
27
+ end
28
+
29
+ # A report with all additions and deletions
30
+ # @return [String]
31
+ def to_s
32
+ to_str
33
+ end
34
+
35
+ private
36
+
37
+ # @param [Array] path Keys from compared objects to fetch the compared values
38
+ # @param [Object] value value from a compared object at +@path+
39
+ # @param [Mode::ADDITION, Mode::DELETION] mode
40
+ def initialize(path:, value:, mode: Mode::ADDITION)
41
+ @path = path.to_ary
42
+ @value = value
43
+ @mode = mode
44
+ end
45
+
46
+ # Visual representation of keys from compared objects needed to fetch the compared values
47
+ # @return [String]
48
+ def diff_prefix
49
+ # TOFIX poor naming
50
+ @path.map { |key| "[#{key}]" }.join
51
+ end
52
+ end
53
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HashDeepDiff
4
- VERSION = '0.3.3'
4
+ # Version of a gem
5
+ VERSION = '0.5.0'
5
6
  end
@@ -3,6 +3,8 @@
3
3
  require 'hash_deep_diff/version'
4
4
  require 'hash_deep_diff/comparison'
5
5
 
6
+ # Global namespace
6
7
  module HashDeepDiff
7
- class Error < StandardError; end
8
+ # value was not found
9
+ NO_VALUE = Class.new(NilClass)
8
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hash_deep_diff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bohdan Pohorilets
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-13 00:00:00.000000000 Z
11
+ date: 2022-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.17.2
19
+ version: 2.3.11
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.17.2
26
+ version: 2.3.11
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: guard
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 1.5.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-yard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 2.2.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 2.2.1
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: minitest
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -173,10 +187,14 @@ extra_rdoc_files: []
173
187
  files:
174
188
  - ".bundle/.keep"
175
189
  - ".bundle/config"
190
+ - ".github/workflows/ci.yml"
176
191
  - ".gitignore"
177
192
  - ".rubocop.yml"
193
+ - ".yardopts"
194
+ - CHANGELOG.md
178
195
  - Gemfile
179
196
  - Guardfile
197
+ - MIT-LICENSE
180
198
  - README.md
181
199
  - Rakefile
182
200
  - bin/_guard-core
@@ -186,13 +204,15 @@ files:
186
204
  - bin/rake
187
205
  - bin/rubocop
188
206
  - bin/setup
207
+ - bin/yard
208
+ - bin/yardoc
209
+ - bin/yri
189
210
  - hash_deep_diff.gemspec
190
211
  - lib/hash_deep_diff.rb
212
+ - lib/hash_deep_diff/acts_as_hash.rb
191
213
  - lib/hash_deep_diff/comparison.rb
192
- - lib/hash_deep_diff/delta/acts_as_delta.rb
193
- - lib/hash_deep_diff/delta/inner.rb
194
- - lib/hash_deep_diff/delta/left.rb
195
- - lib/hash_deep_diff/delta/right.rb
214
+ - lib/hash_deep_diff/delta.rb
215
+ - lib/hash_deep_diff/report.rb
196
216
  - lib/hash_deep_diff/version.rb
197
217
  homepage: https://github.com/bpohoriletz/hash_deep_diff
198
218
  licenses:
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'forwardable'
4
-
5
- module HashDeepDiff
6
- module Delta
7
- # This module includes behavior that is needed to use deltas instead of Hash inside this gem
8
- module ActsAsDelta
9
- def self.included(base)
10
- base.prepend(Initialize)
11
- base.include(InstanceMethods)
12
- base.extend(Forwardable)
13
- base.def_delegators :@delta, :==, :each_with_object, :each_key, :[],
14
- :to_a, :empty?, :keys
15
- end
16
-
17
- # Assumes that the class will include method delta that will return a representation of an
18
- # instance of a class as a Hash
19
- module InstanceMethods
20
- # TOFIX poor naming
21
- def diff_prefix
22
- path.map { |key| "[#{key}]" }.join
23
- end
24
-
25
- # TOFIX poor naming
26
- # overrides parameter in initializer
27
- def path
28
- @prefix + [@delta.keys.first]
29
- end
30
-
31
- def to_h
32
- @delta
33
- end
34
-
35
- def to_hash
36
- @delta
37
- end
38
-
39
- def to_s
40
- to_str
41
- end
42
-
43
- def to_str
44
- raise NoMethodError, "expected #{self.class} to implement #to_str"
45
- end
46
-
47
- def complex?
48
- raise NoMethodError, "expected #{self.class} to implement #complex?"
49
- end
50
- end
51
-
52
- # Override #initialize method
53
- module Initialize
54
- def initialize(path:, value:)
55
- # TOFIX this may prohibit usage of hashes with Array keys
56
- if path.respond_to?(:to_ary)
57
- @delta = { path[-1] => value }
58
- @value = value
59
- @prefix = path[0..-2]
60
- else
61
- @delta = { path => value }
62
- @value = value
63
- @prefix = []
64
- end
65
- end
66
- end
67
- end
68
- end
69
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'acts_as_delta'
4
-
5
- module HashDeepDiff
6
- module Delta
7
- # Representation of the pure left diff
8
- # i.e element that are missing in the hash on the right of the comparison
9
- # for example left diff of { a: a } and {} is { a: a }
10
- class Inner
11
- include Delta::ActsAsDelta
12
-
13
- def to_str
14
- return diff unless complex?
15
-
16
- HashDeepDiff::Comparison.new(left, right, path).report
17
- end
18
-
19
- def diff
20
- lines = <<~Q
21
- -left#{diff_prefix} = #{left}
22
- +right#{diff_prefix} = #{right}
23
- Q
24
- lines.strip
25
- end
26
-
27
- def complex?
28
- left.respond_to?(:to_hash) && right.respond_to?(:to_hash)
29
- end
30
-
31
- def left
32
- @value[:left]
33
- end
34
-
35
- def right
36
- @value[:right]
37
- end
38
- end
39
- end
40
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'acts_as_delta'
4
-
5
- module HashDeepDiff
6
- module Delta
7
- # Representation of the pure left diff
8
- # i.e element that are missing in the hash on the right of the comparison
9
- # for example left diff of { a: a } and {} is { a: a }
10
- class Left
11
- include Delta::ActsAsDelta
12
-
13
- def to_str
14
- return "+left#{diff_prefix} = #{left}" unless left.respond_to?(:to_hash)
15
-
16
- left.keys.map do |key|
17
- self.class.new(path: path + [key], value: left[key])
18
- end.join("\n").strip
19
- end
20
-
21
- def left
22
- @value
23
- end
24
-
25
- def right
26
- nil
27
- end
28
- end
29
- end
30
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'acts_as_delta'
4
-
5
- module HashDeepDiff
6
- module Delta
7
- # Representation of the pure right diff
8
- # i.e element that are missing in the hash on the right of the comparison
9
- # for example right diff of {} and { a: a } is { a: a }
10
- class Right
11
- include Delta::ActsAsDelta
12
-
13
- def to_str
14
- return "-left#{diff_prefix} = #{right}" unless right.respond_to?(:to_hash)
15
-
16
- right.keys.map do |key|
17
- self.class.new(path: path + [key], value: right[key])
18
- end.join("\n").strip
19
- end
20
-
21
- def left
22
- nil
23
- end
24
-
25
- def right
26
- @value
27
- end
28
- end
29
- end
30
- end