diff_json 0.0.1

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 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: []