cane 1.3.0 → 1.4.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 +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
|