cane 1.4.0 → 2.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.
- data/HISTORY.md +12 -0
- data/README.md +9 -25
- data/lib/cane.rb +7 -18
- data/lib/cane/abc_check.rb +85 -22
- data/lib/cane/cli.rb +3 -5
- data/lib/cane/cli/spec.rb +59 -53
- data/lib/cane/doc_check.rb +30 -16
- data/lib/cane/encoding_aware_iterator.rb +35 -0
- data/lib/cane/file.rb +22 -0
- data/lib/cane/rake_task.rb +12 -41
- data/lib/cane/style_check.rb +36 -8
- data/lib/cane/threshold_check.rb +47 -22
- data/lib/cane/version.rb +1 -1
- data/lib/cane/violation_formatter.rb +33 -25
- data/spec/abc_check_spec.rb +53 -21
- data/spec/cane_spec.rb +21 -12
- data/spec/doc_check_spec.rb +30 -9
- data/spec/encoding_aware_iterator_spec.rb +24 -0
- data/spec/rake_task_spec.rb +13 -0
- data/spec/style_check_spec.rb +10 -4
- data/spec/threshold_check_spec.rb +4 -3
- data/spec/violation_formatter_spec.rb +4 -5
- metadata +8 -7
- data/lib/cane/abc_max_violation.rb +0 -15
- data/lib/cane/cli/translator.rb +0 -71
- data/lib/cane/style_violation.rb +0 -10
- data/lib/cane/syntax_violation.rb +0 -20
- data/lib/cane/threshold_violation.rb +0 -15
data/spec/abc_check_spec.rb
CHANGED
@@ -3,6 +3,10 @@ require 'spec_helper'
|
|
3
3
|
require 'cane/abc_check'
|
4
4
|
|
5
5
|
describe Cane::AbcCheck do
|
6
|
+
def check(file_name, opts = {})
|
7
|
+
described_class.new(opts.merge(abc_glob: file_name))
|
8
|
+
end
|
9
|
+
|
6
10
|
it 'creates an AbcMaxViolation for each method above the threshold' do
|
7
11
|
file_name = make_file(<<-RUBY)
|
8
12
|
class Harness
|
@@ -17,10 +21,10 @@ describe Cane::AbcCheck do
|
|
17
21
|
end
|
18
22
|
RUBY
|
19
23
|
|
20
|
-
violations =
|
24
|
+
violations = check(file_name, abc_max: 1).violations
|
21
25
|
violations.length.should == 1
|
22
|
-
violations[0].should
|
23
|
-
|
26
|
+
violations[0].values_at(:file, :label, :value).should ==
|
27
|
+
[file_name, "Harness#complex_method", 2]
|
24
28
|
end
|
25
29
|
|
26
30
|
it 'sorts violations by complexity' do
|
@@ -37,22 +41,21 @@ describe Cane::AbcCheck do
|
|
37
41
|
end
|
38
42
|
RUBY
|
39
43
|
|
40
|
-
violations =
|
44
|
+
violations = check(file_name, abc_max: 0).violations
|
41
45
|
violations.length.should == 2
|
42
|
-
complexities = violations.map
|
46
|
+
complexities = violations.map {|x| x[:value] }
|
43
47
|
complexities.should == complexities.sort.reverse
|
44
48
|
end
|
45
49
|
|
46
|
-
it 'creates a
|
50
|
+
it 'creates a violation when code cannot be parsed' do
|
47
51
|
file_name = make_file(<<-RUBY)
|
48
52
|
class Harness
|
49
53
|
RUBY
|
50
54
|
|
51
|
-
violations =
|
55
|
+
violations = check(file_name).violations
|
52
56
|
violations.length.should == 1
|
53
|
-
violations[0].should
|
54
|
-
violations[0].
|
55
|
-
violations[0].description.should be_instance_of(String)
|
57
|
+
violations[0][:file].should == file_name
|
58
|
+
violations[0][:description].should be_instance_of(String)
|
56
59
|
end
|
57
60
|
|
58
61
|
it 'skips declared exclusions' do
|
@@ -84,33 +87,62 @@ describe Cane::AbcCheck do
|
|
84
87
|
|
85
88
|
exclusions = %w[ Harness#instance_meth Harness.class_meth
|
86
89
|
Harness::Nested#i_meth Harness::Nested.c_meth ]
|
87
|
-
violations =
|
88
|
-
|
90
|
+
violations = check(file_name,
|
91
|
+
abc_max: 0,
|
92
|
+
abc_exclude: exclusions
|
93
|
+
).violations
|
89
94
|
violations.length.should == 1
|
90
|
-
violations[0].should
|
91
|
-
|
92
|
-
|
95
|
+
violations[0].values_at(:file, :label, :value).should ==
|
96
|
+
[file_name, "Harness::Nested#other_meth", 1]
|
97
|
+
end
|
98
|
+
|
99
|
+
it "creates an AbcMaxViolation for method in assigned anonymous class" do
|
100
|
+
file_name = make_file(<<-RUBY)
|
101
|
+
MyClass = Struct.new(:foo) do
|
102
|
+
def test_method(a)
|
103
|
+
b = a
|
104
|
+
return b if b > 3
|
105
|
+
end
|
106
|
+
end
|
107
|
+
RUBY
|
108
|
+
|
109
|
+
violations = check(file_name, abc_max: 1).violations
|
110
|
+
violations[0][:label] == "MyClass#test_method"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "creates an AbcMaxViolation for method in anonymous class" do
|
114
|
+
file_name = make_file(<<-RUBY)
|
115
|
+
Class.new do
|
116
|
+
def test_method(a)
|
117
|
+
b = a
|
118
|
+
return b if b > 3
|
119
|
+
end
|
120
|
+
end
|
121
|
+
RUBY
|
122
|
+
|
123
|
+
violations = check(file_name, abc_max: 1).violations
|
124
|
+
violations[0][:label].should == "(anon)#test_method"
|
93
125
|
end
|
94
126
|
|
95
|
-
def self.it_should_extract_method_name(
|
96
|
-
it "creates an AbcMaxViolation for #{
|
127
|
+
def self.it_should_extract_method_name(name, label=name, sep='#')
|
128
|
+
it "creates an AbcMaxViolation for #{name}" do
|
97
129
|
file_name = make_file(<<-RUBY)
|
98
130
|
class Harness
|
99
|
-
def #{
|
131
|
+
def #{name}(a)
|
100
132
|
b = a
|
101
133
|
return b if b > 3
|
102
134
|
end
|
103
135
|
end
|
104
136
|
RUBY
|
105
137
|
|
106
|
-
violations =
|
107
|
-
violations[0].
|
138
|
+
violations = check(file_name, abc_max: 1).violations
|
139
|
+
violations[0][:label].should == "Harness#{sep}#{label}"
|
108
140
|
end
|
109
141
|
end
|
110
142
|
|
111
143
|
# These method names all create different ASTs. Which is weird.
|
112
144
|
it_should_extract_method_name 'a'
|
113
|
-
it_should_extract_method_name 'self.a', 'a'
|
145
|
+
it_should_extract_method_name 'self.a', 'a', '.'
|
114
146
|
it_should_extract_method_name 'next'
|
115
147
|
it_should_extract_method_name 'GET'
|
116
148
|
it_should_extract_method_name '`'
|
data/spec/cane_spec.rb
CHANGED
@@ -14,7 +14,7 @@ describe 'Cane' do
|
|
14
14
|
def run(cli_args)
|
15
15
|
result = nil
|
16
16
|
output = capture_stdout do
|
17
|
-
result = Cane::CLI.run(cli_args.split(' '))
|
17
|
+
result = Cane::CLI.run(['--no-abc'] + cli_args.split(' '))
|
18
18
|
end
|
19
19
|
|
20
20
|
[output, result ? 0 : 1]
|
@@ -42,8 +42,8 @@ describe 'Cane' do
|
|
42
42
|
file_name = make_file("whitespace ")
|
43
43
|
|
44
44
|
output, exitstatus = run("--style-glob #{file_name}")
|
45
|
-
exitstatus.should == 1
|
46
45
|
output.should include("Lines violated style requirements")
|
46
|
+
exitstatus.should == 1
|
47
47
|
end
|
48
48
|
|
49
49
|
it 'allows measure to be configured' do
|
@@ -59,7 +59,7 @@ describe 'Cane' do
|
|
59
59
|
|
60
60
|
output, exitstatus = run("--style-glob #{file_name} --style-measure 80")
|
61
61
|
exitstatus.should == 0
|
62
|
-
output.should
|
62
|
+
output.should == ""
|
63
63
|
end
|
64
64
|
|
65
65
|
it 'allows upper bound of failed checks' do
|
@@ -74,8 +74,8 @@ describe 'Cane' do
|
|
74
74
|
file_name = make_file("89")
|
75
75
|
|
76
76
|
output, exitstatus = run("--gte #{file_name},90")
|
77
|
-
exitstatus.should == 1
|
78
77
|
output.should include("Quality threshold crossed")
|
78
|
+
exitstatus.should == 1
|
79
79
|
end
|
80
80
|
|
81
81
|
it 'allows checking of class documentation' do
|
@@ -143,21 +143,30 @@ describe 'Cane' do
|
|
143
143
|
end
|
144
144
|
RUBY
|
145
145
|
|
146
|
-
exclusions_file = make_file(<<-YAML)
|
147
|
-
abc:
|
148
|
-
- Harness#complex_method
|
149
|
-
style:
|
150
|
-
- #{file_name}
|
151
|
-
YAML
|
152
|
-
|
153
146
|
options = [
|
154
147
|
"--abc-glob", file_name,
|
148
|
+
"--abc-exclude", "Harness#complex_method",
|
155
149
|
"--abc-max", 1,
|
156
150
|
"--style-glob", file_name,
|
157
|
-
"--
|
151
|
+
"--style-exclude", file_name
|
158
152
|
].join(' ')
|
159
153
|
|
160
154
|
_, exitstatus = run(options)
|
161
155
|
exitstatus.should == 0
|
162
156
|
end
|
157
|
+
|
158
|
+
it 'handles invalid unicode input' do
|
159
|
+
fn = make_file("\xc3\x28")
|
160
|
+
|
161
|
+
_, exitstatus = run("--style-glob #{fn} --abc-glob #{fn} --doc-glob #{fn}")
|
162
|
+
|
163
|
+
exitstatus.should == 0
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'handles invalid options by showing help' do
|
167
|
+
out, exitstatus = run("--bogus")
|
168
|
+
|
169
|
+
out.should include("Usage:")
|
170
|
+
exitstatus.should == 1
|
171
|
+
end
|
163
172
|
end
|
data/spec/doc_check_spec.rb
CHANGED
@@ -3,6 +3,10 @@ require 'spec_helper'
|
|
3
3
|
require 'cane/doc_check'
|
4
4
|
|
5
5
|
describe Cane::DocCheck do
|
6
|
+
def check(file_name, opts = {})
|
7
|
+
described_class.new(opts.merge(doc_glob: file_name))
|
8
|
+
end
|
9
|
+
|
6
10
|
it 'creates a DocViolation for each undocumented class' do
|
7
11
|
file_name = make_file <<-RUBY
|
8
12
|
# This class is documented
|
@@ -16,17 +20,34 @@ class Meta
|
|
16
20
|
end
|
17
21
|
RUBY
|
18
22
|
|
19
|
-
violations =
|
23
|
+
violations = check(file_name).violations
|
20
24
|
violations.length.should == 2
|
21
25
|
|
22
|
-
violations[0].should
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
violations[0].values_at(:file, :line, :label).should == [
|
27
|
+
file_name, 3, "NoDoc"
|
28
|
+
]
|
29
|
+
|
30
|
+
violations[1].values_at(:file, :line, :label).should == [
|
31
|
+
file_name, 4, "AlsoNoDoc"
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'ignores magic encoding comments' do
|
36
|
+
file_name = make_file <<-RUBY
|
37
|
+
# coding = utf-8
|
38
|
+
class NoDoc; end
|
39
|
+
# -*- encoding : utf-8 -*-
|
40
|
+
class AlsoNoDoc; end
|
41
|
+
RUBY
|
42
|
+
|
43
|
+
violations = check(file_name).violations
|
44
|
+
violations.length.should == 2
|
26
45
|
|
27
|
-
violations[
|
28
|
-
|
29
|
-
|
30
|
-
violations[1].
|
46
|
+
violations[0].values_at(:file, :line, :label).should == [
|
47
|
+
file_name, 2, "NoDoc"
|
48
|
+
]
|
49
|
+
violations[1].values_at(:file, :line, :label).should == [
|
50
|
+
file_name, 4, "AlsoNoDoc"
|
51
|
+
]
|
31
52
|
end
|
32
53
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
require 'cane/encoding_aware_iterator'
|
5
|
+
|
6
|
+
# Example bad input from:
|
7
|
+
# http://stackoverflow.com/questions/1301402/example-invalid-utf8-string
|
8
|
+
describe Cane::EncodingAwareIterator do
|
9
|
+
it 'handles non-UTF8 input' do
|
10
|
+
lines = ["\xc3\x28"]
|
11
|
+
result = described_class.new(lines).map_with_index do |line, number|
|
12
|
+
[line =~ /\s/, number]
|
13
|
+
end
|
14
|
+
result.should == [[nil, 0]]
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'does not enter an infinite loop on persistently bad input' do
|
18
|
+
->{
|
19
|
+
described_class.new([""]).map_with_index do |line, number|
|
20
|
+
"\xc3\x28" =~ /\s/
|
21
|
+
end
|
22
|
+
}.should raise_error(ArgumentError)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'cane/rake_task'
|
4
|
+
|
5
|
+
describe Cane::RakeTask do
|
6
|
+
it 'adds a new threshold' do
|
7
|
+
task = described_class.new do |t|
|
8
|
+
t.add_threshold 'coverage', :>=, 99
|
9
|
+
end
|
10
|
+
|
11
|
+
task.options[:gte].should == [["coverage", 99]]
|
12
|
+
end
|
13
|
+
end
|
data/spec/style_check_spec.rb
CHANGED
@@ -3,6 +3,10 @@ require 'spec_helper'
|
|
3
3
|
require 'cane/style_check'
|
4
4
|
|
5
5
|
describe Cane::StyleCheck do
|
6
|
+
def check(file_name, opts = {})
|
7
|
+
described_class.new(opts.merge(style_glob: file_name))
|
8
|
+
end
|
9
|
+
|
6
10
|
let(:ruby_with_style_issue) do
|
7
11
|
[
|
8
12
|
"def test ",
|
@@ -14,16 +18,18 @@ describe Cane::StyleCheck do
|
|
14
18
|
it 'creates a StyleViolation for each method above the threshold' do
|
15
19
|
file_name = make_file(ruby_with_style_issue)
|
16
20
|
|
17
|
-
violations =
|
21
|
+
violations = check(file_name, style_measure: 80).violations
|
18
22
|
violations.length.should == 2
|
19
|
-
violations[0].should be_instance_of(StyleViolation)
|
20
23
|
end
|
21
24
|
|
22
25
|
it 'skips declared exclusions' do
|
23
26
|
file_name = make_file(ruby_with_style_issue)
|
24
27
|
|
25
|
-
violations =
|
26
|
-
|
28
|
+
violations = check(file_name,
|
29
|
+
style_measure: 80,
|
30
|
+
style_exclude: [file_name]
|
31
|
+
).violations
|
32
|
+
|
27
33
|
violations.length.should == 0
|
28
34
|
end
|
29
35
|
end
|
@@ -2,11 +2,12 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
require 'cane/threshold_check'
|
4
4
|
|
5
|
-
describe ThresholdCheck do
|
5
|
+
describe Cane::ThresholdCheck do
|
6
6
|
it 'returns a value of unavailable when file cannot be read' do
|
7
|
-
check = ThresholdCheck.new([[
|
7
|
+
check = Cane::ThresholdCheck.new(gte: [['bogus_file', 20]])
|
8
8
|
violations = check.violations
|
9
9
|
violations.length.should == 1
|
10
|
-
violations[0].
|
10
|
+
violations[0][:label].should ==
|
11
|
+
'bogus_file is unavailable, should be >= 20'
|
11
12
|
end
|
12
13
|
end
|
@@ -4,10 +4,9 @@ require 'cane/violation_formatter'
|
|
4
4
|
|
5
5
|
describe Cane::ViolationFormatter do
|
6
6
|
def violation(description)
|
7
|
-
|
8
|
-
description: description
|
9
|
-
|
10
|
-
)
|
7
|
+
{
|
8
|
+
description: description
|
9
|
+
}
|
11
10
|
end
|
12
11
|
|
13
12
|
it 'includes number of violations in the group header' do
|
@@ -15,7 +14,7 @@ describe Cane::ViolationFormatter do
|
|
15
14
|
end
|
16
15
|
|
17
16
|
it 'includes total number of violations' do
|
18
|
-
violations = [violation("FAIL1"),violation("FAIL2")]
|
17
|
+
violations = [violation("FAIL1"), violation("FAIL2")]
|
19
18
|
result = described_class.new(violations).to_s
|
20
19
|
result.should include("Total Violations: 2")
|
21
20
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cane
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -70,22 +70,21 @@ files:
|
|
70
70
|
- spec/abc_check_spec.rb
|
71
71
|
- spec/cane_spec.rb
|
72
72
|
- spec/doc_check_spec.rb
|
73
|
+
- spec/encoding_aware_iterator_spec.rb
|
74
|
+
- spec/rake_task_spec.rb
|
73
75
|
- spec/spec_helper.rb
|
74
76
|
- spec/style_check_spec.rb
|
75
77
|
- spec/threshold_check_spec.rb
|
76
78
|
- spec/violation_formatter_spec.rb
|
77
79
|
- lib/cane/abc_check.rb
|
78
|
-
- lib/cane/abc_max_violation.rb
|
79
80
|
- lib/cane/cli/spec.rb
|
80
|
-
- lib/cane/cli/translator.rb
|
81
81
|
- lib/cane/cli.rb
|
82
82
|
- lib/cane/doc_check.rb
|
83
|
+
- lib/cane/encoding_aware_iterator.rb
|
84
|
+
- lib/cane/file.rb
|
83
85
|
- lib/cane/rake_task.rb
|
84
86
|
- lib/cane/style_check.rb
|
85
|
-
- lib/cane/style_violation.rb
|
86
|
-
- lib/cane/syntax_violation.rb
|
87
87
|
- lib/cane/threshold_check.rb
|
88
|
-
- lib/cane/threshold_violation.rb
|
89
88
|
- lib/cane/version.rb
|
90
89
|
- lib/cane/violation_formatter.rb
|
91
90
|
- lib/cane.rb
|
@@ -123,6 +122,8 @@ test_files:
|
|
123
122
|
- spec/abc_check_spec.rb
|
124
123
|
- spec/cane_spec.rb
|
125
124
|
- spec/doc_check_spec.rb
|
125
|
+
- spec/encoding_aware_iterator_spec.rb
|
126
|
+
- spec/rake_task_spec.rb
|
126
127
|
- spec/spec_helper.rb
|
127
128
|
- spec/style_check_spec.rb
|
128
129
|
- spec/threshold_check_spec.rb
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Cane
|
2
|
-
|
3
|
-
# Value object used by AbcCheck for a method that is too complicated.
|
4
|
-
class AbcMaxViolation < Struct.new(:file_name, :detail, :complexity)
|
5
|
-
def columns
|
6
|
-
[file_name, detail, complexity]
|
7
|
-
end
|
8
|
-
|
9
|
-
def description
|
10
|
-
"Methods exceeded maximum allowed ABC complexity"
|
11
|
-
end
|
12
|
-
|
13
|
-
alias_method :sort_index, :complexity
|
14
|
-
end
|
15
|
-
end
|
data/lib/cane/cli/translator.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
|
3
|
-
module Cane
|
4
|
-
module CLI
|
5
|
-
|
6
|
-
# Translates CLI options with given defaults to a hash suitable to be
|
7
|
-
# passed to `Cane.run`.
|
8
|
-
class Translator < Struct.new(:options, :defaults)
|
9
|
-
def to_hash
|
10
|
-
result = {}
|
11
|
-
translate_abc_options(result)
|
12
|
-
translate_doc_options(result)
|
13
|
-
translate_style_options(result)
|
14
|
-
|
15
|
-
result[:threshold] = options.fetch(:threshold, [])
|
16
|
-
result[:max_violations] = option_with_default(:max_violations).to_i
|
17
|
-
|
18
|
-
result
|
19
|
-
end
|
20
|
-
|
21
|
-
def translate_abc_options(result)
|
22
|
-
result[:abc] = {
|
23
|
-
files: option_with_default(:abc_glob),
|
24
|
-
max: option_with_default(:abc_max).to_i,
|
25
|
-
exclusions: exclusions_for('abc')
|
26
|
-
} unless check_disabled(:no_abc, [:abc_glob, :abc_max])
|
27
|
-
end
|
28
|
-
|
29
|
-
def translate_style_options(result)
|
30
|
-
result[:style] = {
|
31
|
-
files: option_with_default(:style_glob),
|
32
|
-
measure: option_with_default(:style_measure).to_i,
|
33
|
-
exclusions: exclusions_for('style')
|
34
|
-
} unless check_disabled(:no_style, [:style_glob])
|
35
|
-
end
|
36
|
-
|
37
|
-
def translate_doc_options(result)
|
38
|
-
result[:doc] = {
|
39
|
-
files: option_with_default(:doc_glob),
|
40
|
-
} unless check_disabled(:no_doc, [:doc_glob])
|
41
|
-
end
|
42
|
-
|
43
|
-
def check_disabled(check, params)
|
44
|
-
relevant_options = options.keys & params + [check]
|
45
|
-
|
46
|
-
check == relevant_options[-1]
|
47
|
-
end
|
48
|
-
|
49
|
-
def option_with_default(key)
|
50
|
-
options.fetch(key, defaults.fetch(key))
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def exclusions_for(tool)
|
56
|
-
Array(exclusions[tool])
|
57
|
-
end
|
58
|
-
|
59
|
-
def exclusions
|
60
|
-
@exclusions ||= begin
|
61
|
-
if file = options[:exclusions_file]
|
62
|
-
YAML.load_file(file)
|
63
|
-
else
|
64
|
-
{}
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
end
|
71
|
-
end
|