hashdiff 0.0.2 → 0.0.3

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.
@@ -1,8 +1,11 @@
1
+ language: ruby
1
2
  rvm:
2
3
  - 1.8.7
3
4
  - 1.9.2
5
+ - 1.9.3
4
6
  - rbx
5
7
  - rbx-2.0
6
8
  - ree
7
9
  - jruby
8
10
  - ruby-head
11
+ script: "bundle exec rake spec"
@@ -0,0 +1 @@
1
+ --no-private
data/README.md CHANGED
@@ -9,15 +9,28 @@ HashDiff is tested on following platforms:
9
9
 
10
10
  - 1.8.7
11
11
  - 1.9.2
12
+ - 1.9.3
12
13
  - rbx
13
14
  - rbx-2.0
14
15
  - ree
15
16
  - jruby
16
17
  - ruby-head
17
18
 
19
+ Usage
20
+ ------------
21
+ If you're using bundler, add following:
22
+
23
+ gem 'hashdiff'
24
+
25
+ Or, you can run `gem install hashdiff`, then add following line to your ruby file which uses HashDiff:
26
+
27
+ require 'hashdiff'
28
+
18
29
  Quick Start
19
30
  -----------
20
31
 
32
+ You can find full docs here: [Documentation](http://rubydoc.info/gems/hashdiff)
33
+
21
34
  ### Diff
22
35
 
23
36
  Two simple hash:
@@ -41,7 +54,7 @@ Array in hash:
41
54
  a = {a:[{x:2, y:3, z:4}, {x:11, y:22, z:33}], b:{x:3, z:45}}
42
55
  b = {a:[{y:3}, {x:11, z:33}], b:{y:22}}
43
56
 
44
- diff = HashDiff.best_diff(a, b) # best_diff will try to match similar objects in array in order to generate the smallest change set
57
+ diff = HashDiff.best_diff(a, b)
45
58
  diff.should == [['-', 'a[0].x', 2], ['-', 'a[0].z', 4], ['-', 'a[1].y', 22], ['-', 'b.x', 3], ['-', 'b.z', 45], ['+', 'b.y', 22]]
46
59
 
47
60
  ### Patch
@@ -1,6 +1,22 @@
1
1
  module HashDiff
2
2
 
3
- # try to make the best diff that generates the smallest change set
3
+ # Best diff two objects, which tries to generate the smallest change set.
4
+ #
5
+ # HashDiff.best_diff is only meaningful in case of comparing two objects which includes similar objects in array.
6
+ #
7
+ # @param [Arrary, Hash] obj1
8
+ # @param [Arrary, Hash] obj2
9
+ #
10
+ # @return [Array] an array of changes.
11
+ # e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']]
12
+ #
13
+ # @example
14
+ # a = {'x' => [{'a' => 1, 'c' => 3, 'e' => 5}, {'y' => 3}]}
15
+ # b = {'x' => [{'a' => 1, 'b' => 2, 'e' => 5}] }
16
+ # diff = HashDiff.best_diff(a, b)
17
+ # diff.should == [['-', 'x[0].c', 3], ['+', 'x[0].b', 2], ['-', 'x[1].y', 3], ['-', 'x[1]', {}]]
18
+ #
19
+ # @since 0.0.1
4
20
  def self.best_diff(obj1, obj2)
5
21
  diffs_1 = diff(obj1, obj2, "", 0.3)
6
22
  diffs_2 = diff(obj1, obj2, "", 0.5)
@@ -10,30 +26,36 @@ module HashDiff
10
26
  diffs = diffs.size < diffs_3.size ? diffs : diffs_3
11
27
  end
12
28
 
13
- # compute the diff of two hashes, return an array of changes
14
- # e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']]
29
+ # Compute the diff of two hashes
30
+ #
31
+ # @param [Arrary, Hash] obj1
32
+ # @param [Arrary, Hash] obj2
33
+ # @param [float] similarity A value > 0 and <= 1.
34
+ # This parameter should be ignored in common usage.
35
+ # Similarity is only meaningful if there're similar objects in arrays. See {best_diff}.
36
+ #
37
+ # @return [Array] an array of changes.
38
+ # e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']]
39
+ #
40
+ # @example
41
+ # a = {"a" => 1, "b" => {"b1" => 1, "b2" =>2}}
42
+ # b = {"a" => 1, "b" => {}}
15
43
  #
16
- # NOTE: diff will treat nil as [], {} or "" in comparison according to different context.
17
- #
44
+ # diff = HashDiff.diff(a, b)
45
+ # diff.should == [['-', 'b.b1', 1], ['-', 'b.b2', 2]]
46
+ #
47
+ # @since 0.0.1
18
48
  def self.diff(obj1, obj2, prefix = "", similarity = 0.8)
49
+ if obj1.nil? and obj2.nil?
50
+ return []
51
+ end
52
+
19
53
  if obj1.nil?
20
- if obj2.is_a?(Array)
21
- return diff([], obj2, prefix, similarity)
22
- elsif obj2.is_a?(Hash)
23
- return diff({}, obj2, prefix, similarity)
24
- else
25
- return diff('', obj2, prefix, similarity)
26
- end
54
+ return [['-', prefix, nil]] + changed(obj2, '+', prefix)
27
55
  end
28
56
 
29
57
  if obj2.nil?
30
- if obj1.is_a?(Array)
31
- return diff(obj1, [], prefix, similarity)
32
- elsif obj1.is_a?(Hash)
33
- return diff(obj1, {}, prefix, similarity)
34
- else
35
- return diff(obj1, '', prefix, similarity)
36
- end
58
+ return changed(obj1, '-', prefix) + [['+', prefix, nil]]
37
59
  end
38
60
 
39
61
  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))
@@ -90,6 +112,8 @@ module HashDiff
90
112
  result
91
113
  end
92
114
 
115
+ # @private
116
+ #
93
117
  # diff array using LCS algorithm
94
118
  def self.diff_array(a, b, similarity = 0.8)
95
119
  change_set = []
@@ -1,5 +1,6 @@
1
1
  module HashDiff
2
-
2
+ # @private
3
+ #
3
4
  # caculate array difference using LCS algorithm
4
5
  # http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
5
6
  def self.lcs(a, b, similarity = 0.8)
@@ -1,22 +1,27 @@
1
1
  #
2
- # This class provides methods to diff two hash, patch and unpatch hash
2
+ # This module provides methods to diff two hash, patch and unpatch hash
3
3
  #
4
4
  module HashDiff
5
5
 
6
- # apply changes to object
6
+ # Apply patch to object
7
7
  #
8
- # changes: [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']]
9
- def self.patch(hash, changes)
8
+ # @param [Hash, Array] obj the object to be patchted, can be an Array of a Hash
9
+ # @param [Array] changes e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']]
10
+ #
11
+ # @return the object after patch
12
+ #
13
+ # @since 0.0.1
14
+ def self.patch!(obj, changes)
10
15
  changes.each do |change|
11
16
  parts = decode_property_path(change[1])
12
17
  last_part = parts.last
13
18
 
14
- dest_node = node(hash, parts[0, parts.size-1])
19
+ dest_node = node(obj, parts[0, parts.size-1])
15
20
 
16
21
  if change[0] == '+'
17
22
  if dest_node == nil
18
23
  parent_key = parts[parts.size-2]
19
- parent_node = node(hash, parts[0, parts.size-2])
24
+ parent_node = node(obj, parts[0, parts.size-2])
20
25
  if last_part.is_a?(Fixnum)
21
26
  dest_node = parent_node[parent_key] = []
22
27
  else
@@ -40,13 +45,18 @@ module HashDiff
40
45
  end
41
46
  end
42
47
 
43
- hash
48
+ obj
44
49
  end
45
50
 
46
- # undo changes from object.
51
+ # Unpatch an object
52
+ #
53
+ # @param [Hash, Array] obj the object to be unpatchted, can be an Array of a Hash
54
+ # @param [Array] changes e.g. [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']]
55
+ #
56
+ # @return the object after unpatch
47
57
  #
48
- # changes: [[ '+', 'a.b', '45' ], [ '-', 'a.c', '5' ], [ '~', 'a.x', '45', '63']]
49
- def self.unpatch(hash, changes)
58
+ # @since 0.0.1
59
+ def self.unpatch!(hash, changes)
50
60
  changes.reverse_each do |change|
51
61
  parts = decode_property_path(change[1])
52
62
  last_part = parts.last
@@ -1,5 +1,7 @@
1
1
  module HashDiff
2
2
 
3
+ # @private
4
+ #
3
5
  # return an array of added properties
4
6
  # e.g. [[ '+', 'a.b', 45 ], [ '-', 'a.c', 5 ]]
5
7
  def self.changed(obj, sign, prefix = "")
@@ -35,6 +37,8 @@ module HashDiff
35
37
  results
36
38
  end
37
39
 
40
+ # @private
41
+ #
38
42
  # judge whether two objects are similar
39
43
  def self.similiar?(a, b, similarity = 0.8)
40
44
  count_a = count_nodes(a)
@@ -48,6 +52,8 @@ module HashDiff
48
52
  end
49
53
  end
50
54
 
55
+ # @private
56
+ #
51
57
  # count total nodes for an object
52
58
  def self.count_nodes(obj)
53
59
  return 0 unless obj
@@ -66,6 +72,8 @@ module HashDiff
66
72
  count
67
73
  end
68
74
 
75
+ # @private
76
+ #
69
77
  # decode property path into an array
70
78
  #
71
79
  # e.g. "a.b[3].c" => ['a', 'b', 3, 'c']
@@ -85,6 +93,8 @@ module HashDiff
85
93
  parts.flatten
86
94
  end
87
95
 
96
+ # @private
97
+ #
88
98
  # get the node of hash by given path parts
89
99
  def self.node(hash, parts)
90
100
  temp = hash
@@ -1,3 +1,3 @@
1
1
  module HashDiff
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
@@ -67,13 +67,12 @@ describe HashDiff do
67
67
  diff.should == [['-', 'b[1]', 2], ['-', 'b[0]', 1]]
68
68
  end
69
69
 
70
- # treat nil as empty array
71
70
  it "should be able to diff value changes: array <=> nil" do
72
71
  a = {"a" => 1, "b" => [1, 2]}
73
72
  b = {"a" => 1, "b" => nil}
74
73
 
75
74
  diff = HashDiff.diff(a, b)
76
- diff.should == [['-', 'b[1]', 2], ['-', 'b[0]', 1]]
75
+ diff.should == [['-', 'b[1]', 2], ['-', 'b[0]', 1], ['-', 'b', []], ['+', 'b', nil]]
77
76
  end
78
77
 
79
78
  it "should be able to diff value chagnes: remove array completely" do
@@ -100,13 +99,12 @@ describe HashDiff do
100
99
  diff.should == [['-', 'b.b1', 1], ['-', 'b.b2', 2]]
101
100
  end
102
101
 
103
- # treat nil as empty hash
104
102
  it "should be able to diff value changes: hash <=> nil" do
105
103
  a = {"a" => 1, "b" => {"b1" => 1, "b2" =>2}}
106
104
  b = {"a" => 1, "b" => nil}
107
105
 
108
106
  diff = HashDiff.diff(a, b)
109
- diff.should == [['-', 'b.b1', 1], ['-', 'b.b2', 2]]
107
+ diff.should == [['-', 'b.b1', 1], ['-', 'b.b2', 2], ['-', 'b', {}], ['+', 'b', nil]]
110
108
  end
111
109
 
112
110
  it "should be able to diff similar objects in array" do
@@ -6,11 +6,11 @@ describe HashDiff do
6
6
  b = {"a"=>3, "c"=>11, "d"=>45, "e"=>100, "f"=>200, "g"=>300}
7
7
  diff = HashDiff.diff(a, b)
8
8
 
9
- HashDiff.patch(a, diff).should == b
9
+ HashDiff.patch!(a, diff).should == b
10
10
 
11
11
  a = {"a"=>3, "c"=>11, "d"=>45, "e"=>100, "f"=>200}
12
12
  b = {"a"=>3, "c"=>11, "d"=>45, "e"=>100, "f"=>200, "g"=>300}
13
- HashDiff.unpatch(b, diff).should == a
13
+ HashDiff.unpatch!(b, diff).should == a
14
14
  end
15
15
 
16
16
  it "should be able to patch value type changes" do
@@ -18,11 +18,11 @@ describe HashDiff do
18
18
  b = {"a" => {"a1" => 1, "a2" => 2}}
19
19
  diff = HashDiff.diff(a, b)
20
20
 
21
- HashDiff.patch(a, diff).should == b
21
+ HashDiff.patch!(a, diff).should == b
22
22
 
23
23
  a = {"a" => 3}
24
24
  b = {"a" => {"a1" => 1, "a2" => 2}}
25
- HashDiff.unpatch(b, diff).should == a
25
+ HashDiff.unpatch!(b, diff).should == a
26
26
  end
27
27
 
28
28
  it "should be able to patch value array <=> []" do
@@ -30,11 +30,11 @@ describe HashDiff do
30
30
  b = {"a" => 1, "b" => []}
31
31
  diff = HashDiff.diff(a, b)
32
32
 
33
- HashDiff.patch(a, diff).should == b
33
+ HashDiff.patch!(a, diff).should == b
34
34
 
35
35
  a = {"a" => 1, "b" => [1, 2]}
36
36
  b = {"a" => 1, "b" => []}
37
- HashDiff.unpatch(b, diff).should == a
37
+ HashDiff.unpatch!(b, diff).should == a
38
38
  end
39
39
 
40
40
  it "should be able to patch value array <=> nil" do
@@ -42,12 +42,11 @@ describe HashDiff do
42
42
  b = {"a" => 1, "b" => nil}
43
43
  diff = HashDiff.diff(a, b)
44
44
 
45
- # NOTE: nil is treated as [] in this context
46
- HashDiff.patch(a, diff).should == {"a" => 1, "b" => []}
45
+ HashDiff.patch!(a, diff).should == b
47
46
 
48
47
  a = {"a" => 1, "b" => [1, 2]}
49
48
  b = {"a" => 1, "b" => nil}
50
- HashDiff.unpatch(b, diff).should == a
49
+ HashDiff.unpatch!(b, diff).should == a
51
50
  end
52
51
 
53
52
  it "should be able to patch array value removal" do
@@ -55,11 +54,11 @@ describe HashDiff do
55
54
  b = {"a" => 1}
56
55
  diff = HashDiff.diff(a, b)
57
56
 
58
- HashDiff.patch(a, diff).should == b
57
+ HashDiff.patch!(a, diff).should == b
59
58
 
60
59
  a = {"a" => 1, "b" => [1, 2]}
61
60
  b = {"a" => 1}
62
- HashDiff.unpatch(b, diff).should == a
61
+ HashDiff.unpatch!(b, diff).should == a
63
62
  end
64
63
 
65
64
  it "should be able to patch hash value removal" do
@@ -67,11 +66,11 @@ describe HashDiff do
67
66
  b = {"a" => 1}
68
67
  diff = HashDiff.diff(a, b)
69
68
 
70
- HashDiff.patch(a, diff).should == b
69
+ HashDiff.patch!(a, diff).should == b
71
70
 
72
71
  a = {"a" => 1, "b" => {"b1" => 1, "b2" =>2}}
73
72
  b = {"a" => 1}
74
- HashDiff.unpatch(b, diff).should == a
73
+ HashDiff.unpatch!(b, diff).should == a
75
74
  end
76
75
 
77
76
  it "should be able to patch value hash <=> {}" do
@@ -79,11 +78,11 @@ describe HashDiff do
79
78
  b = {"a" => 1, "b" => {}}
80
79
  diff = HashDiff.diff(a, b)
81
80
 
82
- HashDiff.patch(a, diff).should == b
81
+ HashDiff.patch!(a, diff).should == b
83
82
 
84
83
  a = {"a" => 1, "b" => {"b1" => 1, "b2" =>2}}
85
84
  b = {"a" => 1, "b" => {}}
86
- HashDiff.unpatch(b, diff).should == a
85
+ HashDiff.unpatch!(b, diff).should == a
87
86
  end
88
87
 
89
88
  it "should be able to patch value hash <=> nil" do
@@ -91,12 +90,11 @@ describe HashDiff do
91
90
  b = {"a" => 1, "b" => nil}
92
91
  diff = HashDiff.diff(a, b)
93
92
 
94
- # NOTE: nil will be taken as {} in the context
95
- HashDiff.patch(a, diff).should == {"a" => 1, "b" => {}}
93
+ HashDiff.patch!(a, diff).should == b
96
94
 
97
95
  a = {"a" => 1, "b" => {"b1" => 1, "b2" =>2}}
98
96
  b = {"a" => 1, "b" => nil}
99
- HashDiff.unpatch(b, diff).should == a
97
+ HashDiff.unpatch!(b, diff).should == a
100
98
  end
101
99
 
102
100
  it "should be able to patch value nil removal" do
@@ -104,11 +102,11 @@ describe HashDiff do
104
102
  b = {"a" => 1}
105
103
  diff = HashDiff.diff(a, b)
106
104
 
107
- HashDiff.patch(a, diff).should == b
105
+ HashDiff.patch!(a, diff).should == b
108
106
 
109
107
  a = {"a" => 1, "b" => nil}
110
108
  b = {"a" => 1}
111
- HashDiff.unpatch(b, diff).should == a
109
+ HashDiff.unpatch!(b, diff).should == a
112
110
  end
113
111
 
114
112
  it "should be able to patch similar objects between arrays" do
@@ -116,11 +114,11 @@ describe HashDiff do
116
114
  b = [1, {'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}]
117
115
 
118
116
  diff = HashDiff.diff(a, b)
119
- HashDiff.patch(a, diff).should == b
117
+ HashDiff.patch!(a, diff).should == b
120
118
 
121
119
  a = [{'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5}, 3]
122
120
  b = [1, {'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}]
123
- HashDiff.unpatch(b, diff).should == a
121
+ HashDiff.unpatch!(b, diff).should == a
124
122
  end
125
123
 
126
124
  it "should be able to patch similar & equal objects between arrays" do
@@ -128,11 +126,11 @@ describe HashDiff do
128
126
  b = [1, {'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}]
129
127
 
130
128
  diff = HashDiff.diff(a, b)
131
- HashDiff.patch(a, diff).should == b
129
+ HashDiff.patch!(a, diff).should == b
132
130
 
133
131
  a = [{'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5}, {'x' => 5, 'y' => 6, 'z' => 3}, 1]
134
132
  b = [1, {'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}]
135
- HashDiff.unpatch(b, diff).should == a
133
+ HashDiff.unpatch!(b, diff).should == a
136
134
  end
137
135
 
138
136
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hashdiff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-22 00:00:00.000000000Z
12
+ date: 2012-06-03 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &8876060 !ruby/object:Gem::Requirement
16
+ requirement: &14927020 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '2.0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *8876060
24
+ version_requirements: *14927020
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: yard
27
- requirement: &8874480 !ruby/object:Gem::Requirement
27
+ requirement: &14926640 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *8874480
35
+ version_requirements: *14926640
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: bluecloth
38
- requirement: &8859740 !ruby/object:Gem::Requirement
38
+ requirement: &14926180 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *8859740
46
+ version_requirements: *14926180
47
47
  description: ! ' HashDiff is a diff lib to compute the smallest difference between
48
48
  two hashes. '
49
49
  email:
@@ -55,6 +55,7 @@ files:
55
55
  - .gitignore
56
56
  - .rspec
57
57
  - .travis.yml
58
+ - .yardopts
58
59
  - Gemfile
59
60
  - Gemfile.lock
60
61
  - README.md