diff_json 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 500b28225357427a86dabd6853d94896a4efcaccb9e6f9e8153d2b93535ce183
4
+ data.tar.gz: de6dae135cebc7bb440239bf40a990c53c1849fc0fd0d2e26f867b083b231050
5
+ SHA512:
6
+ metadata.gz: 8200a8a6996d6ab68ba6544d27172fd22e712aef12f9b6d62570084ca54e416cd495a4f13a73145532530609420b97540b80dbd89c772d95470df08d6dd80b75
7
+ data.tar.gz: 912663025aa950e5ded31559b2d3106592e58a44f23129421ed11320302e11caa2b274335ac03d7da2f67757fde3e6d03138bc8b420457370b2dc0fc8169d853
@@ -0,0 +1,460 @@
1
+ module DiffJson
2
+ class Diff
3
+ def initialize(old_json, new_json, **opts)
4
+ @old_json = old_json
5
+ @new_json = new_json
6
+ @opts = {
7
+ :debug => false,
8
+ :ignore_object_keys => [],
9
+ :diff_count_filter => {
10
+ :only => ['$**'],
11
+ :except => []
12
+ }
13
+ }.merge(opts)
14
+ @filtered = @opts[:diff_count_filter] != {
15
+ :only => ['$**'],
16
+ :except => []
17
+ }
18
+ @diff = {
19
+ :count => {
20
+ :all => 0,
21
+ :insert => 0,
22
+ :update => 0,
23
+ :delete => 0,
24
+ :move => 0
25
+ },
26
+ :old => [],
27
+ :new => []
28
+ }
29
+
30
+ calculate
31
+ end
32
+
33
+ def diff
34
+ return @diff
35
+ end
36
+
37
+ def retrieve_output(output_type = :stdout, **output_opts)
38
+ case output_type
39
+ when :stdout
40
+ when :file
41
+ when :html
42
+ html_output = HtmlOutput.new(@diff, **output_opts)
43
+ return html_output
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def calculate
50
+ @diff[:old], @diff[:new] = compare_elements(@old_json, @new_json)
51
+ @calculated = true
52
+ end
53
+
54
+ def compare_elements(old_element, new_element, indent_step = 0, path = '$')
55
+ debug([
56
+ 'ENTER compare_elements',
57
+ "Diffing #{path}"
58
+ ])
59
+
60
+ old_element_lines, new_element_lines = [], []
61
+
62
+ if old_element == new_element
63
+ debug('Equal elements, no diff required')
64
+
65
+ old_element_lines = JSON.pretty_generate(old_element, max_nesting: false, quirks_mode: true).split("\n").map{|el| [' ', "#{indentation(indent_step)}#{el}"]}
66
+ new_element_lines = JSON.pretty_generate(new_element, max_nesting: false, quirks_mode: true).split("\n").map{|el| [' ', "#{indentation(indent_step)}#{el}"]}
67
+ else
68
+ unless value_type(old_element) == value_type(new_element)
69
+ debug('Opposite type element, no diff required')
70
+
71
+ increment_diff_count(path, :insert)
72
+ increment_diff_count(path, :delete)
73
+ old_element_lines, new_element_lines = add_blank_lines(
74
+ JSON.pretty_generate(old_element, max_nesting: false, quirks_mode: true).split("\n").map{|el| ['-', "#{indentation(indent_step)}#{el}"]},
75
+ JSON.pretty_generate(new_element, max_nesting: false, quirks_mode: true).split("\n").map{|el| ['+', "#{indentation(indent_step)}#{el}"]}
76
+ )
77
+ else
78
+ debug("Found #{value_type(old_element)}, diffing")
79
+
80
+ increment_diff_count(path, :update)
81
+ old_element_lines, new_element_lines = self.send("#{value_type(old_element)}_diff", old_element, new_element, indent_step, path)
82
+ end
83
+ end
84
+
85
+ return old_element_lines, new_element_lines
86
+ end
87
+
88
+ def array_diff(old_array, new_array, indent_step, base_path)
89
+ debug('ENTER array_diff')
90
+
91
+ oal, nal = old_array.length, new_array.length
92
+ sal = oal < nal ? oal : nal
93
+ lal = oal > nal ? oal : nal
94
+ old_array_lines, new_array_lines = [[' ', "#{indentation(indent_step)}["]], [[' ', "#{indentation(indent_step)}["]]
95
+ next_step = indent_step + 1
96
+ operations = {
97
+ 'none' => [],
98
+ 'arr_add_index' => [],
99
+ 'arr_drop_index' => [],
100
+ 'arr_send_move' => [],
101
+ 'arr_receive_move' => []
102
+ }
103
+
104
+ # Find indices that were added or dropped, if any
105
+ if oal < nal
106
+ operations['arr_add_index'] += (oal..(nal - 1)).to_a
107
+ elsif oal > nal
108
+ operations['arr_drop_index'] += (nal..(oal - 1)).to_a
109
+ end
110
+
111
+ # Find 'none' and 'move_value' operations
112
+ (old_array | new_array).each do |v|
113
+ # For a given value, find all indices of each array that corresponds
114
+ old_indices, new_indices = array_indices(old_array, v), array_indices(new_array, v)
115
+ # Same index, same value, no diff necessary
116
+ operations['none'] += (old_indices & new_indices)
117
+
118
+ # Pull the skipped indices before calculating movements
119
+ old_indices -= operations['none']
120
+ new_indices -= operations['none']
121
+
122
+ # Find values that were moved from one index to another
123
+ if !old_indices.empty? and !new_indices.empty?
124
+ max_moves = old_indices.length < new_indices.length ? old_indices.length : new_indices.length
125
+ possible_moves = []
126
+ # Make pairs of possible moves
127
+ old_indices.each do |oi|
128
+ new_indices.each do |ni|
129
+ possible_moves << [(oi - ni).abs, [oi, ni]]
130
+ end
131
+ end
132
+ # For the sake of simplicity, we'll arbitrarily decide to use the shortest moves
133
+ possible_moves.sort!{|x,y| x[0] <=> y[0]}
134
+ # Take the first (max_moves) moves and add their operations
135
+ possible_moves[0..(max_moves - 1)].each do |move|
136
+ operations['arr_send_move'] << move[1][0]
137
+ operations['arr_receive_move'] << move[1][1]
138
+ end
139
+ end
140
+ end
141
+
142
+ # Add base diff for each index
143
+ (0..(lal - 1)).each do |i|
144
+ debug("PROCESS INDEX #{i}")
145
+
146
+ item_path = "#{base_path}[#{i}]"
147
+ old_item_lines, new_item_lines = [], []
148
+ item_diff_operations = []
149
+ last_loop = (i == (lal - 1))
150
+
151
+ # Assign current known operations to each index
152
+ (operations.keys).each do |operation|
153
+ if operations[operation].include?(i)
154
+ item_diff_operations << operation
155
+ end
156
+ end
157
+
158
+ # Add arr_change_value, arr_add_value, and arr_drop_value operations
159
+ if item_diff_operations.empty?
160
+ item_diff_operations << 'arr_change_value'
161
+ elsif (
162
+ item_diff_operations.include?('arr_send_move') and
163
+ !item_diff_operations.include?('arr_receive_move') and
164
+ !item_diff_operations.include?('arr_drop_index')
165
+ )
166
+ item_diff_operations << 'arr_add_value'
167
+ elsif (
168
+ !item_diff_operations.include?('arr_send_move') and
169
+ item_diff_operations.include?('arr_receive_move') and
170
+ !item_diff_operations.include?('arr_add_index')
171
+ )
172
+ item_diff_operations << 'arr_drop_value'
173
+ end
174
+
175
+ # Call compare_elements for sub-elements if necessary
176
+ if (!(item_diff_operations & ['none', 'arr_change_value']).empty? and
177
+ is_json_element?(old_array[i]) and is_json_element?(new_array[i])
178
+ )
179
+ old_item_lines, new_item_lines = compare_elements(old_array[i], new_array[i], next_step, item_path)
180
+ else
181
+ # Grab old and new items
182
+ # UndefinedValue class is here to represent the difference between explicit null and non-existent
183
+ old_item = item_diff_operations.include?('arr_add_index') ? UndefinedValue.new : old_array[i]
184
+ new_item = item_diff_operations.include?('arr_drop_index') ? UndefinedValue.new : new_array[i]
185
+
186
+ # Figure out operators for left and right
187
+ if item_diff_operations.include?('none')
188
+ old_operator, new_operator = ' '
189
+ elsif item_diff_operations.include?('arr_change_value')
190
+ increment_diff_count(item_path, :update)
191
+ old_operator, new_operator = '-', '+'
192
+ elsif (item_diff_operations & ['arr_send_move', 'arr_receive_move']).length == 2
193
+ increment_diff_count(item_path, :move)
194
+ old_operator, new_operator = 'M', 'M'
195
+ elsif item_diff_operations.include?('arr_add_value')
196
+ increment_diff_count(item_path, :insert)
197
+ old_operator, new_operator = 'M', '+'
198
+ elsif item_diff_operations.include?('arr_drop_value')
199
+ increment_diff_count(item_path, :delete)
200
+ old_operator, new_operator = '-', 'M'
201
+ elsif item_diff_operations.include?('arr_drop_index')
202
+ if item_diff_operations.include?('arr_send_move')
203
+ increment_diff_count(item_path, :move)
204
+ old_operator, new_operator = 'M', ' '
205
+ else
206
+ increment_diff_count(item_path, :delete)
207
+ old_operator, new_operator = '-', ' '
208
+ end
209
+ elsif item_diff_operations.include?('arr_add_index')
210
+ if item_diff_operations.include?('arr_receive_move')
211
+ old_operator, new_operator = ' ', 'M'
212
+ else
213
+ increment_diff_count(item_path, :insert)
214
+ old_operator, new_operator = ' ', '+'
215
+ end
216
+ end
217
+
218
+ # Gather lines
219
+ if old_item.is_a?(UndefinedValue)
220
+ new_item_lines = JSON.pretty_generate(new_item, max_nesting: false, quirks_mode: true).split("\n").map{|il| [new_operator, "#{indentation(next_step)}#{il}"]}
221
+
222
+ (0..(new_item_lines.length - 1)).each do |i|
223
+ old_item_lines << [' ', '']
224
+ end
225
+ else
226
+ old_item_lines = JSON.pretty_generate(old_item, max_nesting: false, quirks_mode: true).split("\n").map{|il| [old_operator, "#{indentation(next_step)}#{il}"]}
227
+ end
228
+
229
+ if new_item.is_a?(UndefinedValue)
230
+ (0..(old_item_lines.length - 1)).each do |i|
231
+ new_item_lines << [' ', '']
232
+ end
233
+ else
234
+ new_item_lines = JSON.pretty_generate(new_item, max_nesting: false, quirks_mode: true).split("\n").map{|il| [new_operator, "#{indentation(next_step)}#{il}"]}
235
+ end
236
+ end
237
+
238
+ unless old_item_lines.empty?
239
+ old_item_lines.last[1] = "#{old_item_lines.last[1]}," if !last_loop and (old_item_lines.last[1].match(/[^\s]/))
240
+ end
241
+ unless new_item_lines.empty?
242
+ new_item_lines.last[1] = "#{new_item_lines.last[1]}," if !last_loop and (new_item_lines.last[1].match(/[^\s]/))
243
+ end
244
+
245
+ old_item_lines, new_item_lines = add_blank_lines(old_item_lines, new_item_lines)
246
+
247
+ old_array_lines += old_item_lines
248
+ new_array_lines += new_item_lines
249
+ end
250
+
251
+ old_array_lines << [' ', "#{indentation(indent_step)}]"]
252
+ new_array_lines << [' ', "#{indentation(indent_step)}]"]
253
+
254
+ return old_array_lines, new_array_lines
255
+ end
256
+
257
+ def object_diff(old_object, new_object, indent_step, base_path)
258
+ debug('ENTER object_diff')
259
+
260
+ keys = {
261
+ 'all' => (old_object.keys | new_object.keys),
262
+ 'common' => (old_object.keys & new_object.keys),
263
+ 'add' => (new_object.keys - old_object.keys),
264
+ 'drop' => (old_object.keys - new_object.keys)
265
+ }
266
+ old_object_lines, new_object_lines = [[' ', "#{indentation(indent_step)}{"]], [[' ', "#{indentation(indent_step)}{"]]
267
+ next_step = indent_step + 1
268
+
269
+ # For objects, we're taking a much simpler approach, so no movements
270
+ keys['all'].each do |k|
271
+ debug("PROCESS KEY #{k}")
272
+
273
+ item_path = "#{base_path}{#{k}}"
274
+ key_string = "#{k}: "
275
+ old_item_lines, new_item_lines = [], []
276
+ last_loop = (k == keys['all'].last)
277
+
278
+ if keys['common'].include?(k)
279
+ if is_json_element?(old_object[k]) and is_json_element?(new_object[k]) and !@opts[:ignore_object_keys].include?(k)
280
+ old_item_lines, new_item_lines = compare_elements(old_object[k], new_object[k], next_step, item_path)
281
+ else
282
+ if old_object[k] == new_object[k] or @opts[:ignore_object_keys].include?(k)
283
+ old_item_lines = JSON.pretty_generate(old_object[k], max_nesting: false, quirks_mode: true).split("\n").map!{|il| [' ', "#{indentation(next_step)}#{il}"]}
284
+ new_item_lines = JSON.pretty_generate(new_object[k], max_nesting: false, quirks_mode: true).split("\n").map!{|il| [' ', "#{indentation(next_step)}#{il}"]}
285
+ else
286
+ increment_diff_count(item_path, :update)
287
+ old_item_lines = JSON.pretty_generate(old_object[k], max_nesting: false, quirks_mode: true).split("\n").map!{|il| ['-', "#{indentation(next_step)}#{il}"]}
288
+ new_item_lines = JSON.pretty_generate(new_object[k], max_nesting: false, quirks_mode: true).split("\n").map!{|il| ['+', "#{indentation(next_step)}#{il}"]}
289
+ end
290
+ end
291
+ else
292
+ if keys['drop'].include?(k)
293
+ increment_diff_count(item_path, :delete) unless @opts[:ignore_object_keys].include?(k)
294
+ old_item_lines = JSON.pretty_generate(old_object[k], max_nesting: false, quirks_mode: true).split("\n").map!{|il| [@opts[:ignore_object_keys].include?(k) ? ' ' : '-', "#{indentation(next_step)}#{il}"]}
295
+ new_item_lines = []
296
+
297
+ (0..(old_item_lines.length - 1)).each do |i|
298
+ new_item_lines << [' ', '']
299
+ end
300
+ elsif keys['add'].include?(k)
301
+ increment_diff_count(item_path, :insert) unless @opts[:ignore_object_keys].include?(k)
302
+ new_item_lines = JSON.pretty_generate(new_object[k], max_nesting: false, quirks_mode: true).split("\n").map!{|il| [@opts[:ignore_object_keys].include?(k) ? ' ' : '+', "#{indentation(next_step)}#{il}"]}
303
+ old_item_lines = []
304
+
305
+ (0..(new_item_lines.length - 1)).each do |i|
306
+ old_item_lines << [' ', '']
307
+ end
308
+ end
309
+ end
310
+
311
+ unless old_item_lines.empty?
312
+ old_item_lines[0][1].gsub!(/^(?<spaces>\s+)(?<content>.+)$/, "\\k<spaces>#{key_string}\\k<content>")
313
+ old_item_lines.last[1] = "#{old_item_lines.last[1]}," if !last_loop and (old_item_lines.last[1].match(/[^\s]/))
314
+ end
315
+ unless new_item_lines.empty?
316
+ new_item_lines[0][1].gsub!(/^(?<spaces>\s+)(?<content>.+)$/, "\\k<spaces>#{key_string}\\k<content>")
317
+ new_item_lines.last[1] = "#{new_item_lines.last[1]}," if !last_loop and (new_item_lines.last[1].match(/[^\s]/))
318
+ end
319
+
320
+ old_item_lines, new_item_lines = add_blank_lines(old_item_lines, new_item_lines)
321
+
322
+ old_object_lines += old_item_lines
323
+ new_object_lines += new_item_lines
324
+ end
325
+
326
+ old_object_lines << [' ', "#{indentation(indent_step)}}"]
327
+ new_object_lines << [' ', "#{indentation(indent_step)}}"]
328
+
329
+ return old_object_lines, new_object_lines
330
+ end
331
+
332
+ def debug(message)
333
+ puts message if @opts[:debug]
334
+ end
335
+
336
+ def array_indices(array, value)
337
+ indices = []
338
+
339
+ array.each_with_index do |av,i|
340
+ indices << i if av == value
341
+ end
342
+
343
+ return indices
344
+ end
345
+
346
+ def is_json_element?(object)
347
+ return true if ['array', 'object'].include?(value_type(object))
348
+ end
349
+
350
+ def value_type(element)
351
+ case class_name = element.class.name
352
+ when 'Hash'
353
+ return 'object'
354
+ when 'NilClass'
355
+ return 'null'
356
+ when 'TrueClass', 'FalseClass'
357
+ return 'boolean'
358
+ else
359
+ return class_name.downcase
360
+ end
361
+ end
362
+
363
+ def indentation(step)
364
+ step = 0 if step < 0
365
+ ' ' * step
366
+ end
367
+
368
+ def add_blank_lines(left_lines, right_lines)
369
+ if left_lines.length < right_lines.length
370
+ (1..(right_lines.length - left_lines.length)).each do
371
+ left_lines << [' ', '']
372
+ end
373
+ elsif left_lines.length > right_lines.length
374
+ (1..(left_lines.length - right_lines.length)).each do
375
+ right_lines << [' ', '']
376
+ end
377
+ end
378
+
379
+ return left_lines, right_lines
380
+ end
381
+
382
+ def increment_diff_count(path, operation)
383
+ unless @filtered
384
+ @diff[:count][operation] += 1
385
+ else
386
+ do_count = false
387
+
388
+ # Any path prefixes in `only` that match?
389
+ if (
390
+ @opts[:diff_count_filter].key?(:only) and
391
+ @opts[:diff_count_filter][:only].is_a?(Array)
392
+ )
393
+ @opts[:diff_count_filter][:only].each do |only_path|
394
+ only_path_prefix = only_path.gsub(/\*/, '')
395
+ only_path_wildcard = only_path.gsub(/[^\*]/, '')
396
+
397
+ if path.include?(only_path_prefix)
398
+ path_remainder = path.gsub(only_path_prefix, '').split(/(\]\[|\]\{|\}\[|\}\{)/)
399
+
400
+ if (
401
+ (path_remainder.length == 0 and only_path_wildcard.length == 0) or
402
+ (path_remainder.length == 1 and only_path_wildcard.length > 0) or
403
+ (path_remainder.length > 1 and only_path_wildcard.length > 1)
404
+ )
405
+ do_count = true
406
+ break
407
+ end
408
+ else
409
+ next
410
+ end
411
+ end
412
+ else
413
+ do_count = true
414
+ end
415
+
416
+ # Make sure the specific path is not excluded, if we've established that we should probably include it
417
+ if (
418
+ do_count and
419
+ @opts[:diff_count_filter].key?(:except) and
420
+ @opts[:diff_count_filter][:except].is_a?(Array)
421
+ )
422
+ @opts[:diff_count_filter][:except].each do |except_path|
423
+ except_path_prefix = except_path.gsub(/\*/, '')
424
+ except_path_wildcard = except_path.gsub(/[^\*]/, '') || ''
425
+
426
+ if path.include?(except_path_prefix)
427
+ path_remainder = path.gsub(except_path_prefix, '').split(/(\]\[|\]\{|\}\[|\}\{)/)
428
+
429
+ if (
430
+ (path_remainder.length == 0 and except_path_wildcard.length == 0) or
431
+ (path_remainder.length == 1 and except_path_wildcard.length > 0) or
432
+ (path_remainder.length > 1 and except_path_wildcard.length > 1)
433
+ )
434
+ do_count = false
435
+ break
436
+ end
437
+ else
438
+ next
439
+ end
440
+ end
441
+ end
442
+
443
+ # Ensure this operation is allowed for counting
444
+ if (
445
+ do_count and
446
+ @opts[:diff_count_filter].key?(:operations) and
447
+ @opts[:diff_count_filter][:operations].is_a?(Array)
448
+ )
449
+ do_count = false if (
450
+ !@opts[:diff_count_filter][:operations].empty? and
451
+ !@opts[:diff_count_filter][:operations].include?(operation)
452
+ )
453
+ end
454
+
455
+ @diff[:count][:all] += 1 if do_count
456
+ @diff[:count][operation] += 1 if do_count
457
+ end
458
+ end
459
+ end
460
+ end
@@ -0,0 +1,80 @@
1
+ module DiffJson
2
+ class HtmlOutput
3
+
4
+ def initialize(diff, **opts)
5
+ @diff = diff
6
+ @opts = {
7
+ :split => false,
8
+ :table_id_prefix => 'diff_json_view_0'
9
+ }.merge(opts)
10
+
11
+ calculate
12
+ end
13
+
14
+ def output
15
+ return @output
16
+ end
17
+
18
+ def left
19
+ return @output[:left] if @opts[:split]
20
+
21
+ raise 'Method `#left` is only available for split output'
22
+ end
23
+
24
+ def right
25
+ return @output[:right] if @opts[:split]
26
+
27
+ raise 'Method `#right` is only available for split output'
28
+ end
29
+
30
+ private
31
+
32
+ def calculate
33
+ if @opts[:split]
34
+ @output = {
35
+ :left => "<table id=\"#{@opts[:table_id_prefix]}_left\" class=\"diff-json-split-view-left\">\n",
36
+ :right => "<table id=\"#{@opts[:table_id_prefix]}_right\" class=\"diff-json-split-view-right\">\n"
37
+ }
38
+
39
+ (0..(@diff[:old].length - 1)).each do |i|
40
+ @output[:left] += "<tr class=\"diff-json-view-line\">\n"
41
+ @output[:left] += "<td class=\"diff-json-view-line-operator\">#{@diff[:old][i][0].gsub(/\s/, '&nbsp;')}</td>\n"
42
+ @output[:left] += "<td class=\"diff-json-view-line-content #{content_highlight_class(:left, @diff[:old][i][0])}\">#{@diff[:old][i][1].gsub(/\s/, '&nbsp;')}</td>\n"
43
+ @output[:left] += "</tr>\n"
44
+ @output[:right] += "<tr class=\"diff-json-view-line\">\n"
45
+ @output[:right] += "<td class=\"diff-json-view-line-operator\">#{@diff[:new][i][0].gsub(/\s/, '&nbsp;')}</td>\n"
46
+ @output[:right] += "<td class=\"diff-json-view-line-content #{content_highlight_class(:right, @diff[:new][i][0])}\">#{@diff[:new][i][1].gsub(/\s/, '&nbsp;')}</td>\n"
47
+ @output[:right] += "</tr>\n"
48
+ end
49
+
50
+ @output[:left] += "</table>\n"
51
+ @output[:right] += "</table>\n"
52
+ else
53
+ @output = "<table id=\"#{@opts[:table_id_prefix]}_full\" class=\"diff-json-view\">\n"
54
+
55
+ (0..(@diff[:old].length - 1)).each do |i|
56
+ @output += "<tr class=\"diff-json-view-line\">\n"
57
+ @output += "<td class=\"diff-json-view-line-operator\">#{@diff[:old][i][0].gsub(/\s/, '&nbsp;')}</td>\n"
58
+ @output += "<td class=\"diff-json-view-line-content #{content_highlight_class(:left, @diff[:old][i][0])}\">#{@diff[:old][i][1].gsub(/\s/, '&nbsp;')}</td>\n"
59
+ @output += "<td class=\"diff-json-view-line-operator\">#{@diff[:new][i][0].gsub(/\s/, '&nbsp;')}</td>\n"
60
+ @output += "<td class=\"diff-json-view-line-content #{content_highlight_class(:right, @diff[:new][i][0])}\">#{@diff[:new][i][1].gsub(/\s/, '&nbsp;')}</td>\n"
61
+ @output += "</tr>\n"
62
+ end
63
+
64
+ @output += "</table>\n"
65
+ end
66
+ end
67
+
68
+ def content_highlight_class(side, operator)
69
+ if operator == '-'
70
+ return 'diff-json-content-del'
71
+ elsif operator == '+'
72
+ return 'diff-json-content-ins'
73
+ elsif operator == 'M'
74
+ return side == :left ? 'diff-json-content-del' : 'diff-json-content-ins'
75
+ else
76
+ return nil
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,4 @@
1
+ module DiffJson
2
+ class UndefinedValue
3
+ end
4
+ end
data/lib/diff_json.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'json'
2
+
3
+ require_relative './diff_json/undefined_value'
4
+ require_relative './diff_json/diff'
5
+ require_relative './diff_json/html_output'
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: diff_json
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Josh MacLachlan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Diffs two JSON objects and returns a left/right diff view, similar to
14
+ the command line `diff` utility
15
+ email: josh.t.maclachlan@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/diff_json.rb
21
+ - lib/diff_json/diff.rb
22
+ - lib/diff_json/html_output.rb
23
+ - lib/diff_json/undefined_value.rb
24
+ homepage: https://github.com/jtmaclachlan/diff_json
25
+ licenses:
26
+ - GPL-2
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 2.7.6
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: Diffs two JSON objects and returns a left/right diff view, similar to the
48
+ command line `diff` utility
49
+ test_files: []