Skip to content

Commit

Permalink
Change HashDiff Constant to Hashdiff
Browse files Browse the repository at this point in the history
Why This Change Is Necessary
========================================================================

The name of this gem is `hashdiff`. There is another gem named
`hash_diff`. When they are both required via dependencies, errors are
thrown. Additionally since the behavior is different, gems that are
relying on `hashdiff`'s behavior, may get `hash_diff`'s behavior and
vice versa.

The reason for the conflict is because, based on ruby conventions, _'s
are the separators for camel case.

So:

```ruby
'hashdiff'.camelize # => "Hashdiff"

'hash_diff'.camelize # => "HashDiff"
```

Unfortunately instead of making the base module of this gem `Hashdiff`,
it's called `HashDiff` and it conflicts.

How These Changes Address the Issue
========================================================================

We change all of the constants to the new constant, including in
documentation and tests.

Then we add a shim to allow the old constant to be used and therefore
maintain backward compatibility.

Finally we add a warning message for users of the gem that the old
constant will be removed in the next major version bump.

Side Effects Caused By This Change
========================================================================

A new warning is added when loading the gem.
  • Loading branch information
jfelchner committed May 28, 2019
1 parent d6e185b commit 3a8ce95
Show file tree
Hide file tree
Showing 19 changed files with 67 additions and 62 deletions.
50 changes: 25 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# 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)
# 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)

HashDiff is a ruby library to compute the smallest difference between two hashes.
Hashdiff is a ruby library to compute the smallest difference between two hashes.

It also supports comparing two arrays.

HashDiff does not monkey-patch any existing class. All features are contained inside the `HashDiff` module.
Hashdiff does not monkey-patch any existing class. All features are contained inside the `Hashdiff` module.

**Docs**: [Documentation](http://rubydoc.info/gems/hashdiff)


__WARNING__: Don't use the library for comparing large arrays, say ~10K (see #49).

## Why HashDiff?
## Why Hashdiff?

Given two Hashes A and B, sometimes you face the question: what's the smallest modification that can be made to change A into B?

Expand All @@ -21,7 +21,7 @@ An algorithm that responds to this question has to do following:
* Compute recursively -- Arrays and Hashes may be nested arbitrarily in A or B.
* Compute the smallest change -- it should recognize similar child Hashes or child Arrays between A and B.

HashDiff answers the question above using an opinionated approach:
Hashdiff answers the question above using an opinionated approach:

* Hash can be represented as a list of (dot-syntax-path, value) pairs. For example, `{a:[{c:2}]}` can be represented as `["a[0].c", 2]`.
* The change set can be represented using the dot-syntax representation. For example, `[['-', 'b.x', 3], ['~', 'b.z', 45, 30], ['+', 'b.y', 3]]`.
Expand All @@ -46,7 +46,7 @@ Two simple hashes:
a = {a:3, b:2}
b = {}

diff = HashDiff.diff(a, b)
diff = Hashdiff.diff(a, b)
diff.should == [['-', 'a', 3], ['-', 'b', 2]]
```

Expand All @@ -56,7 +56,7 @@ More complex hashes:
a = {a:{x:2, y:3, z:4}, b:{x:3, z:45}}
b = {a:{y:3}, b:{y:3, z:30}}

diff = HashDiff.diff(a, b)
diff = Hashdiff.diff(a, b)
diff.should == [['-', 'a.x', 2], ['-', 'a.z', 4], ['-', 'b.x', 3], ['~', 'b.z', 45, 30], ['+', 'b.y', 3]]
```

Expand All @@ -66,7 +66,7 @@ Arrays in hashes:
a = {a:[{x:2, y:3, z:4}, {x:11, y:22, z:33}], b:{x:3, z:45}}
b = {a:[{y:3}, {x:11, z:33}], b:{y:22}}

diff = HashDiff.best_diff(a, b)
diff = Hashdiff.best_diff(a, b)
diff.should == [['-', 'a[0].x', 2], ['-', 'a[0].z', 4], ['-', 'a[1].y', 22], ['-', 'b.x', 3], ['-', 'b.z', 45], ['+', 'b.y', 22]]
```

Expand All @@ -78,8 +78,8 @@ patch example:
a = {'a' => 3}
b = {'a' => {'a1' => 1, 'a2' => 2}}

diff = HashDiff.diff(a, b)
HashDiff.patch!(a, diff).should == b
diff = Hashdiff.diff(a, b)
Hashdiff.patch!(a, diff).should == b
```

unpatch example:
Expand All @@ -88,8 +88,8 @@ unpatch example:
a = [{'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5}, {'x' => 5, 'y' => 6, 'z' => 3}, 1]
b = [1, {'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}]

diff = HashDiff.diff(a, b) # diff two array is OK
HashDiff.unpatch!(b, diff).should == a
diff = Hashdiff.diff(a, b) # diff two array is OK
Hashdiff.unpatch!(b, diff).should == a
```

### Options
Expand All @@ -106,7 +106,7 @@ You can specify `:delimiter` to be something other than the default dot. For exa
a = {a:{x:2, y:3, z:4}, b:{x:3, z:45}}
b = {a:{y:3}, b:{y:3, z:30}}

diff = HashDiff.diff(a, b, :delimiter => '\t')
diff = Hashdiff.diff(a, b, :delimiter => '\t')
diff.should == [['-', 'a\tx', 2], ['-', 'a\tz', 4], ['-', 'b\tx', 3], ['~', 'b\tz', 45, 30], ['+', 'b\ty', 3]]
```

Expand All @@ -126,7 +126,7 @@ The :numeric_tolerance option allows for a small numeric tolerance.
a = {x:5, y:3.75, z:7}
b = {x:6, y:3.76, z:7}

diff = HashDiff.diff(a, b, :numeric_tolerance => 0.1)
diff = Hashdiff.diff(a, b, :numeric_tolerance => 0.1)
diff.should == [["~", "x", 5, 6]]
```

Expand All @@ -138,7 +138,7 @@ The :strip option strips all strings before comparing.
a = {x:5, s:'foo '}
b = {x:6, s:'foo'}

diff = HashDiff.diff(a, b, :comparison => { :numeric_tolerance => 0.1, :strip => true })
diff = Hashdiff.diff(a, b, :comparison => { :numeric_tolerance => 0.1, :strip => true })
diff.should == [["~", "x", 5, 6]]
```

Expand All @@ -150,7 +150,7 @@ The :case_insensitive option makes string comparisons ignore case.
a = {x:5, s:'FooBar'}
b = {x:6, s:'foobar'}

diff = HashDiff.diff(a, b, :comparison => { :numeric_tolerance => 0.1, :case_insensitive => true })
diff = Hashdiff.diff(a, b, :comparison => { :numeric_tolerance => 0.1, :case_insensitive => true })
diff.should == [["~", "x", 5, 6]]
```

Expand All @@ -164,7 +164,7 @@ is useful for `patch!` when used on hashes without string keys.
a = {x:5}
b = {'x'=>6}

diff = HashDiff.diff(a, b, :array_path => true)
diff = Hashdiff.diff(a, b, :array_path => true)
diff.should == [['-', [:x], 5], ['+', ['x'], 6]]
```

Expand All @@ -173,7 +173,7 @@ For cases where there are arrays in paths their index will be added to the path.
a = {x:[0,1]}
b = {x:[0,2]}

diff = HashDiff.diff(a, b, :array_path => true)
diff = Hashdiff.diff(a, b, :array_path => true)
diff.should == [["-", [:x, 1], 1], ["+", [:x, 1], 2]]
```

Expand All @@ -183,7 +183,7 @@ This shouldn't cause problems if you are comparing an array with a hash:
a = {x:{0=>1}}
b = {x:[1]}

diff = HashDiff.diff(a, b, :array_path => true)
diff = Hashdiff.diff(a, b, :array_path => true)
diff.should == [["~", [:x], {0=>1}, [1]]]
```

Expand All @@ -205,7 +205,7 @@ Note, currently the :similarity option has no effect when :use_lcs is false.
a = {x: [0, 1, 2]}
b = {x: [0, 2, 2, 3]}

diff = HashDiff.diff(a, b, :use_lcs => false)
diff = Hashdiff.diff(a, b, :use_lcs => false)
diff.should == [["~", "x[1]", 1, 2], ["+", "x[3]", 3]]
```

Expand All @@ -217,7 +217,7 @@ It's possible to specify how the values of a key should be compared.
a = {a:'car', b:'boat', c:'plane'}
b = {a:'bus', b:'truck', c:' plan'}

diff = HashDiff.diff(a, b) do |path, obj1, obj2|
diff = Hashdiff.diff(a, b) do |path, obj1, obj2|
case path
when /a|b|c/
obj1.length == obj2.length
Expand All @@ -233,7 +233,7 @@ The yielded params of the comparison block is `|path, obj1, obj2|`, in which pat
a = {a:'car', b:['boat', 'plane'] }
b = {a:'bus', b:['truck', ' plan'] }

diff = HashDiff.diff(a, b) do |path, obj1, obj2|
diff = Hashdiff.diff(a, b) do |path, obj1, obj2|
case path
when 'b[*]'
obj1.length == obj2.length
Expand All @@ -255,11 +255,11 @@ An order difference alone between two arrays can create too many diffs to be use
a = {a:'car', b:['boat', 'plane'] }
b = {a:'car', b:['plane', 'boat'] }

HashDiff.diff(a, b) => [["+", "b[0]", "plane"], ["-", "b[2]", "plane"]]
Hashdiff.diff(a, b) => [["+", "b[0]", "plane"], ["-", "b[2]", "plane"]]

b[:b].sort!

HashDiff.diff(a, b) => []
Hashdiff.diff(a, b) => []
```

## Maintainers
Expand All @@ -269,4 +269,4 @@ HashDiff.diff(a, b) => []

## License

HashDiff is distributed under the MIT-LICENSE.
Hashdiff is distributed under the MIT-LICENSE.
4 changes: 2 additions & 2 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

## v0.3.4 2017-05-01

* performance improvement of HashDiff#similar? #31
* performance improvement of `#similar?` #31

## v0.3.2 2016-12-27

Expand Down Expand Up @@ -64,7 +64,7 @@
## v0.0.5 2012-7-1

* fix a bug in judging whehter two objects are similiar.
* add more spec test for HashDiff.best_diff
* add more spec test for `.best_diff`

## v0.0.4 2012-6-24

Expand Down
7 changes: 4 additions & 3 deletions hashdiff.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ require 'hashdiff/version'

Gem::Specification.new do |s|
s.name = 'hashdiff'
s.version = HashDiff::VERSION
s.version = Hashdiff::VERSION
s.license = 'MIT'
s.summary = ' HashDiff is a diff lib to compute the smallest difference between two hashes. '
s.description = ' HashDiff is a diff lib to compute the smallest difference between two hashes. '
s.summary = ' Hashdiff is a diff lib to compute the smallest difference between two hashes. '
s.description = ' Hashdiff is a diff lib to compute the smallest difference between two hashes. '

s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- Appraisals {spec}/*`.split("\n")

s.require_paths = ['lib']
s.required_ruby_version = Gem::Requirement.new('>= 2.0.0')
s.post_install_message = 'The HashDiff constant used by this gem conflicts with another gem of a similar name. As of version 1.0 the HashDiff constant will be completely removed and replaced by Hashdiff. For more information see https://github.com/liufengyun/hashdiff/issues/45.'

s.authors = ['Liu Fengyun']
s.email = ['[email protected]']
Expand Down
4 changes: 4 additions & 0 deletions lib/hashdiff.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@
require_relative 'hashdiff/diff'
require_relative 'hashdiff/patch'
require_relative 'hashdiff/version'

HashDiff = Hashdiff

warn 'The HashDiff constant used by this gem conflicts with another gem of a similar name. As of version 1.0 the HashDiff constant will be completely removed and replaced by Hashdiff. For more information see https://github.com/liufengyun/hashdiff/issues/45.'
14 changes: 7 additions & 7 deletions lib/hashdiff/compare_hashes.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

module HashDiff
module Hashdiff
# @private
# Used to compare hashes
class CompareHashes
Expand All @@ -19,8 +19,8 @@ def call(obj1, obj2, opts = {})

# add deleted properties
deleted_keys.each do |k|
change_key = HashDiff.prefix_append_key(opts[:prefix], k, opts)
custom_result = HashDiff.custom_compare(opts[:comparison], change_key, obj1[k], nil)
change_key = Hashdiff.prefix_append_key(opts[:prefix], k, opts)
custom_result = Hashdiff.custom_compare(opts[:comparison], change_key, obj1[k], nil)

if custom_result
result.concat(custom_result)
Expand All @@ -31,16 +31,16 @@ def call(obj1, obj2, opts = {})

# recursive comparison for common keys
common_keys.each do |k|
prefix = HashDiff.prefix_append_key(opts[:prefix], k, opts)
prefix = Hashdiff.prefix_append_key(opts[:prefix], k, opts)

result.concat(HashDiff.diff(obj1[k], obj2[k], opts.merge(prefix: prefix)))
result.concat(Hashdiff.diff(obj1[k], obj2[k], opts.merge(prefix: prefix)))
end

# added properties
added_keys.each do |k|
change_key = HashDiff.prefix_append_key(opts[:prefix], k, opts)
change_key = Hashdiff.prefix_append_key(opts[:prefix], k, opts)

custom_result = HashDiff.custom_compare(opts[:comparison], change_key, nil, obj2[k])
custom_result = Hashdiff.custom_compare(opts[:comparison], change_key, nil, obj2[k])

if custom_result
result.concat(custom_result)
Expand Down
8 changes: 4 additions & 4 deletions lib/hashdiff/diff.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# frozen_string_literal: true

module HashDiff
module Hashdiff
# Best diff two objects, which tries to generate the smallest change set using different similarity values.
#
# HashDiff.best_diff is useful in case of comparing two objects which include similar hashes in arrays.
# Hashdiff.best_diff is useful in case of comparing two objects which include similar hashes in arrays.
#
# @param [Array, Hash] obj1
# @param [Array, Hash] obj2
Expand All @@ -23,7 +23,7 @@ module HashDiff
# @example
# a = {'x' => [{'a' => 1, 'c' => 3, 'e' => 5}, {'y' => 3}]}
# b = {'x' => [{'a' => 1, 'b' => 2, 'e' => 5}] }
# diff = HashDiff.best_diff(a, b)
# diff = Hashdiff.best_diff(a, b)
# diff.should == [['-', 'x[0].c', 3], ['+', 'x[0].b', 2], ['-', 'x[1].y', 3], ['-', 'x[1]', {}]]
#
# @since 0.0.1
Expand Down Expand Up @@ -69,7 +69,7 @@ def self.best_diff(obj1, obj2, options = {}, &block)
# a = {"a" => 1, "b" => {"b1" => 1, "b2" =>2}}
# b = {"a" => 1, "b" => {}}
#
# diff = HashDiff.diff(a, b)
# diff = Hashdiff.diff(a, b)
# diff.should == [['-', 'b.b1', 1], ['-', 'b.b2', 2]]
#
# @since 0.0.1
Expand Down
2 changes: 1 addition & 1 deletion lib/hashdiff/lcs.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

module HashDiff
module Hashdiff
# @private
#
# caculate array difference using LCS algorithm
Expand Down
10 changes: 5 additions & 5 deletions lib/hashdiff/lcs_compare_arrays.rb
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
# frozen_string_literal: true

module HashDiff
module Hashdiff
# @private
# Used to compare arrays using the lcs algorithm
class LcsCompareArrays
class << self
def call(obj1, obj2, opts = {})
result = []

changeset = HashDiff.diff_array_lcs(obj1, obj2, opts) do |lcs|
changeset = Hashdiff.diff_array_lcs(obj1, obj2, opts) do |lcs|
# use a's index for similarity
lcs.each do |pair|
prefix = HashDiff.prefix_append_array_index(opts[:prefix], pair[0], opts)
prefix = Hashdiff.prefix_append_array_index(opts[:prefix], pair[0], opts)

result.concat(HashDiff.diff(obj1[pair[0]], obj2[pair[1]], opts.merge(prefix: prefix)))
result.concat(Hashdiff.diff(obj1[pair[0]], obj2[pair[1]], opts.merge(prefix: prefix)))
end
end

changeset.each do |change|
next if change[0] != '-' && change[0] != '+'

change_key = HashDiff.prefix_append_array_index(opts[:prefix], change[1], opts)
change_key = Hashdiff.prefix_append_array_index(opts[:prefix], change[1], opts)

result << [change[0], change_key, change[2]]
end
Expand Down
10 changes: 5 additions & 5 deletions lib/hashdiff/linear_compare_array.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

module HashDiff
module Hashdiff
# @private
#
# Used to compare arrays in a linear complexity, which produces longer diffs
Expand Down Expand Up @@ -80,8 +80,8 @@ def compare_at_index
end

def item_difference(old_item, new_item, item_index)
prefix = HashDiff.prefix_append_array_index(options[:prefix], item_index, options)
HashDiff.diff(old_item, new_item, options.merge(prefix: prefix))
prefix = Hashdiff.prefix_append_array_index(options[:prefix], item_index, options)
Hashdiff.diff(old_item, new_item, options.merge(prefix: prefix))
end

# look ahead in the new array to see if the current item appears later
Expand Down Expand Up @@ -137,12 +137,12 @@ def append_deletions_before_match(match_index)
end

def append_addition(item, index)
key = HashDiff.prefix_append_array_index(options[:prefix], index, options)
key = Hashdiff.prefix_append_array_index(options[:prefix], index, options)
additions << ['+', key, item]
end

def append_deletion(item, index)
key = HashDiff.prefix_append_array_index(options[:prefix], index, options)
key = Hashdiff.prefix_append_array_index(options[:prefix], index, options)
deletions << ['-', key, item]
end

Expand Down
2 changes: 1 addition & 1 deletion lib/hashdiff/patch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#
# This module provides methods to diff two hash, patch and unpatch hash
#
module HashDiff
module Hashdiff
# Apply patch to object
#
# @param [Hash, Array] obj the object to be patched, can be an Array or a Hash
Expand Down
2 changes: 1 addition & 1 deletion lib/hashdiff/util.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

module HashDiff
module Hashdiff
# @private
#
# judge whether two objects are similar
Expand Down
Loading

0 comments on commit 3a8ce95

Please sign in to comment.