roodi 1.4.0 → 2.0.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.
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