simplabs-excellent 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/History.txt +3 -0
  2. data/README.markdown +30 -0
  3. data/VERSION.yml +4 -0
  4. data/bin/excellent +21 -0
  5. data/lib/simplabs/excellent.rb +12 -0
  6. data/lib/simplabs/excellent/checks.rb +16 -0
  7. data/lib/simplabs/excellent/checks/abc_metric_method_check.rb +80 -0
  8. data/lib/simplabs/excellent/checks/assignment_in_conditional_check.rb +38 -0
  9. data/lib/simplabs/excellent/checks/base.rb +42 -0
  10. data/lib/simplabs/excellent/checks/case_missing_else_check.rb +25 -0
  11. data/lib/simplabs/excellent/checks/class_line_count_check.rb +34 -0
  12. data/lib/simplabs/excellent/checks/class_name_check.rb +37 -0
  13. data/lib/simplabs/excellent/checks/class_variable_check.rb +25 -0
  14. data/lib/simplabs/excellent/checks/control_coupling_check.rb +32 -0
  15. data/lib/simplabs/excellent/checks/cyclomatic_complexity_block_check.rb +32 -0
  16. data/lib/simplabs/excellent/checks/cyclomatic_complexity_check.rb +39 -0
  17. data/lib/simplabs/excellent/checks/cyclomatic_complexity_method_check.rb +33 -0
  18. data/lib/simplabs/excellent/checks/empty_rescue_body_check.rb +39 -0
  19. data/lib/simplabs/excellent/checks/for_loop_check.rb +25 -0
  20. data/lib/simplabs/excellent/checks/line_count_check.rb +45 -0
  21. data/lib/simplabs/excellent/checks/method_line_count_check.rb +34 -0
  22. data/lib/simplabs/excellent/checks/method_name_check.rb +34 -0
  23. data/lib/simplabs/excellent/checks/module_line_count_check.rb +34 -0
  24. data/lib/simplabs/excellent/checks/module_name_check.rb +34 -0
  25. data/lib/simplabs/excellent/checks/name_check.rb +32 -0
  26. data/lib/simplabs/excellent/checks/parameter_number_check.rb +37 -0
  27. data/lib/simplabs/excellent/core.rb +2 -0
  28. data/lib/simplabs/excellent/core/checking_visitor.rb +34 -0
  29. data/lib/simplabs/excellent/core/error.rb +31 -0
  30. data/lib/simplabs/excellent/core/extensions/underscore.rb +27 -0
  31. data/lib/simplabs/excellent/core/iterator_visitor.rb +29 -0
  32. data/lib/simplabs/excellent/core/parse_tree_runner.rb +88 -0
  33. data/lib/simplabs/excellent/core/parser.rb +33 -0
  34. data/lib/simplabs/excellent/core/visitable_sexp.rb +31 -0
  35. data/spec/checks/abc_metric_method_check_spec.rb +94 -0
  36. data/spec/checks/assignment_in_conditional_check_spec.rb +73 -0
  37. data/spec/checks/case_missing_else_check_spec.rb +42 -0
  38. data/spec/checks/class_line_count_check_spec.rb +49 -0
  39. data/spec/checks/class_name_check_spec.rb +48 -0
  40. data/spec/checks/class_variable_check_spec.rb +26 -0
  41. data/spec/checks/control_coupling_check_spec.rb +32 -0
  42. data/spec/checks/cyclomatic_complexity_block_check_spec.rb +51 -0
  43. data/spec/checks/cyclomatic_complexity_method_check_spec.rb +184 -0
  44. data/spec/checks/empty_rescue_body_check_spec.rb +132 -0
  45. data/spec/checks/for_loop_check_spec.rb +52 -0
  46. data/spec/checks/method_line_count_check_spec.rb +50 -0
  47. data/spec/checks/method_name_check_spec.rb +91 -0
  48. data/spec/checks/module_line_count_check_spec.rb +49 -0
  49. data/spec/checks/module_name_check_spec.rb +37 -0
  50. data/spec/checks/parameter_number_check_spec.rb +61 -0
  51. data/spec/core/extensions/underscore_spec.rb +13 -0
  52. data/spec/spec_helper.rb +11 -0
  53. metadata +115 -0
@@ -0,0 +1,39 @@
1
+ require 'simplabs/excellent/checks/base'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ class CyclomaticComplexityCheck < Base
10
+
11
+ COMPLEXITY_NODE_TYPES = [:if, :while, :until, :for, :rescue, :case, :when, :and, :or]
12
+
13
+ def initialize(threshold)
14
+ super()
15
+ @threshold = threshold
16
+ end
17
+
18
+ protected
19
+
20
+ def count_complexity(node)
21
+ count_branches(node) + 1
22
+ end
23
+
24
+ private
25
+
26
+ def count_branches(node)
27
+ count = 0
28
+ count = count + 1 if COMPLEXITY_NODE_TYPES.include? node.node_type
29
+ node.children.each { |child| count += count_branches(child) }
30
+ count
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,33 @@
1
+ require 'simplabs/excellent/checks/cyclomatic_complexity_check'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ class CyclomaticComplexityMethodCheck < CyclomaticComplexityCheck
10
+
11
+ DEFAULT_THRESHOLD = 8
12
+
13
+ def initialize(options = {})
14
+ complexity = options[:threshold] || DEFAULT_THRESHOLD
15
+ super(complexity)
16
+ end
17
+
18
+ def interesting_nodes
19
+ [:defn]
20
+ end
21
+
22
+ def evaluate(node)
23
+ score = count_complexity(node)
24
+ add_error('Method {{method}} has cyclomatic complexity of {{score}}.', { :method => node[1], :score => score }) unless score <= @threshold
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,39 @@
1
+ require 'simplabs/excellent/checks/base'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ class EmptyRescueBodyCheck < Base
10
+
11
+ STATEMENT_NODES = [:fcall, :return, :attrasgn, :vcall, :call, :str, :lit]
12
+
13
+ def interesting_nodes
14
+ [:resbody]
15
+ end
16
+
17
+ def evaluate(node)
18
+ add_error('Rescue block is empty.', {}, -1) unless has_statement?(node)
19
+ end
20
+
21
+ private
22
+
23
+ def has_statement?(node)
24
+ return true if STATEMENT_NODES.include?(node.node_type)
25
+ return true if assigning_other_than_exception_to_local_variable?(node)
26
+ return true if node.children.any? { |child| has_statement?(child) }
27
+ end
28
+
29
+ def assigning_other_than_exception_to_local_variable?(node)
30
+ node.node_type == :lasgn && node[2].to_a != [:gvar, :$!]
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,25 @@
1
+ require 'simplabs/excellent/checks/base'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ class ForLoopCheck < Base
10
+
11
+ def interesting_nodes
12
+ [:for]
13
+ end
14
+
15
+ def evaluate(node)
16
+ add_error('For loop used.')
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,45 @@
1
+ require 'simplabs/excellent/checks/base'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ class LineCountCheck < Base
10
+
11
+ def initialize(interesting_nodes, threshold)
12
+ super()
13
+ @interesting_nodes = interesting_nodes
14
+ @threshold = threshold
15
+ end
16
+
17
+ def interesting_nodes
18
+ @interesting_nodes
19
+ end
20
+
21
+ def evaluate(node)
22
+ line_count = count_lines(node_to_count(node)) - 1
23
+ add_error(*error_args(node, line_count)) unless line_count <= @threshold
24
+ end
25
+
26
+ protected
27
+
28
+ def node_to_count(node)
29
+ node
30
+ end
31
+
32
+ def count_lines(node, line_numbers = [])
33
+ count = 0
34
+ line_numbers << node.line
35
+ node.children.each { |child| count += count_lines(child, line_numbers) }
36
+ line_numbers.uniq.length
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,34 @@
1
+ require 'simplabs/excellent/checks/line_count_check'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ class MethodLineCountCheck < LineCountCheck
10
+
11
+ DEFAULT_THRESHOLD = 20
12
+
13
+ def initialize(options = {})
14
+ threshold = options[:threshold] || DEFAULT_THRESHOLD
15
+ super([:defn], threshold)
16
+ end
17
+
18
+ protected
19
+
20
+ def node_to_count(node)
21
+ node[3][1]
22
+ end
23
+
24
+ def error_args(node, line_count)
25
+ ['Method {{method}} has {{count}} lines.', { :method => node[1], :count => line_count }]
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,34 @@
1
+ require 'simplabs/excellent/checks/base'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ class MethodNameCheck < NameCheck
10
+
11
+ DEFAULT_PATTERN = /^[_a-z<>=\[|+-\/\*\~\%\&`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
12
+
13
+ def initialize(options = {})
14
+ pattern = options['pattern'] || DEFAULT_PATTERN
15
+ super([:defn], pattern)
16
+ end
17
+
18
+ def find_name(node)
19
+ node[1]
20
+ end
21
+
22
+ protected
23
+
24
+ def error_args(node)
25
+ ['Bad method name {{method}}.', { :method => node[1] }]
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,34 @@
1
+ require 'simplabs/excellent/checks/line_count_check'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ class ModuleLineCountCheck < LineCountCheck
10
+
11
+ DEFAULT_THRESHOLD = 300
12
+
13
+ def initialize(options = {})
14
+ threshold = options[:threshold] || DEFAULT_THRESHOLD
15
+ super([:module], threshold)
16
+ end
17
+
18
+ protected
19
+
20
+ def node_to_count(node)
21
+ node[2]
22
+ end
23
+
24
+ def error_args(node, line_count)
25
+ ['Module {{module}} has {{count}} lines.', { :module => node[1], :count => line_count }]
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,34 @@
1
+ require 'simplabs/excellent/checks/base'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ class ModuleNameCheck < NameCheck
10
+
11
+ DEFAULT_PATTERN = /^[A-Z][a-zA-Z0-9]*$/
12
+
13
+ def initialize(options = {})
14
+ pattern = options['pattern'] || DEFAULT_PATTERN
15
+ super([:module], pattern)
16
+ end
17
+
18
+ def find_name(node)
19
+ node[1].class == Symbol ? node[1] : node[1].last
20
+ end
21
+
22
+ protected
23
+
24
+ def error_args(node)
25
+ ['Bad module name {{module}}.', { :module => node[1] }]
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,32 @@
1
+ require 'simplabs/excellent/checks/base'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ class NameCheck < Base
10
+
11
+ def initialize(interesting_nodes, pattern)
12
+ super()
13
+ @interesting_nodes = interesting_nodes
14
+ @pattern = pattern
15
+ end
16
+
17
+ def interesting_nodes
18
+ @interesting_nodes
19
+ end
20
+
21
+ def evaluate(node)
22
+ name = find_name(node)
23
+ add_error(*error_args(node)) unless name.to_s =~ @pattern
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,37 @@
1
+ require 'simplabs/excellent/checks/base'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Checks
8
+
9
+ class ParameterNumberCheck < Base
10
+
11
+ DEFAULT_THRESHOLD = 3
12
+
13
+ def initialize(options = {})
14
+ super()
15
+ @threshold = options[:threshold] || DEFAULT_THRESHOLD
16
+ end
17
+
18
+ def interesting_nodes
19
+ [:defn]
20
+ end
21
+
22
+ def evaluate(node)
23
+ method_name = node[1]
24
+ parameters = node[2][1..-1]
25
+ parameter_count = parameters.inject(0) { |count, each| count = count + (each.class == Symbol ? 1 : 0) }
26
+ unless parameter_count <= @threshold
27
+ add_error('Method {{method}} has {{parameter_count}} parameters.', { :method => method_name, :parameter_count => parameter_count })
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,2 @@
1
+ require 'simplabs/excellent/core/parse_tree_runner'
2
+ require 'simplabs/excellent/core/extensions/underscore'
@@ -0,0 +1,34 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Core
6
+
7
+ class CheckingVisitor
8
+
9
+ def initialize(*checks)
10
+ @checks ||= {}
11
+ checks.first.each do |check|
12
+ nodes = check.interesting_nodes
13
+ nodes.each do |node|
14
+ @checks[node] ||= []
15
+ @checks[node] << check
16
+ @checks[node].uniq!
17
+ end
18
+ end
19
+ end
20
+
21
+ def visit(node)
22
+ @last_newline = node if node.node_type == :newline
23
+ checks = @checks[node.node_type]
24
+ checks.each { |check| check.evaluate_node(node) } unless checks.nil?
25
+ nil
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,31 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Core
6
+
7
+ class Error
8
+
9
+ attr_reader :check, :info, :filename, :line_number, :message, :message_template
10
+
11
+ def initialize(check, message, filename, line_number, info)
12
+ @check = check.to_s.underscore.to_sym
13
+ @info = info
14
+ @filename = filename
15
+ @line_number = line_number.to_i
16
+
17
+ @message = ''
18
+ if !message.nil?
19
+ @message_template = message
20
+ @info.each { |key, value| message.gsub!(/\{\{#{key}\}\}/, value.to_s) }
21
+ @message = message
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end