simplediff-ruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +39 -0
  3. data/lib/simplediff-ruby.rb +63 -0
  4. data/test/test.rb +125 -0
  5. metadata +78 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 828b2453c0f2dc00a611c3db384401a97f743e42
4
+ data.tar.gz: 4ad216cdfefe86ded7e46bde4d6050f9b703800d
5
+ SHA512:
6
+ metadata.gz: e392f127f0e96620191b8d6263bea380a2e591bc1b419f0c5093feba24e209266a6841d507d1efa7070db05f67825a21a9c998a8eb01d33495636368fbbce473
7
+ data.tar.gz: 6dd464e0bb9235dd66a0ba4ca787ac1b783d87f4a170e17a4a01fa83a6754f907d521e86848edbd791ba5022ce775d3effb70d0db261a289722bbbae78c62825
@@ -0,0 +1,39 @@
1
+
2
+ SimpleDiff Ruby 1.0
3
+ ==============
4
+
5
+ **simplediff-ruby** is a Ruby gem implementation of the [simplediff](https://github.com/paulgb/simplediff) algorithm. It finds the difference between two arrays of strings, numbers, hashes, or arrays.
6
+
7
+ Use
8
+ ----
9
+
10
+ `require` the gem to expose the `SimpleDiff` class.
11
+
12
+ require "simplediff-ruby"
13
+
14
+ Then call `SimpleDiff.compare` to find the diffs.
15
+
16
+ source = ['The', 'quick', 'brown', 'fox']
17
+ target = ['The', 'slow', 'green', 'turtle']
18
+ SimpleDiff.compare(source, target)
19
+
20
+ The output will be an array of hashes representing the differences between the compared arrays.
21
+
22
+ [
23
+ { change: '=', content: ['The'] },
24
+ { change: '-', content: ['quick', 'brown', 'fox'] },
25
+ { change: '+', content: ['slow', 'green', 'turtle'] }
26
+ ]
27
+
28
+ Test
29
+ ----
30
+
31
+ Use `ruby test/test.rb` to run the test cases.
32
+
33
+ $ ruby test/test.rb
34
+
35
+ License
36
+ -------
37
+
38
+ SimpleDiff Ruby is available under a zlib/libpng
39
+ license. See the provided `LICENSE` file for more details.
@@ -0,0 +1,63 @@
1
+ class SimpleDiff
2
+ # Diffing algorithm taken from - https://github.com/paulgb/simplediff
3
+ def self.compare(source, target)
4
+ source ||= []
5
+ target ||= []
6
+ max = si_start = ti_start = 0
7
+
8
+ source_matches = Hash.new { |h, k| h[k] = [] }
9
+ source.each_with_index { |el, i| source_matches[el] << i }
10
+
11
+ subsequences = Hash.new { |k, v| k[v] = 0 }
12
+
13
+ target.each_with_index do |el, ti|
14
+ # Generate a new hash to use for the current iteration
15
+ _subsequences = Hash.new { |k, v| k[v] = 0 }
16
+
17
+ # This will return an array of indices that represent the positions
18
+ # of tokens
19
+ # in the source list that match the current token in the target list
20
+ source_matches[el].each do |si|
21
+ # This sets the current subsequence hash to have the length of any
22
+ # subsequence found at a the current source index (si) - 1. This is how
23
+ # subsequence lengths are tracked.
24
+ #
25
+ # If that length is greater than the current max the indexes where the
26
+ # sequence is found are persisted
27
+ if (_subsequences[si] = subsequences[si - 1] + 1) > max
28
+ max = _subsequences[si]
29
+ si_start = si - max + 1
30
+ ti_start = ti - max + 1
31
+ end
32
+ end
33
+
34
+ # Assign the current subsequence hash to the previous one so it can be passed
35
+ # into the next iteration.
36
+ subsequences = _subsequences
37
+ end
38
+
39
+ if max > 0
40
+ # If the max is greater than 0 a subsequence must have been found. This marks
41
+ # the sequence as being the same in both the source and the target. It then
42
+ # recursively looks at either side of the source and target list to try and
43
+ # find more subsequences.
44
+ start = [source[0...si_start], target[0...ti_start]]
45
+ middle = [
46
+ { change: '=', content: target[ti_start...(ti_start + max)] }
47
+ ]
48
+ finish = [
49
+ source[(si_start + max)..-1],
50
+ target[(ti_start + max)..-1]
51
+ ]
52
+
53
+ compare(*start) + middle + compare(*finish)
54
+ else
55
+ # If no subsequence is found then anything in the source list must be a
56
+ # removal and anything in the target must be an addition.
57
+ [].tap do |changes|
58
+ changes << { change: '-', content: source } if !source.empty?
59
+ changes << { change: '+', content: target } if !target.empty?
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,125 @@
1
+ require_relative "../lib/simplediff-ruby"
2
+ require "minitest/autorun"
3
+ require "minitest/reporters"
4
+
5
+ Minitest::Reporters.use!(Minitest::Reporters::DefaultReporter.new)
6
+ class SimpleDiffTest < MiniTest::Test
7
+ test_cases = {
8
+ insertions: [
9
+ {
10
+ source: [1, 3, 4],
11
+ target: [1, 2, 3, 4],
12
+ diff: [
13
+ {change: '=', content: [1]},
14
+ {change: '+', content: [2]},
15
+ {change: '=', content: [3, 4]}
16
+ ]
17
+ },
18
+ {
19
+ source: [1, 2, 3, 8, 9, 12, 13],
20
+ target: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
21
+ diff: [
22
+ {change: '=', content: [1, 2, 3]},
23
+ {change: '+', content: [4, 5, 6, 7]},
24
+ {change: '=', content: [8, 9]},
25
+ {change: '+', content: [10, 11]},
26
+ {change: '=', content: [12, 13]},
27
+ {change: '+', content: [14, 15]}
28
+ ]
29
+ },
30
+ {
31
+ source: [1, 2, 3, 4, 5],
32
+ target: [1, 2, 2, 3, 4, 5],
33
+ diff: [
34
+ {change: '=', content: [1]},
35
+ {change: '+', content: [2]},
36
+ {change: '=', content: [2, 3, 4, 5]}
37
+ ]
38
+ },
39
+ {
40
+ source: [1, 2, 3, 4, 5],
41
+ target: [1, 2, 2, 3, 4, 4, 5],
42
+ diff: [
43
+ {change: '=', content: [1]},
44
+ {change: '+', content: [2]},
45
+ {change: '=', content: [2, 3, 4]},
46
+ {change: '+', content: [4]},
47
+ {change: '=', content: [5]}
48
+ ]
49
+ },
50
+ {
51
+ source: [1, 2, 3, 4, 5],
52
+ target: [1, 2, 1, 2, 3, 3, 2, 1, 4, 5],
53
+ diff: [
54
+ {change: '+', content: [1, 2]},
55
+ {change: '=',content: [1, 2, 3]},
56
+ {change: '+', content: [3, 2, 1]},
57
+ {change: '=',content: [4, 5]}
58
+ ]
59
+ }
60
+ ],
61
+ deletions: [
62
+ {
63
+ source: [1, 2, 3, 4, 5],
64
+ target: [1, 2, 5],
65
+ diff: [
66
+ {change: '=', content: [1, 2]},
67
+ {change: '-', content: [3, 4]},
68
+ {change: '=', content: [5]}
69
+ ]
70
+ },
71
+ {
72
+ source: [1, 2, 3, 4, 5, 6, 7, 8],
73
+ target: [3, 6, 7],
74
+ diff: [
75
+ {change: '-', content: [1, 2]},
76
+ {change: '=', content: [3]},
77
+ {change: '-', content: [4, 5]},
78
+ {change: '=', content: [6, 7]},
79
+ {change: '-', content: [8]}
80
+ ]
81
+ },
82
+ {
83
+ source: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5],
84
+ target: [1, 2, 3, 4, 5],
85
+ diff: [
86
+ {change: '=', content: [1, 2, 3, 4, 5]},
87
+ {change: '-', content: [1, 2, 3, 4, 5]}
88
+ ]
89
+ }
90
+ ],
91
+ changes: [
92
+ {
93
+ source: ['The', 'quick', 'brown', 'fox'],
94
+ target: ['The', 'slow', 'green', 'turtle'],
95
+ diff: [
96
+ {change: '=', content: ['The']},
97
+ {change: '-', content: ['quick', 'brown', 'fox']},
98
+ {change: '+', content: ['slow', 'green', 'turtle']}
99
+ ]
100
+ },
101
+ {
102
+ source: ['jumps', 'over', 'the', 'lazy', 'dog'],
103
+ target: ['walks', 'around', 'the', 'orange', 'cat'],
104
+ diff: [
105
+ {change: '-', content: ['jumps', 'over']},
106
+ {change: '+', content: ['walks', 'around']},
107
+ {change: '=', content: ['the']},
108
+ {change: '-', content: ['lazy', 'dog']},
109
+ {change: '+', content: ['orange', 'cat']}
110
+ ]
111
+ }
112
+ ]
113
+ }
114
+
115
+ test_cases.each do |key, cases|
116
+ cases.each do |test_case|
117
+ define_method("test_#{key}_#{test_case[:source]}-#{test_case[:target]}".to_sym) do
118
+ assert_equal(
119
+ test_case[:diff],
120
+ SimpleDiff.compare(test_case[:source], test_case[:target])
121
+ )
122
+ end
123
+ end
124
+ end
125
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simplediff-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - albertldlan
8
+ - JaredShay
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2017-11-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitest
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: minitest-reporters
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ description: Ruby implemention of https://github.com/paulgb/simplediff.
43
+ email:
44
+ - albertldlan@gmail.com
45
+ - Jared.Shay@gmail.com
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - README.md
51
+ - lib/simplediff-ruby.rb
52
+ - test/test.rb
53
+ homepage: https://github.com/albertldlan/simplediff-ruby
54
+ licenses:
55
+ - zlib-acknowledgement
56
+ metadata: {}
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 2.5.1
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: Ruby implemention of https://github.com/paulgb/simplediff.
77
+ test_files:
78
+ - test/test.rb