unified 1.0.0

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.
Files changed (39) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +33 -0
  6. data/Rakefile +1 -0
  7. data/lib/unified.rb +8 -0
  8. data/lib/unified/chunk.rb +88 -0
  9. data/lib/unified/diff.rb +74 -0
  10. data/lib/unified/line.rb +21 -0
  11. data/lib/unified/parser.rb +53 -0
  12. data/lib/unified/transformer.rb +25 -0
  13. data/lib/unified/version.rb +3 -0
  14. data/spec/chunk_spec.rb +134 -0
  15. data/spec/diff_spec.rb +203 -0
  16. data/spec/examples/invalid/no_chunk_header.diff +10 -0
  17. data/spec/examples/invalid/no_content.diff +0 -0
  18. data/spec/examples/invalid/no_modifications.diff +11 -0
  19. data/spec/examples/invalid/no_modified_file_header.diff +6 -0
  20. data/spec/examples/invalid/no_original_file_header.diff +6 -0
  21. data/spec/examples/valid/.DS_Store +0 -0
  22. data/spec/examples/valid/commit_revision.diff +5 -0
  23. data/spec/examples/valid/modified_filename.diff +7 -0
  24. data/spec/examples/valid/more_additions.diff +7 -0
  25. data/spec/examples/valid/more_deletions.diff +10 -0
  26. data/spec/examples/valid/multi_chunk.diff +19 -0
  27. data/spec/examples/valid/new_file.diff +8 -0
  28. data/spec/examples/valid/no_newline.diff +29 -0
  29. data/spec/examples/valid/null_git.diff +8 -0
  30. data/spec/examples/valid/removed_file.diff +8 -0
  31. data/spec/examples/valid/simple.diff +11 -0
  32. data/spec/examples/valid/simple_git.diff +29 -0
  33. data/spec/line_spec.rb +45 -0
  34. data/spec/parser_spec.rb +344 -0
  35. data/spec/spec_helper.rb +21 -0
  36. data/spec/support/matchers/parse_matchers.rb +53 -0
  37. data/spec/transformer_spec.rb +82 -0
  38. data/unified.gemspec +26 -0
  39. metadata +173 -0
@@ -0,0 +1,203 @@
1
+ require "unified/diff"
2
+ require "spec_helper"
3
+
4
+ describe "Unified::Diff" do
5
+
6
+ def lines
7
+ [" Line 1", "+Line 2", "-Line 3"]
8
+ end
9
+
10
+ def chunk(orig = 1, mod = 1, lines = lines)
11
+ Unified::Chunk.new(original: orig, modified: mod, lines: lines, section_header: "A header")
12
+ end
13
+
14
+ def diff(lines = lines)
15
+ Unified::Diff.new original_filename: "Original", modified_filename: "Modified",
16
+ original_revision: "OrigRev", modified_revision: "ModRev",
17
+ chunks: [chunk(1, 1, lines), chunk(25, 25, lines), chunk(50, 50, lines)]
18
+ end
19
+
20
+ describe ".parse!" do
21
+ it "parses all valid diff files" do
22
+ valid_diffs.each do |content|
23
+ expect { Unified::Diff.parse!(content) }.not_to raise_error
24
+ end
25
+ end
26
+
27
+ it "returns a Unified::Diff object" do
28
+ valid_diffs.each do |content|
29
+ expect(Unified::Diff.parse!(content)).to be_a_kind_of Unified::Diff
30
+ end
31
+ end
32
+
33
+ it "raises a Unified::ParseError for invalid diffs" do
34
+ invalid_diffs.each do |content|
35
+ expect { Unified::Diff.parse!(content) }.to raise_error(Unified::ParseError)
36
+ end
37
+ end
38
+
39
+ it "provides a line number and character for invalid diffs" do
40
+ invalid_diffs.each do |content|
41
+ begin
42
+ Unified::Diff.parse!(content)
43
+ rescue Unified::ParseError => e
44
+ e.message.should match /line \d+/
45
+ e.message.should match /char \d+/
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#original_filename" do
52
+ it "returns the correct value" do
53
+ diff.original_filename.should == "Original"
54
+ end
55
+ end
56
+
57
+ describe "#modified_filename" do
58
+ it "returns the correct value" do
59
+ diff.modified_filename.should == "Modified"
60
+ end
61
+ end
62
+
63
+ describe "#original_revision" do
64
+ it "returns the correct value" do
65
+ diff.original_revision.should == "OrigRev"
66
+ end
67
+ end
68
+
69
+ describe "#modified_revision" do
70
+ it "returns the correct value" do
71
+ diff.modified_revision.should == "ModRev"
72
+ end
73
+ end
74
+
75
+ describe "#chunks" do
76
+ it "returns the correct number of chunks" do
77
+ diff.chunks.size.should == 3
78
+ end
79
+ it "returns chunk objects" do
80
+ diff.chunks.each do |chunk|
81
+ chunk.should be_a_kind_of Unified::Chunk
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "#number_of_added_lines" do
87
+ it "returns the correct value" do
88
+ diff.number_of_added_lines.should == 3
89
+ end
90
+ end
91
+
92
+ describe "#number_of_deleted_lines" do
93
+ it "returns the correct value" do
94
+ diff.number_of_deleted_lines.should == 3
95
+ end
96
+ end
97
+
98
+ describe "#number_of_unchanged_lines" do
99
+ it "returns the correct value" do
100
+ diff.number_of_unchanged_lines.should == 3
101
+ end
102
+ end
103
+
104
+ describe "#number_of_modified_lines" do
105
+ it "returns the number of additions plus the number of deletions" do
106
+ diff.number_of_modified_lines.should == 6
107
+ end
108
+ end
109
+
110
+ describe "#total_number_of_lines" do
111
+ it "returns the total number of lines" do
112
+ diff.total_number_of_lines.should == 9
113
+ end
114
+ end
115
+
116
+ describe "#proportion_of_deleted_lines" do
117
+ it "returns 0 when there are no deleted lines" do
118
+ lines = [" Line 1", "+Line 2"]
119
+ diff(lines).proportion_of_deleted_lines.should == 0
120
+ end
121
+
122
+ it "returns 100 when there are only deleted lines" do
123
+ lines = ["-Line 1", "-Line 2"]
124
+ diff(lines).proportion_of_deleted_lines.should == 100
125
+ end
126
+
127
+ it "returns 50 when there are equal added and deleted lines" do
128
+ lines = ["-Line 1", "+Line 2", " Line 3"]
129
+ diff(lines).proportion_of_deleted_lines.should == 50
130
+ end
131
+
132
+ it "rounds to the nearest integer when the lines are not evenly divisible" do
133
+ lines = ["-Line 1", "-Line 2", "+Line 3"]
134
+ diff(lines).proportion_of_deleted_lines.should == 67
135
+ end
136
+ end
137
+
138
+ describe "#proportion_of_added_lines" do
139
+ it "returns 0 when there are no added lines" do
140
+ lines = [" Line 1", "-Line 2"]
141
+ diff(lines).proportion_of_added_lines.should == 0
142
+ end
143
+
144
+ it "returns 100 when there are only added lines" do
145
+ lines = ["+Line 1", "+Line 2"]
146
+ diff(lines).proportion_of_added_lines.should == 100
147
+ end
148
+
149
+ it "returns 50 when there are equal added and added lines" do
150
+ lines = ["-Line 1", "+Line 2", " Line 3"]
151
+ diff(lines).proportion_of_added_lines.should == 50
152
+ end
153
+
154
+ it "rounds to the nearest integer when the lines are not evenly divisible" do
155
+ lines = ["-Line 1", "-Line 2", "+Line 3"]
156
+ diff(lines).proportion_of_added_lines.should == 33
157
+ end
158
+ end
159
+
160
+ describe "#each_chunk" do
161
+ it "iterates once for every chunk" do
162
+ index = 0
163
+ diff.each_chunk do
164
+ index += 1
165
+ end
166
+
167
+ index.should == 3
168
+ end
169
+
170
+ it "passes a chunk to the block" do
171
+ diff.each_chunk do |chunk|
172
+ expect(chunk).to be_a_kind_of Unified::Chunk
173
+ end
174
+ end
175
+ end
176
+
177
+ describe "#header" do
178
+ it "returns a valid diff header" do
179
+ diff.header.should == "--- Original\tOrigRev\n+++ Modified\tModRev"
180
+ end
181
+ end
182
+
183
+ describe "#to_s" do
184
+ it "returns the full diff output" do
185
+ diff.to_s.should == <<-EOF.strip
186
+ --- Original\tOrigRev
187
+ +++ Modified\tModRev
188
+ @@ -1,2 +1,2 @@ A header
189
+ Line 1
190
+ +Line 2
191
+ -Line 3
192
+ @@ -25,2 +25,2 @@ A header
193
+ Line 1
194
+ +Line 2
195
+ -Line 3
196
+ @@ -50,2 +50,2 @@ A header
197
+ Line 1
198
+ +Line 2
199
+ -Line 3
200
+ EOF
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,10 @@
1
+ --- Original 2013-03-23 21:06:27.000000000 +0000
2
+ +++ Original 2013-03-23 21:06:48.000000000 +0000
3
+ Line 1
4
+ Line 2
5
+ Line 3
6
+ -Line 5
7
+ -Line 6
8
+ -Line 7
9
+ +Line 4
10
+ +Line 5
File without changes
@@ -0,0 +1,11 @@
1
+ --- Original 2013-03-23 21:06:27.000000000 +0000
2
+ +++ Original 2013-03-23 21:06:48.000000000 +0000
3
+ @@ -1,4 +1,4 @@
4
+ Line 1
5
+ Line 2
6
+ Line 3
7
+ Line 5
8
+ Line 6
9
+ Line 7
10
+ Line 4
11
+ Line 5
@@ -0,0 +1,6 @@
1
+ --- Original 2013-03-23 21:06:48.000000000 +0000
2
+ @@ -1,4 +1,4 @@
3
+ -Line1
4
+ +Line 1
5
+ -Line 3
6
+ -Line 4
@@ -0,0 +1,6 @@
1
+ +++ Modified 2013-03-23 21:06:48.000000000 +0000
2
+ @@ -1,4 +1,4 @@
3
+ -Line1
4
+ +Line 1
5
+ -Line 3
6
+ -Line 4
@@ -0,0 +1,5 @@
1
+ --- File1 (revision 815)
2
+ +++ File1a (working copy)
3
+ @@ -1 +1 @@
4
+ -testing1
5
+ +tester1
@@ -0,0 +1,7 @@
1
+ --- Original 2013-03-23 21:06:27.000000000 +0000
2
+ +++ Modified 2013-03-23 21:06:48.000000000 +0000
3
+ @@ -1,4 +1,4 @@
4
+ -Line1
5
+ +Line 1
6
+ -Line 3
7
+ -Line 4
@@ -0,0 +1,7 @@
1
+ --- File1 (revision 815)
2
+ +++ File1a (working copy)
3
+ @@ -1,4 +1,4 @@
4
+ -testing1
5
+ +tester1
6
+ +One
7
+ +Two
@@ -0,0 +1,10 @@
1
+ --- Original 2013-03-23 21:06:27.000000000 +0000
2
+ +++ Original 2013-03-23 21:06:48.000000000 +0000
3
+ @@ -1,6 +1,4 @@
4
+ Line 1
5
+ Line 2
6
+ Line 3
7
+ -Line 5
8
+ -Line 6
9
+ -Line 7
10
+ +Line 4
@@ -0,0 +1,19 @@
1
+ --- multi_chunk 2013-03-24 14:38:43.000000000 +0000
2
+ +++ multi_chunk 2013-03-24 14:38:52.000000000 +0000
3
+ @@ -1,7 +1,7 @@
4
+ Line 1
5
+ Line 2
6
+ Line 3
7
+ -Line4
8
+ +Line 4
9
+ Line 5
10
+ Line 6
11
+ Line 7
12
+ @@ -15,6 +15,6 @@
13
+ Line 15
14
+ Line 16
15
+ Line 17
16
+ -Line18
17
+ +Line 18
18
+ Line 19
19
+ Line 20
@@ -0,0 +1,8 @@
1
+ --- spec/factories/diffs/new_file.diff 2013-03-30 21:32:55.000000000 +0000
2
+ +++ spec/factories/diffs/new_file.diff 2013-03-30 21:32:44.000000000 +0000
3
+ @@ -0,0 +1,5 @@
4
+ +This is a new file
5
+ +And these are
6
+ +all
7
+ +new
8
+ +lines
@@ -0,0 +1,29 @@
1
+ --- a/app/controllers/api/v1/code_reviews_controller.rb
2
+ +++ b/app/controllers/api/v1/code_reviews_controller.rb
3
+ @@ -1,24 +1,24 @@
4
+ module Api
5
+ module V1
6
+ class CodeReviewsController < ApplicationController
7
+ respond_to :json
8
+
9
+ def create
10
+ @review = CodeReview.create raw: params[:content]
11
+ render :json => {
12
+ url: code_review_path(@review),
13
+ expires_at: @review.expires_at
14
+ }
15
+ end
16
+
17
+ def show
18
+ - @review = CodeReview.not_expired.find_by_secure_hash! params[:id]
19
+ + @review = CodeReview.not_expired.find_by_token! params[:id]
20
+ sleep 0.5 and raise ActiveRecord::RecordNotFound if @review.nil?
21
+ render :json => {
22
+ content: @review.raw,
23
+ expires_at: @review.expires_at
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,8 @@
1
+ --- /dev/null
2
+ +++ /a/new/file
3
+ @@ -0,0 +1,5 @@
4
+ +This is a new file
5
+ +And these are
6
+ +all
7
+ +new
8
+ +lines
@@ -0,0 +1,8 @@
1
+ --- spec/factories/diffs/new_file.diff 2013-03-31 13:56:25.000000000 +0100
2
+ +++ spec/factories/diffs/new_file.diff 2013-03-31 14:05:49.000000000 +0100
3
+ @@ -1,5 +0,0 @@
4
+ -This is a removed file
5
+ -And these are
6
+ -all
7
+ -removed
8
+ -lines
@@ -0,0 +1,11 @@
1
+ --- Original 2013-03-23 21:06:27.000000000 +0000
2
+ +++ Original 2013-03-23 21:06:48.000000000 +0000
3
+ @@ -1,6 +1,5 @@ A section heading
4
+ Line 1
5
+ Line 2
6
+ Line 3
7
+ -Line 5
8
+ -Line 6
9
+ -Line 7
10
+ +Line 4
11
+ +Line 5
@@ -0,0 +1,29 @@
1
+ --- a/app/controllers/api/v1/code_reviews_controller.rb
2
+ +++ b/app/controllers/api/v1/code_reviews_controller.rb
3
+ @@ -1,24 +1,24 @@
4
+ module Api
5
+ module V1
6
+ class CodeReviewsController < ApplicationController
7
+ respond_to :json
8
+
9
+ def create
10
+ @review = CodeReview.create raw: params[:content]
11
+ render :json => {
12
+ url: code_review_path(@review),
13
+ expires_at: @review.expires_at
14
+ }
15
+ end
16
+
17
+ def show
18
+ - @review = CodeReview.not_expired.find_by_secure_hash! params[:id]
19
+ + @review = CodeReview.not_expired.find_by_token! params[:id]
20
+ sleep 0.5 and raise ActiveRecord::RecordNotFound if @review.nil?
21
+ render :json => {
22
+ content: @review.raw,
23
+ expires_at: @review.expires_at
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,45 @@
1
+ require 'unified/line'
2
+
3
+ describe "Unified::Line" do
4
+ it "extends from String" do
5
+ line = Unified::Line.new "A line"
6
+ line.should respond_to(:gsub)
7
+ line.should respond_to(:size)
8
+ end
9
+
10
+ context "additional methods" do
11
+ let(:addition) { Unified::Line.new "+An addition" }
12
+ let(:deletion) { Unified::Line.new "-A deletion" }
13
+ let(:unchanged) { Unified::Line.new " Unchanged" }
14
+
15
+ describe "#addition?" do
16
+ it "is true when a line begins with '+'" do
17
+ addition.addition?.should be_true
18
+ end
19
+ it "is false when a line doesn't begin with '-'" do
20
+ deletion.addition?.should be_false
21
+ unchanged.addition?.should be_false
22
+ end
23
+ end
24
+
25
+ describe "#deletion?" do
26
+ it "is true when a line begins with '-'" do
27
+ deletion.deletion?.should be_true
28
+ end
29
+ it "is false when a line doesn't begin with '-'" do
30
+ addition.deletion?.should be_false
31
+ unchanged.deletion?.should be_false
32
+ end
33
+ end
34
+
35
+ describe "#unchanged?" do
36
+ it "is true when a line begins with ' '" do
37
+ unchanged.unchanged?.should be_true
38
+ end
39
+ it "is false when a line doesn't begin with ' '" do
40
+ addition.unchanged?.should be_false
41
+ deletion.unchanged?.should be_false
42
+ end
43
+ end
44
+ end
45
+ end