Skip to content

Allow more complex filtering #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ gemspec
group :test do
gem 'rake'
end
gem 'rspec'
1 change: 1 addition & 0 deletions lib/json-diff.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'json-diff/diff'
require 'json-diff/v2/diff'
require 'json-diff/index-map'
require 'json-diff/operation'
require 'json-diff/version'
150 changes: 150 additions & 0 deletions lib/json-diff/v2/diff.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
module JsonDiff
class V2
attr_reader :opts, :changes

def self.diff(before, after, opts = {})
runner = new(opts)
runner.diff(before, after)
runner.changes
end

def initialize(opts = {})
@opts = opts
@changes = []
end

def diff_hash(before, after, path)
lost = before.keys - after.keys
lost.each do |key|
inner_path = JsonDiff.extend_json_pointer(path, key)
changes << JsonDiff.remove(inner_path, include_was(path) ? before[key] : nil)
end

if include_addition(:hash, path)
gained = after.keys - before.keys
gained.each do |key|
inner_path = JsonDiff.extend_json_pointer(path, key)
changes << JsonDiff.add(inner_path, after[key])
end
end

kept = before.keys & after.keys
kept.each do |key|
inner_path = JsonDiff.extend_json_pointer(path, key)
diff(before[key], after[key], inner_path)
end
end

def diff_array(before, after, path)
if before.size == 0
if include_addition(:array, path)
after.each_with_index do |item, index|
inner_path = JsonDiff.extend_json_pointer(path, index)
changes << JsonDiff.add(inner_path, item)
end
end
elsif after.size == 0
before.each do |item|
# Delete elements from the start.
inner_path = JsonDiff.extend_json_pointer(path, 0)
changes << JsonDiff.remove(inner_path, include_was(path) ? item : nil)
end
else
pairing = JsonDiff.array_pairing(before, after, opts)
# FIXME: detect replacements.

# All detected moves that do not reach the similarity limit are deleted
# and re-added.
pairing[:pairs].select! do |pair|
sim = pair[2]
kept = (sim >= 0.5)
if !kept
pairing[:removed] << pair[0]
pairing[:added] << pair[1]
end
kept
end

pairing[:pairs].each do |pair|
before_index, after_index = pair
inner_path = JsonDiff.extend_json_pointer(path, before_index)
diff(before[before_index], after[after_index], inner_path)
end

if !original_indices(path)
# Recompute indices to account for offsets from insertions and
# deletions.
pairing = JsonDiff.array_changes(pairing)
end

pairing[:removed].each do |before_index|
inner_path = JsonDiff.extend_json_pointer(path, before_index)
changes << JsonDiff.remove(inner_path, include_was(path) ? before[before_index] : nil)
end

pairing[:pairs].each do |pair|
before_index, after_index = pair
inner_before_path = JsonDiff.extend_json_pointer(path, before_index)
inner_after_path = JsonDiff.extend_json_pointer(path, after_index)

if before_index != after_index && include_moves(path)
changes << JsonDiff.move(inner_before_path, inner_after_path)
end
end

if include_addition(:array, path)
pairing[:added].each do |after_index|
inner_path = JsonDiff.extend_json_pointer(path, after_index)
changes << JsonDiff.add(inner_path, after[after_index])
end
end
end
end

def diff(before, after, path = '')
if before.is_a?(Hash)
if !after.is_a?(Hash)
changes << JsonDiff.replace(path, include_was(path) ? before : nil, after) if include_replace(path)
else
diff_hash(before, after, path)
end
elsif before.is_a?(Array)
if !after.is_a?(Array)
changes << JsonDiff.replace(path, include_was(path) ? before : nil, after) if include_replace(path)
else
diff_array(before, after, path)
end
else
if before != after
changes << JsonDiff.replace(path, include_was(path) ? before : nil, after) if include_replace(path)
end
end
end

def include_addition(type, path)
return true if opts[:additions] == nil
opts[:additions].respond_to?(:call) ? opts[:additions].call(type, path) : opts[:additions]
end

def include_moves(path)
return true if opts[:moves] == nil
opts[:moves].respond_to?(:call) ? opts[:moves].call(path) : opts[:moves]
end

def include_replace(path)
return true if opts[:replace] == nil
opts[:replace].respond_to?(:call) ? opts[:replace].call(path) : opts[:replace]
end

def include_was(path)
return false if opts[:include_was] == nil
opts[:include_was].respond_to?(:call) ? opts[:include_was].call(path) : opts[:include_was]
end

def original_indices(path)
return false if opts[:original_indices] == nil
opts[:original_indices].respond_to?(:call) ? opts[:original_indices].call(path) : opts[:original_indices]
end
end
end

2 changes: 1 addition & 1 deletion lib/json-diff/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module JsonDiff
VERSION = '0.4.1'
VERSION = '0.5.0-dwhenry'
end
32 changes: 16 additions & 16 deletions spec/json-diff/diff_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
# Arrays

it "should be able to diff two empty arrays" do
diff = JsonDiff.diff([], [])
diff = described_class.diff([], [])
expect(diff).to eql([])
end

it "should be able to diff an empty array with a filled one" do
diff = JsonDiff.diff([], [1, 2, 3], include_was: true)
diff = described_class.diff([], [1, 2, 3], include_was: true)
expect(diff).to eql([
{'op' => 'add', 'path' => "/0", 'value' => 1},
{'op' => 'add', 'path' => "/1", 'value' => 2},
Expand All @@ -18,7 +18,7 @@
end

it "should be able to diff a filled array with an empty one" do
diff = JsonDiff.diff([1, 2, 3], [], include_was: true)
diff = described_class.diff([1, 2, 3], [], include_was: true)
expect(diff).to eql([
{'op' => 'remove', 'path' => "/0", 'was' => 1},
{'op' => 'remove', 'path' => "/0", 'was' => 2},
Expand All @@ -27,7 +27,7 @@
end

it "should be able to diff a 1-array with a filled one" do
diff = JsonDiff.diff([0], [1, 2, 3], include_was: true)
diff = described_class.diff([0], [1, 2, 3], include_was: true)
expect(diff).to eql([
{'op' => 'remove', 'path' => "/0", 'was' => 0},
{'op' => 'add', 'path' => "/0", 'value' => 1},
Expand All @@ -37,7 +37,7 @@
end

it "should be able to diff a filled array with a 1-array" do
diff = JsonDiff.diff([1, 2, 3], [0], include_was: true)
diff = described_class.diff([1, 2, 3], [0], include_was: true)
expect(diff).to eql([
{'op' => 'remove', 'path' => "/2", 'was' => 3},
{'op' => 'remove', 'path' => "/1", 'was' => 2},
Expand All @@ -47,7 +47,7 @@
end

it "should be able to diff two integer arrays" do
diff = JsonDiff.diff([1, 2, 3, 4, 5], [6, 4, 3, 2], include_was: true)
diff = described_class.diff([1, 2, 3, 4, 5], [6, 4, 3, 2], include_was: true)
expect(diff).to eql([
{'op' => 'remove', 'path' => "/4", 'was' => 5},
{'op' => 'remove', 'path' => "/0", 'was' => 1},
Expand All @@ -58,12 +58,12 @@
end

it "should be able to diff a ring switch" do
diff = JsonDiff.diff([1, 2, 3], [2, 3, 1], include_was: true)
diff = described_class.diff([1, 2, 3], [2, 3, 1], include_was: true)
expect(diff).to eql([{"op" => "move", "from" => "/0", "path" => "/2"}])
end

it "should be able to diff a ring switch with removals and additions" do
diff = JsonDiff.diff([1, 2, 3, 4], [5, 3, 4, 2], include_was: true)
diff = described_class.diff([1, 2, 3, 4], [5, 3, 4, 2], include_was: true)
expect(diff).to eql([
{"op" => "remove", "path" => "/0", "was" => 1},
{"op" => "move", "from" => "/0", "path" => "/2"},
Expand All @@ -72,7 +72,7 @@
end

it "should be able to diff an array with many additions at its start" do
diff = JsonDiff.diff([0], [1, 2, 3, 0])
diff = described_class.diff([0], [1, 2, 3, 0])
expect(diff).to eql([
{'op' => 'add', 'path' => "/0", 'value' => 1},
{'op' => 'add', 'path' => "/1", 'value' => 2},
Expand All @@ -81,7 +81,7 @@
end

it "should be able to diff two arrays with mixed content" do
diff = JsonDiff.diff(["laundry", 12, {'pillar' => 0}, true], [true, {'pillar' => 1}, 3, 12], include_was: true)
diff = described_class.diff(["laundry", 12, {'pillar' => 0}, true], [true, {'pillar' => 1}, 3, 12], include_was: true)
expect(diff).to eql([
{'op' => 'replace', 'path' => "/2/pillar", 'was' => 0, 'value' => 1},
{'op' => 'remove', 'path' => "/0", 'was' => "laundry"},
Expand All @@ -94,7 +94,7 @@
# Objects

it "should be able to diff two objects with mixed content" do
diff = JsonDiff.diff(
diff = described_class.diff(
{'string' => "laundry", 'number' => 12, 'object' => {'pillar' => 0}, 'list' => [2, 4, 1], 'bool' => false, 'null' => nil},
{'string' => "laundry", 'number' => 12, 'object' => {'pillar' => 1}, 'list' => [1, 2, 3], 'bool' => true, 'null' => nil},
include_was: true)
Expand All @@ -110,7 +110,7 @@
# Trans-type

it "should be able to diff two objects of mixed type" do
diff = JsonDiff.diff(0, "0", include_was: true)
diff = described_class.diff(0, "0", include_was: true)
expect(diff).to eql([
{'op' => 'replace', 'path' => '', 'was' => 0, 'value' => "0"}
])
Expand All @@ -119,7 +119,7 @@
# Options

it "should be able to diff two integer arrays with original indices" do
diff = JsonDiff.diff([1, 2, 3, 4, 5], [6, 4, 3, 2], original_indices: true)
diff = described_class.diff([1, 2, 3, 4, 5], [6, 4, 3, 2], original_indices: true)
expect(diff).to eql([
{'op' => 'remove', 'path' => "/4"},
{'op' => 'remove', 'path' => "/0"},
Expand All @@ -130,7 +130,7 @@
end

it "should be able to diff two integer arrays without move operations" do
diff = JsonDiff.diff([1, 2, 3, 4, 5], [6, 4, 3, 2], moves: false)
diff = described_class.diff([1, 2, 3, 4, 5], [6, 4, 3, 2], moves: false)
expect(diff).to eql([
{'op' => 'remove', 'path' => "/4"},
{'op' => 'remove', 'path' => "/0"},
Expand All @@ -139,7 +139,7 @@
end

it "should be able to diff two integer arrays without add operations" do
diff = JsonDiff.diff([1, 2, 3, 4, 5], [6, 4, 3, 2], additions: false)
diff = described_class.diff([1, 2, 3, 4, 5], [6, 4, 3, 2], additions: false)
expect(diff).to eql([
{'op' => 'remove', 'path' => "/4"},
{'op' => 'remove', 'path' => "/0"},
Expand All @@ -159,7 +159,7 @@
end
end

diff = JsonDiff.diff([
diff = described_class.diff([
{id: 1, we: "must", start: "somewhere"},
{id: 2, and: "this", will: "do"},
], [
Expand Down
Loading