Class: Hash
- Defined in:
- lib/strokedb/core_ext/hash.rb,
lib/strokedb/core_ext/blank.rb,
lib/strokedb/sync/diff/hash.rb,
lib/strokedb/util/serialization.rb
Overview
:nodoc:
Direct Known Subclasses
Instance Method Summary collapse
-
#_stroke_split_merge_result(result) ⇒ Object
In case of conflict, result is copied to result1,2 and nullified.
- #except(*keys) ⇒ Object
- #reverse_merge(other_hash) ⇒ Object
- #reverse_merge!(other_hash) ⇒ Object
- #stringify_keys ⇒ Object
- #stroke_diff(to) ⇒ Object
-
#stroke_merge(patch1, patch2) ⇒ Object
Hash conflict may occur: 1) If key accures in conflicting groups (e.g.
- #stroke_patch(patch) ⇒ Object
- #to_raw ⇒ Object
Instance Method Details
#_stroke_split_merge_result(result) ⇒ Object
In case of conflict, result is copied to result1,2 and nullified.
182 183 184 |
# File 'lib/strokedb/sync/diff/hash.rb', line 182 def _stroke_split_merge_result(result) return [result.dup, result.dup, nil] end |
#except(*keys) ⇒ Object
11 12 13 |
# File 'lib/strokedb/core_ext/hash.rb', line 11 def except(*keys) reject { |key,| keys.include?(key.to_sym) or keys.include?(key.to_s) } end |
#reverse_merge(other_hash) ⇒ Object
15 16 17 |
# File 'lib/strokedb/core_ext/hash.rb', line 15 def reverse_merge(other_hash) other_hash.merge(self) end |
#reverse_merge!(other_hash) ⇒ Object
19 20 21 |
# File 'lib/strokedb/core_ext/hash.rb', line 19 def reverse_merge!(other_hash) replace(reverse_merge(other_hash)) end |
#stringify_keys ⇒ Object
4 5 6 7 8 9 |
# File 'lib/strokedb/core_ext/hash.rb', line 4 def stringify_keys inject({}) do |, (key, value)| [key.to_s] = value end end |
#stroke_diff(to) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/strokedb/sync/diff/hash.rb', line 3 def stroke_diff(to) return super(to) unless to.is_a?(Hash) return nil if self == to all_keys = self.keys | to.keys deleted_slots = [] inserted_slots = {} diffed_slots = {} all_keys.each do |k| unless to.key?(k) deleted_slots << k else unless self.key?(k) inserted_slots[k] = to[k] else diff = self[k].stroke_diff(to[k]) diffed_slots[k] = diff if diff end end end [deleted_slots, inserted_slots, diffed_slots] end |
#stroke_merge(patch1, patch2) ⇒ Object
Hash conflict may occur: 1) If key accures in conflicting groups
(e.g. deleted v.s. inserted, deleted vs. diffed)
2) If slot diff yields conflict
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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/strokedb/sync/diff/hash.rb', line 44 def stroke_merge(patch1, patch2) unless patch1 && patch2 return _stroke_automerged(stroke_patch(patch1 || patch2)) end del1, ins1, dif1 = patch1[0].dup, patch1[1].dup, patch1[2].dup del2, ins2, dif2 = patch2[0].dup, patch2[1].dup, patch2[2].dup # TEMPORARY: inconsistency check conflict_d1i2 = del1 & ins2.keys conflict_d2i1 = del2 & ins1.keys conflict_i1f2 = ins1.keys & dif2.keys conflict_i2f1 = ins2.keys & dif1.keys unless conflict_d1i2.empty? && conflict_d2i1.empty? && conflict_i1f2.empty? && conflict_i2f1.empty? raise "Fatal inconsistency on stroke_merge detected!" end overlapping_keys = ((del1 + ins1.keys + dif1.keys) & (del2 + ins2.keys + dif2.keys)) # make hash for faster inclusion tests overlapping_keys_hash = overlapping_keys.inject({}) do |h, k| h[k] = 1; h end result = self.dup # 1. Merge non-overlapping updates (del1 + del2 - overlapping_keys).each do |k| del1.delete(k) del2.delete(k) result.delete(k) end ins1.dup.each do |k, v| unless overlapping_keys_hash[k] result[k] = v ins1.delete(k) end end ins2.dup.each do |k, v| unless overlapping_keys_hash[k] result[k] = v ins2.delete(k) end end dif1.dup.each do |k, diff| unless overlapping_keys_hash[k] result[k] = stroke_patch(diff) dif1.delete(k) end end dif2.dup.each do |k, diff| unless overlapping_keys_hash[k] result[k] = stroke_patch(diff) dif2.delete(k) end end # 2. Resolve overlapping keys # # Overlapping key may be in such pairs: # # [dif, dif] <- possible conflict # [dif, ins] <- anomaly # [ins, ins] <- possible conflict # [del, ins] <- anomaly # [del, dif] <- conflict # [del, del] <- not a conflict # # (and in reverse order as well) result1 = nil result2 = nil del1.each do |k| if ins2.key?(k) raise "Fatal inconsistency on stroke_merge detected: delete + insert" elsif dif2.key?(k) # Conflict. Split result if not splitted. result1, result2, result = _stroke_split_merge_result(result) if result result1.delete(k) result2[k] = result2[k].stroke_patch(dif2[k]) else # [del, del] if result result.delete(k) else result1.delete(k) result2.delete(k) end end end dif1.each do |k, diff| if ins2.key?(k) raise "Fatal inconsistency on stroke_merge detected: diff + insert" elsif dif2.key?(k) # possible conflict conflict, r1, r2 = self[k].stroke_merge(diff, dif2[k]) if conflict result1, result2, result = _stroke_split_merge_result(result) if result result1[k] = r1 result2[k] = r2 else if result result[k] = r2 else result1[k] = r2 result2[k] = r2 end end else # [dif, del] <- conflict # Conflict. Split result if not splitted. result1, result2, result = _stroke_split_merge_result(result) if result result1[k] = result1[k].stroke_patch(diff) result2.delete(k) end end ins1.each do |k, obj| if ins2.key?(k) # possible conflict if obj != ins2[k] result1, result2, result = _stroke_split_merge_result(result) if result result1[k] = obj result2[k] = ins2[k] else if result result[k] = obj else result1[k] = obj result2[k] = obj end end else # delete or diff raise "Fatal inconsistency on stroke_merge detected: insert + (delete|diff)" end end result ? _stroke_automerged(result) : _stroke_conflicted(result1, result2) end |
#stroke_patch(patch) ⇒ Object
28 29 30 31 32 33 34 35 36 37 |
# File 'lib/strokedb/sync/diff/hash.rb', line 28 def stroke_patch(patch) return self unless patch return patch[1] if patch[0] == PATCH_REPLACE res = self.dup deleted_slots, inserted_slots, diffed_slots = patch deleted_slots.each {|k| res.delete(k) } res.merge!(inserted_slots) diffed_slots.each {|k,v| res[k] = self[k].stroke_patch(v) } res end |
#to_raw ⇒ Object
12 13 14 15 16 17 18 19 20 |
# File 'lib/strokedb/util/serialization.rb', line 12 def to_raw raw_hash = {} map do |k,v| _k = k.respond_to?(:to_raw) ? k.to_raw : k _v = v.respond_to?(:to_raw) ? v.to_raw : v raw_hash[_k] = _v end raw_hash end |