roodi 1.3.0 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +7 -0
  3. data/Rakefile +1 -0
  4. data/lib/roodi.rb +1 -1
  5. data/lib/roodi/checks.rb +3 -0
  6. data/lib/roodi/checks/abc_metric_method_check.rb +77 -0
  7. data/lib/roodi/checks/check.rb +6 -2
  8. data/lib/roodi/checks/class_name_check.rb +1 -1
  9. data/lib/roodi/checks/class_variable_check.rb +24 -0
  10. data/lib/roodi/checks/control_coupling_check.rb +21 -0
  11. data/lib/roodi/checks/cyclomatic_complexity_check.rb +3 -3
  12. data/lib/roodi/checks/cyclomatic_complexity_method_check.rb +1 -1
  13. data/lib/roodi/checks/empty_rescue_body_check.rb +9 -4
  14. data/lib/roodi/checks/method_name_check.rb +1 -1
  15. data/lib/roodi/core/error.rb +17 -0
  16. data/roodi.yml +2 -1
  17. data/spec/roodi/checks/abc_metric_method_check_spec.rb +89 -0
  18. data/spec/roodi/checks/assignment_in_conditional_check_spec.rb +5 -5
  19. data/spec/roodi/checks/case_missing_else_check_spec.rb +1 -1
  20. data/spec/roodi/checks/class_line_count_check_spec.rb +1 -1
  21. data/spec/roodi/checks/class_name_check_spec.rb +1 -1
  22. data/spec/roodi/checks/class_variable_check_spec.rb +17 -0
  23. data/spec/roodi/checks/control_coupling_check_spec.rb +23 -0
  24. data/spec/roodi/checks/cyclomatic_complexity_block_check_spec.rb +1 -1
  25. data/spec/roodi/checks/cyclomatic_complexity_method_check_spec.rb +1 -1
  26. data/spec/roodi/checks/empty_rescue_body_check_spec.rb +50 -2
  27. data/spec/roodi/checks/for_loop_check_spec.rb +1 -1
  28. data/spec/roodi/checks/method_line_count_check_spec.rb +1 -1
  29. data/spec/roodi/checks/method_name_check_spec.rb +12 -89
  30. data/spec/roodi/checks/module_line_count_check_spec.rb +1 -1
  31. data/spec/roodi/checks/module_name_check_spec.rb +1 -1
  32. data/spec/roodi/checks/parameter_number_check_spec.rb +2 -2
  33. data/spec/spec_helper.rb +1 -0
  34. metadata +10 -3
data/History.txt CHANGED
@@ -1,3 +1,7 @@
1
+ = 1.3.1
2
+
3
+ * wrapped errors in an object to become more usable as an API.
4
+
1
5
  = 1.3.0
2
6
 
3
7
  * added case missing else check.
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
@@ -30,3 +30,4 @@ task :roodi do
30
30
  roodi(Dir.glob(pattern))
31
31
  end
32
32
 
33
+ task :default => :spec
data/lib/roodi.rb CHANGED
@@ -2,5 +2,5 @@ require 'roodi/checks'
2
2
  require 'roodi/core'
3
3
 
4
4
  module Roodi
5
- VERSION = '1.3.0'
5
+ VERSION = '1.3.2'
6
6
  end
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
@@ -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
- evaluate(node)
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 << "#{position(offset)} - #{error}"
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 nameing convention makes your code easier to read.
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 complexity_node_types.include? node.node_type
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]}\" has a cyclomatic complexity is #{complexity}. It should be #{@complexity} or less." unless complexity <= @complexity
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
- found_statement = false
22
- found_statement = found_statement || node.node_type == :fcall
23
- node.children.each { |child| found_statement = found_statement || has_statement?(child) }
24
- found_statement
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<>=\[\]|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
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<>=\[\]|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/ }
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\" has a cyclomatic complexity is #{complexity}. It should be 0 or less.")
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
- it "should accept << as a method name" do
54
- content = <<-END
55
- def <<
56
- end
57
- END
58
- @roodi.check_content(content)
59
- @roodi.errors.should be_empty
60
- end
61
-
62
- it "should accept >> as a method name" do
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<>=\\[\\]|+-\\/\\*`]+[_a-z0-9_<>=~@\\[\\]]*[=!\\?]?$/")
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
@@ -1,2 +1,3 @@
1
1
  $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
2
2
  require 'roodi'
3
+ require 'spec'
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.0
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: 2008-09-19 00:00:00 +10:00
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.7.0
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