roodi 1.4.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. data/History.txt +8 -0
  2. data/Manifest.txt +3 -1
  3. data/Rakefile +2 -2
  4. data/lib/roodi.rb +1 -1
  5. data/lib/roodi/checks.rb +1 -0
  6. data/lib/roodi/checks/abc_metric_method_check.rb +1 -1
  7. data/lib/roodi/checks/assignment_in_conditional_check.rb +4 -2
  8. data/lib/roodi/checks/case_missing_else_check.rb +1 -1
  9. data/lib/roodi/checks/check.rb +28 -4
  10. data/lib/roodi/checks/class_variable_check.rb +1 -1
  11. data/lib/roodi/checks/control_coupling_check.rb +2 -2
  12. data/lib/roodi/checks/cyclomatic_complexity_block_check.rb +11 -4
  13. data/lib/roodi/checks/cyclomatic_complexity_check.rb +24 -6
  14. data/lib/roodi/checks/cyclomatic_complexity_method_check.rb +12 -4
  15. data/lib/roodi/checks/empty_rescue_body_check.rb +7 -8
  16. data/lib/roodi/checks/for_loop_check.rb +1 -1
  17. data/lib/roodi/checks/line_count_check.rb +1 -1
  18. data/lib/roodi/checks/name_check.rb +1 -1
  19. data/lib/roodi/checks/npath_complexity_check.rb +73 -0
  20. data/lib/roodi/checks/npath_complexity_method_check.rb +28 -0
  21. data/lib/roodi/checks/parameter_number_check.rb +1 -1
  22. data/lib/roodi/core/checking_visitor.rb +7 -4
  23. data/lib/roodi/core/parser.rb +9 -2
  24. data/lib/roodi/core/runner.rb +3 -3
  25. data/lib/roodi/core/visitable_sexp.rb +6 -1
  26. data/spec/roodi/checks/assignment_in_conditional_check_spec.rb +41 -0
  27. data/spec/roodi/checks/cyclomatic_complexity_block_check_spec.rb +31 -0
  28. data/spec/roodi/checks/cyclomatic_complexity_method_check_spec.rb +17 -0
  29. data/spec/roodi/checks/npath_complexity_method_check_spec.rb +53 -0
  30. metadata +5 -13
  31. data/lib/roodi/core/iterator_visitor.rb +0 -19
@@ -1,3 +1,11 @@
1
+ = 2.0.0
2
+
3
+ * Changed internal structure to use a more pure visitor like pattern.
4
+ * Got *much* faster as a result of the change.
5
+ * Design change fixed 'feature' where nested blocks would all get listed if the inner one exceeded complexity.
6
+ * Outline for NPath complexity check is now possible. Not working yet though.
7
+ * Removed dependency on facets library.
8
+
1
9
  = 1.4.0
2
10
 
3
11
  * Upgraded from ParseTree to ruby_parser.
@@ -25,11 +25,12 @@ lib/roodi/checks/method_name_check.rb
25
25
  lib/roodi/checks/module_line_count_check.rb
26
26
  lib/roodi/checks/module_name_check.rb
27
27
  lib/roodi/checks/name_check.rb
28
+ lib/roodi/checks/npath_complexity_check.rb
29
+ lib/roodi/checks/npath_complexity_method_check.rb
28
30
  lib/roodi/checks/parameter_number_check.rb
29
31
  lib/roodi/core.rb
30
32
  lib/roodi/core/checking_visitor.rb
31
33
  lib/roodi/core/error.rb
32
- lib/roodi/core/iterator_visitor.rb
33
34
  lib/roodi/core/parser.rb
34
35
  lib/roodi/core/runner.rb
35
36
  lib/roodi/core/visitable_sexp.rb
@@ -50,5 +51,6 @@ spec/roodi/checks/method_line_count_check_spec.rb
50
51
  spec/roodi/checks/method_name_check_spec.rb
51
52
  spec/roodi/checks/module_line_count_check_spec.rb
52
53
  spec/roodi/checks/module_name_check_spec.rb
54
+ spec/roodi/checks/npath_complexity_method_check_spec.rb
53
55
  spec/roodi/checks/parameter_number_check_spec.rb
54
56
  spec/spec_helper.rb
data/Rakefile CHANGED
@@ -8,12 +8,12 @@ require 'roodi'
8
8
 
9
9
  Hoe.new('roodi', Roodi::VERSION) do |p|
10
10
  p.developer('Marty Andrews', 'marty@cogentconsulting.com.au')
11
- p.extra_deps = ['ruby_parser', 'facets']
11
+ p.extra_deps = ['ruby_parser']
12
12
  p.remote_rdoc_dir = ''
13
13
  end
14
14
 
15
15
  def roodi(ruby_files)
16
- roodi = Roodi::Core::ParseTreeRunner.new
16
+ roodi = Roodi::Core::Runner.new
17
17
  ruby_files.each { |file| roodi.check_file(file) }
18
18
  roodi.errors.each {|error| puts error}
19
19
  puts "\nFound #{roodi.errors.size} errors."
@@ -2,5 +2,5 @@ require 'roodi/checks'
2
2
  require 'roodi/core'
3
3
 
4
4
  module Roodi
5
- VERSION = '1.4.0'
5
+ VERSION = '2.0.0'
6
6
  end
@@ -13,4 +13,5 @@ require 'roodi/checks/method_line_count_check'
13
13
  require 'roodi/checks/method_name_check'
14
14
  require 'roodi/checks/module_line_count_check'
15
15
  require 'roodi/checks/module_name_check'
16
+ require 'roodi/checks/npath_complexity_method_check'
16
17
  require 'roodi/checks/parameter_number_check'
@@ -25,7 +25,7 @@ module Roodi
25
25
  [:defn]
26
26
  end
27
27
 
28
- def evaluate(node)
28
+ def evaluate_start(node)
29
29
  method_name = node[1]
30
30
  a = count_assignments(node)
31
31
  b = count_branches(node)
@@ -15,7 +15,7 @@ module Roodi
15
15
  [:if, :while]
16
16
  end
17
17
 
18
- def evaluate(node)
18
+ def evaluate_start(node)
19
19
  add_error("Found = in conditional. It should probably be an ==") if has_assignment?(node[1])
20
20
  end
21
21
 
@@ -24,7 +24,9 @@ module Roodi
24
24
  def has_assignment?(node)
25
25
  found_assignment = false
26
26
  found_assignment = found_assignment || node.node_type == :lasgn
27
- node.children.each { |child| found_assignment = found_assignment || has_assignment?(child) }
27
+ if (node.node_type == :and or node.node_type == :or)
28
+ node.children.each { |child| found_assignment = found_assignment || has_assignment?(child) }
29
+ end
28
30
  found_assignment
29
31
  end
30
32
  end
@@ -12,7 +12,7 @@ module Roodi
12
12
  [:case]
13
13
  end
14
14
 
15
- def evaluate(node)
15
+ def evaluate_start(node)
16
16
  add_error "Case statement is missing an else clause." unless node.last
17
17
  end
18
18
  end
@@ -3,19 +3,43 @@ require 'roodi/core/error'
3
3
  module Roodi
4
4
  module Checks
5
5
  class Check
6
+ NODE_TYPES = [:defn, :module, :resbody, :lvar, :cvar, :class, :if, :while, :until, :for, :rescue, :case, :when, :and, :or]
7
+
6
8
  def initialize
7
9
  @errors = []
8
10
  end
9
11
 
12
+ NODE_TYPES.each do |node|
13
+ start_node_method = "evaluate_start_#{node}"
14
+ end_node_method = "evaluate_end_#{node}"
15
+ define_method(start_node_method) { } unless self.respond_to?(start_node_method)
16
+ define_method(end_node_method) { } unless self.respond_to?(end_node_method)
17
+ end
18
+
10
19
  def position(offset = 0)
11
20
  "#{@line[2]}:#{@line[1] + offset}"
12
21
  end
22
+
23
+ def evaluate_start(node)
24
+ end
25
+
26
+ def evaluate_end(node)
27
+ end
13
28
 
14
- def evaluate_node(node)
29
+ def evaluate_node(position, node)
15
30
  @node = 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
31
+ eval_method = "evaluate_#{position}_#{node.node_type}"
32
+ self.send(eval_method, node)
33
+ end
34
+
35
+ def evaluate_node_start(node)
36
+ evaluate_node(:start, node)
37
+ evaluate_start(node)
38
+ end
39
+
40
+ def evaluate_node_end(node)
41
+ evaluate_node(:end, node)
42
+ evaluate_end(node)
19
43
  end
20
44
 
21
45
  def add_error(error)
@@ -16,7 +16,7 @@ module Roodi
16
16
  [:cvar]
17
17
  end
18
18
 
19
- def evaluate(node)
19
+ def evaluate_start(node)
20
20
  add_error "Don't use class variables. You might want to try a different design."
21
21
  end
22
22
  end
@@ -7,12 +7,12 @@ module Roodi
7
7
  [:defn, :lvar]
8
8
  end
9
9
 
10
- def evaluate_defn(node)
10
+ def evaluate_start_defn(node)
11
11
  @method_name = node[1]
12
12
  @arguments = node[2][1..-1]
13
13
  end
14
14
 
15
- def evaluate_lvar(node)
15
+ def evaluate_start_lvar(node)
16
16
  add_error "Method \"#{@method_name}\" uses the argument \"#{node[1]}\" for internal control." if @arguments.detect {|each| each == node[1]}
17
17
  end
18
18
  end
@@ -20,12 +20,19 @@ module Roodi
20
20
  end
21
21
 
22
22
  def interesting_nodes
23
- [:iter]
23
+ [:iter] + COMPLEXITY_NODE_TYPES
24
24
  end
25
25
 
26
- def evaluate(node)
27
- complexity = count_complexity(node)
28
- add_error "Block cyclomatic complexity is #{complexity}. It should be #{@complexity} or less." unless complexity <= @complexity
26
+ def evaluate_start_iter(node)
27
+ increase_depth
28
+ end
29
+
30
+ def evaluate_end_iter(node)
31
+ decrease_depth
32
+ end
33
+
34
+ def evaluate_matching_end
35
+ add_error "Block cyclomatic complexity is #{@count}. It should be #{@complexity} or less." unless @count <= @complexity
29
36
  end
30
37
  end
31
38
  end
@@ -8,6 +8,14 @@ module Roodi
8
8
  def initialize(complexity)
9
9
  super()
10
10
  @complexity = complexity
11
+ @count = 0
12
+ @counting = 0
13
+ end
14
+
15
+ COMPLEXITY_NODE_TYPES.each do |type|
16
+ define_method "evaluate_start_#{type}" do
17
+ @count = @count + 1 if counting?
18
+ end
11
19
  end
12
20
 
13
21
  protected
@@ -15,14 +23,24 @@ module Roodi
15
23
  def count_complexity(node)
16
24
  count_branches(node) + 1
17
25
  end
26
+
27
+ def increase_depth
28
+ @count = 1 unless counting?
29
+ @counting = @counting + 1
30
+ end
18
31
 
32
+ def decrease_depth
33
+ @counting = @counting - 1
34
+ if @counting <= 0
35
+ @counting = 0
36
+ evaluate_matching_end
37
+ end
38
+ end
39
+
19
40
  private
20
-
21
- def count_branches(node)
22
- count = 0
23
- count = count + 1 if COMPLEXITY_NODE_TYPES.include? node.node_type
24
- node.children.each {|node| count += count_branches(node)}
25
- count
41
+
42
+ def counting?
43
+ @counting > 0
26
44
  end
27
45
  end
28
46
  end
@@ -20,12 +20,20 @@ module Roodi
20
20
  end
21
21
 
22
22
  def interesting_nodes
23
- [:defn]
23
+ [:defn] + COMPLEXITY_NODE_TYPES
24
24
  end
25
25
 
26
- def evaluate(node)
27
- complexity = count_complexity(node)
28
- add_error "Method name \"#{node[1]}\" cyclomatic complexity is #{complexity}. It should be #{@complexity} or less." unless complexity <= @complexity
26
+ def evaluate_start_defn(node)
27
+ @method_name = @node[1]
28
+ increase_depth
29
+ end
30
+
31
+ def evaluate_end_defn(node)
32
+ decrease_depth
33
+ end
34
+
35
+ def evaluate_matching_end
36
+ add_error "Method name \"#{@method_name}\" cyclomatic complexity is #{@count}. It should be #{@complexity} or less." unless @count <= @complexity
29
37
  end
30
38
  end
31
39
  end
@@ -7,26 +7,25 @@ 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, :nil, :call]
10
+ STATEMENT_NODES = [:fcall, :return, :attrasgn, :vcall, :nil, :call, :lasgn]
11
11
 
12
12
  def interesting_nodes
13
13
  [:resbody]
14
14
  end
15
15
 
16
- def evaluate(node)
17
- add_error("Rescue block should not be empty.") unless has_statement?(node)
16
+ def evaluate_start(node)
17
+ add_error("Rescue block should not be empty.") unless has_statement?(node.children[1])
18
18
  end
19
19
 
20
20
  private
21
21
 
22
22
  def has_statement?(node)
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) }
23
+ false unless node
24
+ has_local_statement?(node) or node.children.any? { |child| has_statement?(child) } if node
26
25
  end
27
26
 
28
- def assigning_other_than_exception_to_local_variable?(node)
29
- node.node_type == :lasgn && node[2].to_a != [:gvar, :$!]
27
+ def has_local_statement?(node)
28
+ STATEMENT_NODES.include?(node.node_type)
30
29
  end
31
30
  end
32
31
  end
@@ -12,7 +12,7 @@ module Roodi
12
12
  [:for]
13
13
  end
14
14
 
15
- def evaluate(node)
15
+ def evaluate_start(node)
16
16
  add_error "Don't use 'for' loops. Use Enumerable.each instead."
17
17
  end
18
18
  end
@@ -14,7 +14,7 @@ module Roodi
14
14
  @interesting_nodes
15
15
  end
16
16
 
17
- def evaluate(node)
17
+ def evaluate_start(node)
18
18
  line_count = count_lines(node)
19
19
  add_error "#{@message_prefix} \"#{node[1]}\" has #{line_count} lines. It should have #{@line_count} or less." unless line_count <= @line_count
20
20
  end
@@ -14,7 +14,7 @@ module Roodi
14
14
  @interesting_nodes
15
15
  end
16
16
 
17
- def evaluate(node)
17
+ def evaluate_start(node)
18
18
  name = find_name(node)
19
19
  add_error "#{@message_prefix} name \"#{name}\" should match pattern #{@pattern.inspect}" unless name.to_s =~ @pattern
20
20
  end
@@ -0,0 +1,73 @@
1
+ require 'roodi/checks/check'
2
+
3
+ module Roodi
4
+ module Checks
5
+ class NpathComplexityCheck < Check
6
+ # , :when, :and, :or
7
+ MULTIPLYING_NODE_TYPES = [:if, :while, :until, :for, :case]
8
+ ADDING_NODE_TYPES = [:rescue]
9
+ COMPLEXITY_NODE_TYPES = MULTIPLYING_NODE_TYPES + ADDING_NODE_TYPES
10
+
11
+ def initialize(complexity)
12
+ super()
13
+ @complexity = complexity
14
+ @value_stack = []
15
+ @current_value = 1
16
+ end
17
+
18
+ def evalute_start_if(node)
19
+ push_value
20
+ end
21
+
22
+ def evalute_start_while(node)
23
+ push_value
24
+ end
25
+
26
+ def evalute_start_until(node)
27
+ push_value
28
+ end
29
+
30
+ def evalute_start_for(node)
31
+ push_value
32
+ end
33
+
34
+ def evalute_start_case(node)
35
+ push_value
36
+ end
37
+
38
+ def evalute_start_rescue(node)
39
+ push_value
40
+ end
41
+
42
+ MULTIPLYING_NODE_TYPES.each do |type|
43
+ define_method "evaluate_end_#{type}" do |node|
44
+ leave_multiplying_conditional
45
+ end
46
+ end
47
+
48
+ ADDING_NODE_TYPES.each do |type|
49
+ define_method "evaluate_end_#{type}" do |node|
50
+ leave_multiplying_conditional
51
+ end
52
+ end
53
+
54
+ protected
55
+
56
+ def push_value
57
+ @value_stack.push @current_value
58
+ @current_value = 1
59
+ end
60
+
61
+ def leave_multiplying_conditional
62
+ pop = @value_stack.pop
63
+ @current_value = (@current_value + 1) * pop
64
+ end
65
+
66
+ def leave_adding_conditional
67
+ pop = @value_stack.pop
68
+ puts "#{type}, so adding #{pop}"
69
+ @current_value = @current_value - 1 + pop
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,28 @@
1
+ require 'roodi/checks/npath_complexity_check'
2
+
3
+ module Roodi
4
+ module Checks
5
+ # Checks Npath complexity of a method against a specified limit.
6
+ class NpathComplexityMethodCheck < NpathComplexityCheck
7
+ DEFAULT_COMPLEXITY = 8
8
+
9
+ def initialize(options = {})
10
+ complexity = options['complexity'] || DEFAULT_COMPLEXITY
11
+ super(complexity)
12
+ end
13
+
14
+ def interesting_nodes
15
+ [:defn] + COMPLEXITY_NODE_TYPES
16
+ end
17
+
18
+ def evaluate_start_defn(node)
19
+ @method_name = @node[1]
20
+ push_value
21
+ end
22
+
23
+ def evaluate_end_defn(node)
24
+ add_error "Method name \"#{@method_name}\" n-path complexity is #{@current_value}. It should be #{@complexity} or less." unless @current_value <= @complexity
25
+ end
26
+ end
27
+ end
28
+ end
@@ -19,7 +19,7 @@ module Roodi
19
19
  [:defn]
20
20
  end
21
21
 
22
- def evaluate(node)
22
+ def evaluate_start(node)
23
23
  method_name = node[1]
24
24
  arguments = node[2]
25
25
  parameter_count = arguments.inject(-1) { |count, each| count = count + (each.class == Symbol ? 1 : 0) }
@@ -13,11 +13,14 @@ module Roodi
13
13
  end
14
14
  end
15
15
 
16
- def visit(node)
16
+ def visit(node)
17
17
  checks = @checks[node.node_type]
18
- checks.each {|check| check.evaluate_node(node)} unless checks.nil?
19
- nil
20
- end
18
+ checks.each {|check| check.evaluate_node_start(node)} unless checks.nil?
19
+
20
+ node.visitable_children.each {|sexp| sexp.accept(self)}
21
+
22
+ checks.each {|check| check.evaluate_node_end(node)} unless checks.nil?
23
+ end
21
24
  end
22
25
  end
23
26
  end
@@ -1,7 +1,5 @@
1
1
  require 'rubygems'
2
2
  require 'ruby_parser'
3
- require 'facets'
4
-
5
3
 
6
4
  module Roodi
7
5
  module Core
@@ -14,6 +12,15 @@ module Roodi
14
12
 
15
13
  private
16
14
 
15
+ def silence_stream(stream)
16
+ old_stream = stream.dup
17
+ stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
18
+ stream.sync = true
19
+ yield
20
+ ensure
21
+ stream.reopen(old_stream)
22
+ end
23
+
17
24
  def silent_parse(content, filename)
18
25
  @parser ||= RubyParser.new
19
26
  @parser.parse(content, filename)
@@ -2,7 +2,6 @@ require 'pp'
2
2
  require 'yaml'
3
3
 
4
4
  require 'roodi/core/checking_visitor'
5
- require 'roodi/core/iterator_visitor'
6
5
  require 'roodi/core/parser'
7
6
  require 'roodi/core/visitable_sexp'
8
7
 
@@ -16,13 +15,14 @@ module Roodi
16
15
  def initialize(*checks)
17
16
  @config = DEFAULT_CONFIG
18
17
  @checks = checks unless checks.empty?
18
+ @checks ||= load_checks
19
+ @checker ||= CheckingVisitor.new(@checks)
19
20
  @parser = Parser.new
20
21
  end
21
22
 
22
23
  def check(filename, content)
23
- @checks ||= load_checks
24
24
  node = parse(filename, content)
25
- node.accept(IteratorVisitor.new(CheckingVisitor.new(@checks))) if node
25
+ node.accept(@checker) if node
26
26
  end
27
27
 
28
28
  def check_content(content)
@@ -13,8 +13,13 @@ class Sexp
13
13
  def children
14
14
  find_all { | sexp | Sexp === sexp }
15
15
  end
16
-
16
+
17
17
  def is_language_node?
18
18
  first.class == Symbol
19
19
  end
20
+
21
+ def visitable_children
22
+ parent = is_language_node? ? sexp_body : self
23
+ parent.children
24
+ end
20
25
  end
@@ -61,4 +61,45 @@ describe Roodi::Checks::AssignmentInConditionalCheck do
61
61
  errors.should_not be_empty
62
62
  errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
63
63
  end
64
+
65
+ it "should reject an assignment after an 'and'" do
66
+ content = <<-END
67
+ call_foo if bar and bam = baz
68
+ END
69
+ @roodi.check_content(content)
70
+ errors = @roodi.errors
71
+ errors.should_not be_empty
72
+ errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
73
+ end
74
+
75
+
76
+ it "should reject an assignment after an 'or'" do
77
+ content = <<-END
78
+ call_foo if bar or bam = baz
79
+ END
80
+ @roodi.check_content(content)
81
+ errors = @roodi.errors
82
+ errors.should_not be_empty
83
+ errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
84
+ end
85
+
86
+ it "should reject an assignment after an '&&'" do
87
+ content = <<-END
88
+ call_foo if bar && bam = baz
89
+ END
90
+ @roodi.check_content(content)
91
+ errors = @roodi.errors
92
+ errors.should_not be_empty
93
+ errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
94
+ end
95
+
96
+ it "should reject an assignment after an '||'" do
97
+ content = <<-END
98
+ call_foo if bar || bam = baz
99
+ END
100
+ @roodi.check_content(content)
101
+ errors = @roodi.errors
102
+ errors.should_not be_empty
103
+ errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
104
+ end
64
105
  end
@@ -33,4 +33,35 @@ describe Roodi::Checks::CyclomaticComplexityBlockCheck do
33
33
  END
34
34
  verify_content_complexity(content, 2)
35
35
  end
36
+
37
+ it "should evaluate real example 1 correctly" do
38
+ content = <<-END
39
+ def method_name
40
+ UNIXMbox.lock(@filename) {|f|
41
+ begin
42
+ f.each do |line|
43
+ if /\AFrom / === line
44
+ w.close if w
45
+ File.utime time, time, port.filename if time
46
+
47
+ port = @real.new_port
48
+ w = port.wopen
49
+ time = fromline2time(line)
50
+ else
51
+ w.print line if w
52
+ end
53
+ end
54
+ ensure
55
+ if w and not w.closed?
56
+ w.close
57
+ File.utime time, time, port.filename if time
58
+ end
59
+ end
60
+ f.truncate(0) unless @readonly
61
+ @updated = Time.now
62
+ }
63
+ end
64
+ END
65
+ verify_content_complexity(content, 9)
66
+ end
36
67
  end
@@ -180,4 +180,21 @@ describe Roodi::Checks::CyclomaticComplexityMethodCheck do
180
180
  verify_content_complexity(content, 5)
181
181
  end
182
182
 
183
+ it "should count only a single method" do
184
+ content = <<-END
185
+ def method_name_1
186
+ call_foo if some_condition
187
+ end
188
+ def method_name_2
189
+ call_foo if some_condition
190
+ end
191
+ END
192
+
193
+ @roodi.check_content(content)
194
+ errors = @roodi.errors
195
+ errors.should_not be_empty
196
+ errors[0].to_s.should eql("dummy-file.rb:1 - Method name \"method_name_1\" cyclomatic complexity is 2. It should be 0 or less.")
197
+ errors[1].to_s.should eql("dummy-file.rb:4 - Method name \"method_name_2\" cyclomatic complexity is 2. It should be 0 or less.")
198
+ end
199
+
183
200
  end
@@ -0,0 +1,53 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe Roodi::Checks::NpathComplexityMethodCheck do
4
+ before(:each) do
5
+ @roodi = Roodi::Core::Runner.new(Roodi::Checks::NpathComplexityMethodCheck.new({'complexity' => 0}))
6
+ end
7
+
8
+ def verify_content_complexity(content, complexity)
9
+ @roodi.check_content(content)
10
+ errors = @roodi.errors
11
+ errors.should_not be_empty
12
+ errors[0].to_s.should eql("dummy-file.rb:1 - Method name \"method_name\" n-path complexity is #{complexity}. It should be 0 or less.")
13
+ end
14
+
15
+ it "should default to 1" do
16
+ content = <<-END
17
+ def method_name
18
+ end
19
+ END
20
+ verify_content_complexity(content, 1)
21
+ end
22
+
23
+ it "should find an if block" do
24
+ content = <<-END
25
+ def method_name
26
+ call_foo if some_condition
27
+ end
28
+ END
29
+ verify_content_complexity(content, 2)
30
+ end
31
+
32
+ it "should find nested if block" do
33
+ pending "NPath Complexity implementation that can support 'else' blocks"
34
+ content = <<-END
35
+ def method_name
36
+ if (value1)
37
+ foo
38
+ else
39
+ bar
40
+ end
41
+ if (value2)
42
+ bam
43
+ else
44
+ baz
45
+ end
46
+ if (value3)
47
+ one
48
+ end
49
+ end
50
+ END
51
+ verify_content_complexity(content, 8)
52
+ end
53
+ end
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.4.0
4
+ version: 2.0.0
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: 2009-05-08 00:00:00 +10:00
12
+ date: 2009-08-23 00:00:00 +10:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,16 +22,6 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: "0"
24
24
  version:
25
- - !ruby/object:Gem::Dependency
26
- name: facets
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: "0"
34
- version:
35
25
  - !ruby/object:Gem::Dependency
36
26
  name: hoe
37
27
  type: :development
@@ -82,11 +72,12 @@ files:
82
72
  - lib/roodi/checks/module_line_count_check.rb
83
73
  - lib/roodi/checks/module_name_check.rb
84
74
  - lib/roodi/checks/name_check.rb
75
+ - lib/roodi/checks/npath_complexity_check.rb
76
+ - lib/roodi/checks/npath_complexity_method_check.rb
85
77
  - lib/roodi/checks/parameter_number_check.rb
86
78
  - lib/roodi/core.rb
87
79
  - lib/roodi/core/checking_visitor.rb
88
80
  - lib/roodi/core/error.rb
89
- - lib/roodi/core/iterator_visitor.rb
90
81
  - lib/roodi/core/parser.rb
91
82
  - lib/roodi/core/runner.rb
92
83
  - lib/roodi/core/visitable_sexp.rb
@@ -107,6 +98,7 @@ files:
107
98
  - spec/roodi/checks/method_name_check_spec.rb
108
99
  - spec/roodi/checks/module_line_count_check_spec.rb
109
100
  - spec/roodi/checks/module_name_check_spec.rb
101
+ - spec/roodi/checks/npath_complexity_method_check_spec.rb
110
102
  - spec/roodi/checks/parameter_number_check_spec.rb
111
103
  - spec/spec_helper.rb
112
104
  has_rdoc: true
@@ -1,19 +0,0 @@
1
- module Roodi
2
- module Core
3
- class IteratorVisitor
4
- def initialize(payload)
5
- @payload = payload
6
- end
7
-
8
- def visit(visited)
9
- visited.accept(@payload)
10
- visitable_nodes = visited.is_language_node? ? visited.sexp_body : visited
11
- visitable_nodes.each do |child|
12
- if child.class == Sexp then
13
- child.accept(self)
14
- end
15
- end
16
- end
17
- end
18
- end
19
- end