hashdiff_sym 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,147 @@
1
+ module HashDiffSym
2
+
3
+ # @private
4
+ #
5
+ # judge whether two objects are similar
6
+ def self.similar?(a, b, options = {})
7
+ opts = { :similarity => 0.8 }.merge(options)
8
+
9
+ count_a = count_nodes(a)
10
+ count_b = count_nodes(b)
11
+ diffs = count_diff diff(a, b, opts)
12
+
13
+ if count_a + count_b == 0
14
+ return true
15
+ else
16
+ (1 - diffs.to_f/(count_a + count_b).to_f) >= opts[:similarity]
17
+ end
18
+ end
19
+
20
+ # @private
21
+ #
22
+ # count node differences
23
+ def self.count_diff(diffs)
24
+ diffs.inject(0) do |sum, item|
25
+ old_change_count = count_nodes(item[2])
26
+ new_change_count = count_nodes(item[3])
27
+ sum += (old_change_count + new_change_count)
28
+ end
29
+ end
30
+
31
+ # @private
32
+ #
33
+ # count total nodes for an object
34
+ def self.count_nodes(obj)
35
+ return 0 unless obj
36
+
37
+ count = 0
38
+ if obj.is_a?(Array)
39
+ obj.each {|e| count += count_nodes(e) }
40
+ elsif obj.is_a?(Hash)
41
+ obj.each {|k, v| count += count_nodes(v) }
42
+ else
43
+ return 1
44
+ end
45
+
46
+ count
47
+ end
48
+
49
+ # @private
50
+ #
51
+ # decode property path into an array
52
+ # @param [String] path Property-string
53
+ # @param [String] delimiter Property-string delimiter
54
+ #
55
+ # e.g. "a.b[3].c" => ['a', 'b', 3, 'c']
56
+ def self.decode_property_path(path, delimiter='.')
57
+ parts = path.split(delimiter).collect do |part|
58
+ if part =~ /^(.*)\[(\d+)\]$/
59
+ if $1.size > 0
60
+ [import_key($1), $2.to_i]
61
+ else
62
+ $2.to_i
63
+ end
64
+ else
65
+ import_key(part)
66
+ end
67
+ end
68
+
69
+ parts.flatten
70
+ end
71
+
72
+ # @private
73
+ #
74
+ # get the node of hash by given path parts
75
+ def self.node(hash, parts)
76
+ temp = hash
77
+ parts.each do |part|
78
+ temp = temp[part]
79
+ end
80
+ temp
81
+ end
82
+
83
+ # @private
84
+ #
85
+ # check for equality or "closeness" within given tolerance
86
+ def self.compare_values(obj1, obj2, options = {})
87
+ if (options[:numeric_tolerance].is_a? Numeric) &&
88
+ [obj1, obj2].all? { |v| v.is_a? Numeric }
89
+ return (obj1 - obj2).abs <= options[:numeric_tolerance]
90
+ end
91
+
92
+ if options[:strip] == true
93
+ obj1 = obj1.strip if obj1.respond_to?(:strip)
94
+ obj2 = obj2.strip if obj2.respond_to?(:strip)
95
+ end
96
+
97
+ if options[:case_insensitive] == true
98
+ obj1 = obj1.downcase if obj1.respond_to?(:downcase)
99
+ obj2 = obj2.downcase if obj2.respond_to?(:downcase)
100
+ end
101
+
102
+ obj1 == obj2
103
+ end
104
+
105
+ # @private
106
+ #
107
+ # check if objects are comparable
108
+ def self.comparable?(obj1, obj2, strict = true)
109
+ [Array, Hash].each do |type|
110
+ return true if obj1.is_a?(type) && obj2.is_a?(type)
111
+ end
112
+ return true if !strict && obj1.is_a?(Numeric) && obj2.is_a?(Numeric)
113
+ obj1.is_a?(obj2.class) && obj2.is_a?(obj1.class)
114
+ end
115
+
116
+ # @private
117
+ #
118
+ # try custom comparison
119
+ def self.custom_compare(method, key, obj1, obj2)
120
+ if method
121
+ res = method.call(key, obj1, obj2)
122
+
123
+ # nil != false here
124
+ if res == false
125
+ return [['~', key, obj1, obj2]]
126
+ elsif res == true
127
+ return []
128
+ end
129
+ end
130
+ end
131
+
132
+ def self.export_key(key)
133
+ if key.class == Symbol
134
+ ":#{key}"
135
+ else
136
+ key
137
+ end
138
+ end
139
+
140
+ def self.import_key(key)
141
+ if key[0] == ":"
142
+ key[1..-1].to_sym
143
+ else
144
+ key
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,3 @@
1
+ module HashDiffSym
2
+ VERSION = '0.3.1'
3
+ end
@@ -0,0 +1,5 @@
1
+ require 'hashdiff_sym/util'
2
+ require 'hashdiff_sym/lcs'
3
+ require 'hashdiff_sym/diff'
4
+ require 'hashdiff_sym/patch'
5
+ require 'hashdiff_sym/version'
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashDiffSym do
4
+ it "should be able to best diff" do
5
+ a = {'x' => [{'a' => 1, 'c' => 3, 'e' => 5}, {'y' => 3}]}
6
+ b = {'x' => [{'a' => 1, 'b' => 2, 'e' => 5}] }
7
+
8
+ diff = HashDiffSym.best_diff(a, b)
9
+ diff.should == [["-", "x[0].c", 3], ["+", "x[0].b", 2], ["-", "x[1]", {"y"=>3}]]
10
+ end
11
+
12
+ it "should use custom delimiter when provided" do
13
+ a = {'x' => [{'a' => 1, 'c' => 3, 'e' => 5}, {'y' => 3}]}
14
+ b = {'x' => [{'a' => 1, 'b' => 2, 'e' => 5}] }
15
+
16
+ diff = HashDiffSym.best_diff(a, b, :delimiter => "\t")
17
+ diff.should == [["-", "x[0]\tc", 3], ["+", "x[0]\tb", 2], ["-", "x[1]", {"y"=>3}]]
18
+ end
19
+
20
+ it "should use custom comparison when provided" do
21
+ a = {'x' => [{'a' => 'foo', 'c' => 'goat', 'e' => 'snake'}, {'y' => 'baz'}]}
22
+ b = {'x' => [{'a' => 'bar', 'b' => 'cow', 'e' => 'puppy'}] }
23
+
24
+ diff = HashDiffSym.best_diff(a, b) do |path, obj1, obj2|
25
+ case path
26
+ when /^x\[.\]\..$/
27
+ obj1.length == obj2.length if obj1 and obj2
28
+ end
29
+ end
30
+
31
+ diff.should == [["-", "x[0].c", 'goat'], ["+", "x[0].b", 'cow'], ["-", "x[1]", {"y"=>'baz'}]]
32
+ end
33
+
34
+ it "should be able to best diff array in hash" do
35
+ a = {"menu" => {
36
+ "id" => "file",
37
+ "value" => "File",
38
+ "popup" => {
39
+ "menuitem" => [
40
+ {"value" => "New", "onclick" => "CreateNewDoc()"},
41
+ {"value" => "Close", "onclick" => "CloseDoc()"}
42
+ ]
43
+ }
44
+ }}
45
+
46
+ b = {"menu" => {
47
+ "id" => "file 2",
48
+ "value" => "File",
49
+ "popup" => {
50
+ "menuitem" => [
51
+ {"value" => "New1", "onclick" => "CreateNewDoc()"},
52
+ {"value" => "Open", "onclick" => "OpenDoc()"},
53
+ {"value" => "Close", "onclick" => "CloseDoc()"}
54
+ ]
55
+ }
56
+ }}
57
+
58
+ diff = HashDiffSym.best_diff(a, b)
59
+ diff.should == [
60
+ ['~', 'menu.id', 'file', 'file 2'],
61
+ ['~', 'menu.popup.menuitem[0].value', 'New', 'New1'],
62
+ ['+', 'menu.popup.menuitem[1]', {"value" => "Open", "onclick" => "OpenDoc()"}]
63
+ ]
64
+ end
65
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashDiffSym do
4
+ it "should be able to diff two equal array" do
5
+ a = [1, 2, 3]
6
+ b = [1, 2, 3]
7
+
8
+ diff = HashDiffSym.diff_array(a, b)
9
+ diff.should == []
10
+ end
11
+
12
+ it "should be able to diff two arrays with one element in common" do
13
+ a = [1, 2, 3]
14
+ b = [1, 8, 7]
15
+
16
+ diff = HashDiffSym.diff_array(a, b)
17
+ diff.should == [['-', 2, 3], ['-', 1, 2], ['+', 1, 8], ['+', 2, 7]]
18
+ end
19
+
20
+ it "should be able to diff two arrays with nothing in common" do
21
+ a = [1, 2]
22
+ b = []
23
+
24
+ diff = HashDiffSym.diff_array(a, b)
25
+ diff.should == [['-', 1, 2], ['-', 0, 1]]
26
+ end
27
+
28
+ it "should be able to diff an empty array with an non-empty array" do
29
+ a = []
30
+ b = [1, 2]
31
+
32
+ diff = HashDiffSym.diff_array(a, b)
33
+ diff.should == [['+', 0, 1], ['+', 1, 2]]
34
+ end
35
+
36
+ it "should be able to diff two arrays with two elements in common" do
37
+ a = [1, 3, 5, 7]
38
+ b = [2, 3, 7, 5]
39
+
40
+ diff = HashDiffSym.diff_array(a, b)
41
+ diff.should == [['-', 0, 1], ['+', 0, 2], ['+', 2, 7], ['-', 4, 7]]
42
+ end
43
+
44
+ it "should be able to test two arrays with two common elements in different order" do
45
+ a = [1, 3, 4, 7]
46
+ b = [2, 3, 7, 5]
47
+
48
+ diff = HashDiffSym.diff_array(a, b)
49
+ diff.should == [['-', 0, 1], ['+', 0, 2], ['-', 2, 4], ['+', 3, 5]]
50
+ end
51
+
52
+ it "should be able to diff two arrays with similar elements" do
53
+ a = [{'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5}, 3]
54
+ b = [1, {'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}]
55
+ diff = HashDiffSym.diff_array(a, b)
56
+ diff.should == [['+', 0, 1], ['-', 2, 3]]
57
+ end
58
+
59
+ end
60
+
@@ -0,0 +1,263 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashDiffSym do
4
+ it "should be able to diff two empty hashes" do
5
+ diff = HashDiffSym.diff({}, {})
6
+ diff.should == []
7
+ end
8
+
9
+ it "should be able to diff an hash with an empty hash" do
10
+ a = { 'a' => 3, 'b' => 2 }
11
+ b = {}
12
+
13
+ diff = HashDiffSym.diff(a, b)
14
+ diff.should == [['-', 'a', 3], ['-', 'b', 2]]
15
+
16
+ diff = HashDiffSym.diff(b, a)
17
+ diff.should == [['+', 'a', 3], ['+', 'b', 2]]
18
+ end
19
+
20
+ it "should be able to diff two equal hashes" do
21
+ diff = HashDiffSym.diff({ 'a' => 2, 'b' => 2}, { 'a' => 2, 'b' => 2 })
22
+ diff.should == []
23
+ end
24
+
25
+ it "should be able to diff two hashes with equivalent numerics, when strict is false" do
26
+ diff = HashDiffSym.diff({ 'a' => 2.0, 'b' => 2 }, { 'a' => 2, 'b' => 2.0 }, :strict => false)
27
+ diff.should == []
28
+ end
29
+
30
+ it "should be able to diff changes in hash value" do
31
+ diff = HashDiffSym.diff({ 'a' => 2, 'b' => 3, 'c' => " hello" }, { 'a' => 2, 'b' => 4, 'c' => "hello" })
32
+ diff.should == [['~', 'b', 3, 4], ['~', 'c', " hello", "hello"]]
33
+ end
34
+
35
+ it "should be able to diff changes in hash value which is array" do
36
+ diff = HashDiffSym.diff({ 'a' => 2, 'b' => [1, 2, 3] }, { 'a' => 2, 'b' => [1, 3, 4]})
37
+ diff.should == [['-', 'b[1]', 2], ['+', 'b[2]', 4]]
38
+ end
39
+
40
+ it "should be able to diff changes in hash value which is hash" do
41
+ diff = HashDiffSym.diff({ 'a' => { 'x' => 2, 'y' => 3, 'z' => 4 }, 'b' => { 'x' => 3, 'z' => 45 } },
42
+ { 'a' => { 'y' => 3 }, 'b' => { 'y' => 3, 'z' => 30 } })
43
+ diff.should == [['-', 'a.x', 2], ['-', 'a.z', 4], ['-', 'b.x', 3], ['~', 'b.z', 45, 30], ['+', 'b.y', 3]]
44
+ end
45
+
46
+ it "should be able to diff similar objects in array" do
47
+ diff = HashDiffSym.best_diff({ 'a' => [{ 'x' => 2, 'y' => 3, 'z' => 4 }, { 'x' => 11, 'y' => 22, 'z' => 33 }], 'b' => { 'x' => 3, 'z' => 45 } },
48
+ { 'a' => [{ 'y' => 3 }, { 'x' => 11, 'z' => 33 }], 'b' => { 'y' => 22 } })
49
+ diff.should == [['-', 'a[0].x', 2], ['-', 'a[0].z', 4], ['-', 'a[1].y', 22], ['-', 'b.x', 3], ['-', 'b.z', 45], ['+', 'b.y', 22]]
50
+ end
51
+
52
+ it 'should be able to diff addition of key value pair' do
53
+ a = {"a"=>3, "c"=>11, "d"=>45, "e"=>100, "f"=>200}
54
+ b = {"a"=>3, "c"=>11, "d"=>45, "e"=>100, "f"=>200, "g"=>300}
55
+
56
+ diff = HashDiffSym.diff(a, b)
57
+ diff.should == [['+', 'g', 300]]
58
+
59
+ diff = HashDiffSym.diff(b, a)
60
+ diff.should == [['-', 'g', 300]]
61
+ end
62
+
63
+ it 'should be able to diff value type changes' do
64
+ a = {"a" => 3}
65
+ b = {"a" => {"a1" => 1, "a2" => 2}}
66
+
67
+ diff = HashDiffSym.diff(a, b)
68
+ diff.should == [['~', 'a', 3, {"a1" => 1, "a2" => 2}]]
69
+
70
+ diff = HashDiffSym.diff(b, a)
71
+ diff.should == [['~', 'a', {"a1" => 1, "a2" => 2}, 3]]
72
+ end
73
+
74
+ it "should be able to diff value changes: array <=> []" do
75
+ a = {"a" => 1, "b" => [1, 2]}
76
+ b = {"a" => 1, "b" => []}
77
+
78
+ diff = HashDiffSym.diff(a, b)
79
+ diff.should == [['-', 'b[1]', 2], ['-', 'b[0]', 1]]
80
+ end
81
+
82
+ it "should be able to diff value changes: array <=> nil" do
83
+ a = {"a" => 1, "b" => [1, 2]}
84
+ b = {"a" => 1, "b" => nil}
85
+
86
+ diff = HashDiffSym.diff(a, b)
87
+ diff.should == [["~", "b", [1, 2], nil]]
88
+ end
89
+
90
+ it "should be able to diff value chagnes: remove array completely" do
91
+ a = {"a" => 1, "b" => [1, 2]}
92
+ b = {"a" => 1}
93
+
94
+ diff = HashDiffSym.diff(a, b)
95
+ diff.should == [["-", "b", [1, 2]]]
96
+ end
97
+
98
+ it "should be able to diff value changes: remove whole hash" do
99
+ a = {"a" => 1, "b" => {"b1" => 1, "b2" =>2}}
100
+ b = {"a" => 1}
101
+
102
+ diff = HashDiffSym.diff(a, b)
103
+ diff.should == [["-", "b", {"b1"=>1, "b2"=>2}]]
104
+ end
105
+
106
+ it "should be able to diff value changes: hash <=> {}" do
107
+ a = {"a" => 1, "b" => {"b1" => 1, "b2" =>2}}
108
+ b = {"a" => 1, "b" => {}}
109
+
110
+ diff = HashDiffSym.diff(a, b)
111
+ diff.should == [['-', 'b.b1', 1], ['-', 'b.b2', 2]]
112
+ end
113
+
114
+ it "should be able to diff value changes: hash <=> nil" do
115
+ a = {"a" => 1, "b" => {"b1" => 1, "b2" =>2}}
116
+ b = {"a" => 1, "b" => nil}
117
+
118
+ diff = HashDiffSym.diff(a, b)
119
+ diff.should == [["~", "b", {"b1"=>1, "b2"=>2}, nil]]
120
+ end
121
+
122
+ it "should be able to diff similar objects in array" do
123
+ a = [{'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5}, 3]
124
+ b = [1, {'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}]
125
+
126
+ diff = HashDiffSym.diff(a, b)
127
+ diff.should == [['-', '[0].d', 4], ['+', '[0]', 1], ['-', '[2]', 3]]
128
+ end
129
+
130
+ it "should be able to diff similar & equal objects in array" do
131
+ a = [{'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5}, {'x' => 5, 'y' => 6, 'z' => 3}, 3]
132
+ b = [{'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}, 3]
133
+
134
+ diff = HashDiffSym.diff(a, b)
135
+ diff.should == [["-", "[0].d", 4], ["-", "[1]", {"x"=>5, "y"=>6, "z"=>3}]]
136
+ end
137
+
138
+ it "should use custom delimiter when provided" do
139
+ a = [{'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5}, {'x' => 5, 'y' => 6, 'z' => 3}, 3]
140
+ b = [{'a' => 1, 'b' => 2, 'c' => 3, 'e' => 5}, 3]
141
+
142
+ diff = HashDiffSym.diff(a, b, :similarity => 0.8, :delimiter => "\t")
143
+ diff.should == [["-", "[0]\td", 4], ["-", "[1]", {"x"=>5, "y"=>6, "z"=>3}]]
144
+ end
145
+
146
+ it "should transform symbols to string correctly" do
147
+ diff = HashDiffSym.diff({ 'a' => { x: 2, y: 3, z: 4 }, 'b' => { x: 3, z: [1, 2, 3] } },
148
+ { 'a' => { y: 3 }, 'b' => { y: 3, z: [2, 3, 4] } })
149
+ diff.should == [['-', 'a.:x', 2], ['-', 'a.:z', 4], ['-', 'b.:x', 3], ["-", "b.:z[0]", 1], ["+", "b.:z[2]", 4], ['+', 'b.:y', 3]]
150
+ end
151
+
152
+ context 'when :numeric_tolerance requested' do
153
+ it "should be able to diff changes in hash value" do
154
+ a = {'a' => 0.558, 'b' => 0.0, 'c' => 0.65, 'd' => 'fin'}
155
+ b = {'a' => 0.557, 'b' => 'hats', 'c' => 0.67, 'd' => 'fin'}
156
+
157
+ diff = HashDiffSym.diff(a, b, :numeric_tolerance => 0.01)
158
+ diff.should == [["~", "b", 0.0, 'hats'], ["~", "c", 0.65, 0.67]]
159
+
160
+ diff = HashDiffSym.diff(b, a, :numeric_tolerance => 0.01)
161
+ diff.should == [["~", "b", 'hats', 0.0], ["~", "c", 0.67, 0.65]]
162
+ end
163
+
164
+ it "should be able to diff changes in nested values" do
165
+ a = {'a' => {'x' => 0.4, 'y' => 0.338}, 'b' => [13, 68.03]}
166
+ b = {'a' => {'x' => 0.6, 'y' => 0.341}, 'b' => [14, 68.025]}
167
+
168
+ diff = HashDiffSym.diff(a, b, :numeric_tolerance => 0.01)
169
+ diff.should == [["~", "a.x", 0.4, 0.6], ["-", "b[0]", 13], ["+", "b[0]", 14]]
170
+
171
+ diff = HashDiffSym.diff(b, a, :numeric_tolerance => 0.01)
172
+ diff.should == [["~", "a.x", 0.6, 0.4], ["-", "b[0]", 14], ["+", "b[0]", 13]]
173
+ end
174
+ end
175
+
176
+ context 'when :strip requested' do
177
+ it "should strip strings before comparing" do
178
+ a = { 'a' => " foo", 'b' => "fizz buzz"}
179
+ b = { 'a' => "foo", 'b' => "fizzbuzz"}
180
+ diff = HashDiffSym.diff(a, b, :strip => true)
181
+ diff.should == [['~', 'b', "fizz buzz", "fizzbuzz"]]
182
+ end
183
+
184
+ it "should strip nested strings before comparing" do
185
+ a = { 'a' => { 'x' => " foo" }, 'b' => ["fizz buzz", "nerf"] }
186
+ b = { 'a' => { 'x' => "foo" }, 'b' => ["fizzbuzz", "nerf"] }
187
+ diff = HashDiffSym.diff(a, b, :strip => true)
188
+ diff.should == [['-', 'b[0]', "fizz buzz"], ['+', 'b[0]', "fizzbuzz"]]
189
+ end
190
+ end
191
+
192
+ context 'when :case_insensitive requested' do
193
+ it "should strip strings before comparing" do
194
+ a = { 'a' => "Foo", 'b' => "fizz buzz"}
195
+ b = { 'a' => "foo", 'b' => "fizzBuzz"}
196
+ diff = HashDiffSym.diff(a, b, :case_insensitive => true)
197
+ diff.should == [['~', 'b', "fizz buzz", "fizzBuzz"]]
198
+ end
199
+
200
+ it "should ignore case on nested strings before comparing" do
201
+ a = { 'a' => { 'x' => "Foo" }, 'b' => ["fizz buzz", "nerf"] }
202
+ b = { 'a' => { 'x' => "foo" }, 'b' => ["fizzbuzz", "nerf"] }
203
+ diff = HashDiffSym.diff(a, b, :case_insensitive => true)
204
+ diff.should == [['-', 'b[0]', "fizz buzz"], ['+', 'b[0]', "fizzbuzz"]]
205
+ end
206
+ end
207
+
208
+ context 'when both :strip and :numeric_tolerance requested' do
209
+ it 'should apply filters to proper object types' do
210
+ a = { 'a' => " foo", 'b' => 35, 'c' => 'bar', 'd' => 'baz' }
211
+ b = { 'a' => "foo", 'b' => 35.005, 'c' => 'bar', 'd' => 18.5}
212
+ diff = HashDiffSym.diff(a, b, :strict => false, :numeric_tolerance => 0.01, :strip => true)
213
+ diff.should == [['~', 'd', "baz", 18.5]]
214
+ end
215
+ end
216
+
217
+ context "when both :strip and :case_insensitive requested" do
218
+ it "should apply both filters to strings" do
219
+ a = { 'a' => " Foo", 'b' => "fizz buzz"}
220
+ b = { 'a' => "foo", 'b' => "fizzBuzz"}
221
+ diff = HashDiffSym.diff(a, b, :case_insensitive => true, :strip => true)
222
+ diff.should == [['~', 'b', "fizz buzz", "fizzBuzz"]]
223
+ end
224
+ end
225
+
226
+ context 'with custom comparison' do
227
+ let(:a) { { 'a' => 'car', 'b' => 'boat', 'c' => 'plane'} }
228
+ let(:b) { { 'a' => 'bus', 'b' => 'truck', 'c' => ' plan'} }
229
+
230
+ it 'should compare using proc specified in block' do
231
+ diff = HashDiffSym.diff(a, b) do |prefix, obj1, obj2|
232
+ case prefix
233
+ when /a|b|c/
234
+ obj1.length == obj2.length
235
+ end
236
+ end
237
+ diff.should == [['~', 'b', 'boat', 'truck']]
238
+ end
239
+
240
+ it 'should yield added keys' do
241
+ x = { 'a' => 'car', 'b' => 'boat'}
242
+ y = { 'a' => 'car' }
243
+
244
+ diff = HashDiffSym.diff(x, y) do |prefix, obj1, obj2|
245
+ case prefix
246
+ when /b/
247
+ true
248
+ end
249
+ end
250
+ diff.should == []
251
+ end
252
+
253
+ it 'should compare with both proc and :strip when both provided' do
254
+ diff = HashDiffSym.diff(a, b, :strip => true) do |prefix, obj1, obj2|
255
+ case prefix
256
+ when 'a'
257
+ obj1.length == obj2.length
258
+ end
259
+ end
260
+ diff.should == [['~', 'b', 'boat', 'truck'], ['~', 'c', 'plane', ' plan']]
261
+ end
262
+ end
263
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe HashDiffSym do
4
+ it "should be able to find LCS between two equal array" do
5
+ a = [1, 2, 3]
6
+ b = [1, 2, 3]
7
+
8
+ lcs = HashDiffSym.lcs(a, b)
9
+ lcs.should == [[0, 0], [1, 1], [2, 2]]
10
+ end
11
+
12
+ it "should be able to find LCS between two close arrays" do
13
+ a = [1.05, 2, 3.25]
14
+ b = [1.06, 2, 3.24]
15
+
16
+ lcs = HashDiffSym.lcs(a, b, :numeric_tolerance => 0.1)
17
+ lcs.should == [[0, 0], [1, 1], [2, 2]]
18
+ end
19
+
20
+ it "should strip strings when finding LCS if requested" do
21
+ a = ['foo', 'bar', 'baz']
22
+ b = [' foo', 'bar', 'zab']
23
+
24
+ lcs = HashDiffSym.lcs(a, b, :strip => true)
25
+ lcs.should == [[0, 0], [1, 1]]
26
+ end
27
+
28
+ it "should be able to find LCS with one common elements" do
29
+ a = [1, 2, 3]
30
+ b = [1, 8, 7]
31
+
32
+ lcs = HashDiffSym.lcs(a, b)
33
+ lcs.should == [[0, 0]]
34
+ end
35
+
36
+ it "should be able to find LCS with two common elements" do
37
+ a = [1, 3, 5, 7]
38
+ b = [2, 3, 7, 5]
39
+
40
+ lcs = HashDiffSym.lcs(a, b)
41
+ lcs.should == [[1, 1], [2, 3]]
42
+ end
43
+
44
+ it "should be able to find LCS with two close elements" do
45
+ a = [1, 3.05, 5, 7]
46
+ b = [2, 3.06, 7, 5]
47
+
48
+ lcs = HashDiffSym.lcs(a, b, :numeric_tolerance => 0.1)
49
+ lcs.should == [[1, 1], [2, 3]]
50
+ end
51
+
52
+ it "should be able to find LCS with two common elements in different ordering" do
53
+ a = [1, 3, 4, 7]
54
+ b = [2, 3, 7, 5]
55
+
56
+ lcs = HashDiffSym.lcs(a, b)
57
+ lcs.should == [[1, 1], [3, 2]]
58
+ end
59
+
60
+ it "should be able to find LCS with a similarity value" do
61
+ a = [
62
+ {"value" => "New", "onclick" => "CreateNewDoc()"},
63
+ {"value" => "Close", "onclick" => "CloseDoc()"}
64
+ ]
65
+ b = [
66
+ {"value" => "New1", "onclick" => "CreateNewDoc()"},
67
+ {"value" => "Open", "onclick" => "OpenDoc()"},
68
+ {"value" => "Close", "onclick" => "CloseDoc()"}
69
+ ]
70
+
71
+ lcs = HashDiffSym.lcs(a, b, :similarity => 0.5)
72
+ lcs.should == [[0, 0], [1, 2]]
73
+ end
74
+ end
75
+