/
diff.rb
137 lines (121 loc) · 3.62 KB
/
diff.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
require 'diff/lcs'
module StrokeDB
PATCH_REPLACE = 'R'.freeze
PATCH_PLUS = '+'.freeze
PATCH_MINUS = '-'.freeze
PATCH_DIFF = 'D'.freeze
class SlotDiffStrategy
def self.diff(from, to)
to
end
end
class DefaultSlotDiff < SlotDiffStrategy
def self.diff(from, to)
unless from.class == to.class # if value types are not the same
to # then return new value
else
case to
when /@##{UUID_RE}/, /@##{UUID_RE}.#{VERSION_RE}/
to
when Array, String
::Diff::LCS.diff(from, to).map do |d|
d.map do |change|
change.to_a
end
end
when Hash
::Diff::LCS.diff(from.sort_by{|e| e.to_s}, to.sort_by{|e| e.to_s}).map do |d|
d.map do |change|
[change.to_a.first, {change.to_a.last.first => change.to_a.last.last}]
end
end
else
to
end
end
end
def self.patch(from, patch)
case from
when /@##{UUID_RE}/, /@##{UUID_RE}.#{VERSION_RE}/
patch
when String, Array
lcs_patch = patch.map do |d|
d.map do |change|
::Diff::LCS::Change.from_a(change)
end
end
::Diff::LCS.patch!(from, lcs_patch)
when Hash
lcs_patch = patch.map do |d|
d.map_with_index do |change, index|
::Diff::LCS::Change.from_a([change.first, index, [change.last.keys.first, change.last.values.first]])
end
end
diff = ::Diff::LCS.patch!(from.sort_by{|e| e.to_s}, lcs_patch)
hash = {}
diff.each do |v|
hash[v.first] = v.last
end
hash
else
patch
end
end
end
Diff = Meta.new do
on_initialization do |diff|
diff.added_slots = {} unless diff[:added_slots]
diff.removed_slots = {} unless diff[:removed_slots]
diff.updated_slots = {} unless diff[:updated_slots]
diff.send!(:compute_diff) if diff.new?
end
def different?
!updated_slots.empty? || !removed_slots.empty? || !added_slots.empty?
end
def patch!(document)
added_slots.each_pair do |addition, value|
document[addition] = value
end
removed_slots.keys.each do |removal|
document.remove_slot!(removal)
end
updated_slots.each_pair do |update, value|
if sk = strategy_class_for(update)
document[update] = sk.patch(document[update], value)
else
document[update] =value
end
end
end
protected
def compute_diff
additions = to.slotnames - from.slotnames
additions.each do |addition|
self.added_slots[addition] = to[addition]
end
removals = from.slotnames - to.slotnames
removals.each do |removal|
self.removed_slots[removal] = from[removal]
end
updates = (to.slotnames - additions - ['version']).select {|slotname| to[slotname] != from[slotname]}
updates.each do |update|
unless sk = strategy_class_for(update)
self.updated_slots[update] = to[update]
else
self.updated_slots[update] = sk.diff(from[update], to[update])
end
end
end
def strategy_class_for(slotname)
if from.meta && strategy = from.meta["diff_strategy_#{slotname}"]
_strategy_class = strategy.camelize.constantize rescue nil
return _strategy_class if _strategy_class && _strategy_class.ancestors.include?(SlotDiffStrategy)
end
false
end
end
end
require 'sync/diff/default'
require 'sync/diff/string'
require 'sync/diff/array'
require 'sync/diff/hash'