Class: DeeperHash::Diff

Inherits:
Object
  • Object
show all
Defined in:
lib/deeper_hash/diff.rb

Defined Under Namespace

Classes: Stack

Instance Method Summary collapse

Constructor Details

#initialize(h1, h2) ⇒ Diff

Returns a new instance of Diff.



25
26
27
28
# File 'lib/deeper_hash/diff.rb', line 25

def initialize(h1, h2)
  @h1 = h1
  @h2 = h2
end

Instance Method Details

#arr_diff(a1, a2) ⇒ Object



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
# File 'lib/deeper_hash/diff.rb', line 72

def arr_diff(a1, a2)
  if a1.size == a2.size
    common_1 = a1; common_2 = a2; appended = []; detached = []
  elsif a1.size > a2.size
    if a2.size == 0
      common_1 = []; common_2 = []; appended = []; detached = a1
    else
      common_1 = a1[0..(a2.size-1)]; common_2 = a2; appended = []; detached = a1[a2.size..-1]
    end
  else
    if a1.size == 0
      common_1 = []; common_2 = []; appended = a2; detached = []
    else
      common_1 = a1; common_2 = a2[0..(a1.size-1)]; appended = a2[a1.size..-1]; detached = []
    end
  end
  {}.tap do |res|
    res[:arr] = a1
    res[:appended] = appended unless appended.empty?
    res[:detached] = detached unless detached.empty?
    unless common_1.empty?
      common_1.each_with_index do |v, i|
        val_1 = v
        val_2 = common_2[i]
        if val_1 != val_2
          res[:changed_el] ||= []
          if val_1.is_a?(Array) && val_2.is_a?(Array)
            res[:changed_el] << {updated_arr: arr_diff(val_1, val_2), index: i}
          elsif val_1.is_a?(Hash) && val_2.is_a?(Hash)
            res[:changed_el] << {updated_hash: Diff.new(val_1, val_2).diff, index: i} 
          else
            res[:changed_el] << {from: val_1, to: val_2, index: i}
          end
        end
      end
    end
  end
end

#attribute_change_logged?(attr) ⇒ Boolean

Returns:

  • (Boolean)


115
116
117
# File 'lib/deeper_hash/diff.rb', line 115

def attribute_change_logged?(attr)
  @attr_change_log.include?(@diff_stack.to_a + [attr])
end

#diffObject



30
31
32
33
34
35
36
# File 'lib/deeper_hash/diff.rb', line 30

def diff
  @diff_stack = Stack.new
  @result = {}
  @attr_change_log = []
  process_diff(@h1, @h2)
  @result
end

#log_attribute_change(attr) ⇒ Object



111
112
113
# File 'lib/deeper_hash/diff.rb', line 111

def log_attribute_change(attr)
  @attr_change_log << @diff_stack.to_a + [attr]
end

#process_diff(h1, h2) ⇒ Object



38
39
40
41
# File 'lib/deeper_hash/diff.rb', line 38

def process_diff(h1, h2)
  process_diff_pair(h1, h2, :removed_key)
  process_diff_pair(h2, h1, :added_key)
end

#process_diff_pair(h1, h2, action) ⇒ Object



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
# File 'lib/deeper_hash/diff.rb', line 43

def process_diff_pair(h1, h2, action)
  h1.each do |k,v|
    next if attribute_change_logged?(k)

    if h2[k]
      if v != h2[k]
        if v.is_a?(Hash) && h2[k].is_a?(Hash)
          @diff_stack.push(k)
          process_diff(v, h2[k])
          @diff_stack.pop
        else
          if v.is_a?(Array) && h2[k].is_a?(Array)
            @result.deep_set(arr_diff(v, h2[k]), *(@diff_stack.to_a + [k, :updated_arr]))
          else
            @result.deep_set({from: v, to: h2[k]}, *(@diff_stack.to_a + [k, :updated_val]))
          end
          log_attribute_change(k)
        end
      end
    else
      dir = @diff_stack.to_a + [action]
      val = (@result.dig(*dir).is_a?(Array) ? @result.dig(*dir) : [])
      val << [k, v]
      @result.deep_set(val, *dir)
      log_attribute_change(k)
    end
  end
end