diff_json 0.1.2 → 1.1.0
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.
- checksums.yaml +4 -4
- data/lib/diff_json.rb +3 -3
- data/lib/diff_json/diff.rb +77 -479
- data/lib/diff_json/diff/json_diffing.rb +201 -0
- data/lib/diff_json/diff/json_mapping.rb +122 -0
- data/lib/diff_json/output/html_output.rb +268 -0
- data/lib/diff_json/{undefined_value.rb → output/undefined_value.rb} +0 -0
- metadata +27 -12
- data/lib/diff_json/html_output.rb +0 -98
@@ -0,0 +1,201 @@
|
|
1
|
+
module JsonDiffing
|
2
|
+
private
|
3
|
+
|
4
|
+
def diff_check(old_element, new_element, base_path = '')
|
5
|
+
diff_operations = {}
|
6
|
+
|
7
|
+
if (old_element.is_a?(Array) and new_element.is_a?(Array)) or (old_element.is_a?(Hash) and new_element.is_a?(Hash))
|
8
|
+
element_operations = case old_element.class.name
|
9
|
+
when 'Array'
|
10
|
+
diff_array(old_element, new_element, base_path)
|
11
|
+
when 'Hash'
|
12
|
+
diff_hash(old_element, new_element, base_path)
|
13
|
+
end
|
14
|
+
|
15
|
+
if @opts[:track_structure_updates]
|
16
|
+
element_operations[base_path] = [{op: :update}] if element_operations.select{|k,v| count_path?(k, "#{base_path}/*")}.length > 0
|
17
|
+
end
|
18
|
+
|
19
|
+
diff_operations.merge!(element_operations)
|
20
|
+
else
|
21
|
+
diff_operations[base_path] = [{op: :replace, path: base_path, from: old_element, value: new_element}] unless old_element == new_element
|
22
|
+
end
|
23
|
+
|
24
|
+
return diff_operations
|
25
|
+
end
|
26
|
+
|
27
|
+
def diff_array(old_array, new_array, base_path)
|
28
|
+
return {} if old_array == new_array
|
29
|
+
|
30
|
+
diff_operations = {}
|
31
|
+
add_drop_operations = {}
|
32
|
+
last_shared_index = (old_array.length - 1)
|
33
|
+
|
34
|
+
if @opts[:replace_primitives_arrays]
|
35
|
+
if @old_map[base_path][:array_type] == :primitives and @new_map[base_path][:array_type] == :primitives
|
36
|
+
diff_operations[base_path] = [{op: :replace, path: base_path, from: old_array, value: new_array}]
|
37
|
+
return diff_operations
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if @opts[:track_array_moves]
|
42
|
+
old_array_map = old_array.each_with_index.map{|v,i| [i, v]}
|
43
|
+
new_array_map = new_array.each_with_index.map{|v,i| [i, v]}
|
44
|
+
shared_elements = (old_array_map & new_array_map)
|
45
|
+
old_move_check = (old_array_map - shared_elements)
|
46
|
+
new_move_check = (new_array_map - shared_elements)
|
47
|
+
possible_moves = []
|
48
|
+
max_moves = (old_move_check.length < new_move_check.length ? old_move_check.length : new_move_check.length)
|
49
|
+
|
50
|
+
if max_moves > 0
|
51
|
+
old_move_check.each do |omc|
|
52
|
+
destinations = new_move_check.map{|v| omc[1] == v[1] ? [(omc[0] - v[0]).abs, omc[0], v[0]] : nil}.compact.sort_by{|x| x[0]}
|
53
|
+
if !destinations.empty? and possible_moves.length < max_moves
|
54
|
+
possible_moves << {op: :move, from: "#{base_path}/#{destinations.first[1]}", path: "#{base_path}/#{destinations.first[2]}"}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if new_array.length > old_array.length
|
61
|
+
new_array[(old_array.length)..(new_array.length - 1)].each_with_index do |value, i|
|
62
|
+
element_path = "#{base_path}/#{(old_array.length + i)}"
|
63
|
+
add_drop_operations[element_path] = [{op: :add, path: element_path, value: value}]
|
64
|
+
|
65
|
+
if @opts[:track_array_moves]
|
66
|
+
element_move_search = possible_moves.select{|x| x[:path] == element_path}
|
67
|
+
add_drop_operations[element_path] += element_move_search
|
68
|
+
end
|
69
|
+
end
|
70
|
+
elsif old_array.length > new_array.length
|
71
|
+
last_shared_index = new_array.length - 1
|
72
|
+
|
73
|
+
old_array[(new_array.length)..(old_array.length - 1)].each_with_index do |value, i|
|
74
|
+
element_index = (new_array.length + i)
|
75
|
+
element_path = "#{base_path}/#{element_index}"
|
76
|
+
add_drop_operations[element_path] = [{op: :remove, path: element_path, value: old_array[element_index]}]
|
77
|
+
|
78
|
+
if @opts[:track_array_moves]
|
79
|
+
element_move_search = possible_moves.select{|x| x[:from] == element_path}
|
80
|
+
add_drop_operations[element_path] += element_move_search
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
(0..last_shared_index).each do |i|
|
86
|
+
index_path = "#{base_path}/#{i}"
|
87
|
+
|
88
|
+
if @opts[:track_array_moves]
|
89
|
+
element_move_search = possible_moves.select{|x| x[:from] == index_path or x[:path] == index_path}
|
90
|
+
element_move_search << {op: :replace, path: index_path, value: new_array[i]} if element_move_search.length == 1
|
91
|
+
diff_operations.merge!(element_move_search.empty? ? diff_check(old_array[i], new_array[i], index_path) : {index_path => element_move_search})
|
92
|
+
else
|
93
|
+
unless @opts[:ignore_paths].include?(index_path)
|
94
|
+
diff_operations.merge!(diff_check(old_array[i], new_array[i], index_path))
|
95
|
+
else
|
96
|
+
diff_operations[index_path] = [{op: :ignore}]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
diff_operations.merge!(add_drop_operations)
|
102
|
+
|
103
|
+
return diff_operations
|
104
|
+
end
|
105
|
+
|
106
|
+
def diff_hash(old_hash, new_hash, base_path)
|
107
|
+
return {} if old_hash == new_hash
|
108
|
+
|
109
|
+
diff_operations = {}
|
110
|
+
old_keys, new_keys = old_hash.keys, new_hash.keys
|
111
|
+
common_keys, added_keys, dropped_keys = (old_keys & new_keys), (new_keys - old_keys), (old_keys - new_keys)
|
112
|
+
|
113
|
+
common_keys.each do |ck|
|
114
|
+
element_path = "#{base_path}/#{ck}"
|
115
|
+
|
116
|
+
unless @opts[:ignore_paths].include?(element_path)
|
117
|
+
diff_operations.merge!(diff_check(old_hash[ck], new_hash[ck], element_path))
|
118
|
+
else
|
119
|
+
diff_operations[element_path] = [{op: :ignore}]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
added_keys.each do |ak|
|
124
|
+
element_path = "#{base_path}/#{ak}"
|
125
|
+
diff_operations[element_path] = [{op: :add, path: element_path, value: new_hash[ak]}]
|
126
|
+
end
|
127
|
+
|
128
|
+
dropped_keys.each do |dk|
|
129
|
+
element_path = "#{base_path}/#{dk}"
|
130
|
+
diff_operations[element_path] = [{op: :remove, path: element_path}]
|
131
|
+
end
|
132
|
+
|
133
|
+
return diff_operations
|
134
|
+
end
|
135
|
+
|
136
|
+
def generate_sub_diffs
|
137
|
+
sub_diffs = {}
|
138
|
+
|
139
|
+
@opts[:sub_diffs].each do |k,v|
|
140
|
+
sub_diff_paths = @all_paths.select{|x| count_path?(x, k)}
|
141
|
+
old_elements = @old_map.select{|k,v| sub_diff_paths.include?(k)}.values.map{|x| {x[:value][v[:key]] => x[:value]}}.reduce(:merge)
|
142
|
+
new_elements = @new_map.select{|k,v| sub_diff_paths.include?(k)}.values.map{|x| {x[:value][v[:key]] => x[:value]}}.reduce(:merge)
|
143
|
+
|
144
|
+
(old_elements.keys + new_elements.keys).uniq.each do |sub_diff_id|
|
145
|
+
sub_diffs["#{k}::#{sub_diff_id}"] = DiffJson::Diff.new((old_elements[sub_diff_id] || {}), (new_elements[sub_diff_id] || {}), **v[:opts]) unless old_elements[sub_diff_id] == new_elements[sub_diff_id]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
return sub_diffs
|
150
|
+
end
|
151
|
+
|
152
|
+
def find_counts(diff_structure)
|
153
|
+
counts = {
|
154
|
+
ignore: 0,
|
155
|
+
add: 0,
|
156
|
+
replace: 0,
|
157
|
+
remove: 0
|
158
|
+
}
|
159
|
+
counts[:move] = 0 if @opts[:track_array_moves]
|
160
|
+
counts[:update] = 0 if @opts[:track_structure_updates]
|
161
|
+
|
162
|
+
diff_structure.each do |path, operations|
|
163
|
+
inclusion = path_inclusion(path)
|
164
|
+
|
165
|
+
operations.each do |op|
|
166
|
+
counts[op[:op]] += 1 if (inclusion.include?(op[:op]) and ([:ignore, :add, :replace, :remove, :update].include?(op[:op]) or (op[:op] == :move and path == op[:from])))
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
return counts
|
171
|
+
end
|
172
|
+
|
173
|
+
def path_inclusion(path)
|
174
|
+
if @opts[:count_operations].include?('**')
|
175
|
+
return @opts[:count_operations]['**']
|
176
|
+
else
|
177
|
+
@opts[:count_operations].each do |path_set, operations|
|
178
|
+
return operations if count_path?(path, path_set)
|
179
|
+
end
|
180
|
+
|
181
|
+
return []
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def count_path?(path, inclusion)
|
186
|
+
inclusion_base = inclusion.gsub(/\/?\**$/, '')
|
187
|
+
inclusion_wildcard = /\**$/.match(inclusion)[0]
|
188
|
+
|
189
|
+
if path.include?(inclusion_base)
|
190
|
+
trailing_elements = path.gsub(/^#{inclusion_base}\/?/, '').split('/').length
|
191
|
+
|
192
|
+
return true if (
|
193
|
+
(trailing_elements == 0 and inclusion_wildcard == '') or
|
194
|
+
(trailing_elements == 1 and inclusion_wildcard == '*') or
|
195
|
+
(trailing_elements > 0 and inclusion_wildcard == '**')
|
196
|
+
)
|
197
|
+
end
|
198
|
+
|
199
|
+
return false
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module JsonMapping
|
2
|
+
private
|
3
|
+
|
4
|
+
def path_indentation(path)
|
5
|
+
return 0 if path.empty?
|
6
|
+
return path.sub('/', '').split('/').length
|
7
|
+
end
|
8
|
+
|
9
|
+
def sortable_path(path)
|
10
|
+
return [''] if path.empty?
|
11
|
+
return path.split('/').map{|p| (p =~ /^\d+$/).nil? ? p : p.to_i}
|
12
|
+
end
|
13
|
+
|
14
|
+
def gather_paths(old_paths, new_paths, sort = false)
|
15
|
+
gathered_paths = []
|
16
|
+
|
17
|
+
if sort
|
18
|
+
sortable_paths = (old_paths | new_paths).map{|path| sortable_path(path)}
|
19
|
+
|
20
|
+
sortable_paths.sort! do |x,y|
|
21
|
+
last_index = x.length > y.length ? (x.length - 1) : (y.length - 1)
|
22
|
+
sort_value = nil
|
23
|
+
|
24
|
+
(0..last_index).each do |i|
|
25
|
+
next if x[i] == y[i]
|
26
|
+
|
27
|
+
sort_value = case [x[i].class.name, y[i].class.name]
|
28
|
+
when ['NilClass', 'Fixnum'], ['NilClass', 'Integer'], ['NilClass', 'String'], ['Fixnum', 'String'], ['Integer', 'String']
|
29
|
+
-1
|
30
|
+
when ['Fixnum', 'NilClass'], ['Integer', 'NilClass'], ['String', 'NilClass'], ['String', 'Fixnum'], ['String', 'Integer']
|
31
|
+
1
|
32
|
+
else
|
33
|
+
x[i] <=> y[i]
|
34
|
+
end
|
35
|
+
|
36
|
+
break unless sort_value.nil?
|
37
|
+
end
|
38
|
+
|
39
|
+
sort_value
|
40
|
+
end
|
41
|
+
|
42
|
+
return sortable_paths.map{|path| path.join('/')}
|
43
|
+
else
|
44
|
+
### Implementation in progress, for now, raise error
|
45
|
+
raise 'Natural sort order is WIP, for now, do not override the :path_sort option'
|
46
|
+
end
|
47
|
+
|
48
|
+
return gathered_paths
|
49
|
+
end
|
50
|
+
|
51
|
+
def is_structure?(value)
|
52
|
+
return (value.is_a?(Array) or value.is_a?(Hash))
|
53
|
+
end
|
54
|
+
|
55
|
+
def element_metadata(path, value, **overrides)
|
56
|
+
hash_list = (value.is_a?(Array) ? value.map{|x| x.hash} : [])
|
57
|
+
is_structure = is_structure?(value)
|
58
|
+
array_type = nil
|
59
|
+
|
60
|
+
if is_structure and value.is_a?(Array)
|
61
|
+
structure_detection = value.map{|v| is_structure?(v)}.uniq
|
62
|
+
|
63
|
+
array_type = if structure_detection.empty?
|
64
|
+
:empty
|
65
|
+
elsif structure_detection.length > 1
|
66
|
+
:mixed
|
67
|
+
else
|
68
|
+
(structure_detection.first ? :structures : :primitives)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
return {
|
73
|
+
:hash_list => hash_list,
|
74
|
+
:indentation => path_indentation(path),
|
75
|
+
:index => 0,
|
76
|
+
:key => nil,
|
77
|
+
:length => (is_structure ? value.length : nil),
|
78
|
+
:trailing_comma => false,
|
79
|
+
:type => (is_structure ? value.class.name.downcase.to_sym : :primitive),
|
80
|
+
:array_type => array_type,
|
81
|
+
:value => value
|
82
|
+
}.merge(overrides)
|
83
|
+
end
|
84
|
+
|
85
|
+
def map_json(json, base_path, index, parent_length = 1, **metadata_overrides)
|
86
|
+
map = {}
|
87
|
+
map[base_path] = element_metadata(base_path, json, index: index, trailing_comma: (index < (parent_length - 1)), **metadata_overrides)
|
88
|
+
|
89
|
+
if json.is_a?(Array)
|
90
|
+
json.each_with_index do |value, i|
|
91
|
+
index_path = "#{base_path}/#{i}"
|
92
|
+
|
93
|
+
if value.is_a?(Array)
|
94
|
+
map.merge!(map_json(value, index_path, i, value.length))
|
95
|
+
elsif value.is_a?(Hash)
|
96
|
+
map.merge!(map_json(value, index_path, i, value.keys.length))
|
97
|
+
else
|
98
|
+
map[index_path] = element_metadata(index_path, value, index: i, trailing_comma: (i < (json.length - 1)))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
elsif json.is_a?(Hash)
|
102
|
+
json = (@opts[:path_sort] == :sorted ? json.to_a.sort.to_h : json)
|
103
|
+
key_index = 0
|
104
|
+
|
105
|
+
json.each do |key, value|
|
106
|
+
key_path = "#{base_path}/#{key}"
|
107
|
+
|
108
|
+
if value.is_a?(Array)
|
109
|
+
map.merge!(map_json(value, key_path, key_index, value.length, key: key))
|
110
|
+
elsif value.is_a?(Hash)
|
111
|
+
map.merge!(map_json(value, key_path, key_index, value.keys.length, key: key))
|
112
|
+
else
|
113
|
+
map[key_path] = element_metadata(key_path, value, index: key_index, trailing_comma: (key_index < (json.keys.length - 1)), key: key)
|
114
|
+
end
|
115
|
+
|
116
|
+
key_index = key_index.next
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
return map
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
module DiffJson
|
2
|
+
class HtmlOutput
|
3
|
+
def initialize(diff, **opts)
|
4
|
+
@diff = diff
|
5
|
+
@opts = {
|
6
|
+
table_id_prefix: 'diff_json_view_0',
|
7
|
+
markup_type: :bootstrap
|
8
|
+
}
|
9
|
+
@markup = build
|
10
|
+
end
|
11
|
+
|
12
|
+
def diff
|
13
|
+
return @diff
|
14
|
+
end
|
15
|
+
|
16
|
+
def markup
|
17
|
+
return @markup
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def build
|
23
|
+
new_markup = {main: table_markup(@diff, @opts[:table_id_prefix]), sub_diffs: {}}
|
24
|
+
|
25
|
+
@diff.sub_diffs.each do |sdid, sub_diff|
|
26
|
+
new_markup[:sub_diffs][sdid] = HtmlOutput.new(sub_diff, @opts)
|
27
|
+
end
|
28
|
+
|
29
|
+
return new_markup
|
30
|
+
end
|
31
|
+
|
32
|
+
def table_markup(table_diff, table_id_prefix)
|
33
|
+
markup_lines = {left: "", right: "", full: "", sub_diffs: {}}
|
34
|
+
|
35
|
+
html_opener = self.method("html_#{@opts[:markup_type]}_opener".to_sym).call(table_id_prefix)
|
36
|
+
markup_lines[:left] = html_opener[:left]
|
37
|
+
markup_lines[:right] = html_opener[:right]
|
38
|
+
markup_lines[:full] = html_opener[:full]
|
39
|
+
|
40
|
+
hierarchy_lock = nil
|
41
|
+
structure_queue = []
|
42
|
+
|
43
|
+
table_diff.paths.each_with_index do |path, i|
|
44
|
+
skip_path = (!hierarchy_lock.nil? and !(path =~ /^#{hierarchy_lock}.+$/).nil?)
|
45
|
+
unless skip_path
|
46
|
+
if !hierarchy_lock.nil?
|
47
|
+
hierarchy_lock = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
old_element, new_element = table_diff.json_map(:old)[path], table_diff.json_map(:new)[path]
|
51
|
+
|
52
|
+
operations = (table_diff.diff[path] || []).map{|op|
|
53
|
+
case op[:op]
|
54
|
+
when :ignore, :add, :replace, :remove
|
55
|
+
op[:op]
|
56
|
+
when :move
|
57
|
+
op[:from] == path ? :send_move : :receive_move
|
58
|
+
end
|
59
|
+
}.compact
|
60
|
+
|
61
|
+
if operations.empty? or operations.include?(:ignore)
|
62
|
+
left_operators = ' '
|
63
|
+
right_operators = ' '
|
64
|
+
else
|
65
|
+
left_operators = [operations.include?(:send_move) ? 'M' : ' ', (operations & [:replace, :remove]).length > 0 ? '-' : ' '].join
|
66
|
+
right_operators = [operations.include?(:receive_move) ? 'M' : ' ', (operations & [:add, :replace]).length > 0 ? '+' : ' '].join
|
67
|
+
end
|
68
|
+
|
69
|
+
if !old_element.nil? and !new_element.nil?
|
70
|
+
if (old_element[:value] == new_element[:value]) or (operations & [:ignore, :replace]).length > 0 or (operations & [:send_move, :receive_move]).length == 2
|
71
|
+
old_lines, new_lines = balance_output(old_element[:value], new_element[:value], indentation: old_element[:indentation], old_key: old_element[:key], new_key: new_element[:key], old_comma: old_element[:trailing_comma], new_comma: new_element[:trailing_comma])
|
72
|
+
hierarchy_lock = path unless old_element[:type] == :primitive and new_element[:type] == :primitive
|
73
|
+
else
|
74
|
+
old_lines = jpg(nil, structure: true, structure_position: :open, structure_type: old_element[:type], indentation: old_element[:indentation], key: old_element[:key], trailing_comma: false)
|
75
|
+
new_lines = jpg(nil, structure: true, structure_position: :open, structure_type: new_element[:type], indentation: new_element[:indentation], key: new_element[:key], trailing_comma: false)
|
76
|
+
structure_queue.push({path: path, type: old_element[:type], indentation: old_element[:indentation], old_comma: old_element[:trailing_comma], new_comma: new_element[:trailing_comma]})
|
77
|
+
end
|
78
|
+
elsif old_element.nil?
|
79
|
+
old_lines, new_lines = balance_output(UndefinedValue.new, new_element[:value], indentation: new_element[:indentation], new_key: new_element[:key], new_comma: new_element[:trailing_comma])
|
80
|
+
hierarchy_lock = path unless new_element[:type] == :primitive
|
81
|
+
else
|
82
|
+
old_lines, new_lines = balance_output(old_element[:value], UndefinedValue.new, indentation: old_element[:indentation], old_key: old_element[:key], old_comma: old_element[:trailing_comma])
|
83
|
+
hierarchy_lock = path unless old_element[:type] == :primitive
|
84
|
+
end
|
85
|
+
|
86
|
+
compiled_lines = self.method("html_#{@opts[:markup_type]}_lines".to_sym).call(left_operators, old_lines, right_operators, new_lines)
|
87
|
+
markup_lines[:left] << compiled_lines[:left]
|
88
|
+
markup_lines[:right] << compiled_lines[:right]
|
89
|
+
markup_lines[:full] << compiled_lines[:full]
|
90
|
+
end
|
91
|
+
|
92
|
+
unless structure_queue.empty?
|
93
|
+
if i == (table_diff.paths.length - 1)
|
94
|
+
structure_queue.reverse.each do |sq|
|
95
|
+
old_lines = jpg(nil, structure: true, structure_position: :close, structure_type: sq[:type], indentation: sq[:indentation], trailing_comma: sq[:old_comma])
|
96
|
+
new_lines = jpg(nil, structure: true, structure_position: :close, structure_type: sq[:type], indentation: sq[:indentation], trailing_comma: sq[:new_comma])
|
97
|
+
compiled_lines = self.method("html_#{@opts[:markup_type]}_lines".to_sym).call(' ', old_lines, ' ', new_lines)
|
98
|
+
markup_lines[:left] << compiled_lines[:left]
|
99
|
+
markup_lines[:right] << compiled_lines[:right]
|
100
|
+
markup_lines[:full] << compiled_lines[:full]
|
101
|
+
end
|
102
|
+
else
|
103
|
+
if (table_diff.paths[(i+1)] =~ /^#{structure_queue.last[:path]}.+$/).nil?
|
104
|
+
sq = structure_queue.pop
|
105
|
+
old_lines = jpg(nil, structure: true, structure_position: :close, structure_type: sq[:type], indentation: sq[:indentation], trailing_comma: sq[:old_comma])
|
106
|
+
new_lines = jpg(nil, structure: true, structure_position: :close, structure_type: sq[:type], indentation: sq[:indentation], trailing_comma: sq[:new_comma])
|
107
|
+
compiled_lines = self.method("html_#{@opts[:markup_type]}_lines".to_sym).call(' ', old_lines, ' ', new_lines)
|
108
|
+
markup_lines[:left] << compiled_lines[:left]
|
109
|
+
markup_lines[:right] << compiled_lines[:right]
|
110
|
+
markup_lines[:full] << compiled_lines[:full]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
html_closer = self.method("html_#{@opts[:markup_type]}_closer".to_sym).call
|
117
|
+
markup_lines[:left] << html_closer[:left]
|
118
|
+
markup_lines[:right] << html_closer[:right]
|
119
|
+
markup_lines[:full] << html_closer[:full]
|
120
|
+
|
121
|
+
return markup_lines
|
122
|
+
end
|
123
|
+
|
124
|
+
def html_table_opener(table_id_prefix)
|
125
|
+
compiled_lines = {}
|
126
|
+
compiled_lines[:left] = "<table id=\"#{table_id_prefix}_left\" class=\"diff-json-view diff-json-split-view-left\">\n"
|
127
|
+
compiled_lines[:right] = "<table id=\"#{table_id_prefix}_right\" class=\"diff-json-view diff-json-split-view-right\">\n"
|
128
|
+
compiled_lines[:full] = "<table id=\"#{table_id_prefix}_full\" class=\"diff-json-view diff-json-full-view\">\n"
|
129
|
+
|
130
|
+
return compiled_lines
|
131
|
+
end
|
132
|
+
|
133
|
+
def html_table_lines(left_operators, left_lines, right_operators, right_lines)
|
134
|
+
compiled_lines = {left: "", right: "", full: ""}
|
135
|
+
|
136
|
+
(0..(left_lines.length - 1)).each do |i|
|
137
|
+
compiled_lines[:left] << <<-EOL
|
138
|
+
<tr class="diff-json-view-line">
|
139
|
+
<td class="diff-json-view-line-operator"><pre>#{left_operators unless left_lines[i].empty?}</pre></td>
|
140
|
+
<td class="diff-json-view-line-content"><pre class="diff-json-line-breaker">#{left_lines[i]}</pre></td>
|
141
|
+
</tr>
|
142
|
+
EOL
|
143
|
+
compiled_lines[:right] << <<-EOL
|
144
|
+
<tr class="diff-json-view-line">
|
145
|
+
<td class="diff-json-view-line-operator"><pre>#{right_operators unless right_lines[i].empty?}</pre></td>
|
146
|
+
<td class="diff-json-view-line-content"><pre class="diff-json-line-breaker">#{right_lines[i]}</pre></td>
|
147
|
+
</tr>
|
148
|
+
EOL
|
149
|
+
compiled_lines[:full] << <<-EOL
|
150
|
+
<tr class="diff-json-view-line">
|
151
|
+
<div class="row">
|
152
|
+
<td class="diff-json-view-line-operator"><pre>#{left_operators unless left_lines[i].empty?}</pre></td>
|
153
|
+
<td class="diff-json-view-line-content"><pre class="diff-json-line-breaker">#{left_lines[i]}</pre></td>
|
154
|
+
<td class="diff-json-view-column-break"></td>
|
155
|
+
<td class="diff-json-view-line-operator"><pre>#{right_operators unless right_lines[i].empty?}</pre></td>
|
156
|
+
<td class="diff-json-view-line-content"><pre class="diff-json-line-breaker">#{right_lines[i]}</pre></td>
|
157
|
+
</div>
|
158
|
+
</tr>
|
159
|
+
EOL
|
160
|
+
end
|
161
|
+
|
162
|
+
return compiled_lines
|
163
|
+
end
|
164
|
+
|
165
|
+
def html_table_closer
|
166
|
+
compiled_lines = {}
|
167
|
+
compiled_lines[:left] = "</table>"
|
168
|
+
compiled_lines[:right] = "</table>"
|
169
|
+
compiled_lines[:full] = "</table>"
|
170
|
+
|
171
|
+
return compiled_lines
|
172
|
+
end
|
173
|
+
|
174
|
+
def html_bootstrap_opener(table_id_prefix)
|
175
|
+
compiled_lines = {}
|
176
|
+
compiled_lines[:left] = "<div id=\"#{table_id_prefix}_left\" class=\"diff-json-view diff-json-split-view-left col-xs-6 col-6\">\n"
|
177
|
+
compiled_lines[:right] = "<div id=\"#{table_id_prefix}_right\" class=\"diff-json-view diff-json-split-view-right col-xs-6 col-6\">\n"
|
178
|
+
compiled_lines[:full] = "<div id=\"#{table_id_prefix}_full\" class=\"diff-json-view diff-json-full-view col-xs-12 col-12\">\n"
|
179
|
+
|
180
|
+
return compiled_lines
|
181
|
+
end
|
182
|
+
|
183
|
+
def html_bootstrap_lines(left_operators, left_lines, right_operators, right_lines)
|
184
|
+
compiled_lines = {left: "", right: "", full: ""}
|
185
|
+
|
186
|
+
(0..(left_lines.length - 1)).each do |i|
|
187
|
+
compiled_lines[:left] << <<-EOL
|
188
|
+
<div class="diff-json-view-line row">
|
189
|
+
<div class="diff-json-view-line-operator col-xs-1"><pre>#{left_operators unless left_lines[i].empty?}</pre></div>
|
190
|
+
<div class="diff-json-view-line-content col-xs-11"><pre class="diff-json-line-breaker #{highlight_class(left_operators) unless left_lines[i].empty?}">#{left_lines[i]}</pre></div>
|
191
|
+
</div>
|
192
|
+
EOL
|
193
|
+
compiled_lines[:right] << <<-EOL
|
194
|
+
<div class="diff-json-view-line row">
|
195
|
+
<div class="diff-json-view-line-operator col-xs-1"><pre>#{right_operators unless right_lines[i].empty?}</pre></div>
|
196
|
+
<div class="diff-json-view-line-content col-xs-11"><pre class="diff-json-line-breaker #{highlight_class(right_operators) unless right_lines[i].empty?}">#{right_lines[i]}</pre></div>
|
197
|
+
</div>
|
198
|
+
EOL
|
199
|
+
compiled_lines[:full] << <<-EOL
|
200
|
+
<div class="diff-json-view-line row">
|
201
|
+
<div class="diff-json-view-line-left col-xs-6">
|
202
|
+
<pre class="diff-json-line-breaker #{highlight_class(left_operators) unless left_lines[i].empty?}">#{left_operators unless left_lines[i].empty?} #{left_lines[i]}</pre>
|
203
|
+
</div>
|
204
|
+
<div class="diff-json-view-line-right col-xs-6">
|
205
|
+
<pre class="diff-json-line-breaker #{highlight_class(right_operators) unless right_lines[i].empty?}">#{right_operators unless right_lines[i].empty?} #{right_lines[i]}</pre>
|
206
|
+
</div>
|
207
|
+
</div>
|
208
|
+
EOL
|
209
|
+
end
|
210
|
+
|
211
|
+
return compiled_lines
|
212
|
+
end
|
213
|
+
|
214
|
+
def html_bootstrap_closer
|
215
|
+
compiled_lines = {}
|
216
|
+
compiled_lines[:left] = "</div>"
|
217
|
+
compiled_lines[:right] = "</div>"
|
218
|
+
compiled_lines[:full] = "</div>"
|
219
|
+
|
220
|
+
return compiled_lines
|
221
|
+
end
|
222
|
+
|
223
|
+
def highlight_class(operators)
|
224
|
+
return '' if operators.empty?
|
225
|
+
return 'diff-json-content-ins' if operators.include?('+')
|
226
|
+
return 'diff-json-content-del' if operators.include?('-')
|
227
|
+
return 'diff-json-content-mov' if operators.include?('M')
|
228
|
+
end
|
229
|
+
|
230
|
+
def balance_output(old_element, new_element, indentation: 0, old_key: nil, new_key: nil, old_comma: false, new_comma: false)
|
231
|
+
old_lines, new_lines = jpg(old_element, indentation: indentation, key: old_key, trailing_comma: old_comma), jpg(new_element, indentation: indentation, key: new_key, trailing_comma: new_comma)
|
232
|
+
return old_lines, new_lines if old_lines.length == new_lines.length
|
233
|
+
|
234
|
+
if old_lines.length > new_lines.length
|
235
|
+
(old_lines.length - new_lines.length).times do
|
236
|
+
new_lines << ''
|
237
|
+
end
|
238
|
+
else
|
239
|
+
(new_lines.length - old_lines.length).times do
|
240
|
+
old_lines << ''
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
return old_lines, new_lines
|
245
|
+
end
|
246
|
+
|
247
|
+
def jpg(json_element, structure: false, structure_position: :open, structure_type: :array, indentation: 0, key: nil, trailing_comma: false)
|
248
|
+
return [] if json_element.is_a?(DiffJson::UndefinedValue)
|
249
|
+
if structure
|
250
|
+
generated_element = case [structure_position, structure_type]
|
251
|
+
when [:open, :array]
|
252
|
+
['[']
|
253
|
+
when [:close, :array]
|
254
|
+
[']']
|
255
|
+
when [:open, :hash]
|
256
|
+
['{']
|
257
|
+
when [:close, :hash]
|
258
|
+
['}']
|
259
|
+
end
|
260
|
+
else
|
261
|
+
generated_element = JSON.pretty_generate(json_element, max_nesting: false, quirks_mode: true).lines
|
262
|
+
end
|
263
|
+
generated_element[0].prepend("#{key}: ") unless key.nil?
|
264
|
+
generated_element.last << ',' if trailing_comma
|
265
|
+
return generated_element.map{|line| line.prepend(' ' * indentation)}
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|