codegrade 0.0.1

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.
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