roodi 1.3.0 → 1.3.2
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.txt +4 -0
- data/Manifest.txt +7 -0
- data/Rakefile +1 -0
- data/lib/roodi.rb +1 -1
- data/lib/roodi/checks.rb +3 -0
- data/lib/roodi/checks/abc_metric_method_check.rb +77 -0
- data/lib/roodi/checks/check.rb +6 -2
- data/lib/roodi/checks/class_name_check.rb +1 -1
- data/lib/roodi/checks/class_variable_check.rb +24 -0
- data/lib/roodi/checks/control_coupling_check.rb +21 -0
- data/lib/roodi/checks/cyclomatic_complexity_check.rb +3 -3
- data/lib/roodi/checks/cyclomatic_complexity_method_check.rb +1 -1
- data/lib/roodi/checks/empty_rescue_body_check.rb +9 -4
- data/lib/roodi/checks/method_name_check.rb +1 -1
- data/lib/roodi/core/error.rb +17 -0
- data/roodi.yml +2 -1
- data/spec/roodi/checks/abc_metric_method_check_spec.rb +89 -0
- data/spec/roodi/checks/assignment_in_conditional_check_spec.rb +5 -5
- data/spec/roodi/checks/case_missing_else_check_spec.rb +1 -1
- data/spec/roodi/checks/class_line_count_check_spec.rb +1 -1
- data/spec/roodi/checks/class_name_check_spec.rb +1 -1
- data/spec/roodi/checks/class_variable_check_spec.rb +17 -0
- data/spec/roodi/checks/control_coupling_check_spec.rb +23 -0
- data/spec/roodi/checks/cyclomatic_complexity_block_check_spec.rb +1 -1
- data/spec/roodi/checks/cyclomatic_complexity_method_check_spec.rb +1 -1
- data/spec/roodi/checks/empty_rescue_body_check_spec.rb +50 -2
- data/spec/roodi/checks/for_loop_check_spec.rb +1 -1
- data/spec/roodi/checks/method_line_count_check_spec.rb +1 -1
- data/spec/roodi/checks/method_name_check_spec.rb +12 -89
- data/spec/roodi/checks/module_line_count_check_spec.rb +1 -1
- data/spec/roodi/checks/module_name_check_spec.rb +1 -1
- data/spec/roodi/checks/parameter_number_check_spec.rb +2 -2
- data/spec/spec_helper.rb +1 -0
- metadata +10 -3
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
@@ -6,11 +6,14 @@ bin/roodi
|
|
6
6
|
bin/roodi-describe
|
7
7
|
lib/roodi.rb
|
8
8
|
lib/roodi/checks.rb
|
9
|
+
lib/roodi/checks/abc_metric_method_check.rb
|
9
10
|
lib/roodi/checks/assignment_in_conditional_check.rb
|
10
11
|
lib/roodi/checks/case_missing_else_check.rb
|
11
12
|
lib/roodi/checks/check.rb
|
12
13
|
lib/roodi/checks/class_line_count_check.rb
|
13
14
|
lib/roodi/checks/class_name_check.rb
|
15
|
+
lib/roodi/checks/class_variable_check.rb
|
16
|
+
lib/roodi/checks/control_coupling_check.rb
|
14
17
|
lib/roodi/checks/cyclomatic_complexity_block_check.rb
|
15
18
|
lib/roodi/checks/cyclomatic_complexity_check.rb
|
16
19
|
lib/roodi/checks/cyclomatic_complexity_method_check.rb
|
@@ -25,15 +28,19 @@ lib/roodi/checks/name_check.rb
|
|
25
28
|
lib/roodi/checks/parameter_number_check.rb
|
26
29
|
lib/roodi/core.rb
|
27
30
|
lib/roodi/core/checking_visitor.rb
|
31
|
+
lib/roodi/core/error.rb
|
28
32
|
lib/roodi/core/iterator_visitor.rb
|
29
33
|
lib/roodi/core/parse_tree_runner.rb
|
30
34
|
lib/roodi/core/parser.rb
|
31
35
|
lib/roodi/core/visitable_sexp.rb
|
32
36
|
roodi.yml
|
37
|
+
spec/roodi/checks/abc_metric_method_check_spec.rb
|
33
38
|
spec/roodi/checks/assignment_in_conditional_check_spec.rb
|
34
39
|
spec/roodi/checks/case_missing_else_check_spec.rb
|
35
40
|
spec/roodi/checks/class_line_count_check_spec.rb
|
36
41
|
spec/roodi/checks/class_name_check_spec.rb
|
42
|
+
spec/roodi/checks/class_variable_check_spec.rb
|
43
|
+
spec/roodi/checks/control_coupling_check_spec.rb
|
37
44
|
spec/roodi/checks/cyclomatic_complexity_block_check_spec.rb
|
38
45
|
spec/roodi/checks/cyclomatic_complexity_method_check_spec.rb
|
39
46
|
spec/roodi/checks/empty_rescue_body_check_spec.rb
|
data/Rakefile
CHANGED
data/lib/roodi.rb
CHANGED
data/lib/roodi/checks.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
require 'roodi/checks/abc_metric_method_check'
|
1
2
|
require 'roodi/checks/assignment_in_conditional_check'
|
2
3
|
require 'roodi/checks/case_missing_else_check'
|
3
4
|
require 'roodi/checks/class_line_count_check'
|
4
5
|
require 'roodi/checks/class_name_check'
|
6
|
+
require 'roodi/checks/class_variable_check'
|
7
|
+
require 'roodi/checks/control_coupling_check'
|
5
8
|
require 'roodi/checks/cyclomatic_complexity_block_check'
|
6
9
|
require 'roodi/checks/cyclomatic_complexity_method_check'
|
7
10
|
require 'roodi/checks/empty_rescue_body_check'
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'roodi/checks/check'
|
2
|
+
|
3
|
+
module Roodi
|
4
|
+
module Checks
|
5
|
+
# TODO: Add summary
|
6
|
+
#
|
7
|
+
# TODO: Add detail
|
8
|
+
class AbcMetricMethodCheck < Check
|
9
|
+
# ASSIGNMENTS = [:attrasgn, :attrset, :dasgn_curr, :iasgn, :lasgn, :masgn]
|
10
|
+
ASSIGNMENTS = [:lasgn]
|
11
|
+
# BRANCHES = [:if, :else, :while, :until, :for, :rescue, :case, :when, :and, :or]
|
12
|
+
BRANCHES = [:vcall, :call]
|
13
|
+
# CONDITIONS = [:and, :or]
|
14
|
+
CONDITIONS = [:==, :<=, :>=, :<, :>]
|
15
|
+
# = *= /= %= += <<= >>= &= |= ^=
|
16
|
+
OPERATORS = [:*, :/, :%, :+, :<<, :>>, :&, :|, :^]
|
17
|
+
DEFAULT_SCORE = 10
|
18
|
+
|
19
|
+
def initialize(options = {})
|
20
|
+
super()
|
21
|
+
@score = options['score'] || DEFAULT_SCORE
|
22
|
+
end
|
23
|
+
|
24
|
+
def interesting_nodes
|
25
|
+
[:defn]
|
26
|
+
end
|
27
|
+
|
28
|
+
def evaluate(node)
|
29
|
+
method_name = node[1]
|
30
|
+
a = count_assignments(node)
|
31
|
+
b = count_branches(node)
|
32
|
+
c = count_conditionals(node)
|
33
|
+
score = Math.sqrt(a*a + b*b + c*c)
|
34
|
+
add_error "Method name \"#{method_name}\" has an ABC metric score of <#{a},#{b},#{c}> = #{score}. It should be #{@score} or less." unless score <= @score
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def count_assignments(node)
|
40
|
+
count = 0
|
41
|
+
count = count + 1 if assignment?(node)
|
42
|
+
node.children.each {|node| count += count_assignments(node)}
|
43
|
+
count
|
44
|
+
end
|
45
|
+
|
46
|
+
def count_branches(node)
|
47
|
+
count = 0
|
48
|
+
count = count + 1 if branch?(node)
|
49
|
+
node.children.each {|node| count += count_branches(node)}
|
50
|
+
count
|
51
|
+
end
|
52
|
+
|
53
|
+
def count_conditionals(node)
|
54
|
+
count = 0
|
55
|
+
count = count + 1 if conditional?(node)
|
56
|
+
node.children.each {|node| count += count_conditionals(node)}
|
57
|
+
count
|
58
|
+
end
|
59
|
+
|
60
|
+
def assignment?(node)
|
61
|
+
ASSIGNMENTS.include?(node.node_type)
|
62
|
+
end
|
63
|
+
|
64
|
+
def branch?(node)
|
65
|
+
BRANCHES.include?(node.node_type) && !conditional?(node) && !operator?(node)
|
66
|
+
end
|
67
|
+
|
68
|
+
def conditional?(node)
|
69
|
+
(:call == node.node_type) && CONDITIONS.include?(node[2])
|
70
|
+
end
|
71
|
+
|
72
|
+
def operator?(node)
|
73
|
+
(:call == node.node_type) && OPERATORS.include?(node[2])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/roodi/checks/check.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'roodi/core/error'
|
2
|
+
|
1
3
|
module Roodi
|
2
4
|
module Checks
|
3
5
|
class Check
|
@@ -11,11 +13,13 @@ module Roodi
|
|
11
13
|
|
12
14
|
def evaluate_node_at_line(node, line)
|
13
15
|
@line = line
|
14
|
-
|
16
|
+
eval_method = "evaluate_#{node.node_type}"
|
17
|
+
self.send(eval_method, node) if self.respond_to? eval_method
|
18
|
+
evaluate(node) if self.respond_to? :evaluate
|
15
19
|
end
|
16
20
|
|
17
21
|
def add_error(error, offset = 0)
|
18
|
-
@errors << "#{
|
22
|
+
@errors << Roodi::Core::Error.new("#{@line[2]}", "#{@line[1] + offset}", error)
|
19
23
|
end
|
20
24
|
|
21
25
|
def errors
|
@@ -4,7 +4,7 @@ module Roodi
|
|
4
4
|
module Checks
|
5
5
|
# Checks a class name to make sure it matches the specified pattern.
|
6
6
|
#
|
7
|
-
# Keeping to a consistent
|
7
|
+
# Keeping to a consistent naming convention makes your code easier to read.
|
8
8
|
class ClassNameCheck < NameCheck
|
9
9
|
DEFAULT_PATTERN = /^[A-Z][a-zA-Z0-9]*$/
|
10
10
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'roodi/checks/check'
|
2
|
+
|
3
|
+
module Roodi
|
4
|
+
module Checks
|
5
|
+
# Checks to make sure class variables are not being used..
|
6
|
+
#
|
7
|
+
# Class variables in Ruby have a complicated inheritance policy, and their use
|
8
|
+
# can lead to mistakes. Often an alternate design can be used to solve the
|
9
|
+
# problem instead.
|
10
|
+
#
|
11
|
+
# This check is looking for a code smell rather than a definite error. If you're
|
12
|
+
# sure that you're doing the right thing, try turning this check off in your
|
13
|
+
# config file.
|
14
|
+
class ClassVariableCheck < Check
|
15
|
+
def interesting_nodes
|
16
|
+
[:cvar]
|
17
|
+
end
|
18
|
+
|
19
|
+
def evaluate(node)
|
20
|
+
add_error "Don't use class variables. You might want to try a different design."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'roodi/checks/check'
|
2
|
+
|
3
|
+
module Roodi
|
4
|
+
module Checks
|
5
|
+
class ControlCouplingCheck < Check
|
6
|
+
def interesting_nodes
|
7
|
+
[:defn, :lvar]
|
8
|
+
end
|
9
|
+
|
10
|
+
def evaluate_defn(node)
|
11
|
+
@method_name = node[1]
|
12
|
+
@arguments = node[2][1][1]
|
13
|
+
@arguments.delete_at 0
|
14
|
+
end
|
15
|
+
|
16
|
+
def evaluate_lvar(node)
|
17
|
+
add_error "Method \"#{@method_name}\" uses the argument \"#{node[1]}\" for internal control." if @arguments.detect {|each| each == node[1]}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -3,6 +3,8 @@ require 'roodi/checks/check'
|
|
3
3
|
module Roodi
|
4
4
|
module Checks
|
5
5
|
class CyclomaticComplexityCheck < Check
|
6
|
+
COMPLEXITY_NODE_TYPES = [:if, :while, :until, :for, :rescue, :case, :when, :and, :or]
|
7
|
+
|
6
8
|
def initialize(complexity)
|
7
9
|
super()
|
8
10
|
@complexity = complexity
|
@@ -17,10 +19,8 @@ module Roodi
|
|
17
19
|
private
|
18
20
|
|
19
21
|
def count_branches(node)
|
20
|
-
complexity_node_types = [:if, :while, :until, :for, :rescue, :case, :when, :and, :or]
|
21
|
-
|
22
22
|
count = 0
|
23
|
-
count = count + 1 if
|
23
|
+
count = count + 1 if COMPLEXITY_NODE_TYPES.include? node.node_type
|
24
24
|
node.children.each {|node| count += count_branches(node)}
|
25
25
|
count
|
26
26
|
end
|
@@ -25,7 +25,7 @@ module Roodi
|
|
25
25
|
|
26
26
|
def evaluate(node)
|
27
27
|
complexity = count_complexity(node)
|
28
|
-
add_error "Method name \"#{node[1]}\"
|
28
|
+
add_error "Method name \"#{node[1]}\" cyclomatic complexity is #{complexity}. It should be #{@complexity} or less." unless complexity <= @complexity
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -7,6 +7,8 @@ module Roodi
|
|
7
7
|
# When the body of a rescue block is empty, exceptions can get caught and swallowed without
|
8
8
|
# any feedback to the user.
|
9
9
|
class EmptyRescueBodyCheck < Check
|
10
|
+
STATEMENT_NODES = [:fcall, :return, :attrasgn, :vcall]
|
11
|
+
|
10
12
|
def interesting_nodes
|
11
13
|
[:resbody]
|
12
14
|
end
|
@@ -18,10 +20,13 @@ module Roodi
|
|
18
20
|
private
|
19
21
|
|
20
22
|
def has_statement?(node)
|
21
|
-
|
22
|
-
|
23
|
-
node.children.
|
24
|
-
|
23
|
+
return true if STATEMENT_NODES.include?(node.node_type)
|
24
|
+
return true if assigning_other_than_exception_to_local_variable?(node)
|
25
|
+
return true if node.children.any? { |child| has_statement?(child) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def assigning_other_than_exception_to_local_variable?(node)
|
29
|
+
node.node_type == :lasgn && node[2].to_a != [:gvar, :$!]
|
25
30
|
end
|
26
31
|
end
|
27
32
|
end
|
@@ -6,7 +6,7 @@ module Roodi
|
|
6
6
|
#
|
7
7
|
# Keeping to a consistent nameing convention makes your code easier to read.
|
8
8
|
class MethodNameCheck < NameCheck
|
9
|
-
DEFAULT_PATTERN = /^[_a-z<>=\[
|
9
|
+
DEFAULT_PATTERN = /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
|
10
10
|
|
11
11
|
def initialize(options = {})
|
12
12
|
pattern = options['pattern'] || DEFAULT_PATTERN
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Roodi
|
2
|
+
module Core
|
3
|
+
class Error
|
4
|
+
attr_reader :filename, :line_number, :message
|
5
|
+
|
6
|
+
def initialize(filename, line_number, message)
|
7
|
+
@filename = filename
|
8
|
+
@line_number = line_number
|
9
|
+
@message = message
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"#{@filename}:#{@line_number} - #{@message}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/roodi.yml
CHANGED
@@ -2,12 +2,13 @@ AssignmentInConditionalCheck: { }
|
|
2
2
|
CaseMissingElseCheck: { }
|
3
3
|
ClassLineCountCheck: { line_count: 300 }
|
4
4
|
ClassNameCheck: { pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/ }
|
5
|
+
ClassVariableCheck: { }
|
5
6
|
CyclomaticComplexityBlockCheck: { complexity: 4 }
|
6
7
|
CyclomaticComplexityMethodCheck: { complexity: 8 }
|
7
8
|
EmptyRescueBodyCheck: { }
|
8
9
|
ForLoopCheck: { }
|
9
10
|
MethodLineCountCheck: { line_count: 20 }
|
10
|
-
MethodNameCheck: { pattern: !ruby/regexp /^[_a-z<>=\[
|
11
|
+
MethodNameCheck: { pattern: !ruby/regexp /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ }
|
11
12
|
ModuleLineCountCheck: { line_count: 300 }
|
12
13
|
ModuleNameCheck: { pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/ }
|
13
14
|
ParameterNumberCheck: { parameter_count: 5 }
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::AbcMetricMethodCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::ParseTreeRunner.new(Roodi::Checks::AbcMetricMethodCheck.new({'score' => 0}))
|
6
|
+
end
|
7
|
+
|
8
|
+
def verify_content_score(content, a, b, c)
|
9
|
+
score = Math.sqrt(a*a + b*b + c*c)
|
10
|
+
@roodi.check_content(content)
|
11
|
+
errors = @roodi.errors
|
12
|
+
errors.should_not be_empty
|
13
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Method name \"method_name\" has an ABC metric score of <#{a},#{b},#{c}> = #{score}. It should be 0 or less.")
|
14
|
+
end
|
15
|
+
|
16
|
+
# 1. Add one to the assignment count for each occurrence of an assignment
|
17
|
+
# operator, excluding constant declarations:
|
18
|
+
#
|
19
|
+
# = *= /= %= += <<= >>= &= |= ^=
|
20
|
+
describe "when processing assignments" do
|
21
|
+
['=', '*=', '/=', '%=', '+=', '<<=', '>>=', '&=', '|=', '^='].each do |each|
|
22
|
+
it "should find #{each}" do
|
23
|
+
content = <<-END
|
24
|
+
def method_name
|
25
|
+
foo #{each} 1
|
26
|
+
end
|
27
|
+
END
|
28
|
+
verify_content_score(content, 1, 0, 0)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# 3. Add one to the branch count for each function call or class method
|
34
|
+
# call.
|
35
|
+
#
|
36
|
+
# 4. Add one to the branch count for each occurrence of the new operator.
|
37
|
+
describe "when processing branches" do
|
38
|
+
it "should find a virtual method call" do
|
39
|
+
content = <<-END
|
40
|
+
def method_name
|
41
|
+
call_foo
|
42
|
+
end
|
43
|
+
END
|
44
|
+
verify_content_score(content, 0, 1, 0)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should find an explicit method call" do
|
48
|
+
content = <<-END
|
49
|
+
def method_name
|
50
|
+
@object.call_foo
|
51
|
+
end
|
52
|
+
END
|
53
|
+
verify_content_score(content, 0, 1, 0)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should exclude a condition" do
|
57
|
+
content = <<-END
|
58
|
+
def method_name
|
59
|
+
@object.call_foo < 10
|
60
|
+
end
|
61
|
+
END
|
62
|
+
verify_content_score(content, 0, 1, 1)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# 5. Add one to the condition count for each use of a conditional operator:
|
67
|
+
#
|
68
|
+
# == != <= >= < >
|
69
|
+
#
|
70
|
+
# 6. Add one to the condition count for each use of the following
|
71
|
+
# keywords:
|
72
|
+
#
|
73
|
+
# else case default try catch ?
|
74
|
+
#
|
75
|
+
# 7. Add one to the condition count for each unary conditional
|
76
|
+
# expression.
|
77
|
+
describe "when processing conditions" do
|
78
|
+
['==', '!=', '<=', '>=', '<', '>'].each do |each|
|
79
|
+
it "should find #{each}" do
|
80
|
+
content = <<-END
|
81
|
+
def method_name
|
82
|
+
2 #{each} 1
|
83
|
+
end
|
84
|
+
END
|
85
|
+
verify_content_score(content, 0, 0, 1)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -21,7 +21,7 @@ describe Roodi::Checks::AssignmentInConditionalCheck do
|
|
21
21
|
@roodi.check_content(content)
|
22
22
|
errors = @roodi.errors
|
23
23
|
errors.should_not be_empty
|
24
|
-
errors[0].should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
24
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
25
25
|
end
|
26
26
|
|
27
27
|
it "should reject an assignment inside an unless clause" do
|
@@ -31,7 +31,7 @@ describe Roodi::Checks::AssignmentInConditionalCheck do
|
|
31
31
|
@roodi.check_content(content)
|
32
32
|
errors = @roodi.errors
|
33
33
|
errors.should_not be_empty
|
34
|
-
errors[0].should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
34
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
35
35
|
end
|
36
36
|
|
37
37
|
it "should reject an assignment inside a while clause" do
|
@@ -41,7 +41,7 @@ describe Roodi::Checks::AssignmentInConditionalCheck do
|
|
41
41
|
@roodi.check_content(content)
|
42
42
|
errors = @roodi.errors
|
43
43
|
errors.should_not be_empty
|
44
|
-
errors[0].should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
44
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
45
45
|
end
|
46
46
|
|
47
47
|
it "should reject an assignment inside an unless clause" do
|
@@ -51,7 +51,7 @@ describe Roodi::Checks::AssignmentInConditionalCheck do
|
|
51
51
|
@roodi.check_content(content)
|
52
52
|
errors = @roodi.errors
|
53
53
|
errors.should_not be_empty
|
54
|
-
errors[0].should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
54
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
55
55
|
end
|
56
56
|
|
57
57
|
it "should reject an assignment inside a a ternary operator check clause" do
|
@@ -61,6 +61,6 @@ describe Roodi::Checks::AssignmentInConditionalCheck do
|
|
61
61
|
@roodi.check_content(content)
|
62
62
|
errors = @roodi.errors
|
63
63
|
errors.should_not be_empty
|
64
|
-
errors[0].should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
64
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
65
65
|
end
|
66
66
|
end
|
@@ -27,6 +27,6 @@ describe Roodi::Checks::CaseMissingElseCheck do
|
|
27
27
|
@roodi.check_content(content)
|
28
28
|
errors = @roodi.errors
|
29
29
|
errors.should_not be_empty
|
30
|
-
errors[0].should eql("dummy-file.rb:1 - Case statement is missing an else clause.")
|
30
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Case statement is missing an else clause.")
|
31
31
|
end
|
32
32
|
end
|
@@ -34,6 +34,6 @@ describe Roodi::Checks::ClassLineCountCheck do
|
|
34
34
|
@roodi.check_content(content)
|
35
35
|
errors = @roodi.errors
|
36
36
|
errors.should_not be_empty
|
37
|
-
errors[0].should eql("dummy-file.rb:1 - Class \"TwoLineClass\" has 2 lines. It should have 1 or less.")
|
37
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Class \"TwoLineClass\" has 2 lines. It should have 1 or less.")
|
38
38
|
end
|
39
39
|
end
|
@@ -34,6 +34,6 @@ describe Roodi::Checks::ClassNameCheck do
|
|
34
34
|
@roodi.check_content(content)
|
35
35
|
errors = @roodi.errors
|
36
36
|
errors.should_not be_empty
|
37
|
-
errors[0].should eql("dummy-file.rb:1 - Class name \"Bad_ClassName\" should match pattern /^[A-Z][a-zA-Z0-9]*$/")
|
37
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Class name \"Bad_ClassName\" should match pattern /^[A-Z][a-zA-Z0-9]*$/")
|
38
38
|
end
|
39
39
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::ClassVariableCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::ParseTreeRunner.new(Roodi::Checks::ClassVariableCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should reject class variables" do
|
9
|
+
content = <<-END
|
10
|
+
@@foo
|
11
|
+
END
|
12
|
+
@roodi.check_content(content)
|
13
|
+
errors = @roodi.errors
|
14
|
+
errors.should_not be_empty
|
15
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Don't use class variables. You might want to try a different design.")
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::ControlCouplingCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::ParseTreeRunner.new(Roodi::Checks::ControlCouplingCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should reject methods with if checks using a parameter" do
|
9
|
+
content = <<-END
|
10
|
+
def write(quoted, foo)
|
11
|
+
if quoted
|
12
|
+
write_quoted(@value)
|
13
|
+
else
|
14
|
+
puts @value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
END
|
18
|
+
@roodi.check_content(content)
|
19
|
+
errors = @roodi.errors
|
20
|
+
errors.should_not be_empty
|
21
|
+
errors[0].to_s.should eql("dummy-file.rb:2 - Method \"write\" uses the argument \"quoted\" for internal control.")
|
22
|
+
end
|
23
|
+
end
|
@@ -9,7 +9,7 @@ describe Roodi::Checks::CyclomaticComplexityBlockCheck do
|
|
9
9
|
@roodi.check_content(content)
|
10
10
|
errors = @roodi.errors
|
11
11
|
errors.should_not be_empty
|
12
|
-
errors[0].should eql("dummy-file.rb:2 - Block cyclomatic complexity is #{complexity}. It should be 0 or less.")
|
12
|
+
errors[0].to_s.should eql("dummy-file.rb:2 - Block cyclomatic complexity is #{complexity}. It should be 0 or less.")
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should find a simple block" do
|
@@ -9,7 +9,7 @@ describe Roodi::Checks::CyclomaticComplexityMethodCheck do
|
|
9
9
|
@roodi.check_content(content)
|
10
10
|
errors = @roodi.errors
|
11
11
|
errors.should_not be_empty
|
12
|
-
errors[0].should eql("dummy-file.rb:1 - Method name \"method_name\"
|
12
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Method name \"method_name\" cyclomatic complexity is #{complexity}. It should be 0 or less.")
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should find an if block" do
|
@@ -17,6 +17,30 @@ describe Roodi::Checks::EmptyRescueBodyCheck do
|
|
17
17
|
@roodi.errors.should be_empty
|
18
18
|
end
|
19
19
|
|
20
|
+
it "should accept a rescue body with a return" do
|
21
|
+
content = <<-END
|
22
|
+
begin
|
23
|
+
call_method
|
24
|
+
rescue
|
25
|
+
return true
|
26
|
+
end
|
27
|
+
END
|
28
|
+
@roodi.check_content(content)
|
29
|
+
@roodi.errors.should be_empty
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should accept a method call that Ruby won't tell apart from a variable (a vcall)" do
|
33
|
+
content = <<-END
|
34
|
+
begin
|
35
|
+
call_method
|
36
|
+
rescue
|
37
|
+
show_error
|
38
|
+
end
|
39
|
+
END
|
40
|
+
@roodi.check_content(content)
|
41
|
+
@roodi.errors.should be_empty
|
42
|
+
end
|
43
|
+
|
20
44
|
it "should accept a rescue body with content and a parameter" do
|
21
45
|
content = <<-END
|
22
46
|
begin
|
@@ -29,6 +53,30 @@ describe Roodi::Checks::EmptyRescueBodyCheck do
|
|
29
53
|
@roodi.errors.should be_empty
|
30
54
|
end
|
31
55
|
|
56
|
+
it "should accept a rescue body with an assignment" do
|
57
|
+
content = <<-END
|
58
|
+
begin
|
59
|
+
call_method
|
60
|
+
rescue Exception => e
|
61
|
+
my_var = 1
|
62
|
+
end
|
63
|
+
END
|
64
|
+
@roodi.check_content(content)
|
65
|
+
@roodi.errors.should be_empty
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should accept a rescue body with an attribute assignment" do
|
69
|
+
content = <<-END
|
70
|
+
begin
|
71
|
+
call_method
|
72
|
+
rescue Exception => e
|
73
|
+
self.var = 1
|
74
|
+
end
|
75
|
+
END
|
76
|
+
@roodi.check_content(content)
|
77
|
+
@roodi.errors.should be_empty
|
78
|
+
end
|
79
|
+
|
32
80
|
it "should reject an empty rescue block with no parameter" do
|
33
81
|
content = <<-END
|
34
82
|
begin
|
@@ -39,7 +87,7 @@ describe Roodi::Checks::EmptyRescueBodyCheck do
|
|
39
87
|
@roodi.check_content(content)
|
40
88
|
errors = @roodi.errors
|
41
89
|
errors.should_not be_empty
|
42
|
-
errors[0].should eql("dummy-file.rb:3 - Rescue block should not be empty.")
|
90
|
+
errors[0].to_s.should eql("dummy-file.rb:3 - Rescue block should not be empty.")
|
43
91
|
end
|
44
92
|
|
45
93
|
it "should reject an empty rescue block with a parameter" do
|
@@ -52,6 +100,6 @@ describe Roodi::Checks::EmptyRescueBodyCheck do
|
|
52
100
|
@roodi.check_content(content)
|
53
101
|
errors = @roodi.errors
|
54
102
|
errors.should_not be_empty
|
55
|
-
errors[0].should eql("dummy-file.rb:3 - Rescue block should not be empty.")
|
103
|
+
errors[0].to_s.should eql("dummy-file.rb:3 - Rescue block should not be empty.")
|
56
104
|
end
|
57
105
|
end
|
@@ -13,6 +13,6 @@ describe Roodi::Checks::ForLoopCheck do
|
|
13
13
|
@roodi.check_content(content)
|
14
14
|
errors = @roodi.errors
|
15
15
|
errors.should_not be_empty
|
16
|
-
errors[0].should eql("dummy-file.rb:1 - Don't use 'for' loops. Use Enumerable.each instead.")
|
16
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Don't use 'for' loops. Use Enumerable.each instead.")
|
17
17
|
end
|
18
18
|
end
|
@@ -34,6 +34,6 @@ describe Roodi::Checks::MethodLineCountCheck do
|
|
34
34
|
@roodi.check_content(content)
|
35
35
|
errors = @roodi.errors
|
36
36
|
errors.should_not be_empty
|
37
|
-
errors[0].should eql("dummy-file.rb:1 - Method \"two_line_method\" has 2 lines. It should have 1 or less.")
|
37
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Method \"two_line_method\" has 2 lines. It should have 1 or less.")
|
38
38
|
end
|
39
39
|
end
|
@@ -40,7 +40,7 @@ describe Roodi::Checks::MethodNameCheck do
|
|
40
40
|
@roodi.check_content(content)
|
41
41
|
@roodi.errors.should be_empty
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
it "should accept method names ending an equals sign" do
|
45
45
|
content = <<-END
|
46
46
|
def good_method_name=
|
@@ -50,94 +50,17 @@ describe Roodi::Checks::MethodNameCheck do
|
|
50
50
|
@roodi.errors.should be_empty
|
51
51
|
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
content = <<-END
|
64
|
-
def >>
|
53
|
+
describe "when processing non-text based method names" do
|
54
|
+
['<<', '>>', '==', '=', '<', '<=', '>', '>=', '[]', '[]='].each do |each|
|
55
|
+
it "should accept #{each} as a method name" do
|
56
|
+
content = <<-END
|
57
|
+
def #{each}
|
58
|
+
end
|
59
|
+
END
|
60
|
+
@roodi.check_content(content)
|
61
|
+
@roodi.errors.should be_empty
|
62
|
+
end
|
65
63
|
end
|
66
|
-
END
|
67
|
-
@roodi.check_content(content)
|
68
|
-
@roodi.errors.should be_empty
|
69
|
-
end
|
70
|
-
|
71
|
-
it "should accept == as a method name" do
|
72
|
-
content = <<-END
|
73
|
-
def ==
|
74
|
-
end
|
75
|
-
END
|
76
|
-
@roodi.check_content(content)
|
77
|
-
@roodi.errors.should be_empty
|
78
|
-
end
|
79
|
-
|
80
|
-
it "should accept === as a method name" do
|
81
|
-
content = <<-END
|
82
|
-
def ===
|
83
|
-
end
|
84
|
-
END
|
85
|
-
@roodi.check_content(content)
|
86
|
-
@roodi.errors.should be_empty
|
87
|
-
end
|
88
|
-
|
89
|
-
it "should accept < as a method name" do
|
90
|
-
content = <<-END
|
91
|
-
def <
|
92
|
-
end
|
93
|
-
END
|
94
|
-
@roodi.check_content(content)
|
95
|
-
@roodi.errors.should be_empty
|
96
|
-
end
|
97
|
-
|
98
|
-
it "should accept <= as a method name" do
|
99
|
-
content = <<-END
|
100
|
-
def <=
|
101
|
-
end
|
102
|
-
END
|
103
|
-
@roodi.check_content(content)
|
104
|
-
@roodi.errors.should be_empty
|
105
|
-
end
|
106
|
-
|
107
|
-
it "should accept > as a method name" do
|
108
|
-
content = <<-END
|
109
|
-
def >
|
110
|
-
end
|
111
|
-
END
|
112
|
-
@roodi.check_content(content)
|
113
|
-
@roodi.errors.should be_empty
|
114
|
-
end
|
115
|
-
|
116
|
-
it "should accept >= as a method name" do
|
117
|
-
content = <<-END
|
118
|
-
def >=
|
119
|
-
end
|
120
|
-
END
|
121
|
-
@roodi.check_content(content)
|
122
|
-
@roodi.errors.should be_empty
|
123
|
-
end
|
124
|
-
|
125
|
-
it "should accept [] as a method name" do
|
126
|
-
content = <<-END
|
127
|
-
def []
|
128
|
-
end
|
129
|
-
END
|
130
|
-
@roodi.check_content(content)
|
131
|
-
@roodi.errors.should be_empty
|
132
|
-
end
|
133
|
-
|
134
|
-
it "should accept []= as a method name" do
|
135
|
-
content = <<-END
|
136
|
-
def []=
|
137
|
-
end
|
138
|
-
END
|
139
|
-
@roodi.check_content(content)
|
140
|
-
@roodi.errors.should be_empty
|
141
64
|
end
|
142
65
|
|
143
66
|
it "should reject camel case method names" do
|
@@ -148,6 +71,6 @@ describe Roodi::Checks::MethodNameCheck do
|
|
148
71
|
@roodi.check_content(content)
|
149
72
|
errors = @roodi.errors
|
150
73
|
errors.should_not be_empty
|
151
|
-
errors[0].should eql("dummy-file.rb:1 - Method name \"badMethodName\" should match pattern /^[_a-z<>=\\[
|
74
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Method name \"badMethodName\" should match pattern /^[_a-z<>=\\[|+-\\/\\*`]+[_a-z0-9_<>=~@\\[\\]]*[=!\\?]?$/")
|
152
75
|
end
|
153
76
|
end
|
@@ -34,6 +34,6 @@ describe Roodi::Checks::ModuleLineCountCheck do
|
|
34
34
|
@roodi.check_content(content)
|
35
35
|
errors = @roodi.errors
|
36
36
|
errors.should_not be_empty
|
37
|
-
errors[0].should eql("dummy-file.rb:1 - Module \"TwoLineModule\" has 2 lines. It should have 1 or less.")
|
37
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Module \"TwoLineModule\" has 2 lines. It should have 1 or less.")
|
38
38
|
end
|
39
39
|
end
|
@@ -22,6 +22,6 @@ describe Roodi::Checks::ModuleNameCheck do
|
|
22
22
|
@roodi.check_content(content)
|
23
23
|
errors = @roodi.errors
|
24
24
|
errors.should_not be_empty
|
25
|
-
errors[0].should eql("dummy-file.rb:1 - Module name \"Bad_ModuleName\" should match pattern /^[A-Z][a-zA-Z0-9]*$/")
|
25
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Module name \"Bad_ModuleName\" should match pattern /^[A-Z][a-zA-Z0-9]*$/")
|
26
26
|
end
|
27
27
|
end
|
@@ -31,7 +31,7 @@ describe Roodi::Checks::ParameterNumberCheck do
|
|
31
31
|
@roodi.check_content(content)
|
32
32
|
errors = @roodi.errors
|
33
33
|
errors.should_not be_empty
|
34
|
-
errors[0].should eql("dummy-file.rb:1 - Method name \"two_parameter_method\" has 2 parameters. It should have 1 or less.")
|
34
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Method name \"two_parameter_method\" has 2 parameters. It should have 1 or less.")
|
35
35
|
end
|
36
36
|
|
37
37
|
it "should cope with default values on parameters" do
|
@@ -42,6 +42,6 @@ describe Roodi::Checks::ParameterNumberCheck do
|
|
42
42
|
@roodi.check_content(content)
|
43
43
|
errors = @roodi.errors
|
44
44
|
errors.should_not be_empty
|
45
|
-
errors[0].should eql("dummy-file.rb:1 - Method name \"two_parameter_method\" has 2 parameters. It should have 1 or less.")
|
45
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Method name \"two_parameter_method\" has 2 parameters. It should have 1 or less.")
|
46
46
|
end
|
47
47
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roodi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marty Andrews
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-03-07 00:00:00 +11:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -40,7 +40,7 @@ dependencies:
|
|
40
40
|
requirements:
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: 1.
|
43
|
+
version: 1.8.2
|
44
44
|
version:
|
45
45
|
description: Roodi stands for Ruby Object Oriented Design Inferometer. It parses your Ruby code and warns you about design issues you have based on the checks that is has configured.
|
46
46
|
email:
|
@@ -63,11 +63,14 @@ files:
|
|
63
63
|
- bin/roodi-describe
|
64
64
|
- lib/roodi.rb
|
65
65
|
- lib/roodi/checks.rb
|
66
|
+
- lib/roodi/checks/abc_metric_method_check.rb
|
66
67
|
- lib/roodi/checks/assignment_in_conditional_check.rb
|
67
68
|
- lib/roodi/checks/case_missing_else_check.rb
|
68
69
|
- lib/roodi/checks/check.rb
|
69
70
|
- lib/roodi/checks/class_line_count_check.rb
|
70
71
|
- lib/roodi/checks/class_name_check.rb
|
72
|
+
- lib/roodi/checks/class_variable_check.rb
|
73
|
+
- lib/roodi/checks/control_coupling_check.rb
|
71
74
|
- lib/roodi/checks/cyclomatic_complexity_block_check.rb
|
72
75
|
- lib/roodi/checks/cyclomatic_complexity_check.rb
|
73
76
|
- lib/roodi/checks/cyclomatic_complexity_method_check.rb
|
@@ -82,15 +85,19 @@ files:
|
|
82
85
|
- lib/roodi/checks/parameter_number_check.rb
|
83
86
|
- lib/roodi/core.rb
|
84
87
|
- lib/roodi/core/checking_visitor.rb
|
88
|
+
- lib/roodi/core/error.rb
|
85
89
|
- lib/roodi/core/iterator_visitor.rb
|
86
90
|
- lib/roodi/core/parse_tree_runner.rb
|
87
91
|
- lib/roodi/core/parser.rb
|
88
92
|
- lib/roodi/core/visitable_sexp.rb
|
89
93
|
- roodi.yml
|
94
|
+
- spec/roodi/checks/abc_metric_method_check_spec.rb
|
90
95
|
- spec/roodi/checks/assignment_in_conditional_check_spec.rb
|
91
96
|
- spec/roodi/checks/case_missing_else_check_spec.rb
|
92
97
|
- spec/roodi/checks/class_line_count_check_spec.rb
|
93
98
|
- spec/roodi/checks/class_name_check_spec.rb
|
99
|
+
- spec/roodi/checks/class_variable_check_spec.rb
|
100
|
+
- spec/roodi/checks/control_coupling_check_spec.rb
|
94
101
|
- spec/roodi/checks/cyclomatic_complexity_block_check_spec.rb
|
95
102
|
- spec/roodi/checks/cyclomatic_complexity_method_check_spec.rb
|
96
103
|
- spec/roodi/checks/empty_rescue_body_check_spec.rb
|