cane 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.md +5 -0
- data/README.md +21 -0
- data/lib/cane/abc_check.rb +19 -3
- data/lib/cane/cli/spec.rb +2 -0
- data/lib/cane/cli/translator.rb +24 -4
- data/lib/cane/doc_check.rb +1 -1
- data/lib/cane/rake_task.rb +4 -1
- data/lib/cane/style_check.rb +11 -2
- data/lib/cane/version.rb +1 -1
- data/lib/cane/violation_formatter.rb +5 -1
- data/spec/abc_check_spec.rb +37 -0
- data/spec/cane_spec.rb +33 -0
- data/spec/doc_check_spec.rb +2 -0
- data/spec/style_check_spec.rb +14 -3
- data/spec/violation_formatter_spec.rb +6 -0
- metadata +3 -3
data/HISTORY.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Cane History
|
2
2
|
|
3
|
+
## 1.4.0 - 2 July 2012 (1afc999d)
|
4
|
+
|
5
|
+
* Allow files and methods to be whitelisted (#16)
|
6
|
+
* Show total number of violations in output (#14)
|
7
|
+
|
3
8
|
## 1.3.0 - 20 April 2012 (c166dfa0)
|
4
9
|
|
5
10
|
* Remove dependency on tailor. Fewer styles checks are performed, but the three
|
data/README.md
CHANGED
@@ -48,6 +48,7 @@ Customize behaviour with a wealth of options:
|
|
48
48
|
--gte FILE,THRESHOLD If FILE contains a number, verify it is >= to THRESHOLD.
|
49
49
|
|
50
50
|
--max-violations VALUE Max allowed violations (default: 0)
|
51
|
+
--exclusions-file FILE YAML file containing a list of exclusions
|
51
52
|
|
52
53
|
--version Show version
|
53
54
|
-h, --help Show this message
|
@@ -91,6 +92,8 @@ on to an existing application that may already have many violations. By setting
|
|
91
92
|
the maximum to the current number, no immediate changes will be required to
|
92
93
|
your existing code base, but you will be protected from things getting worse.
|
93
94
|
|
95
|
+
You can also consider defining exclusions for each violation (see below).
|
96
|
+
|
94
97
|
## Integrating with SimpleCov
|
95
98
|
|
96
99
|
Any value in a file can be used as a threshold:
|
@@ -115,6 +118,24 @@ You can use a `SimpleCov` formatter to create the required file:
|
|
115
118
|
|
116
119
|
SimpleCov.formatter = SimpleCov::Formatter::QualityFormatter
|
117
120
|
|
121
|
+
## Defining Exclusions
|
122
|
+
|
123
|
+
Occasionally, you may want to permanently ignore specific cane violations.
|
124
|
+
Create a YAML file like so:
|
125
|
+
|
126
|
+
abc:
|
127
|
+
- Some::Fully::Qualified::Class.some_class_method
|
128
|
+
- Some::Fully::Qualified::Class#some_instance_method
|
129
|
+
style:
|
130
|
+
relative/path/to/some/file.rb
|
131
|
+
relative/path/to/some/other/file.rb
|
132
|
+
|
133
|
+
Tell cane about this file using the `--exclusions-file` option:
|
134
|
+
|
135
|
+
> cane --exclusions-file path/to/exclusions.yml
|
136
|
+
|
137
|
+
Currently, only the abc and style checks support exclusions.
|
138
|
+
|
118
139
|
## Compatibility
|
119
140
|
|
120
141
|
Requires MRI 1.9, since it depends on the `ripper` library to calculate
|
data/lib/cane/abc_check.rb
CHANGED
@@ -2,6 +2,7 @@ require 'ripper'
|
|
2
2
|
|
3
3
|
require 'cane/abc_max_violation'
|
4
4
|
require 'cane/syntax_violation'
|
5
|
+
require 'set'
|
5
6
|
|
6
7
|
module Cane
|
7
8
|
|
@@ -23,7 +24,7 @@ module Cane
|
|
23
24
|
when nil
|
24
25
|
InvalidAst.new(file_name)
|
25
26
|
else
|
26
|
-
RubyAst.new(file_name, max_allowed_complexity, ast)
|
27
|
+
RubyAst.new(file_name, max_allowed_complexity, ast, exclusions)
|
27
28
|
end.violations
|
28
29
|
end
|
29
30
|
|
@@ -35,7 +36,8 @@ module Cane
|
|
35
36
|
end
|
36
37
|
|
37
38
|
# Wrapper object around sexps returned from ripper.
|
38
|
-
class RubyAst < Struct.new(:file_name, :max_allowed_complexity,
|
39
|
+
class RubyAst < Struct.new(:file_name, :max_allowed_complexity,
|
40
|
+
:sexps, :exclusions)
|
39
41
|
def violations
|
40
42
|
process_ast(sexps).
|
41
43
|
select { |nesting, complexity| complexity > max_allowed_complexity }.
|
@@ -49,7 +51,9 @@ module Cane
|
|
49
51
|
def process_ast(node, complexity = {}, nesting = [])
|
50
52
|
if method_nodes.include?(node[0])
|
51
53
|
nesting = nesting + [label_for(node)]
|
52
|
-
|
54
|
+
unless excluded?(node, *nesting)
|
55
|
+
complexity[nesting.join(" > ")] = calculate_abc(node)
|
56
|
+
end
|
53
57
|
elsif container_nodes.include?(node[0])
|
54
58
|
parent = node[1][-1][1]
|
55
59
|
nesting = nesting + [parent]
|
@@ -100,6 +104,14 @@ module Cane
|
|
100
104
|
def condition_nodes
|
101
105
|
[:==, :===, :"<>", :"<=", :">=", :"=~", :>, :<, :else, :"<=>"]
|
102
106
|
end
|
107
|
+
|
108
|
+
METH_CHARS = { def: '#', defs: '.' }
|
109
|
+
|
110
|
+
def excluded?(node, *modules, meth_name)
|
111
|
+
meth_char = METH_CHARS.fetch(node.first)
|
112
|
+
description = [modules.join('::'), meth_name].join(meth_char)
|
113
|
+
exclusions.include?(description)
|
114
|
+
end
|
103
115
|
end
|
104
116
|
|
105
117
|
def file_names
|
@@ -113,5 +125,9 @@ module Cane
|
|
113
125
|
def max_allowed_complexity
|
114
126
|
opts.fetch(:max)
|
115
127
|
end
|
128
|
+
|
129
|
+
def exclusions
|
130
|
+
opts.fetch(:exclusions, []).to_set
|
131
|
+
end
|
116
132
|
end
|
117
133
|
end
|
data/lib/cane/cli/spec.rb
CHANGED
data/lib/cane/cli/translator.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
1
3
|
module Cane
|
2
4
|
module CLI
|
3
5
|
|
@@ -18,15 +20,17 @@ module Cane
|
|
18
20
|
|
19
21
|
def translate_abc_options(result)
|
20
22
|
result[:abc] = {
|
21
|
-
files:
|
22
|
-
max:
|
23
|
+
files: option_with_default(:abc_glob),
|
24
|
+
max: option_with_default(:abc_max).to_i,
|
25
|
+
exclusions: exclusions_for('abc')
|
23
26
|
} unless check_disabled(:no_abc, [:abc_glob, :abc_max])
|
24
27
|
end
|
25
28
|
|
26
29
|
def translate_style_options(result)
|
27
30
|
result[:style] = {
|
28
|
-
files:
|
29
|
-
measure:
|
31
|
+
files: option_with_default(:style_glob),
|
32
|
+
measure: option_with_default(:style_measure).to_i,
|
33
|
+
exclusions: exclusions_for('style')
|
30
34
|
} unless check_disabled(:no_style, [:style_glob])
|
31
35
|
end
|
32
36
|
|
@@ -45,6 +49,22 @@ module Cane
|
|
45
49
|
def option_with_default(key)
|
46
50
|
options.fetch(key, defaults.fetch(key))
|
47
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
|
48
68
|
end
|
49
69
|
|
50
70
|
end
|
data/lib/cane/doc_check.rb
CHANGED
data/lib/cane/rake_task.rb
CHANGED
@@ -32,6 +32,8 @@ module Cane
|
|
32
32
|
attr_accessor :no_doc
|
33
33
|
# Max violations to tolerate (default: 0)
|
34
34
|
attr_accessor :max_violations
|
35
|
+
# File containing list of exclusions in YAML format
|
36
|
+
attr_accessor :exclusions_file
|
35
37
|
|
36
38
|
# Add a threshold check. If the file exists and it contains a number,
|
37
39
|
# compare that number with the given value using the operator.
|
@@ -63,7 +65,8 @@ module Cane
|
|
63
65
|
:max_violations,
|
64
66
|
:style_glob,
|
65
67
|
:no_style,
|
66
|
-
:style_measure
|
68
|
+
:style_measure,
|
69
|
+
:exclusions_file
|
67
70
|
].inject(threshold: @threshold) do |opts, setting|
|
68
71
|
value = self.send(setting)
|
69
72
|
opts[setting] = value unless value.nil?
|
data/lib/cane/style_check.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'cane/style_violation'
|
2
|
+
require 'set'
|
2
3
|
|
3
4
|
module Cane
|
4
5
|
|
5
6
|
# Creates violations for files that do not meet style conventions. Only
|
6
7
|
# highly obvious, probable, and non-controversial checks are performed here.
|
7
8
|
# It is not the goal of the tool to provide an extensive style report, but
|
8
|
-
# only to prevent
|
9
|
+
# only to prevent stupid mistakes.
|
9
10
|
class StyleCheck < Struct.new(:opts)
|
10
11
|
def violations
|
11
12
|
file_list.map do |file_path|
|
@@ -30,7 +31,7 @@ module Cane
|
|
30
31
|
end
|
31
32
|
|
32
33
|
def file_list
|
33
|
-
Dir[opts.fetch(:files)]
|
34
|
+
Dir[opts.fetch(:files)].reject { |f| excluded?(f) }
|
34
35
|
end
|
35
36
|
|
36
37
|
def measure
|
@@ -40,6 +41,14 @@ module Cane
|
|
40
41
|
def map_lines(file_path, &block)
|
41
42
|
File.open(file_path).each_line.map.with_index(&block)
|
42
43
|
end
|
44
|
+
|
45
|
+
def exclusions
|
46
|
+
@exclusions ||= opts.fetch(:exclusions, []).to_set
|
47
|
+
end
|
48
|
+
|
49
|
+
def excluded?(file)
|
50
|
+
exclusions.include?(file)
|
51
|
+
end
|
43
52
|
end
|
44
53
|
|
45
54
|
end
|
data/lib/cane/version.rb
CHANGED
@@ -11,11 +11,15 @@ module Cane
|
|
11
11
|
grouped_violations.map do |description, violations|
|
12
12
|
format_group_header(description, violations) +
|
13
13
|
format_violations(violations)
|
14
|
-
end.flatten.join("\n") + "\n\n"
|
14
|
+
end.flatten.join("\n") + "\n\n" + totals + "\n\n"
|
15
15
|
end
|
16
16
|
|
17
17
|
protected
|
18
18
|
|
19
|
+
def totals
|
20
|
+
"Total Violations: #{violations.count}"
|
21
|
+
end
|
22
|
+
|
19
23
|
def format_group_header(description, violations)
|
20
24
|
["", "%s (%i):" % [description, violations.length], ""]
|
21
25
|
end
|
data/spec/abc_check_spec.rb
CHANGED
@@ -55,6 +55,43 @@ describe Cane::AbcCheck do
|
|
55
55
|
violations[0].description.should be_instance_of(String)
|
56
56
|
end
|
57
57
|
|
58
|
+
it 'skips declared exclusions' do
|
59
|
+
file_name = make_file(<<-RUBY)
|
60
|
+
class Harness
|
61
|
+
def instance_meth
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.class_meth
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
module Nested
|
70
|
+
def i_meth
|
71
|
+
true
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.c_meth
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
def other_meth
|
79
|
+
true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
RUBY
|
84
|
+
|
85
|
+
exclusions = %w[ Harness#instance_meth Harness.class_meth
|
86
|
+
Harness::Nested#i_meth Harness::Nested.c_meth ]
|
87
|
+
violations = described_class.new(files: file_name, max: 0,
|
88
|
+
exclusions: exclusions).violations
|
89
|
+
violations.length.should == 1
|
90
|
+
violations[0].should be_instance_of(Cane::AbcMaxViolation)
|
91
|
+
columns = violations[0].columns
|
92
|
+
columns.should == [file_name, "Harness > Nested > other_meth", 1]
|
93
|
+
end
|
94
|
+
|
58
95
|
def self.it_should_extract_method_name(method_name, label=method_name)
|
59
96
|
it "creates an AbcMaxViolation for #{method_name}" do
|
60
97
|
file_name = make_file(<<-RUBY)
|
data/spec/cane_spec.rb
CHANGED
@@ -127,4 +127,37 @@ describe 'Cane' do
|
|
127
127
|
run("--no-doc --doc-glob #{file_name}").should ==
|
128
128
|
run("--doc-glob #{file_name}")
|
129
129
|
end
|
130
|
+
|
131
|
+
it 'supports exclusions' do
|
132
|
+
line_with_whitespace = "whitespace "
|
133
|
+
file_name = make_file(<<-RUBY)
|
134
|
+
# #{line_with_whitespace}
|
135
|
+
class Harness
|
136
|
+
def complex_method(a)
|
137
|
+
if a < 2
|
138
|
+
return "low"
|
139
|
+
else
|
140
|
+
return "high"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
RUBY
|
145
|
+
|
146
|
+
exclusions_file = make_file(<<-YAML)
|
147
|
+
abc:
|
148
|
+
- Harness#complex_method
|
149
|
+
style:
|
150
|
+
- #{file_name}
|
151
|
+
YAML
|
152
|
+
|
153
|
+
options = [
|
154
|
+
"--abc-glob", file_name,
|
155
|
+
"--abc-max", 1,
|
156
|
+
"--style-glob", file_name,
|
157
|
+
"--exclusions-file", exclusions_file
|
158
|
+
].join(' ')
|
159
|
+
|
160
|
+
_, exitstatus = run(options)
|
161
|
+
exitstatus.should == 0
|
162
|
+
end
|
130
163
|
end
|
data/spec/doc_check_spec.rb
CHANGED
@@ -22,9 +22,11 @@ end
|
|
22
22
|
violations[0].should be_instance_of(Cane::UndocumentedClassViolation)
|
23
23
|
violations[0].file_name.should == file_name
|
24
24
|
violations[0].number.should == 3
|
25
|
+
violations[0].columns.last.should eq("NoDoc")
|
25
26
|
|
26
27
|
violations[1].should be_instance_of(Cane::UndocumentedClassViolation)
|
27
28
|
violations[1].file_name.should == file_name
|
28
29
|
violations[1].number.should == 4
|
30
|
+
violations[1].columns.last.should eq("AlsoNoDoc")
|
29
31
|
end
|
30
32
|
end
|
data/spec/style_check_spec.rb
CHANGED
@@ -3,16 +3,27 @@ require 'spec_helper'
|
|
3
3
|
require 'cane/style_check'
|
4
4
|
|
5
5
|
describe Cane::StyleCheck do
|
6
|
-
|
7
|
-
|
6
|
+
let(:ruby_with_style_issue) do
|
7
|
+
[
|
8
8
|
"def test ",
|
9
9
|
"\t1",
|
10
10
|
"end"
|
11
11
|
].join("\n")
|
12
|
-
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'creates a StyleViolation for each method above the threshold' do
|
15
|
+
file_name = make_file(ruby_with_style_issue)
|
13
16
|
|
14
17
|
violations = Cane::StyleCheck.new(files: file_name, measure: 80).violations
|
15
18
|
violations.length.should == 2
|
16
19
|
violations[0].should be_instance_of(StyleViolation)
|
17
20
|
end
|
21
|
+
|
22
|
+
it 'skips declared exclusions' do
|
23
|
+
file_name = make_file(ruby_with_style_issue)
|
24
|
+
|
25
|
+
violations = Cane::StyleCheck.new(files: file_name, measure: 80,
|
26
|
+
exclusions: [file_name]).violations
|
27
|
+
violations.length.should == 0
|
28
|
+
end
|
18
29
|
end
|
@@ -13,4 +13,10 @@ describe Cane::ViolationFormatter do
|
|
13
13
|
it 'includes number of violations in the group header' do
|
14
14
|
described_class.new([violation("FAIL")]).to_s.should include("(1)")
|
15
15
|
end
|
16
|
+
|
17
|
+
it 'includes total number of violations' do
|
18
|
+
violations = [violation("FAIL1"),violation("FAIL2")]
|
19
|
+
result = described_class.new(violations).to_s
|
20
|
+
result.should include("Total Violations: 2")
|
21
|
+
end
|
16
22
|
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: 1.
|
4
|
+
version: 1.4.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-07-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -114,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
114
|
version: '0'
|
115
115
|
requirements: []
|
116
116
|
rubyforge_project:
|
117
|
-
rubygems_version: 1.8.
|
117
|
+
rubygems_version: 1.8.24
|
118
118
|
signing_key:
|
119
119
|
specification_version: 3
|
120
120
|
summary: Fails your build if code quality thresholds are not met. Provides complexity
|