hashdiff_sym 0.3.1

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.
@@ -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
+