codegrade 0.0.1

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
+ SHA1:
3
+ metadata.gz: e387a4ec843d880c7c20f2f251851de9797ad772
4
+ data.tar.gz: 6b22863716a4f1ea94f8c68020db34d32ddbb400
5
+ SHA512:
6
+ metadata.gz: 2c8d198ee979cf3d2a8b8851ea8269c3c6f8d41be20154ae39af2f17d2c8abb8dc53881d23d693acab4caabb589a76015708a2709440c7cb0f704a9ab0fb1a76
7
+ data.tar.gz: 123e9b9abfc205e094889c5bc6af56c810aa0c54fd9aaff6f81236a9851c7dc172333b6935cb9b07a78ab54313434fe44f001c4af21d66ba47740994f424e2a8
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Visuality
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # Codegrade
2
+
3
+ ## Requirements
4
+
5
+ CMake is required to build Rugged gem. On Mac you can install it with command:
6
+
7
+ brew install cmake
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/codegrade ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ $LOAD_PATH.unshift(File.dirname(File.realpath(__FILE__)) + '/../lib')
5
+
6
+ require 'codegrade'
7
+
8
+ offenses = Codegrade::Grader::Grader.new(Codegrade::Commit.new).grade
9
+ Codegrade::Formatter.new(offenses).print
data/codegrade.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'codegrade/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "codegrade"
8
+ spec.version = Codegrade::VERSION
9
+ spec.authors = ["Michał Młoźniak", "Karol Słuszniak"]
10
+ spec.email = ["contact@visuality.pl"]
11
+ spec.summary = %q{Grade your git commits}
12
+ spec.description = %q{This tool grades your commit messages and all changes files}
13
+ spec.homepage = "http://www.visuality.pl"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency('bundler', '~> 1.7')
22
+ spec.add_development_dependency('rake', '~> 10.0')
23
+ spec.add_development_dependency('rspec', '~> 3.1')
24
+ spec.add_runtime_dependency('rubocop', '~> 0.27')
25
+ spec.add_runtime_dependency('rugged', '~> 0.21')
26
+ end
data/lib/codegrade.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'ostruct'
2
+ require 'rubocop'
3
+ require 'rugged'
4
+
5
+ require 'codegrade/commit'
6
+ require 'codegrade/formatter'
7
+ require 'codegrade/offense'
8
+ require 'codegrade/grader/commit_message'
9
+ require 'codegrade/grader/grader'
10
+ require 'codegrade/grader/rubocop'
@@ -0,0 +1,43 @@
1
+ module Codegrade
2
+ class Commit
3
+ attr_reader :sha, :working_directory
4
+
5
+ def initialize(working_directory = '.', sha = nil)
6
+ @working_directory = File.expand_path(working_directory)
7
+ @sha = sha
8
+ end
9
+
10
+ def author
11
+ commit.author
12
+ end
13
+
14
+ def message
15
+ commit.message
16
+ end
17
+
18
+ def files
19
+ parse_git_tree(commit.tree, working_directory)
20
+ end
21
+
22
+ private
23
+
24
+ def repo
25
+ @repo ||= Rugged::Repository.new(working_directory)
26
+ end
27
+
28
+ def commit
29
+ @commit ||= sha.nil? ? repo.last_commit : repo.lookup(sha)
30
+ end
31
+
32
+ def parse_git_tree(tree, path)
33
+ files = []
34
+
35
+ tree.walk_blobs do |root, entry|
36
+ path = File.expand_path(root, working_directory)
37
+ files.push(File.join(path, entry[:name]))
38
+ end
39
+
40
+ files
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,35 @@
1
+ module Codegrade
2
+ class Formatter
3
+ def initialize(offenses)
4
+ @offenses = offenses
5
+ end
6
+
7
+ def print
8
+ group_by_categories
9
+ working_directory = File.expand_path('.')
10
+
11
+ @categories.each do |category, offenses|
12
+ puts "#{category} (#{offenses.size}):"
13
+ puts
14
+
15
+ offenses.each do |offense|
16
+ file = offense.file.gsub(working_directory, '')[1..-1]
17
+ puts "- #{file}:#{offense.line_number}:#{offense.column_number}"
18
+ end
19
+
20
+ puts
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def group_by_categories
27
+ @categories = Hash.new
28
+
29
+ @offenses.each do |offense|
30
+ @categories[offense.category] ||= []
31
+ @categories[offense.category].push(offense)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,236 @@
1
+ module Codegrade
2
+ module Grader
3
+ class CommitMessage
4
+ attr_reader :offenses
5
+
6
+ def initialize(message)
7
+ @message = message
8
+ end
9
+
10
+ def grade
11
+ clear_offenses
12
+ parse_commit_message
13
+ end
14
+
15
+ private
16
+
17
+ def clear_offenses
18
+ @offenses = []
19
+ end
20
+
21
+ def parse_commit_message
22
+ lines = @message.split("\n")
23
+
24
+ paragraph, paragraph_start, paragraph_line = [], 1, 0
25
+ inside_punctation = false
26
+
27
+ lines.each_with_index do |line, index|
28
+ paragraph << line
29
+ paragraph_line += 1
30
+
31
+ line_number = index + 1
32
+ blank_line = blank?(line)
33
+ title_line = line_number == 1
34
+ end_of_paragraph = (index == lines.length - 1 ||
35
+ blank?(lines[index + 1])) && !title_line &&
36
+ paragraph.any? && !blank?(paragraph.last)
37
+
38
+ if line.start_with?('* ')
39
+ check_punctation_no_separating_line(inside_punctation, line_number)
40
+
41
+ inside_punctation = true
42
+ end
43
+
44
+ if title_line
45
+ check_title_leading_lowercase(line, line_number)
46
+ check_title_too_long(line, line_number)
47
+ check_title_trailing_dot(line, line_number)
48
+ end
49
+
50
+ if end_of_paragraph
51
+ if inside_punctation
52
+ check_punctation_leading_lowercase(paragraph, paragraph_start)
53
+ check_punctation_trailing_dot(paragraph, paragraph_start)
54
+ else
55
+ check_paragraph_leading_lowercase(paragraph, paragraph_start)
56
+ check_paragraph_no_trailing_dot(paragraph, paragraph_start)
57
+ end
58
+ end
59
+
60
+ if blank_line
61
+ check_redundant_empty_line(paragraph, line_number)
62
+
63
+ paragraph, paragraph_start, paragraph_line = [], line_number + 1, 0
64
+ inside_punctation = false
65
+ else
66
+ check_title_multiple_lines(line_number)
67
+
68
+ check_line_trailing_whitespace(line, line_number)
69
+ check_line_too_long(line, line_number) unless title_line
70
+ end
71
+
72
+ if inside_punctation
73
+ if paragraph_line > 1
74
+ check_punctation_leading_whitespace(line, line_number)
75
+ end
76
+ else
77
+ if ! blank_line
78
+ check_line_leading_whitespace(line, line_number)
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ def check_title_leading_lowercase(line, line_number)
85
+ if line[0].downcase == line[0]
86
+ add_offense(
87
+ :category => 'title_leading_lowercase',
88
+ :line_number => line_number,
89
+ :column_number => 1)
90
+ end
91
+ end
92
+
93
+ def check_title_too_long(line, line_number)
94
+ if line.length > 50
95
+ add_offense(
96
+ :category => 'title_too_long',
97
+ :line_number => line_number,
98
+ :column_number => 51)
99
+ end
100
+ end
101
+
102
+ def check_title_trailing_dot(line, line_number)
103
+ if (m = line.match(/\.\s*$/))
104
+ add_offense(
105
+ :category => 'title_trailing_dot',
106
+ :line_number => line_number,
107
+ :column_number => m.begin(0))
108
+ end
109
+ end
110
+
111
+ def check_title_multiple_lines(line_number)
112
+ if line_number == 2
113
+ add_offense(
114
+ :category => 'title_multiple_lines',
115
+ :line_number => line_number)
116
+ end
117
+ end
118
+
119
+ def check_redundant_empty_line(paragraph, line_number)
120
+ if paragraph.select { |line| !blank?(line) }.empty?
121
+ add_offense(
122
+ :category => 'redundant_empty_line',
123
+ :line_number => line_number)
124
+ end
125
+ end
126
+
127
+ def check_line_trailing_whitespace(line, line_number)
128
+ if (m = line.match(/\s+$/))
129
+ add_offense(
130
+ :category => 'line_trailing_whitespace',
131
+ :line_number => line_number,
132
+ :column_number => m.begin(0) + 1)
133
+ end
134
+ end
135
+
136
+ def check_line_leading_whitespace(line, line_number)
137
+ if (m = line.match(/^\s+/))
138
+ add_offense(
139
+ :category => 'line_leading_whitespace',
140
+ :line_number => line_number,
141
+ :column_number => m.end(0))
142
+ end
143
+ end
144
+
145
+ def check_line_too_long(line, line_number)
146
+ if line.length > 70
147
+ add_offense(
148
+ :category => 'line_too_long',
149
+ :line_number => line_number,
150
+ :column_number => 71)
151
+ end
152
+ end
153
+
154
+ def check_paragraph_leading_lowercase(paragraph, paragraph_start)
155
+ line = strip(paragraph.first)
156
+
157
+ if line[0].downcase == line[0] &&
158
+ !link?(line.split[0])
159
+ add_offense(
160
+ :category => 'paragraph_leading_lowercase',
161
+ :line_number => paragraph_start,
162
+ :column_number => 1)
163
+ end
164
+ end
165
+
166
+ def check_paragraph_no_trailing_dot(paragraph, paragraph_start)
167
+ line = strip(paragraph.last)
168
+
169
+ if line[-1] != '.' &&
170
+ !link?(line.split[-1])
171
+ add_offense(
172
+ :category => 'paragraph_no_trailing_dot',
173
+ :line_number => paragraph_start + paragraph.length - 1,
174
+ :column_number => paragraph.last.length)
175
+ end
176
+ end
177
+
178
+ def check_punctation_no_separating_line(inside_punctation, line_number)
179
+ if inside_punctation
180
+ add_offense(
181
+ :category => 'punctation_no_separating_line',
182
+ :line_number => line_number)
183
+ end
184
+ end
185
+
186
+ def check_punctation_leading_whitespace(line, line_number)
187
+ if (m = line.match(/^\s*/)) && m.end(0) != 2
188
+ add_offense(
189
+ :category => 'punctation_leading_whitespace',
190
+ :line_number => line_number,
191
+ :column_number => m.end(0))
192
+ end
193
+ end
194
+
195
+ def check_punctation_leading_lowercase(paragraph, paragraph_start)
196
+ line = strip(paragraph.first)
197
+
198
+ if line[2].downcase == line[2] &&
199
+ !link?(line.split[0])
200
+ add_offense(
201
+ :category => 'punctation_leading_lowercase',
202
+ :line_number => paragraph_start,
203
+ :column_number => 3)
204
+ end
205
+ end
206
+
207
+ def check_punctation_trailing_dot(paragraph, paragraph_start)
208
+ line = strip(paragraph.last)
209
+
210
+ if line[-1] == '.' &&
211
+ !link?(line.split[-1])
212
+ add_offense(
213
+ :category => 'punctation_trailing_dot',
214
+ :line_number => paragraph_start + paragraph.length - 1,
215
+ :column_number => paragraph.last.length)
216
+ end
217
+ end
218
+
219
+ def add_offense(params)
220
+ @offenses << Codegrade::Offense.new(params)
221
+ end
222
+
223
+ def strip(text)
224
+ text.match(/(\S.*\S)/).to_s
225
+ end
226
+
227
+ def blank?(text)
228
+ text.match(/^\s*$/)
229
+ end
230
+
231
+ def link?(text)
232
+ text.include?('://') || text.start_with?('www.')
233
+ end
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,30 @@
1
+ module Codegrade
2
+ module Grader
3
+ class Grader
4
+ attr_reader :commit
5
+
6
+ def initialize(commit)
7
+ @commit = commit
8
+ end
9
+
10
+ def grade
11
+ offenses = []
12
+
13
+ commit_message = CommitMessage.new(@commit.message)
14
+ commit_message.grade
15
+ offenses.concat(commit_message.offenses)
16
+ ruby_files.each do |file|
17
+ offenses.concat(Rubocop.new(file).grade)
18
+ end
19
+
20
+ offenses
21
+ end
22
+
23
+ private
24
+
25
+ def ruby_files
26
+ commit.files.select { |filename| File.extname(filename) == '.rb' }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ require 'rubocop'
2
+
3
+ module Codegrade
4
+ module Grader
5
+ class Rubocop
6
+ attr_reader :config_store, :file
7
+
8
+ def initialize(file)
9
+ @config_store = RuboCop::ConfigStore.new
10
+ @file = file
11
+ end
12
+
13
+ def grade
14
+ processed_file = RuboCop::ProcessedSource.from_file(file)
15
+ config = config_store.for(processed_file.path)
16
+ team = RuboCop::Cop::Team.new(classes, config, {})
17
+
18
+ team.inspect_file(processed_file).map do |rubocop_offense|
19
+ Offense.new(category: rubocop_offense.message,
20
+ line_number: rubocop_offense.line,
21
+ column_number: rubocop_offense.real_column,
22
+ file: file,
23
+ source: rubocop_offense.location.source)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def classes
30
+ skipped_cops = ['Style/AlignParameters']
31
+
32
+ RuboCop::Cop::Cop.all.reject do |cop|
33
+ cop.rails? || skipped_cops.include?(cop.cop_name)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ module Codegrade
2
+ class Offense < OpenStruct
3
+ def initialize(params)
4
+ params = OpenStruct.new(params)
5
+
6
+ super(
7
+ :category => params[:category],
8
+ :line_number => params[:line_number],
9
+ :column_number => params[:column_number],
10
+ :file => params[:file],
11
+ :source => params[:source]
12
+ )
13
+ end
14
+
15
+ def message
16
+ m = category.split('_').join(' ')
17
+ m = m[0].upcase + m[1..-1]
18
+
19
+ m
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module Codegrade
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,337 @@
1
+ require 'spec_helper'
2
+
3
+ describe Codegrade::Grader::CommitMessage do
4
+ def find_offense(grader, conditions = {})
5
+ grader.offenses.find do |error|
6
+ all_met = true
7
+
8
+ conditions.each do |key, value|
9
+ if error[key] != value
10
+ all_met = false
11
+ break
12
+ end
13
+ end
14
+
15
+ all_met
16
+ end
17
+ end
18
+
19
+ context 'proper reference commit' do
20
+ let(:message) do
21
+ "Implement perfect commit
22
+
23
+ This is some text. It can have sentences as it's a
24
+ regular paragraph. It's close to unlimited.
25
+
26
+ http://www.google.pl/
27
+
28
+ * This is a first punctor with multiple lines
29
+ and an useful content
30
+
31
+ * This is a second, with just one line
32
+
33
+ Ending comment here."
34
+ end
35
+
36
+ it 'returns no errors' do
37
+ grader = Codegrade::Grader::CommitMessage.new(message)
38
+ grader.grade
39
+
40
+ expect(grader.offenses).to be_empty
41
+ end
42
+ end
43
+
44
+ context 'title starting with small letter' do
45
+ let(:message) { "wrong commit" }
46
+
47
+ it 'returns relevant error' do
48
+ grader = Codegrade::Grader::CommitMessage.new(message)
49
+ grader.grade
50
+
51
+ expect(find_offense(grader,
52
+ :category => 'title_leading_lowercase',
53
+ :line_number => 1)).not_to be_nil
54
+ end
55
+ end
56
+
57
+ context 'title too long' do
58
+ let(:message) { "Wrong commit with more that 50 chars that is just wrong" }
59
+
60
+ it 'returns relevant error' do
61
+ grader = Codegrade::Grader::CommitMessage.new(message)
62
+ grader.grade
63
+
64
+ expect(grader.offenses.count).to eq 1
65
+ expect(find_offense(grader,
66
+ :category => 'title_too_long',
67
+ :line_number => 1)).not_to be_nil
68
+ end
69
+ end
70
+
71
+ context 'title ending with a dot' do
72
+ let(:message) { "Wrong commit with dot." }
73
+
74
+ it 'returns relevant error' do
75
+ grader = Codegrade::Grader::CommitMessage.new(message)
76
+ grader.grade
77
+
78
+ expect(grader.offenses.count).to eq 1
79
+ expect(find_offense(grader,
80
+ :category => 'title_trailing_dot',
81
+ :line_number => 1,
82
+ :column_number => 21)).not_to be_nil
83
+ end
84
+ end
85
+
86
+ context 'title with multiple lines' do
87
+ let(:message) { "Wrong commit with\ntitle in multiple lines." }
88
+
89
+ it 'returns relevant error' do
90
+ grader = Codegrade::Grader::CommitMessage.new(message)
91
+ grader.grade
92
+
93
+ expect(grader.offenses.count).to eq 1
94
+ expect(find_offense(grader,
95
+ :category => 'title_multiple_lines',
96
+ :line_number => 2)).not_to be_nil
97
+ end
98
+ end
99
+
100
+ context 'redundant empty lines' do
101
+ let(:message) do
102
+ "Commit
103
+
104
+
105
+ One redundant empty line above.\n \n
106
+ One more redundant blank line above.
107
+
108
+
109
+
110
+ Two here."
111
+ end
112
+
113
+ it 'returns relevant errors' do
114
+ grader = Codegrade::Grader::CommitMessage.new(message)
115
+ grader.grade
116
+
117
+ expect(grader.offenses.count).to eq 4
118
+ expect(find_offense(grader,
119
+ :category => 'redundant_empty_line',
120
+ :line_number => 3)).not_to be_nil
121
+ expect(find_offense(grader,
122
+ :category => 'redundant_empty_line',
123
+ :line_number => 6)).not_to be_nil
124
+ expect(find_offense(grader,
125
+ :category => 'redundant_empty_line',
126
+ :line_number => 9)).not_to be_nil
127
+ expect(find_offense(grader,
128
+ :category => 'redundant_empty_line',
129
+ :line_number => 10)).not_to be_nil
130
+ end
131
+ end
132
+
133
+ context 'line with trailing whitespace' do
134
+ let(:message) do
135
+ "Commit \n
136
+ Another line. \nLast line."
137
+ end
138
+
139
+ it 'returns relevant errors' do
140
+ grader = Codegrade::Grader::CommitMessage.new(message)
141
+ grader.grade
142
+
143
+ expect(grader.offenses.count).to eq 2
144
+ expect(find_offense(grader,
145
+ :category => 'line_trailing_whitespace',
146
+ :line_number => 1,
147
+ :column_number => 7)).not_to be_nil
148
+ expect(find_offense(grader,
149
+ :category => 'line_trailing_whitespace',
150
+ :line_number => 3,
151
+ :column_number => 14)).not_to be_nil
152
+ end
153
+ end
154
+
155
+ context 'line outside punctation with leading whitespace' do
156
+ let(:message) do
157
+ "Commit
158
+
159
+ Wrong indent.
160
+
161
+ Another
162
+ and one more.
163
+
164
+ * Punctation
165
+ OK"
166
+ end
167
+
168
+ it 'returns relevant errors' do
169
+ grader = Codegrade::Grader::CommitMessage.new(message)
170
+ grader.grade
171
+
172
+ expect(grader.offenses.count).to eq 3
173
+ expect(find_offense(grader,
174
+ :category => 'line_leading_whitespace',
175
+ :line_number => 3,
176
+ :column_number => 2)).not_to be_nil
177
+ expect(find_offense(grader,
178
+ :category => 'line_leading_whitespace',
179
+ :line_number => 5,
180
+ :column_number => 1)).not_to be_nil
181
+ expect(find_offense(grader,
182
+ :category => 'line_leading_whitespace',
183
+ :line_number => 6,
184
+ :column_number => 6)).not_to be_nil
185
+ end
186
+ end
187
+
188
+ context 'too long line outside of title' do
189
+ let(:message) do
190
+ "Commit
191
+
192
+ Another line that is longer than 70 characters and violates our sacred rules."
193
+ end
194
+
195
+ it 'returns relevant error' do
196
+ grader = Codegrade::Grader::CommitMessage.new(message)
197
+ grader.grade
198
+
199
+ expect(find_offense(grader,
200
+ :category => 'line_too_long',
201
+ :line_number => 3,
202
+ :column_number => 71)).not_to be_nil
203
+ end
204
+ end
205
+
206
+ context 'paragraph starting with small letter' do
207
+ let(:message) do
208
+ "Commit
209
+
210
+ wrong para.
211
+
212
+ Good.
213
+
214
+ wrong again."
215
+ end
216
+
217
+ it 'returns relevant errors' do
218
+ grader = Codegrade::Grader::CommitMessage.new(message)
219
+ grader.grade
220
+
221
+ expect(grader.offenses.count).to eq 3
222
+ expect(find_offense(grader,
223
+ :category => 'paragraph_leading_lowercase',
224
+ :line_number => 3)).not_to be_nil
225
+ expect(find_offense(grader,
226
+ :category => 'paragraph_leading_lowercase',
227
+ :line_number => 7)).not_to be_nil
228
+ end
229
+ end
230
+
231
+ context 'punctation with no separating line' do
232
+ let(:message) do
233
+ "Commit
234
+
235
+ * Punctation
236
+ OK
237
+
238
+ * Punctation
239
+ close 1
240
+ * Punctation
241
+ close 2"
242
+ end
243
+
244
+ it 'returns relevant errors' do
245
+ grader = Codegrade::Grader::CommitMessage.new(message)
246
+ grader.grade
247
+
248
+ expect(find_offense(grader,
249
+ :category => 'punctation_no_separating_line',
250
+ :line_number => 8)).not_to be_nil
251
+ end
252
+ end
253
+
254
+ context 'punctation with wrong leading whitespace' do
255
+ let(:message) do
256
+ "Commit
257
+
258
+ * Punctation
259
+ small
260
+
261
+ * Punctation
262
+ OK
263
+
264
+ * Punctation
265
+ big"
266
+ end
267
+
268
+ it 'returns relevant errors' do
269
+ grader = Codegrade::Grader::CommitMessage.new(message)
270
+ grader.grade
271
+
272
+ expect(find_offense(grader,
273
+ :category => 'punctation_leading_whitespace',
274
+ :line_number => 4,
275
+ :column_number => 0)).not_to be_nil
276
+ expect(find_offense(grader,
277
+ :category => 'punctation_leading_whitespace',
278
+ :line_number => 10,
279
+ :column_number => 3)).not_to be_nil
280
+ end
281
+ end
282
+
283
+ context 'punctation starting with small letter' do
284
+ let(:message) do
285
+ "Commit
286
+
287
+ * punctation
288
+
289
+ * Punctation
290
+
291
+ * xunctation"
292
+ end
293
+
294
+ it 'returns relevant errors' do
295
+ grader = Codegrade::Grader::CommitMessage.new(message)
296
+ grader.grade
297
+
298
+ expect(grader.offenses.count).to eq 2
299
+ expect(find_offense(grader,
300
+ :category => 'punctation_leading_lowercase',
301
+ :line_number => 3,
302
+ :column_number => 3)).not_to be_nil
303
+ expect(find_offense(grader,
304
+ :category => 'punctation_leading_lowercase',
305
+ :line_number => 7,
306
+ :column_number => 3)).not_to be_nil
307
+ end
308
+ end
309
+
310
+ context 'punctation ending with a dot' do
311
+ let(:message) do
312
+ "Commit
313
+
314
+ * Punctation.
315
+
316
+ * Punctation
317
+
318
+ * Punctation
319
+ end."
320
+ end
321
+
322
+ it 'returns relevant errors' do
323
+ grader = Codegrade::Grader::CommitMessage.new(message)
324
+ grader.grade
325
+
326
+ expect(grader.offenses.count).to eq 2
327
+ expect(find_offense(grader,
328
+ :category => 'punctation_trailing_dot',
329
+ :line_number => 3,
330
+ :column_number => 13)).not_to be_nil
331
+ expect(find_offense(grader,
332
+ :category => 'punctation_trailing_dot',
333
+ :line_number => 8,
334
+ :column_number => 6)).not_to be_nil
335
+ end
336
+ end
337
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Codegrade::Grader::Grader do
4
+ it 'combines all grades in one array' do
5
+ allow_any_instance_of(Codegrade::Grader::CommitMessage).to receive(:grade)
6
+ allow_any_instance_of(Codegrade::Grader::CommitMessage).to receive(:offenses).and_return([Codegrade::Offense.new(category: 'Commit message error')])
7
+ allow_any_instance_of(Codegrade::Grader::Rubocop).to receive(:grade).and_return([Codegrade::Offense.new(category: 'Ruby syntax error')])
8
+ commit = Codegrade::Commit.new
9
+ allow(commit).to receive(:message).and_return('Some commit message')
10
+ allow(commit).to receive(:files).and_return(['file.rb'])
11
+
12
+ offenses = Codegrade::Grader::Grader.new(commit).grade
13
+
14
+ expect(offenses.map(&:category)).to include('Commit message error')
15
+ expect(offenses.map(&:category)).to include('Ruby syntax error')
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+
4
+ describe Codegrade::Grader::Rubocop do
5
+ def make_tempfile(content)
6
+ file = Tempfile.new('file.rb')
7
+ file.write(content)
8
+ file.rewind
9
+
10
+ file
11
+ end
12
+
13
+ def close_tempfile(file)
14
+ file.close
15
+ file.unlink
16
+ end
17
+
18
+ it 'detects errors in ruby code' do
19
+ file = make_tempfile(<<-EOD)
20
+ class Test
21
+ puts 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry standard dummy text ever since the 1500s.'
22
+ end
23
+ EOD
24
+
25
+ offenses = Codegrade::Grader::Rubocop.new(file).grade
26
+ offense = offenses.detect { |o| o.category =~ /Line is too long/ }
27
+
28
+ expect(offense.category).to eq('Line is too long. [165/80]')
29
+ expect(offense.line_number).to eq(2)
30
+
31
+ close_tempfile(file)
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ require_relative '../lib/codegrade'
2
+
3
+ RSpec.configure do |config|
4
+ config.expect_with :rspec do |expectations|
5
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
6
+ end
7
+
8
+ config.mock_with :rspec do |mocks|
9
+ mocks.verify_partial_doubles = true
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: codegrade
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michał Młoźniak
8
+ - Karol Słuszniak
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-12-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.7'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.7'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.1'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.1'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rubocop
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '0.27'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.27'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rugged
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '0.21'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '0.21'
84
+ description: This tool grades your commit messages and all changes files
85
+ email:
86
+ - contact@visuality.pl
87
+ executables:
88
+ - codegrade
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - ".gitignore"
93
+ - ".rspec"
94
+ - Gemfile
95
+ - Gemfile.lock
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - bin/codegrade
100
+ - codegrade.gemspec
101
+ - lib/codegrade.rb
102
+ - lib/codegrade/commit.rb
103
+ - lib/codegrade/formatter.rb
104
+ - lib/codegrade/grader/commit_message.rb
105
+ - lib/codegrade/grader/grader.rb
106
+ - lib/codegrade/grader/rubocop.rb
107
+ - lib/codegrade/offense.rb
108
+ - lib/codegrade/version.rb
109
+ - spec/models/grader/commit_message_spec.rb
110
+ - spec/models/grader/grader_spec.rb
111
+ - spec/models/grader/rubocop_spec.rb
112
+ - spec/spec_helper.rb
113
+ homepage: http://www.visuality.pl
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.2.2
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: Grade your git commits
137
+ test_files:
138
+ - spec/models/grader/commit_message_spec.rb
139
+ - spec/models/grader/grader_spec.rb
140
+ - spec/models/grader/rubocop_spec.rb
141
+ - spec/spec_helper.rb