hashdiff 0.0.6 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://secure.travis-ci.org/liufengyun/hashdiff.png)](http://travis-ci.org/liufengyun/hashdiff)
|
1
|
+
# HashDiff [![Build Status](https://secure.travis-ci.org/liufengyun/hashdiff.png)](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
|