hashdiff 0.0.6 → 0.1.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 +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +1 -1
- data/README.md +15 -9
- data/changelog.md +4 -0
- data/lib/hashdiff/diff.rb +39 -20
- data/lib/hashdiff/patch.rb +10 -4
- data/lib/hashdiff/util.rb +1 -1
- data/lib/hashdiff/version.rb +1 -1
- data/spec/hashdiff/best_diff_spec.rb +1 -2
- data/spec/hashdiff/diff_spec.rb +1 -1
- data/spec/hashdiff/patch_spec.rb +0 -1
- data/spec/hashdiff/util_spec.rb +0 -1
- metadata +13 -22
- data/Gemfile.lock +0 -30
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8dbf060a0b7537d436fdf1a5000e37e16227bdbe
|
4
|
+
data.tar.gz: 4c2b8998e3f78648c542cc4c8c7a3c95de4a6256
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 239b84f71304f154f89ab66c2b654bde48ccdaa3d1ed5748bb89f6449ea7ac8a34749a32f702e0684ea6e070940d4444c38705da413bdf017c6b3a1d6715a516
|
7
|
+
data.tar.gz: d20d20328d875a9bd52f6087b3a1cf494534346303e1def382f0a3b836adcb047c99f421a5263d072ebad01830f2b3101962b01ef2eb3bcd9adbf313c3c799c0
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
# HashDiff
|
2
|
-
|
3
|
-
[](http://travis-ci.org/liufengyun/hashdiff)
|
1
|
+
# HashDiff [](http://travis-ci.org/liufengyun/hashdiff)
|
4
2
|
|
5
3
|
HashDiff is a ruby library to compute the smallest difference between two hashes.
|
6
4
|
|
@@ -25,10 +23,6 @@ HashDiff answers the question above in an opinionated approach:
|
|
25
23
|
* It compares Arrays using LCS(longest common subsequence) algorithm.
|
26
24
|
* It recoganize similar Hashes in Array using a similarity value(0 < similarity <= 1).
|
27
25
|
|
28
|
-
## Compatibility
|
29
|
-
|
30
|
-
HashDiff is tested against `1.9.2`, `1.9.3` and `ruby-head`. It should work on other versions as well.
|
31
|
-
|
32
26
|
## Usage
|
33
27
|
|
34
28
|
If you're using bundler, add following to the Gemfile:
|
@@ -85,10 +79,22 @@ unpatch example:
|
|
85
79
|
diff = HashDiff.diff(a, b) # diff two array is OK
|
86
80
|
HashDiff.unpatch!(b, diff).should == a
|
87
81
|
|
82
|
+
### Options
|
83
|
+
|
84
|
+
You can specify the delimiter to be something else than the dot. For example:
|
85
|
+
|
86
|
+
a = {a:{x:2, y:3, z:4}, b:{x:3, z:45}}
|
87
|
+
b = {a:{y:3}, b:{y:3, z:30}}
|
88
|
+
|
89
|
+
diff = HashDiff.diff(a, b, :delimiter => '\t')
|
90
|
+
diff.should == [['-', 'a\tx', 2], ['-', 'a\tz', 4], ['-', 'b\tx', 3], ['~', 'b\tz', 45, 30], ['+', 'b\ty', 3]]
|
91
|
+
|
92
|
+
In cases you have similar hash objects in array, you can pass suitable `:similarity` value instead of the default `0.8`.
|
93
|
+
|
88
94
|
## Contributors
|
89
95
|
|
90
|
-
- @liufengyun
|
91
|
-
- @m-o-e
|
96
|
+
- [@liufengyun](https://github.com/liufengyun)
|
97
|
+
- [@m-o-e](https://github.com/m-o-e)
|
92
98
|
|
93
99
|
## License
|
94
100
|
|
data/changelog.md
CHANGED
data/lib/hashdiff/diff.rb
CHANGED
@@ -2,10 +2,12 @@ module HashDiff
|
|
2
2
|
|
3
3
|
# Best diff two objects, which tries to generate the smallest change set using different similarity values.
|
4
4
|
#
|
5
|
-
# HashDiff.best_diff is
|
5
|
+
# HashDiff.best_diff is useful in case of comparing two objects which includes similar hashes in array.
|
6
6
|
#
|
7
7
|
# @param [Arrary, Hash] obj1
|
8
8
|
# @param [Arrary, Hash] obj2
|
9
|
+
# @param [Hash] options
|
10
|
+
# `options` supports `:delimiter`. Default value for `:delimiter` is `.`(dot).
|
9
11
|
#
|
10
12
|
# @return [Array] an array of changes.
|
11
13
|
# e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']]
|
@@ -17,14 +19,17 @@ module HashDiff
|
|
17
19
|
# diff.should == [['-', 'x[0].c', 3], ['+', 'x[0].b', 2], ['-', 'x[1].y', 3], ['-', 'x[1]', {}]]
|
18
20
|
#
|
19
21
|
# @since 0.0.1
|
20
|
-
def self.best_diff(obj1, obj2,
|
21
|
-
|
22
|
+
def self.best_diff(obj1, obj2, options = {})
|
23
|
+
opts = {similarity: 0.3}.merge!(options)
|
24
|
+
diffs_1 = diff(obj1, obj2, opts)
|
22
25
|
count_1 = count_diff diffs_1
|
23
26
|
|
24
|
-
|
27
|
+
opts = {similarity: 0.5}.merge!(options)
|
28
|
+
diffs_2 = diff(obj1, obj2, opts)
|
25
29
|
count_2 = count_diff diffs_2
|
26
30
|
|
27
|
-
|
31
|
+
opts = {similarity: 0.8}.merge!(options)
|
32
|
+
diffs_3 = diff(obj1, obj2, opts)
|
28
33
|
count_3 = count_diff diffs_3
|
29
34
|
|
30
35
|
count, diffs = count_1 < count_2 ? [count_1, diffs_1] : [count_2, diffs_2]
|
@@ -35,10 +40,12 @@ module HashDiff
|
|
35
40
|
#
|
36
41
|
# @param [Arrary, Hash] obj1
|
37
42
|
# @param [Arrary, Hash] obj2
|
38
|
-
# @param [
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
43
|
+
# @param [Hash] options
|
44
|
+
# `options` can contain `:similarity` or `:delimiter`.
|
45
|
+
#
|
46
|
+
# `:similarity` should be between (0, 1]. The default value is `0.8`. `:similarity` is meaningful if there're similar hashes in arrays. See {best_diff}.
|
47
|
+
#
|
48
|
+
# `:delimiter` defaults to `.`(dot).
|
42
49
|
#
|
43
50
|
# @return [Array] an array of changes.
|
44
51
|
# e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']]
|
@@ -51,41 +58,53 @@ module HashDiff
|
|
51
58
|
# diff.should == [['-', 'b.b1', 1], ['-', 'b.b2', 2]]
|
52
59
|
#
|
53
60
|
# @since 0.0.1
|
54
|
-
def self.diff(obj1, obj2,
|
61
|
+
def self.diff(obj1, obj2, options = {})
|
62
|
+
opts = {
|
63
|
+
:prefix => '',
|
64
|
+
:similarity => 0.8,
|
65
|
+
:delimiter => '.'
|
66
|
+
}
|
67
|
+
|
68
|
+
opts = opts.merge!(options)
|
69
|
+
|
55
70
|
if obj1.nil? and obj2.nil?
|
56
71
|
return []
|
57
72
|
end
|
58
73
|
|
59
74
|
if obj1.nil?
|
60
|
-
return [['~', prefix, nil, obj2]]
|
75
|
+
return [['~', opts[:prefix], nil, obj2]]
|
61
76
|
end
|
62
77
|
|
63
78
|
if obj2.nil?
|
64
|
-
return [['~', prefix, obj1, nil]]
|
79
|
+
return [['~', opts[:prefix], obj1, nil]]
|
65
80
|
end
|
66
81
|
|
67
82
|
if !(obj1.is_a?(Array) and obj2.is_a?(Array)) and !(obj1.is_a?(Hash) and obj2.is_a?(Hash)) and !(obj1.is_a?(obj2.class) or obj2.is_a?(obj1.class))
|
68
|
-
return [['~', prefix, obj1, obj2]]
|
83
|
+
return [['~', opts[:prefix], obj1, obj2]]
|
69
84
|
end
|
70
85
|
|
71
86
|
result = []
|
72
87
|
if obj1.is_a?(Array)
|
73
|
-
changeset = diff_array(obj1, obj2, similarity) do |lcs|
|
88
|
+
changeset = diff_array(obj1, obj2, opts[:similarity]) do |lcs|
|
74
89
|
# use a's index for similarity
|
75
90
|
lcs.each do |pair|
|
76
|
-
result.concat(diff(obj1[pair[0]], obj2[pair[1]], "#{prefix}[#{pair[0]}]"
|
91
|
+
result.concat(diff(obj1[pair[0]], obj2[pair[1]], opts.merge(prefix: "#{opts[:prefix]}[#{pair[0]}]")))
|
77
92
|
end
|
78
93
|
end
|
79
94
|
|
80
95
|
changeset.each do |change|
|
81
96
|
if change[0] == '-'
|
82
|
-
result << ['-', "#{prefix}[#{change[1]}]", change[2]]
|
97
|
+
result << ['-', "#{opts[:prefix]}[#{change[1]}]", change[2]]
|
83
98
|
elsif change[0] == '+'
|
84
|
-
result << ['+', "#{prefix}[#{change[1]}]", change[2]]
|
99
|
+
result << ['+', "#{opts[:prefix]}[#{change[1]}]", change[2]]
|
85
100
|
end
|
86
101
|
end
|
87
102
|
elsif obj1.is_a?(Hash)
|
88
|
-
|
103
|
+
if opts[:prefix].empty?
|
104
|
+
prefix = ""
|
105
|
+
else
|
106
|
+
prefix = "#{opts[:prefix]}#{opts[:delimiter]}"
|
107
|
+
end
|
89
108
|
|
90
109
|
deleted_keys = []
|
91
110
|
common_keys = []
|
@@ -102,7 +121,7 @@ module HashDiff
|
|
102
121
|
deleted_keys.each {|k| result << ['-', "#{prefix}#{k}", obj1[k]] }
|
103
122
|
|
104
123
|
# recursive comparison for common keys
|
105
|
-
common_keys.each {|k| result.concat(diff(obj1[k], obj2[k], "#{prefix}#{k}"
|
124
|
+
common_keys.each {|k| result.concat(diff(obj1[k], obj2[k], opts.merge(prefix: "#{prefix}#{k}"))) }
|
106
125
|
|
107
126
|
# added properties
|
108
127
|
obj2.each do |k, v|
|
@@ -112,7 +131,7 @@ module HashDiff
|
|
112
131
|
end
|
113
132
|
else
|
114
133
|
return [] if obj1 == obj2
|
115
|
-
return [['~', prefix, obj1, obj2]]
|
134
|
+
return [['~', opts[:prefix], obj1, obj2]]
|
116
135
|
end
|
117
136
|
|
118
137
|
result
|
data/lib/hashdiff/patch.rb
CHANGED
@@ -7,12 +7,15 @@ module HashDiff
|
|
7
7
|
#
|
8
8
|
# @param [Hash, Array] obj the object to be patchted, can be an Array of a Hash
|
9
9
|
# @param [Array] changes e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']]
|
10
|
-
# @param [
|
10
|
+
# @param [Hash] options
|
11
|
+
# `options` supports `:delimiter`. Default value for `:delimiter` is `.`(dot).
|
11
12
|
#
|
12
13
|
# @return the object after patch
|
13
14
|
#
|
14
15
|
# @since 0.0.1
|
15
|
-
def self.patch!(obj, changes,
|
16
|
+
def self.patch!(obj, changes, options = {})
|
17
|
+
delimiter = options[:delimiter] || '.'
|
18
|
+
|
16
19
|
changes.each do |change|
|
17
20
|
parts = decode_property_path(change[1], delimiter)
|
18
21
|
last_part = parts.last
|
@@ -43,12 +46,15 @@ module HashDiff
|
|
43
46
|
#
|
44
47
|
# @param [Hash, Array] obj the object to be unpatchted, can be an Array of a Hash
|
45
48
|
# @param [Array] changes e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']]
|
46
|
-
# @param [
|
49
|
+
# @param [Hash] options
|
50
|
+
# `options` supports `:delimiter`. Default value for `:delimiter` is `.`(dot).
|
47
51
|
#
|
48
52
|
# @return the object after unpatch
|
49
53
|
#
|
50
54
|
# @since 0.0.1
|
51
|
-
def self.unpatch!(obj, changes,
|
55
|
+
def self.unpatch!(obj, changes, options = {})
|
56
|
+
delimiter = options[:delimiter] || '.'
|
57
|
+
|
52
58
|
changes.reverse_each do |change|
|
53
59
|
parts = decode_property_path(change[1], delimiter)
|
54
60
|
last_part = parts.last
|
data/lib/hashdiff/util.rb
CHANGED
@@ -6,7 +6,7 @@ module HashDiff
|
|
6
6
|
def self.similar?(a, b, similarity = 0.8)
|
7
7
|
count_a = count_nodes(a)
|
8
8
|
count_b = count_nodes(b)
|
9
|
-
diffs = count_diff diff(a, b,
|
9
|
+
diffs = count_diff diff(a, b, :similarity => similarity)
|
10
10
|
|
11
11
|
if count_a + count_b == 0
|
12
12
|
return true
|
data/lib/hashdiff/version.rb
CHANGED
@@ -13,11 +13,10 @@ describe HashDiff do
|
|
13
13
|
a = {'x' => [{'a' => 1, 'c' => 3, 'e' => 5}, {'y' => 3}]}
|
14
14
|
b = {'x' => [{'a' => 1, 'b' => 2, 'e' => 5}] }
|
15
15
|
|
16
|
-
diff = HashDiff.best_diff(a, b, "\t")
|
16
|
+
diff = HashDiff.best_diff(a, b, :delimiter => "\t")
|
17
17
|
diff.should == [["-", "x[0]\tc", 3], ["+", "x[0]\tb", 2], ["-", "x[1]", {"y"=>3}]]
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
20
|
it "should be able to best diff array in hash" do
|
22
21
|
a = {"menu" => {
|
23
22
|
"id" => "file",
|
data/spec/hashdiff/diff_spec.rb
CHANGED
@@ -127,7 +127,7 @@ describe HashDiff do
|
|
127
127
|
a = [{'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5}, {'x' => 5, 'y' => 6, 'z' => 3}, 3]
|
128
128
|
b = [{'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}, 3]
|
129
129
|
|
130
|
-
diff = HashDiff.diff(a, b,
|
130
|
+
diff = HashDiff.diff(a, b, similarity: 0.8, delimiter: "\t")
|
131
131
|
diff.should == [["-", "[0]\td", 4], ["-", "[1]", {"x"=>5, "y"=>6, "z"=>3}]]
|
132
132
|
end
|
133
133
|
|
data/spec/hashdiff/patch_spec.rb
CHANGED
data/spec/hashdiff/util_spec.rb
CHANGED
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hashdiff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Liu Fengyun
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-08-25 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rspec
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -30,37 +27,33 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: yard
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - '>='
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - '>='
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: bluecloth
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - '>='
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '0'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
52
|
+
- - '>='
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '0'
|
62
|
-
description:
|
63
|
-
|
55
|
+
description: ' HashDiff is a diff lib to compute the smallest difference between two
|
56
|
+
hashes. '
|
64
57
|
email:
|
65
58
|
- liufengyunchina@gmail.com
|
66
59
|
executables: []
|
@@ -72,7 +65,6 @@ files:
|
|
72
65
|
- .travis.yml
|
73
66
|
- .yardopts
|
74
67
|
- Gemfile
|
75
|
-
- Gemfile.lock
|
76
68
|
- LICENSE
|
77
69
|
- README.md
|
78
70
|
- Rakefile
|
@@ -93,27 +85,26 @@ files:
|
|
93
85
|
- spec/spec_helper.rb
|
94
86
|
homepage: https://github.com/liufengyun/hashdiff
|
95
87
|
licenses: []
|
88
|
+
metadata: {}
|
96
89
|
post_install_message:
|
97
90
|
rdoc_options: []
|
98
91
|
require_paths:
|
99
92
|
- lib
|
100
93
|
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
-
none: false
|
102
94
|
requirements:
|
103
|
-
- -
|
95
|
+
- - '>='
|
104
96
|
- !ruby/object:Gem::Version
|
105
97
|
version: 1.8.7
|
106
98
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
-
none: false
|
108
99
|
requirements:
|
109
|
-
- -
|
100
|
+
- - '>='
|
110
101
|
- !ruby/object:Gem::Version
|
111
102
|
version: '0'
|
112
103
|
requirements: []
|
113
104
|
rubyforge_project:
|
114
|
-
rubygems_version:
|
105
|
+
rubygems_version: 2.0.3
|
115
106
|
signing_key:
|
116
|
-
specification_version:
|
107
|
+
specification_version: 4
|
117
108
|
summary: HashDiff is a diff lib to compute the smallest difference between two hashes.
|
118
109
|
test_files: []
|
119
110
|
has_rdoc:
|
data/Gemfile.lock
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
hashdiff (0.0.5)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: http://rubygems.org/
|
8
|
-
specs:
|
9
|
-
bluecloth (2.2.0)
|
10
|
-
diff-lcs (1.1.3)
|
11
|
-
rake (0.9.2.2)
|
12
|
-
rspec (2.10.0)
|
13
|
-
rspec-core (~> 2.10.0)
|
14
|
-
rspec-expectations (~> 2.10.0)
|
15
|
-
rspec-mocks (~> 2.10.0)
|
16
|
-
rspec-core (2.10.1)
|
17
|
-
rspec-expectations (2.10.0)
|
18
|
-
diff-lcs (~> 1.1.3)
|
19
|
-
rspec-mocks (2.10.1)
|
20
|
-
yard (0.8.1)
|
21
|
-
|
22
|
-
PLATFORMS
|
23
|
-
ruby
|
24
|
-
|
25
|
-
DEPENDENCIES
|
26
|
-
bluecloth
|
27
|
-
hashdiff!
|
28
|
-
rake
|
29
|
-
rspec (~> 2.0)
|
30
|
-
yard
|