├── .gitignore
├── README.rdoc
├── Rakefile
├── VERSION.yml
├── differ.gemspec
├── lib
├── differ.rb
└── differ
│ ├── change.rb
│ ├── diff.rb
│ ├── format
│ ├── ascii.rb
│ ├── color.rb
│ └── html.rb
│ └── string.rb
└── spec
├── differ
├── change_spec.rb
├── diff_spec.rb
├── format
│ ├── ascii_spec.rb
│ ├── color_spec.rb
│ └── html_spec.rb
└── string_spec.rb
├── differ_spec.rb
└── spec_helper.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sw?
2 | .DS_Store
3 | coverage
4 | rdoc
5 | pkg
6 |
--------------------------------------------------------------------------------
/README.rdoc:
--------------------------------------------------------------------------------
1 | = Differ
2 |
3 | As streams of text swirled before the young man's eyes, his mind swam with
4 | thoughts of many things. They would have to wait, however, as he focussed his
5 | full concentration on the shifting patterns ahead of him. A glint of light
6 | reflecting off a piece of buried code caught his eye and any hope he had was
7 | lost. For the very moment he glanced aside, the landscape became Different.
8 | The young man gave a small sigh and trudged onward in solemn resignation,
9 | fated to wander the desolate codebanks in perpetuity.
10 |
11 | Differ 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 | sudo gem install differ
19 |
20 | == Usage
21 |
22 | There are a number of ways to use Differ, depending on your situation and needs.
23 |
24 | @original = "Epic lolcat fail!"
25 | @current = "Epic wolfman fail!"
26 |
27 | You can call the Differ module directly.
28 |
29 | require 'differ'
30 |
31 | There are a number of built-in diff methods to choose from...
32 |
33 | @diff = Differ.diff_by_line(@current, @original)
34 | # => "{"Epic lolcat fail!" >> "Epic wolfman fail!"}"
35 |
36 | @diff = Differ.diff_by_word(@current, @original)
37 | # => "Epic {"lolcat" >> "wolfman"} fail!"
38 |
39 | @diff = Differ.diff_by_char(@current, @original)
40 | # => "Epic {+"wo"}l{-"olcat "}f{+"m"}a{+"n fa"}il!"
41 |
42 | ... or call #diff directly and supply your own boundary string!
43 |
44 | @diff = Differ.diff(@current, @original) # implicitly by line!
45 | # => "{"Epic lolcat fail!" >> "Epic wolfman fail!"}"
46 |
47 | @diff = Differ.diff(@current, @original, 'i')
48 | # => "Epi{"c lolcat fa" >> "c wolfman fa"}il"
49 |
50 | If you would like something a little more inline...
51 |
52 | require 'differ/string'
53 |
54 | @diff = @current.diff(@original) # implicitly by line!
55 | # => "{"Epic lolcat fail!" >> "Epic wolfman fail!"}"
56 |
57 | ... or a lot more inline...
58 |
59 | @diff = (@current - @original) # implicitly by line!
60 | # => "{"Epic lolcat fail!" >> "Epic wolfman fail!"}"
61 |
62 | $; = ' '
63 | @diff = (@current - @original)
64 | # => "Epic {"lolcat" >> "wolfman"} fail!"
65 |
66 | ... we've pretty much got you covered.
67 |
68 | === Output Formatting
69 |
70 | Need a different output format? We've got a few of those too.
71 |
72 | Differ.format = :ascii # <- Default
73 | Differ.format = :color
74 | Differ.format = :html
75 |
76 | Differ.format = MyCustomFormatModule
77 |
78 | Don't want to change the system-wide default for only a single diff output?
79 | Yeah, me either.
80 |
81 | @diff = (@current - @original)
82 | @diff.format_as(:color)
83 |
84 | == Copyright
85 |
86 | Copyright (c) 2009 Pieter Vande Bruggen.
87 |
88 | (The GIFT License, v1)
89 |
90 | Permission is hereby granted to use this software and/or its source code for
91 | whatever purpose you should choose. Seriously, go nuts. Use it for your personal
92 | RSS feed reader, your wildly profitable social network, or your mission to Mars.
93 |
94 | I don't care, it's yours. Change the name on it if you want -- in fact, if you
95 | start significantly changing what it does, I'd rather you did! Make it your own
96 | little work of art, complete with a stylish flowing signature in the corner. All
97 | I really did was give you the canvas. And my blessing.
98 |
99 | Know always right from wrong, and let others see your good works.
100 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rake'
2 |
3 | begin
4 | require 'jeweler'
5 | Jeweler::Tasks.new do |gem|
6 | gem.name = "differ"
7 | gem.summary = "A simple gem for generating string diffs"
8 | gem.email = "pvande@gmail.com"
9 | gem.homepage = "http://github.com/pvande/differ"
10 | gem.authors = [ "Pieter van de Bruggen" ]
11 | end
12 | rescue LoadError
13 | puts "Jeweler not available. Install it with: gem install jeweler"
14 | end
15 |
16 | require 'rake/rdoctask'
17 | Rake::RDocTask.new do |rdoc|
18 | rdoc.rdoc_dir = 'rdoc'
19 | rdoc.title = 'differ'
20 | rdoc.options << '--line-numbers' << '--inline-source'
21 | rdoc.rdoc_files.include('README*')
22 | rdoc.rdoc_files.include('lib/**/*.rb')
23 | end
24 |
25 | require 'spec/rake/spectask'
26 | Spec::Rake::SpecTask.new(:spec) do |spec|
27 | spec.libs << 'lib' << 'spec'
28 | spec.spec_files = FileList['spec/**/*_spec.rb']
29 | end
30 |
31 | Spec::Rake::SpecTask.new(:rcov) do |spec|
32 | spec.libs << 'lib' << 'spec'
33 | spec.pattern = 'spec/**/*_spec.rb'
34 | spec.rcov = true
35 | end
36 |
37 |
38 | task :default => :spec
39 |
--------------------------------------------------------------------------------
/VERSION.yml:
--------------------------------------------------------------------------------
1 | ---
2 | :patch: 2
3 | :major: 0
4 | :minor: 1
5 |
--------------------------------------------------------------------------------
/differ.gemspec:
--------------------------------------------------------------------------------
1 | # Generated by jeweler
2 | # DO NOT EDIT THIS FILE DIRECTLY
3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4 | # -*- encoding: utf-8 -*-
5 |
6 | Gem::Specification.new do |s|
7 | s.name = %q{differ}
8 | s.version = "0.1.2"
9 |
10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11 | s.authors = ["Pieter van de Bruggen"]
12 | s.date = %q{2011-02-17}
13 | s.email = %q{pvande@gmail.com}
14 | s.extra_rdoc_files = [
15 | "README.rdoc"
16 | ]
17 | s.files = [
18 | "README.rdoc",
19 | "Rakefile",
20 | "VERSION.yml",
21 | "differ.gemspec",
22 | "lib/differ.rb",
23 | "lib/differ/change.rb",
24 | "lib/differ/diff.rb",
25 | "lib/differ/format/ascii.rb",
26 | "lib/differ/format/color.rb",
27 | "lib/differ/format/html.rb",
28 | "lib/differ/string.rb",
29 | "spec/differ/change_spec.rb",
30 | "spec/differ/diff_spec.rb",
31 | "spec/differ/format/ascii_spec.rb",
32 | "spec/differ/format/color_spec.rb",
33 | "spec/differ/format/html_spec.rb",
34 | "spec/differ/string_spec.rb",
35 | "spec/differ_spec.rb",
36 | "spec/spec_helper.rb"
37 | ]
38 | s.homepage = %q{http://github.com/pvande/differ}
39 | s.require_paths = ["lib"]
40 | s.rubygems_version = %q{1.3.7}
41 | s.summary = %q{A simple gem for generating string diffs}
42 | s.test_files = [
43 | "spec/differ/change_spec.rb",
44 | "spec/differ/diff_spec.rb",
45 | "spec/differ/format/ascii_spec.rb",
46 | "spec/differ/format/color_spec.rb",
47 | "spec/differ/format/html_spec.rb",
48 | "spec/differ/string_spec.rb",
49 | "spec/differ_spec.rb",
50 | "spec/spec_helper.rb"
51 | ]
52 |
53 | if s.respond_to? :specification_version then
54 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
55 | s.specification_version = 3
56 |
57 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
58 | else
59 | end
60 | else
61 | end
62 | end
63 |
64 |
--------------------------------------------------------------------------------
/lib/differ.rb:
--------------------------------------------------------------------------------
1 | require 'differ/change'
2 | require 'differ/diff'
3 | require 'differ/format/ascii'
4 | require 'differ/format/color'
5 | require 'differ/format/html'
6 |
7 | module Differ
8 | class << self
9 |
10 | def diff(target, source, separator = "\n")
11 | old_sep, $; = $;, separator
12 |
13 | target = target.split(separator)
14 | source = source.split(separator)
15 |
16 | $; = '' if separator.is_a? Regexp
17 |
18 | @diff = Diff.new
19 | advance(target, source) until source.empty? || target.empty?
20 | @diff.insert(*target) || @diff.delete(*source)
21 | return @diff
22 | ensure
23 | $; = old_sep
24 | end
25 |
26 | def diff_by_char(to, from)
27 | diff(to, from, '')
28 | end
29 |
30 | def diff_by_word(to, from)
31 | diff(to, from, /\b/)
32 | end
33 |
34 | def diff_by_line(to, from)
35 | diff(to, from, "\n")
36 | end
37 |
38 | def format=(f)
39 | @format = format_for(f)
40 | end
41 |
42 | def format
43 | return @format || Format::Ascii
44 | end
45 |
46 | def format_for(f)
47 | case f
48 | when Module then f
49 | when :ascii then Format::Ascii
50 | when :color then Format::Color
51 | when :html then Format::HTML
52 | when nil then nil
53 | else raise "Unknown format type #{f.inspect}"
54 | end
55 | end
56 |
57 | private
58 | def advance(target, source)
59 | del, add = source.shift, target.shift
60 |
61 | prioritize_insert = target.length > source.length
62 | insert = target.index(del)
63 | delete = source.index(add)
64 |
65 | if del == add
66 | @diff.same(add)
67 | elsif insert && prioritize_insert
68 | change(:insert, target.unshift(add), insert)
69 | elsif delete
70 | change(:delete, source.unshift(del), delete)
71 | elsif insert && !prioritize_insert
72 | change(:insert, target.unshift(add), insert)
73 | else
74 | @diff.insert(add) && @diff.delete(del)
75 | end
76 | end
77 |
78 | def change(method, array, index)
79 | @diff.send(method, *array.slice!(0..index))
80 | @diff.same(array.shift)
81 | end
82 | end
83 | end
--------------------------------------------------------------------------------
/lib/differ/change.rb:
--------------------------------------------------------------------------------
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.format(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
--------------------------------------------------------------------------------
/lib/differ/diff.rb:
--------------------------------------------------------------------------------
1 | module Differ
2 | class Diff
3 | def initialize
4 | @raw = []
5 | end
6 |
7 | def same(*str)
8 | return if str.empty?
9 | if @raw.last.is_a? String
10 | @raw.last << sep
11 | elsif @raw.last.is_a? Change
12 | if @raw.last.change?
13 | @raw << sep
14 | else
15 | change = @raw.pop
16 | if change.insert? && @raw.last
17 | @raw.last << sep if change.insert.sub!(/^#{Regexp.quote(sep)}/, '')
18 | end
19 | if change.delete? && @raw.last
20 | @raw.last << sep if change.delete.sub!(/^#{Regexp.quote(sep)}/, '')
21 | end
22 | @raw << change
23 |
24 | @raw.last.insert << sep if @raw.last.insert?
25 | @raw.last.delete << sep if @raw.last.delete?
26 | @raw << ''
27 | end
28 | else
29 | @raw << ''
30 | end
31 | @raw.last << str.join(sep)
32 | end
33 |
34 | def delete(*str)
35 | return if str.empty?
36 | if @raw.last.is_a? Change
37 | change = @raw.pop
38 | if change.insert? && @raw.last
39 | @raw.last << sep if change.insert.sub!(/^#{Regexp.quote(sep)}/, '')
40 | end
41 | change.delete << sep if change.delete?
42 | else
43 | change = Change.new(:delete => @raw.empty? ? '' : sep)
44 | end
45 |
46 | @raw << change
47 | @raw.last.delete << str.join(sep)
48 | end
49 |
50 | def insert(*str)
51 | return if str.empty?
52 | if @raw.last.is_a? Change
53 | change = @raw.pop
54 | if change.delete? && @raw.last
55 | @raw.last << sep if change.delete.sub!(/^#{Regexp.quote(sep)}/, '')
56 | end
57 | change.insert << sep if change.insert?
58 | else
59 | change = Change.new(:insert => @raw.empty? ? '' : sep)
60 | end
61 |
62 | @raw << change
63 | @raw.last.insert << str.join(sep)
64 | end
65 |
66 | def ==(other)
67 | @raw == other.raw_array
68 | end
69 |
70 | def to_s
71 | @raw.join()
72 | end
73 |
74 | def format_as(f)
75 | f = Differ.format_for(f)
76 | @raw.inject('') do |sum, part|
77 | part = case part
78 | when String then part
79 | when Change then f.format(part)
80 | end
81 | sum << part
82 | end
83 | end
84 |
85 | protected
86 | def raw_array
87 | @raw
88 | end
89 |
90 | private
91 | def sep
92 | "#{$;}"
93 | end
94 | end
95 | end
96 |
--------------------------------------------------------------------------------
/lib/differ/format/ascii.rb:
--------------------------------------------------------------------------------
1 | module Differ
2 | module Format
3 | module Ascii
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 | 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
--------------------------------------------------------------------------------
/lib/differ/format/color.rb:
--------------------------------------------------------------------------------
1 | module Differ
2 | module Format
3 | module Color
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 | 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
--------------------------------------------------------------------------------
/lib/differ/format/html.rb:
--------------------------------------------------------------------------------
1 | module Differ
2 | module Format
3 | module HTML
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 | def as_insert(change)
14 | %Q{#{change.insert}}
15 | end
16 |
17 | def as_delete(change)
18 | %Q{#{change.delete}}
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
--------------------------------------------------------------------------------
/lib/differ/string.rb:
--------------------------------------------------------------------------------
1 | module Differ
2 | module StringDiffer
3 | def diff(old)
4 | Differ.diff(self, old, $; || "\n")
5 | end
6 | alias :- :diff
7 | end
8 | end
9 |
10 | String.class_eval do
11 | include Differ::StringDiffer
12 | end
--------------------------------------------------------------------------------
/spec/differ/change_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Differ::Change do
4 | before(:each) do
5 | @format = Module.new { def self.format(c); end }
6 | Differ.format = @format
7 | end
8 |
9 | describe '(empty)' do
10 | before(:each) do
11 | @change = Differ::Change.new()
12 | end
13 |
14 | it 'should have a default insert' do
15 | @change.insert.should == ''
16 | end
17 |
18 | it 'should have a default delete' do
19 | @change.delete.should == ''
20 | end
21 |
22 | it 'should stringify to ""' do
23 | @format.should_receive(:format).once.and_return('')
24 | @change.to_s.should == ''
25 | end
26 | end
27 |
28 | describe '(insert only)' do
29 | before(:each) do
30 | @change = Differ::Change.new(:insert => 'foo')
31 | end
32 |
33 | it 'should populate the :insert parameter' do
34 | @change.insert.should == 'foo'
35 | end
36 |
37 | it 'should have a default delete' do
38 | @change.delete.should == ''
39 | end
40 |
41 | it { (@change).should be_an_insert }
42 | end
43 |
44 | describe '(delete only)' do
45 | before(:each) do
46 | @change = Differ::Change.new(:delete => 'bar')
47 | end
48 |
49 | it 'should have a default :insert' do
50 | @change.insert.should == ''
51 | end
52 |
53 | it 'should populate the :delete parameter' do
54 | @change.delete.should == 'bar'
55 | end
56 |
57 | it { (@change).should be_a_delete }
58 | end
59 |
60 | describe '(both insert and delete)' do
61 | before(:each) do
62 | @change = Differ::Change.new(:insert => 'foo', :delete => 'bar')
63 | end
64 |
65 | it 'should populate the :insert parameter' do
66 | @change.insert.should == 'foo'
67 | end
68 |
69 | it 'should populate the :delete parameter' do
70 | @change.delete.should == 'bar'
71 | end
72 |
73 | it { (@change).should be_an_insert }
74 | it { (@change).should be_a_delete }
75 | it { (@change).should be_a_change }
76 | end
77 |
78 | it "should stringify via the current format's #format method" do
79 | @format.should_receive(:format).once
80 | Differ::Change.new.to_s
81 | end
82 | end
--------------------------------------------------------------------------------
/spec/differ/diff_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Differ::Diff do
4 | before(:each) do
5 | $; = nil
6 | @diff = Differ::Diff.new
7 | end
8 |
9 | describe '#to_s' do
10 | before(:each) do
11 | @format = Differ.format
12 | end
13 |
14 | it 'should concatenate the result list' do
15 | diff('a', 'b', 'c').to_s.should == 'abc'
16 | end
17 |
18 | it 'should concatenate without regard for the $;' do
19 | $; = '*'
20 | diff('a', 'b', 'c').to_s.should == 'abc'
21 | end
22 |
23 | it 'should delegate insertion changes to Differ#format' do
24 | i = +'b'
25 | @format.should_receive(:format).once.with(i).and_return('!')
26 | diff('a', i, 'c').to_s.should == 'a!c'
27 | end
28 | end
29 |
30 | describe '#format_as' do
31 | before(:each) do
32 | @change = +'b'
33 | Differ.format = Module.new { def self.format(c); raise :error; end }
34 | @format = Module.new { def self.format(c); end }
35 | end
36 |
37 | it 'should delegate change formatting to the given format' do
38 | @format.should_receive(:format).once.with(@change).and_return('!')
39 | diff('a', @change, 'c').format_as(@format).should == 'a!c'
40 | end
41 |
42 | it 'should use Differ#format_for to grab the correct format' do
43 | Differ.should_receive(:format_for).once.with(@format)
44 | diff().format_as(@format)
45 | end
46 | end
47 |
48 | describe '#same' do
49 | it 'should append to the result list' do
50 | @diff.same('c')
51 | @diff.should == diff('c')
52 | end
53 |
54 | it 'should concatenate its arguments' do
55 | @diff.same('a', 'b', 'c', 'd')
56 | @diff.should == diff('abcd')
57 | end
58 |
59 | it 'should join its arguments with $;' do
60 | $; = '*'
61 | @diff.same(*'a*b*c*d'.split)
62 | @diff.should == diff('a*b*c*d')
63 | end
64 |
65 | describe 'when the last result was a String' do
66 | before(:each) do
67 | @diff = diff('a')
68 | end
69 |
70 | it 'should append to the last result' do
71 | @diff.same('b')
72 | @diff.should == diff('ab')
73 | end
74 |
75 | it 'should join to the last result with $;' do
76 | $; = '*'
77 | @diff.same('b')
78 | @diff.should == diff('a*b')
79 | end
80 | end
81 |
82 | describe 'when the last result was a change' do
83 | before(:each) do
84 | @diff = diff('z' >> 'd')
85 | end
86 |
87 | it 'should append to the result list' do
88 | @diff.same('a')
89 | @diff.should == diff(('z' >> 'd'), 'a')
90 | end
91 |
92 | it 'should prepend $; to the result' do
93 | $; = '*'
94 | @diff.same('a')
95 | @diff.should == diff(('z' >> 'd'), '*a')
96 | end
97 |
98 | it "should do nothing to a leading $; on the insert" do
99 | @diff = diff('a', ('*-' >> '*+'))
100 | $; = '*'
101 | @diff.same('c')
102 | @diff.should == diff('a', ('*-' >> '*+'), '*c')
103 | end
104 | end
105 |
106 | describe 'when the last result was just a delete' do
107 | before(:each) do
108 | @diff = diff(-'z')
109 | end
110 |
111 | it 'should append to the result list' do
112 | @diff.same('a')
113 | @diff.should == diff(-'z', 'a')
114 | end
115 |
116 | it 'should append $; to the previous result' do
117 | $; = '*'
118 | @diff.same('a')
119 | @diff.should == diff(-'z*', 'a')
120 | end
121 |
122 | it "should relocate a leading $; on the delete to the previous item" do
123 | @diff = diff('a', -'*b')
124 | $; = '*'
125 | @diff.same('c')
126 | @diff.should == diff('a*', -'b*', 'c')
127 | end
128 | end
129 |
130 | describe 'when the last result was just an insert' do
131 | before(:each) do
132 | @diff = diff(+'z')
133 | end
134 |
135 | it 'should append to the result list' do
136 | @diff.same('a')
137 | @diff.should == diff(+'z', 'a')
138 | end
139 |
140 | it 'should append $; to the previous result' do
141 | $; = '*'
142 | @diff.same('a')
143 | @diff.should == diff(+'z*', 'a')
144 | end
145 |
146 | it "should relocate a leading $; on the insert to the previous item" do
147 | @diff = diff('a', +'*b')
148 | $; = '*'
149 | @diff.same('c')
150 | @diff.should == diff('a*', +'b*', 'c')
151 | end
152 | end
153 | end
154 |
155 | describe '#delete' do
156 | it 'should append to the result list' do
157 | @diff.delete('c')
158 | @diff.should == diff(-'c')
159 | end
160 |
161 | it 'should concatenate its arguments' do
162 | @diff.delete('a', 'b', 'c', 'd')
163 | @diff.should == diff(-'abcd')
164 | end
165 |
166 | it 'should join its arguments with $;' do
167 | $; = '*'
168 | @diff.delete(*'a*b*c*d'.split)
169 | @diff.should == diff(-'a*b*c*d')
170 | end
171 |
172 | describe 'when the last result was a Change' do
173 | describe '(delete)' do
174 | before(:each) do
175 | @diff = diff(-'a')
176 | end
177 |
178 | it 'should append to the last result' do
179 | @diff.delete('b')
180 | @diff.should == diff(-'ab')
181 | end
182 |
183 | it 'should join to the last result with $;' do
184 | $; = '*'
185 | @diff.delete('b')
186 | @diff.should == diff(-'a*b')
187 | end
188 | end
189 |
190 | describe '(insert)' do
191 | before(:each) do
192 | @diff = diff(+'a')
193 | end
194 |
195 | it "should turn the insert into a change" do
196 | @diff.delete('b')
197 | @diff.should == diff('b' >> 'a')
198 | end
199 |
200 | it "should relocate a leading $; on the insert to the previous item" do
201 | @diff = diff('a', +'*b')
202 | $; = '*'
203 | @diff.delete('z')
204 | @diff.should == diff('a*', ('z' >> 'b'))
205 | end
206 | end
207 | end
208 |
209 | describe 'when the last result was not a Change' do
210 | before(:each) do
211 | @diff = diff('a')
212 | end
213 |
214 | it 'should append a Change to the result list' do
215 | @diff.delete('b')
216 | @diff.should == diff('a', -'b')
217 | end
218 |
219 | it 'should prepend $; to the result' do
220 | $; = '*'
221 | @diff.delete('b')
222 | @diff.should == diff('a', -'*b')
223 | end
224 | end
225 | end
226 |
227 | describe '#insert' do
228 | it 'should append to the result list' do
229 | @diff.insert('c')
230 | @diff.should == diff(+'c')
231 | end
232 |
233 | it 'should concatenate its arguments' do
234 | @diff.insert('a', 'b', 'c', 'd')
235 | @diff.should == diff(+'abcd')
236 | end
237 |
238 | it 'should join its arguments with $;' do
239 | $; = '*'
240 | @diff.insert(*'a*b*c*d'.split)
241 | @diff.should == diff(+'a*b*c*d')
242 | end
243 |
244 | describe 'when the last result was a Change' do
245 | describe '(delete)' do
246 | before(:each) do
247 | @diff = diff(-'b')
248 | end
249 |
250 | it "should not change the 'insert' portion of the last result" do
251 | @diff.insert('a')
252 | @diff.should == diff('b' >> 'a')
253 | end
254 |
255 | it "should relocate a leading $; on the delete to the previous item" do
256 | @diff = diff('a', -'*b')
257 | $; = '*'
258 | @diff.insert('z')
259 | @diff.should == diff('a*', ('b' >> 'z'))
260 | end
261 | end
262 |
263 | describe '(insert)' do
264 | before(:each) do
265 | @diff = diff(+'a')
266 | end
267 |
268 | it 'should append to the last result' do
269 | @diff.insert('b')
270 | @diff.should == diff(+'ab')
271 | end
272 |
273 | it 'should join to the last result with $;' do
274 | $; = '*'
275 | @diff.insert('b')
276 | @diff.should == diff(+'a*b')
277 | end
278 | end
279 | end
280 |
281 | describe 'when the last result was not a Change' do
282 | before(:each) do
283 | @diff = diff('a')
284 | end
285 |
286 | it 'should append a Change to the result list' do
287 | @diff.insert('b')
288 | @diff.should == diff('a', +'b')
289 | end
290 |
291 | it 'should prepend $; to the result' do
292 | $; = '*'
293 | @diff.insert('b')
294 | @diff.should == diff('a', +'*b')
295 | end
296 | end
297 | end
298 | end
--------------------------------------------------------------------------------
/spec/differ/format/ascii_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Differ::Format::Ascii do
4 | it 'should format inserts well' do
5 | @expected = '{+"SAMPLE"}'
6 | Differ::Format::Ascii.format(+'SAMPLE').should == @expected
7 | end
8 |
9 | it 'should format deletes well' do
10 | @expected = '{-"SAMPLE"}'
11 | Differ::Format::Ascii.format(-'SAMPLE').should == @expected
12 | end
13 |
14 | it 'should format changes well' do
15 | @expected = '{"THEN" >> "NOW"}'
16 | Differ::Format::Ascii.format('THEN' >> 'NOW').should == @expected
17 | end
18 | end
--------------------------------------------------------------------------------
/spec/differ/format/color_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Differ::Format::Color do
4 | it 'should format inserts well' do
5 | @expected = "\033[32mSAMPLE\033[0m"
6 | Differ::Format::Color.format(+'SAMPLE').should == @expected
7 | end
8 |
9 | it 'should format deletes well' do
10 | @expected = "\033[31mSAMPLE\033[0m"
11 | Differ::Format::Color.format(-'SAMPLE').should == @expected
12 | end
13 |
14 | it 'should format changes well' do
15 | @expected = "\033[31mTHEN\033[0m\033[32mNOW\033[0m"
16 | Differ::Format::Color.format('THEN' >> 'NOW').should == @expected
17 | end
18 | end
--------------------------------------------------------------------------------
/spec/differ/format/html_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Differ::Format::HTML do
4 | it 'should format inserts well' do
5 | @expected = 'SAMPLE'
6 | Differ::Format::HTML.format(+'SAMPLE').should == @expected
7 | end
8 |
9 | it 'should format deletes well' do
10 | @expected = 'SAMPLE'
11 | Differ::Format::HTML.format(-'SAMPLE').should == @expected
12 | end
13 |
14 | it 'should format changes well' do
15 | @expected = 'THENNOW'
16 | Differ::Format::HTML.format('THEN' >> 'NOW').should == @expected
17 | end
18 | end
--------------------------------------------------------------------------------
/spec/differ/string_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'differ/string'
3 |
4 | describe Differ::StringDiffer do
5 | it 'should be automatically mixed into String' do
6 | String.included_modules.should include(Differ::StringDiffer)
7 | end
8 |
9 | before(:each) do
10 | $; = nil
11 | end
12 |
13 | describe '#diff' do
14 | it 'should call Differ#diff' do
15 | Differ.should_receive(:diff).with('TO', 'FROM', "\n").once
16 | 'TO'.diff('FROM')
17 | end
18 |
19 | it 'should call Differ#diff with $;' do
20 | $; = 'x'
21 | Differ.should_receive(:diff).with('TO', 'FROM', $;).once
22 | 'TO'.diff('FROM')
23 | end
24 | end
25 |
26 | describe '#-' do
27 | it 'should call Differ#diff' do
28 | Differ.should_receive(:diff).with('TO', 'FROM', "\n").once
29 | 'TO' - 'FROM'
30 | end
31 |
32 | it 'should call Differ#diff with $;' do
33 | $; = 'x'
34 | Differ.should_receive(:diff).with('TO', 'FROM', $;).once
35 | 'TO' - 'FROM'
36 | end
37 | end
38 | end
--------------------------------------------------------------------------------
/spec/differ_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Differ do
4 | describe '#format' do
5 | before(:each) { Differ.format = nil }
6 |
7 | it 'should return the last value it was set to' do
8 | Differ.format = Differ::Format::HTML
9 | Differ.format.should == Differ::Format::HTML
10 | end
11 |
12 | it 'should default to Differ::Format::Ascii' do
13 | Differ.format.should == Differ::Format::Ascii
14 | end
15 | end
16 |
17 | describe '#format=' do
18 | it 'should call #format_for with the passed argument' do
19 | Differ.should_receive(:format_for).with(:format).once
20 | Differ.format = :format
21 | end
22 |
23 | it 'should raise an error on undefined behavior' do
24 | lambda {
25 | Differ.format = 'threeve'
26 | }.should raise_error('Unknown format type "threeve"')
27 | end
28 | end
29 |
30 | describe '#format_for' do
31 | before(:each) { Differ.format = nil }
32 |
33 | it 'should store any module passed to it' do
34 | formatter = Module.new
35 | Differ.format_for(formatter).should == formatter
36 | end
37 |
38 | it 'should permit nil (default behavior)' do
39 | Differ.format_for(nil).should == nil
40 | end
41 |
42 | it 'should raise an error on undefined behavior' do
43 | lambda {
44 | Differ.format_for('threeve')
45 | }.should raise_error('Unknown format type "threeve"')
46 | end
47 |
48 | describe 'when passed a symbol' do
49 | it 'should translate the symbol :ascii into Differ::Format::Ascii' do
50 | Differ.format_for(:ascii).should == Differ::Format::Ascii
51 | end
52 |
53 | it 'should translate the symbol :color into Differ::Format::Color' do
54 | Differ.format_for(:color).should == Differ::Format::Color
55 | end
56 |
57 | it 'should translate the symbol :html into Differ::Format::HTML' do
58 | Differ.format_for(:html).should == Differ::Format::HTML
59 | end
60 | end
61 | end
62 |
63 | describe '#diff_by_char' do
64 | def diff_by_char
65 | Differ.send(:diff_by_char, @to, @from)
66 | end
67 |
68 | before(:each) do
69 | @to = @from = 'self'
70 | end
71 |
72 | it 'should hande no-change situations' do
73 | @expected = diff('self')
74 | diff_by_char.should == @expected
75 | end
76 |
77 | it 'should handle prepends' do
78 | @to = "myself"
79 | @expected = diff(+'my', 'self')
80 | diff_by_char.should == @expected
81 | end
82 |
83 | it 'should handle appends' do
84 | @to = 'self-interest'
85 | @expected = diff('self', +'-interest')
86 | diff_by_char.should == @expected
87 | end
88 |
89 | it 'should handle leading deletes' do
90 | @to = 'elf'
91 | @expected = diff(-'s', 'elf')
92 | diff_by_char.should == @expected
93 | end
94 |
95 | it 'should handle trailing deletes' do
96 | @to = 'sel'
97 | @expected = diff('sel', -'f')
98 | diff_by_char.should == @expected
99 | end
100 |
101 | it 'should handle simultaneous leading changes' do
102 | @to = 'wood-elf'
103 | @expected = diff(('s' >> 'wood-'), 'elf')
104 | diff_by_char.should == @expected
105 | end
106 |
107 | it 'should handle simultaneous trailing changes' do
108 | @to = "seasoning"
109 | @expected = diff('se', ('lf' >> 'asoning'))
110 | diff_by_char.should == @expected
111 | end
112 |
113 | it 'should handle full-string changes' do
114 | @to = 'turgid'
115 | @expected = diff('self' >> 'turgid')
116 | diff_by_char.should == @expected
117 | end
118 |
119 | it 'should handle complex string additions' do
120 | @to = 'my sleeplife'
121 | @expected = diff(+'my ', 's', +'l', 'e', +'ep', 'l', +'i', 'f', +'e')
122 | diff_by_char.should == @expected
123 | end
124 |
125 | it 'should handle complex string deletions' do
126 | @from = 'my sleeplife'
127 | @expected = diff(-'my ', 's', -'l', 'e', -'ep', 'l', -'i', 'f', -'e')
128 | diff_by_char.should == @expected
129 | end
130 |
131 | it 'should handle complex string changes' do
132 | @from = 'my sleeplife'
133 | @to = 'seasonal'
134 | @expected = diff(-'my ', 's', -'l', 'e', ('ep' >> 'asona'), 'l', -'ife')
135 | diff_by_char.should == @expected
136 | end
137 | end
138 |
139 | describe '#diff_by_word' do
140 | def diff_by_word
141 | Differ.send(:diff_by_word, @to, @from)
142 | end
143 |
144 | before(:each) do
145 | @to = @from = 'the daylight will come'
146 | end
147 |
148 | it 'should hande no-change situations' do
149 | @expected = diff('the daylight will come')
150 | diff_by_word.should == @expected
151 | end
152 |
153 | it 'should handle prepends' do
154 | @to = "surely the daylight will come"
155 | @expected = diff(+'surely ', 'the daylight will come')
156 | diff_by_word.should == @expected
157 | end
158 |
159 | it 'should handle appends' do
160 | @to = 'the daylight will come in the morning'
161 | @expected = diff('the daylight will come', +' in the morning')
162 | diff_by_word.should == @expected
163 | end
164 |
165 | it 'should handle leading deletes' do
166 | @to = 'daylight will come'
167 | @expected = diff(-'the ', 'daylight will come')
168 | diff_by_word.should == @expected
169 | end
170 |
171 | it 'should handle trailing deletes' do
172 | @to = 'the daylight'
173 | @expected = diff('the daylight', -' will come')
174 | diff_by_word.should == @expected
175 | end
176 |
177 | it 'should handle simultaneous leading changes' do
178 | @to = 'some daylight will come'
179 | @expected = diff(('the' >> 'some'), ' daylight will come')
180 | diff_by_word.should == @expected
181 | end
182 |
183 | it 'should handle simultaneous trailing changes' do
184 | @to = "the daylight will flood the room"
185 | @expected = diff('the daylight will ', ('come' >> 'flood the room'))
186 | diff_by_word.should == @expected
187 | end
188 |
189 | it 'should handle full-string changes' do
190 | @to = 'if we should expect it'
191 | @expected = diff(
192 | ('the' >> 'if'),
193 | ' ',
194 | ('daylight' >> 'we'),
195 | ' ',
196 | ('will' >> 'should'),
197 | ' ',
198 | ('come' >> 'expect it')
199 | )
200 | diff_by_word.should == @expected
201 | end
202 |
203 | it 'should handle complex string additions' do
204 | @to = 'the fresh daylight will surely come'
205 | @expected = diff('the ', +'fresh ', 'daylight will ', +'surely ', 'come')
206 | diff_by_word.should == @expected
207 | end
208 |
209 | it 'should handle complex string deletions' do
210 | @from = 'the fresh daylight will surely come'
211 | @expected = diff('the ', -'fresh ', 'daylight will ', -'surely ', 'come')
212 | diff_by_word.should == @expected
213 | end
214 |
215 | it 'should handle complex string changes' do
216 | @from = 'the fresh daylight will surely come'
217 | @to = 'something fresh will become surly'
218 | @expected = diff(
219 | ('the' >> 'something'),
220 | ' fresh ',
221 | -'daylight ',
222 | 'will ',
223 | ( 'surely' >> 'become'),
224 | ' ',
225 | ( 'come' >> 'surly' )
226 | )
227 | diff_by_word.should == @expected
228 | end
229 | end
230 |
231 | describe '#diff_by_line' do
232 | def diff_by_line
233 | Differ.send(:diff_by_line, @to, @from)
234 | end
235 |
236 | before(:each) do
237 | @to = @from = <<-HAIKU.gsub(/ +|\n\Z/, '')
238 | stallion sinks gently
239 | slowly, sleeplessly
240 | following harp flails
241 | HAIKU
242 | end
243 |
244 | it 'should hande no-change situations' do
245 | @expected = diff(@to)
246 | diff_by_line.should == @expected
247 | end
248 |
249 | it 'should handle prepends' do
250 | @to = <<-HAIKU.gsub(/ +|\n\Z/, '')
251 | A Haiku:
252 | stallion sinks gently
253 | slowly, sleeplessly
254 | following harp flails
255 | HAIKU
256 | @expected = diff(+"A Haiku:\n", @from)
257 | diff_by_line.should == @expected
258 | end
259 |
260 | it 'should handle appends' do
261 | @to = <<-HAIKU.gsub(/ +|\n\Z/, '')
262 | stallion sinks gently
263 | slowly, sleeplessly
264 | following harp flails
265 | -- http://everypoet.net
266 | HAIKU
267 | @expected = diff(@from, +"\n-- http://everypoet.net")
268 | diff_by_line.should == @expected
269 | end
270 |
271 | it 'should handle leading deletes' do
272 | @from = <<-HAIKU.gsub(/ +|\n\Z/, '')
273 | A Haiku:
274 | stallion sinks gently
275 | slowly, sleeplessly
276 | following harp flails
277 | HAIKU
278 | @expected = diff(-"A Haiku:\n", @to)
279 | diff_by_line.should == @expected
280 | end
281 |
282 | it 'should handle trailing deletes' do
283 | @from = <<-HAIKU.gsub(/ +|\n\Z/, '')
284 | stallion sinks gently
285 | slowly, sleeplessly
286 | following harp flails
287 | -- http://everypoet.net
288 | HAIKU
289 | @expected = diff(@to, -"\n-- http://everypoet.net")
290 | diff_by_line.should == @expected
291 | end
292 |
293 | it 'should handle simultaneous leading changes' do
294 | @to = <<-HAIKU.gsub(/ +|\n\Z/, '')
295 | stallion sings gently
296 | slowly, sleeplessly
297 | following harp flails
298 | HAIKU
299 | @expected = diff(
300 | ('stallion sinks gently' >> 'stallion sings gently'),
301 | "\nslowly, sleeplessly" <<
302 | "\nfollowing harp flails"
303 | )
304 | diff_by_line.should == @expected
305 | end
306 |
307 | it 'should handle simultaneous trailing changes' do
308 | @to = <<-HAIKU.gsub(/ +|\n\Z/, '')
309 | stallion sinks gently
310 | slowly, sleeplessly
311 | drifting ever on
312 | HAIKU
313 | @expected = diff(
314 | "stallion sinks gently\n" <<
315 | "slowly, sleeplessly\n",
316 | ('following harp flails' >> 'drifting ever on')
317 | )
318 | diff_by_line.should == @expected
319 | end
320 |
321 | it 'should handle full-string changes' do
322 | @to = <<-HAIKU.gsub(/ +|\n\Z/, '')
323 | glumly inert coals
324 | slumber lazily, shoulda
325 | used more Burma Shave
326 | HAIKU
327 | @expected = diff(@from >> @to)
328 | diff_by_line.should == @expected
329 | end
330 |
331 | it 'should handle complex string additions' do
332 | @to = <<-HAIKU.gsub(/ +|\n\Z/, '')
333 | A Haiku, with annotation:
334 | stallion sinks gently
335 | slowly, sleeplessly
336 | (flailing)
337 | following harp flails
338 | -- modified from source
339 | HAIKU
340 | @expected = diff(
341 | +"A Haiku, with annotation:\n",
342 | "stallion sinks gently\n" <<
343 | "slowly, sleeplessly\n",
344 | +"(flailing)\n",
345 | 'following harp flails',
346 | +"\n-- modified from source"
347 | )
348 | diff_by_line.should == @expected
349 | end
350 |
351 | it 'should handle complex string deletions' do
352 | @from = <<-HAIKU.gsub(/ +|\n\Z/, '')
353 | A Haiku, with annotation:
354 | stallion sinks gently
355 | slowly, sleeplessly
356 | (flailing)
357 | following harp flails
358 | -- modified from source
359 | HAIKU
360 | @expected = diff(
361 | -"A Haiku, with annotation:\n",
362 | "stallion sinks gently\n" <<
363 | "slowly, sleeplessly\n",
364 | -"(flailing)\n",
365 | 'following harp flails',
366 | -"\n-- modified from source"
367 | )
368 | diff_by_line.should == @expected
369 | end
370 |
371 | it 'should handle complex string changes' do
372 | @to = <<-HAIKU.gsub(/ +|\n\Z/, '')
373 | stallion sings gently
374 | slowly, sleeplessly
375 | (flailing)
376 | following harp flails
377 | -- modified from source
378 | HAIKU
379 | @expected = diff(
380 | ('stallion sinks gently' >> 'stallion sings gently'),
381 | "\nslowly, sleeplessly\n",
382 | +"(flailing)\n",
383 | 'following harp flails',
384 | +"\n-- modified from source"
385 | )
386 | diff_by_line.should == @expected
387 | end
388 | end
389 |
390 | describe '#diff (with arbitrary boundary)' do
391 | def diff_by_comma
392 | Differ.send(:diff, @to, @from, ', ')
393 | end
394 |
395 | before(:each) do
396 | @to = @from = 'alteration, asymmetry, a deviation'
397 | end
398 |
399 | it 'should hande no-change situations' do
400 | @expected = diff('alteration, asymmetry, a deviation')
401 | diff_by_comma.should == @expected
402 | end
403 |
404 | it 'should handle prepends' do
405 | @to = "aberration, alteration, asymmetry, a deviation"
406 | @expected = diff(+'aberration, ', 'alteration, asymmetry, a deviation')
407 | diff_by_comma.should == @expected
408 | end
409 |
410 | it 'should handle appends' do
411 | @to = "alteration, asymmetry, a deviation, change"
412 | @expected = diff('alteration, asymmetry, a deviation', +', change')
413 | diff_by_comma.should == @expected
414 | end
415 |
416 | it 'should handle leading deletes' do
417 | @to = 'asymmetry, a deviation'
418 | @expected = diff(-'alteration, ', 'asymmetry, a deviation')
419 | diff_by_comma.should == @expected
420 | end
421 |
422 | it 'should handle trailing deletes' do
423 | @to = 'alteration, asymmetry'
424 | @expected = diff('alteration, asymmetry', -', a deviation')
425 | diff_by_comma.should == @expected
426 | end
427 |
428 | it 'should handle simultaneous leading changes' do
429 | @to = 'aberration, asymmetry, a deviation'
430 | @expected = diff(('alteration' >> 'aberration'), ', asymmetry, a deviation')
431 | diff_by_comma.should == @expected
432 | end
433 |
434 | it 'should handle simultaneous trailing changes' do
435 | @to = 'alteration, asymmetry, change'
436 | @expected = diff('alteration, asymmetry, ', ('a deviation' >> 'change'))
437 | diff_by_comma.should == @expected
438 | end
439 |
440 | it 'should handle full-string changes' do
441 | @to = 'uniformity, unison, unity'
442 | @expected = diff(@from >> @to)
443 | diff_by_comma.should == @expected
444 | end
445 |
446 | it 'should handle complex string additions' do
447 | @to = 'aberration, alteration, anomaly, asymmetry, a deviation, change'
448 | @expected = diff(
449 | +'aberration, ',
450 | 'alteration, ',
451 | +'anomaly, ',
452 | 'asymmetry, a deviation',
453 | +', change'
454 | )
455 | diff_by_comma.should == @expected
456 | end
457 |
458 | it 'should handle complex string deletions' do
459 | @from = 'aberration, alteration, anomaly, asymmetry, a deviation, change'
460 | @expected = diff(
461 | -'aberration, ',
462 | 'alteration, ',
463 | -'anomaly, ',
464 | 'asymmetry, a deviation',
465 | -', change'
466 | )
467 | diff_by_comma.should == @expected
468 | end
469 |
470 | it 'should handle complex string changes' do
471 | @from = 'a, d, g, gh, x'
472 | @to = 'a, b, c, d, e, f, g, h, i, j'
473 | @expected = diff(
474 | 'a, ',
475 | +'b, c, ',
476 | 'd, ',
477 | +'e, f, ',
478 | 'g, ',
479 | ('gh, x' >> 'h, i, j')
480 | )
481 | diff_by_comma.should == @expected
482 | end
483 | end
484 |
485 | describe '#diff (with implied boundary)' do
486 | def diff_by_line
487 | Differ.send(:diff, @to, @from)
488 | end
489 |
490 | before(:each) do
491 | @to = @from = <<-HAIKU.gsub(/ +|\n\Z/, '')
492 | stallion sinks gently
493 | slowly, sleeplessly
494 | following harp flails
495 | HAIKU
496 | end
497 |
498 | it 'should do diffs by line' do
499 | @to = <<-HAIKU.gsub(/ +|\n\Z/, '')
500 | stallion sinks gently
501 | slowly, restlessly
502 | following harp flails
503 | HAIKU
504 | @expected = diff(
505 | "stallion sinks gently\n",
506 | ('slowly, sleeplessly' >> 'slowly, restlessly'),
507 | "\nfollowing harp flails"
508 | )
509 | diff_by_line.should == @expected
510 | end
511 | end
512 | end
513 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 |
3 | $LOAD_PATH.unshift(File.dirname(__FILE__))
4 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5 | require 'differ'
6 |
7 | Spec::Runner.configure do |config|
8 |
9 | end
10 |
11 | def diff(*parts)
12 | x = Differ::Diff.new
13 | x.instance_variable_set(:@raw, parts)
14 | return x
15 | end
16 |
17 | class String
18 | def +@
19 | Differ::Change.new(:insert => self)
20 | end
21 |
22 | def -@
23 | Differ::Change.new(:delete => self)
24 | end
25 |
26 | def >>(to)
27 | Differ::Change.new(:delete => self, :insert => to)
28 | end
29 | end
30 |
--------------------------------------------------------------------------------