ninja-differ 1.0.0

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: b260977aada3497597330621cbb0babbcaf5f9f98cc06daeec34054d0a5e6994
4
+ data.tar.gz: 842d5b48dbc2f4dd0ce57fa67258a8aac1ba274f47a2b71e792aeaadfc69d393
5
+ SHA512:
6
+ metadata.gz: 9c80cc3497778c90d1123b37b7ecc72a0c19022d09002585b97ff70a548996165c682d52043d9bcf6ad31e8cf649d113fb9876f1ec21f98b3367ac83738e6c16
7
+ data.tar.gz: ea948a01e9a87731c3b857b087fd2e8554cad23e05f5a2be682bc9b9391406cea94d5f3fe846ffa13a0d091c454a85da0ddbf10474053cc98afb405de3273810
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ .vagrant
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "rspec"
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ninja-differ (1.0.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.5.0)
10
+ rspec (3.12.0)
11
+ rspec-core (~> 3.12.0)
12
+ rspec-expectations (~> 3.12.0)
13
+ rspec-mocks (~> 3.12.0)
14
+ rspec-core (3.12.0)
15
+ rspec-support (~> 3.12.0)
16
+ rspec-expectations (3.12.0)
17
+ diff-lcs (>= 1.2.0, < 2.0)
18
+ rspec-support (~> 3.12.0)
19
+ rspec-mocks (3.12.0)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.12.0)
22
+ rspec-support (3.12.0)
23
+
24
+ PLATFORMS
25
+ x86_64-linux
26
+
27
+ DEPENDENCIES
28
+ ninja-differ!
29
+ rspec
30
+
31
+ BUNDLED WITH
32
+ 2.3.24
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright © 2009 Pieter Vande Bruggen.
4
+ Copyright © 2022 Frank Koehl
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+
data/README.md ADDED
@@ -0,0 +1,149 @@
1
+ # NinjaDiffer
2
+
3
+ _NinjaDiffer was forked from [Differ](https://github.com/pvande/differ), by Pieter Vande Bruggen.
4
+ This gem shares logical and grammatical namespace with Differ. You will see the
5
+ names used interchangeably for a time. This is a full replacement for Differ,
6
+ running both gems together is not recommended._
7
+
8
+ > As streams of text swirled before the young man's eyes, his mind swam with thoughts of many things. They would have to wait, however, as he focussed his full concentration on the shifting patterns ahead of him. A glint of light reflecting off a piece of buried code caught his eye and any hope he had was lost. For the very moment he glanced aside, the landscape became Different.
9
+ > The young man gave a small sigh and trudged onward in solemn resignation, fated to wander the desolate codebanks in perpetuity.
10
+
11
+ NinjaDiffer is a flexible, pure-Ruby diff library, suitable for use in both command
12
+ line scripts and web applications. The flexibility comes from the fact that
13
+ diffs can be built at completely arbitrary levels of granularity (some common
14
+ ones are built-in), and can be output in a variety of formats.
15
+
16
+ ## Installation
17
+
18
+ Add this to your gemfile if you use bundler
19
+
20
+ ```ruby
21
+ gem 'ninja-differ'
22
+ ```
23
+
24
+ and bundle to install
25
+
26
+ ```bash
27
+ bundle install
28
+ ```
29
+
30
+ or install it manually
31
+
32
+ ```bash
33
+ sudo gem install differ
34
+ ```
35
+
36
+ ## How do I use this thing?
37
+
38
+ There are a number of ways to use Differ, depending on your situation and needs. Lets examplify:
39
+
40
+ ```ruby
41
+ require 'differ'
42
+
43
+ @original = "Epic lolcat fail!"
44
+ @current = "Epic wolfman fail!"
45
+ ```
46
+
47
+ There are a number of built-in diff_by_* methods to choose from for standard use:
48
+
49
+ ```ruby
50
+ Differ.diff_by_line(@current, @original)
51
+ # => {"Epic lolcat fail!" >> "Epic wolfman fail!"}
52
+
53
+ Differ.diff_by_word(@current, @original)
54
+ # => Epic {"lolcat" >> "wolfman"} fail!
55
+
56
+ Differ.diff_by_char(@current, @original)
57
+ # => Epic {+"wo"}l{-"olcat "}f{+"m"}a{+"n fa"}il!
58
+ ```
59
+
60
+ ### Ok, but that doesn't quite cover my case, I have to split by "whatever".
61
+
62
+ No problem, you can call diff directly and supply your own boundary string:
63
+
64
+ ```ruby
65
+ Differ.diff(@current, @original) # Implicitly by line by default
66
+ # => {"Epic lolcat fail!" >> "Epic wolfman fail!"}
67
+
68
+ Differ.diff(@current, @original, 'i')
69
+ # => Epi{"c lolcat fa" >> "c wolfman fa"}il
70
+ ```
71
+
72
+ ### Yeah, thats nice. But a simple string might not always cut it...
73
+
74
+ Well, you can supply a regex instead of your string if you have to:
75
+
76
+ ```ruby
77
+ Differ.diff(@original, @current, /[a-z]i/)
78
+ # => E{"c wolfman f" >> "c lolcat f"}l!
79
+ ```
80
+
81
+ Include a capture group if you want to keep the separator:
82
+
83
+ ```ruby
84
+ Differ.diff(@original, @current, /([a-z]i)/)
85
+ # => Epi{"c wolfman f" >> "c lolcat f"}ail!
86
+ ```
87
+
88
+ ### Ok, ok, but I don't like having to write "Differ" everywhere.
89
+
90
+ If you would like something a little more inline you can `require 'differ/string'` to get some added inline string magic:
91
+
92
+ ```ruby
93
+ @current.diff(@original) # Implicitly by line by default
94
+ # => {"Epic lolcat fail!" >> "Epic wolfman fail!"}
95
+ ```
96
+
97
+ Or a lot more inline:
98
+
99
+ ```ruby
100
+ @current - @original # Implicitly by line by default
101
+ # => {"Epic lolcat fail!" >> "Epic wolfman fail!"}
102
+
103
+ Differ.separator = ' ' # Custom string
104
+ @current - @original
105
+ # => Epic {"lolcat" >> "wolfman"} fail!
106
+
107
+ Differ.separator = /[a-z]i/ # Custom regex without capture group
108
+ @original - @current
109
+ # => E{"c wolfman f" >> "c lolcat f"}l!
110
+
111
+ Differ.separator = /([a-z]i)/ # Custom regex with capture group
112
+ @original - @current
113
+ # => Epi{"c wolfman f" >> "c lolcat f"}ail!
114
+ ```
115
+
116
+ So we've pretty much got you covered.
117
+
118
+ ## What about output formatting?
119
+
120
+ Need a different output format? We've got a few of those too:
121
+
122
+ ```ruby
123
+ Differ.format = :ascii # Default
124
+ Differ.format = :color
125
+ Differ.format = :html
126
+ Differ.format = :patch
127
+
128
+ Differ.format = MyCustomFormatModule
129
+ ```
130
+
131
+ The formatter must respond to the call method that takes an instant of the Change class as an argument and returns a string.
132
+
133
+ ### But I don't want to change the system-wide default for only a single diff output!
134
+
135
+ Yeah, we either:
136
+
137
+ ```ruby
138
+ diff = @current - @original
139
+ diff.format_as(:color)
140
+ ```
141
+
142
+ Or with your own formatter:
143
+
144
+ ```ruby
145
+ diff = @current - @original
146
+ diff.format_as(->(c){c.to_s})
147
+ ```
148
+
149
+ > Know always right from wrong, and let others see your good works.
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+
2
+ desc "Open an irb session preloaded with this library"
3
+ task :console do
4
+ sh "irb -rubygems -I lib -r differ.rb"
5
+ end
6
+ task :c => :console
7
+
8
+ namespace :gem do
9
+ desc "Connect to RubyGems.org account"
10
+ task :auth do
11
+ sh "curl -u battlebrisket https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials; chmod 0600 ~/.gem/credentials"
12
+ end
13
+
14
+ desc "Build the gem according to gemspec"
15
+ task :build do
16
+ sh "gem build differ.gemspec"
17
+ end
18
+
19
+ require "./lib/differ/version"
20
+ desc "Push the gem to RubyGems.org"
21
+ task :push do
22
+ sh "gem push differ-#{Differ::VERSION}.gem"
23
+ end
24
+ end
25
+
26
+ task :publish do
27
+ Rake::Task["gem:build"].invoke
28
+ Rake::Task["gem:push"].invoke
29
+ end
data/Vagrantfile ADDED
@@ -0,0 +1,47 @@
1
+ # -*- mode: ruby -*-
2
+ # vi: set ft=ruby :
3
+
4
+ NAME = "ninja-differ"
5
+ FQDN = "#{NAME}.example.com"
6
+
7
+ Vagrant.configure("2") do |config|
8
+ config.vm.box = "ubuntu/focal64"
9
+ config.vm.boot_timeout = 900
10
+
11
+ # Use the normal insecure key
12
+ # https://github.com/mitchellh/vagrant/issues/2608
13
+ config.ssh.insert_key = false
14
+
15
+ config.vm.provider :virtualbox do |vb|
16
+ vb.customize ["modifyvm", :id,
17
+ # Basics.
18
+ "--name", NAME,
19
+ "--memory", 4096,
20
+ # I/O APIC must be enabled to support a multi-core guest.
21
+ "--cpus", 2,
22
+ "--ioapic", "on",
23
+ # Enable native host virtualization features (yields better performance).
24
+ "--pae", "on",
25
+ "--hwvirtex", "on",
26
+ "--largepages", "on",
27
+ "--vtxvpid", "on",
28
+ # This causes the virtual machine to proxy DNS requests through the host
29
+ # via NAT. Without this, the default resolver on the guest will introduce
30
+ # a 5 second latency on every HTTP request... which is a real downer.
31
+ "--natdnshostresolver1", "on"
32
+ ]
33
+ end
34
+
35
+ config.vm.hostname = FQDN
36
+ config.vm.provision :shell, path: "provision.sh"
37
+ end
38
+
39
+ #
40
+ # DONT FORGET!
41
+ #
42
+ # Force update VirtualBox Guest Additions
43
+ # Run the following command inside same directory as Vagrantfile
44
+ # Must be done once on your dev system
45
+ #
46
+ # vagrant plugin install vagrant-vbguest
47
+ #
data/differ.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'differ/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ninja-differ"
8
+ spec.version = Differ::VERSION
9
+ spec.authors = ["Frank Koehl", "Pieter van de Bruggen", "Jonas Schubert Erlandsson", "Eddie Cianci", "Chad Boyd"]
10
+ spec.date = "2022-10-26"
11
+ spec.email = ["fkoehl@gmail.com", "pvande@gmail.com", "jonas.schubert.erlandsson@my-codeworks.com", "chad@txt2give.co"]
12
+ spec.description = "A simple gem for generating string diffs"
13
+ spec.summary = "A simple gem for generating string diffs"
14
+ spec.homepage = "https://github.com/BattleBrisket/ninja-differ"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ # spec.add_development_dependency 'rspec', '~> 3.11'
22
+ end
@@ -0,0 +1,30 @@
1
+ module Differ
2
+ class Change # :nodoc:
3
+ attr_accessor :insert, :delete
4
+ def initialize(options = {})
5
+ @insert = options[:insert] || ''
6
+ @delete = options[:delete] || ''
7
+ end
8
+
9
+ def insert?
10
+ !@insert.empty?
11
+ end
12
+
13
+ def delete?
14
+ !@delete.empty?
15
+ end
16
+
17
+ def change?
18
+ !@insert.empty? && !@delete.empty?
19
+ end
20
+
21
+ def to_s
22
+ Differ.format.call(self)
23
+ end
24
+ alias :inspect :to_s
25
+
26
+ def ==(other)
27
+ self.insert == other.insert && self.delete == other.delete
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,97 @@
1
+ module Differ
2
+ class Diff
3
+ attr_reader :raw
4
+
5
+ def initialize
6
+ @raw = []
7
+ end
8
+
9
+ def same(*str)
10
+ return if str.empty?
11
+ if @raw.last.is_a? String
12
+ @raw.last << sep
13
+ elsif @raw.last.is_a? Change
14
+ if @raw.last.change?
15
+ @raw << sep
16
+ else
17
+ change = @raw.pop
18
+ if change.insert? && @raw.last
19
+ @raw.last << sep if change.insert.sub!(/^#{Regexp.quote(sep)}/, '')
20
+ end
21
+ if change.delete? && @raw.last
22
+ @raw.last << sep if change.delete.sub!(/^#{Regexp.quote(sep)}/, '')
23
+ end
24
+ @raw << change
25
+
26
+ @raw.last.insert << sep if @raw.last.insert?
27
+ @raw.last.delete << sep if @raw.last.delete?
28
+ @raw << ''
29
+ end
30
+ else
31
+ @raw << ''
32
+ end
33
+ @raw.last << str.join(sep)
34
+ end
35
+
36
+ def delete(*str)
37
+ return if str.empty?
38
+ if @raw.last.is_a? Change
39
+ change = @raw.pop
40
+ if change.insert? && @raw.last
41
+ @raw.last << sep if change.insert.sub!(/^#{Regexp.quote(sep)}/, '')
42
+ end
43
+ change.delete << sep if change.delete?
44
+ else
45
+ change = Change.new(:delete => @raw.empty? ? '' : sep)
46
+ end
47
+
48
+ @raw << change
49
+ @raw.last.delete << str.join(sep)
50
+ end
51
+
52
+ def insert(*str)
53
+ return if str.empty?
54
+ if @raw.last.is_a? Change
55
+ change = @raw.pop
56
+ if change.delete? && @raw.last
57
+ @raw.last << sep if change.delete.sub!(/^#{Regexp.quote(sep)}/, '')
58
+ end
59
+ change.insert << sep if change.insert?
60
+ else
61
+ change = Change.new(:insert => @raw.empty? ? '' : sep)
62
+ end
63
+
64
+ @raw << change
65
+ @raw.last.insert << str.join(sep)
66
+ end
67
+
68
+ def ==(other)
69
+ @raw == other.raw_array
70
+ end
71
+
72
+ def to_s
73
+ @raw.join()
74
+ end
75
+
76
+ def format_as(f)
77
+ f = Differ.format_for(f)
78
+ @raw.inject('') do |sum, part|
79
+ part = case part
80
+ when String then part
81
+ when Change then f.call(part)
82
+ end
83
+ sum << part
84
+ end
85
+ end
86
+
87
+ protected
88
+ def raw_array
89
+ @raw
90
+ end
91
+
92
+ private
93
+ def sep
94
+ "#{Differ.separator}"
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,27 @@
1
+ module Differ
2
+ module Format
3
+ module Ascii
4
+ class << self
5
+ def call(change)
6
+ (change.change? && as_change(change)) ||
7
+ (change.delete? && as_delete(change)) ||
8
+ (change.insert? && as_insert(change)) ||
9
+ ''
10
+ end
11
+
12
+ private
13
+ def as_insert(change)
14
+ "{+#{change.insert.inspect}}"
15
+ end
16
+
17
+ def as_delete(change)
18
+ "{-#{change.delete.inspect}}"
19
+ end
20
+
21
+ def as_change(change)
22
+ "{#{change.delete.inspect} >> #{change.insert.inspect}}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Differ
2
+ module Format
3
+ module Color
4
+ class << self
5
+ def call(change)
6
+ (change.change? && as_change(change)) ||
7
+ (change.delete? && as_delete(change)) ||
8
+ (change.insert? && as_insert(change)) ||
9
+ ''
10
+ end
11
+
12
+ private
13
+ def as_insert(change)
14
+ "\033[32m#{change.insert}\033[0m"
15
+ end
16
+
17
+ def as_delete(change)
18
+ "\033[31m#{change.delete}\033[0m"
19
+ end
20
+
21
+ def as_change(change)
22
+ as_delete(change) << as_insert(change)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Differ
2
+ module Format
3
+ module HTML
4
+ class << self
5
+ def call(change)
6
+ (change.change? && as_change(change)) ||
7
+ (change.delete? && as_delete(change)) ||
8
+ (change.insert? && as_insert(change)) ||
9
+ ''
10
+ end
11
+
12
+ private
13
+ def as_insert(change)
14
+ %Q{<ins class="differ">#{change.insert}</ins>}
15
+ end
16
+
17
+ def as_delete(change)
18
+ %Q{<del class="differ">#{change.delete}</del>}
19
+ end
20
+
21
+ def as_change(change)
22
+ as_delete(change) << as_insert(change)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ module Differ
2
+ module Format
3
+ module Patch
4
+ class << self
5
+ def format(change)
6
+ (change.change? && as_change(change)) ||
7
+ (change.delete? && as_delete(change)) ||
8
+ (change.insert? && as_insert(change)) ||
9
+ ''
10
+ end
11
+
12
+ private
13
+
14
+ def as_insert(change)
15
+ "\033[32m+ #{change.insert}\033[0m"
16
+ end
17
+
18
+ def as_delete(change)
19
+ "\033[31m- #{change.delete}\033[0m"
20
+ end
21
+
22
+ def as_change(change)
23
+ [as_delete(change), as_insert(change)].join("\n")
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ module Differ
2
+ module StringDiffer
3
+ def diff(old)
4
+ Differ.diff(self, old, Differ.separator || "\n")
5
+ end
6
+ alias :- :diff
7
+
8
+ def diff_by_char(old)
9
+ Differ.diff(self, old, '')
10
+ end
11
+
12
+ def diff_by_word(old)
13
+ Differ.diff(self, old, /\b/)
14
+ end
15
+
16
+ def diff_by_line(old)
17
+ Differ.diff(self, old, "\n")
18
+ end
19
+ end
20
+ end
21
+
22
+ String.class_eval do
23
+ include Differ::StringDiffer
24
+ end
@@ -0,0 +1,3 @@
1
+ module Differ
2
+ VERSION = "1.0.0"
3
+ end
data/lib/differ.rb ADDED
@@ -0,0 +1,97 @@
1
+ require 'differ/change'
2
+ require 'differ/diff'
3
+ require 'differ/format/ascii'
4
+ require 'differ/format/color'
5
+ require 'differ/format/html'
6
+ require 'differ/format/patch'
7
+
8
+ module Differ
9
+ class << self
10
+
11
+ def separator=(separator)
12
+ @@separator = separator
13
+ end
14
+
15
+ def separator
16
+ @@separator
17
+ end
18
+
19
+ def diff(target, source, new_sep = "\n")
20
+ old_sep = self.separator
21
+ self.separator = new_sep
22
+
23
+ target = target.split(new_sep)
24
+ source = source.split(new_sep)
25
+
26
+ self.separator = '' if new_sep.is_a? Regexp
27
+
28
+ @diff = Diff.new
29
+ advance(target, source) until source.empty? || target.empty?
30
+ @diff.insert(*target) || @diff.delete(*source)
31
+ return @diff
32
+ ensure
33
+ self.separator = old_sep
34
+ end
35
+
36
+ def diff_by_char(to, from)
37
+ diff(to, from, '')
38
+ end
39
+
40
+ def diff_by_word(to, from)
41
+ diff(to, from, /\b/)
42
+ end
43
+
44
+ def diff_by_line(to, from)
45
+ diff(to, from, "\n")
46
+ end
47
+
48
+ def format=(f)
49
+ @format = format_for(f)
50
+ end
51
+
52
+ def format
53
+ return @format || Format::Ascii
54
+ end
55
+
56
+ def format_for(f)
57
+ if f.respond_to? :call
58
+ f
59
+ else
60
+ case f
61
+ when :ascii then Format::Ascii
62
+ when :color then Format::Color
63
+ when :html then Format::HTML
64
+ when :patch then Format::Patch
65
+ when nil then nil
66
+ else raise "Unknown format type #{f.inspect}"
67
+ end
68
+ end
69
+ end
70
+
71
+ private
72
+ def advance(target, source)
73
+ del, add = source.shift, target.shift
74
+
75
+ prioritize_insert = target.length > source.length
76
+ insert = target.index(del)
77
+ delete = source.index(add)
78
+
79
+ if del == add
80
+ @diff.same(add)
81
+ elsif insert && prioritize_insert
82
+ change(:insert, target.unshift(add), insert)
83
+ elsif delete
84
+ change(:delete, source.unshift(del), delete)
85
+ elsif insert && !prioritize_insert
86
+ change(:insert, target.unshift(add), insert)
87
+ else
88
+ @diff.insert(add) && @diff.delete(del)
89
+ end
90
+ end
91
+
92
+ def change(method, array, index)
93
+ @diff.send(method, *array.slice!(0..index))
94
+ @diff.same(array.shift)
95
+ end
96
+ end
97
+ end