simplabs-excellent 1.0.1 → 1.2.1

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 (96) hide show
  1. data/History.txt +19 -0
  2. data/README.rdoc +34 -0
  3. data/VERSION.yml +1 -1
  4. data/bin/excellent +21 -6
  5. data/lib/simplabs/excellent.rb +7 -5
  6. data/lib/simplabs/excellent/checks.rb +18 -1
  7. data/lib/simplabs/excellent/checks/abc_metric_method_check.rb +19 -56
  8. data/lib/simplabs/excellent/checks/assignment_in_conditional_check.rb +16 -16
  9. data/lib/simplabs/excellent/checks/base.rb +30 -21
  10. data/lib/simplabs/excellent/checks/case_missing_else_check.rb +13 -5
  11. data/lib/simplabs/excellent/checks/class_line_count_check.rb +10 -8
  12. data/lib/simplabs/excellent/checks/class_name_check.rb +11 -10
  13. data/lib/simplabs/excellent/checks/control_coupling_check.rb +13 -10
  14. data/lib/simplabs/excellent/checks/cyclomatic_complexity_block_check.rb +25 -9
  15. data/lib/simplabs/excellent/checks/cyclomatic_complexity_check.rb +4 -20
  16. data/lib/simplabs/excellent/checks/cyclomatic_complexity_method_check.rb +25 -10
  17. data/lib/simplabs/excellent/checks/duplication_check.rb +50 -0
  18. data/lib/simplabs/excellent/checks/empty_rescue_body_check.rb +10 -18
  19. data/lib/simplabs/excellent/checks/flog_block_check.rb +40 -0
  20. data/lib/simplabs/excellent/checks/flog_check.rb +27 -0
  21. data/lib/simplabs/excellent/checks/flog_class_check.rb +40 -0
  22. data/lib/simplabs/excellent/checks/flog_method_check.rb +40 -0
  23. data/lib/simplabs/excellent/checks/for_loop_check.rb +20 -4
  24. data/lib/simplabs/excellent/checks/line_count_check.rb +3 -21
  25. data/lib/simplabs/excellent/checks/method_line_count_check.rb +9 -7
  26. data/lib/simplabs/excellent/checks/method_name_check.rb +13 -9
  27. data/lib/simplabs/excellent/checks/module_line_count_check.rb +9 -7
  28. data/lib/simplabs/excellent/checks/module_name_check.rb +11 -7
  29. data/lib/simplabs/excellent/checks/name_check.rb +3 -8
  30. data/lib/simplabs/excellent/checks/nested_iterators_check.rb +33 -0
  31. data/lib/simplabs/excellent/checks/parameter_number_check.rb +13 -12
  32. data/lib/simplabs/excellent/checks/rails.rb +17 -0
  33. data/lib/simplabs/excellent/checks/rails/attr_accessible_check.rb +38 -0
  34. data/lib/simplabs/excellent/checks/rails/attr_protected_check.rb +39 -0
  35. data/lib/simplabs/excellent/checks/singleton_variable_check.rb +32 -0
  36. data/lib/simplabs/excellent/extensions/sexp.rb +21 -0
  37. data/lib/simplabs/excellent/extensions/string.rb +23 -0
  38. data/lib/simplabs/excellent/parsing.rb +12 -0
  39. data/lib/simplabs/excellent/parsing/abc_measure.rb +52 -0
  40. data/lib/simplabs/excellent/parsing/block_context.rb +43 -0
  41. data/lib/simplabs/excellent/parsing/call_context.rb +36 -0
  42. data/lib/simplabs/excellent/parsing/case_context.rb +31 -0
  43. data/lib/simplabs/excellent/parsing/class_context.rb +68 -0
  44. data/lib/simplabs/excellent/parsing/code_processor.rb +154 -0
  45. data/lib/simplabs/excellent/parsing/conditional_context.rb +25 -0
  46. data/lib/simplabs/excellent/parsing/cvar_context.rb +28 -0
  47. data/lib/simplabs/excellent/parsing/cyclomatic_complexity_measure.rb +73 -0
  48. data/lib/simplabs/excellent/parsing/flog_measure.rb +192 -0
  49. data/lib/simplabs/excellent/parsing/for_loop_context.rb +15 -0
  50. data/lib/simplabs/excellent/parsing/if_context.rb +38 -0
  51. data/lib/simplabs/excellent/parsing/method_context.rb +50 -0
  52. data/lib/simplabs/excellent/parsing/module_context.rb +29 -0
  53. data/lib/simplabs/excellent/{core → parsing}/parser.rb +4 -2
  54. data/lib/simplabs/excellent/parsing/resbody_context.rb +39 -0
  55. data/lib/simplabs/excellent/parsing/scopeable.rb +34 -0
  56. data/lib/simplabs/excellent/parsing/sexp_context.rb +125 -0
  57. data/lib/simplabs/excellent/parsing/singleton_method_context.rb +55 -0
  58. data/lib/simplabs/excellent/parsing/until_context.rb +24 -0
  59. data/lib/simplabs/excellent/parsing/while_context.rb +24 -0
  60. data/lib/simplabs/excellent/runner.rb +105 -0
  61. data/lib/simplabs/excellent/warning.rb +53 -0
  62. data/spec/checks/abc_metric_method_check_spec.rb +36 -8
  63. data/spec/checks/assignment_in_conditional_check_spec.rb +31 -14
  64. data/spec/checks/case_missing_else_check_spec.rb +8 -8
  65. data/spec/checks/class_line_count_check_spec.rb +24 -11
  66. data/spec/checks/class_name_check_spec.rb +9 -9
  67. data/spec/checks/control_coupling_check_spec.rb +84 -13
  68. data/spec/checks/cyclomatic_complexity_block_check_spec.rb +13 -17
  69. data/spec/checks/cyclomatic_complexity_method_check_spec.rb +32 -6
  70. data/spec/checks/duplication_check_spec.rb +139 -0
  71. data/spec/checks/empty_rescue_body_check_spec.rb +54 -16
  72. data/spec/checks/flog_block_check_spec.rb +28 -0
  73. data/spec/checks/flog_class_check_spec.rb +28 -0
  74. data/spec/checks/flog_method_check_spec.rb +46 -0
  75. data/spec/checks/for_loop_check_spec.rb +11 -11
  76. data/spec/checks/method_line_count_check_spec.rb +11 -12
  77. data/spec/checks/method_name_check_spec.rb +34 -13
  78. data/spec/checks/module_line_count_check_spec.rb +11 -12
  79. data/spec/checks/module_name_check_spec.rb +31 -7
  80. data/spec/checks/nested_iterators_check_spec.rb +44 -0
  81. data/spec/checks/parameter_number_check_spec.rb +48 -12
  82. data/spec/checks/rails/attr_accessible_check_spec.rb +79 -0
  83. data/spec/checks/rails/attr_protected_check_spec.rb +77 -0
  84. data/spec/checks/singleton_variable_check_spec.rb +66 -0
  85. data/spec/{core/extensions/underscore_spec.rb → extensions/string_spec.rb} +1 -1
  86. metadata +58 -15
  87. data/README.markdown +0 -30
  88. data/lib/simplabs/excellent/checks/class_variable_check.rb +0 -25
  89. data/lib/simplabs/excellent/core.rb +0 -2
  90. data/lib/simplabs/excellent/core/checking_visitor.rb +0 -34
  91. data/lib/simplabs/excellent/core/error.rb +0 -31
  92. data/lib/simplabs/excellent/core/extensions/underscore.rb +0 -27
  93. data/lib/simplabs/excellent/core/iterator_visitor.rb +0 -29
  94. data/lib/simplabs/excellent/core/parse_tree_runner.rb +0 -88
  95. data/lib/simplabs/excellent/core/visitable_sexp.rb +0 -31
  96. data/spec/checks/class_variable_check_spec.rb +0 -26
@@ -0,0 +1,12 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Parsing #:nodoc:
6
+ end
7
+
8
+ end
9
+
10
+ end
11
+
12
+ require 'simplabs/excellent/runner'
@@ -0,0 +1,52 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Parsing
6
+
7
+ module AbcMeasure #:nodoc:
8
+
9
+ CONDITIONS = [:==, :<=, :>=, :<, :>]
10
+
11
+ def process_lasgn(exp)
12
+ @assignments ||= 0
13
+ @assignments += 1
14
+ super
15
+ end
16
+
17
+ def process_call(exp)
18
+ handle_call(exp)
19
+ super
20
+ end
21
+
22
+ def process_vcall(exp)
23
+ handle_call(exp)
24
+ super
25
+ end
26
+
27
+ def abc_score
28
+ a = @assignments ||= 0
29
+ b = @branches ||= 0
30
+ c = @conditionals ||= 0
31
+ Math.sqrt(a * a + b * b + c * c)
32
+ end
33
+
34
+ private
35
+
36
+ def handle_call(exp)
37
+ @branches ||= 0
38
+ @conditionals ||= 0
39
+ if CONDITIONS.include?(exp[2])
40
+ @conditionals += 1
41
+ else
42
+ @branches += 1
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,43 @@
1
+ require 'simplabs/excellent/parsing/cyclomatic_complexity_measure'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Parsing
8
+
9
+ class BlockContext < SexpContext #:nodoc:
10
+
11
+ include CyclomaticComplexityMeasure
12
+ include FlogMeasure
13
+
14
+ attr_reader :parameters
15
+ attr_reader :calls
16
+
17
+ def initialize(exp, parent)
18
+ super
19
+ @parameters = []
20
+ @name = 'block'
21
+ @line = exp.line < exp[1].line ? exp.line : exp[1].line
22
+ @calls = Hash.new(0)
23
+ end
24
+
25
+ def full_name
26
+ @name
27
+ end
28
+
29
+ def record_call_to(exp)
30
+ @calls[exp] += 1
31
+ end
32
+
33
+ def inside_block?
34
+ @parent.is_a?(BlockContext)
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,36 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Parsing
6
+
7
+ class CallContext < SexpContext #:nodoc:
8
+
9
+ include Comparable
10
+
11
+ attr_reader :receiver
12
+ attr_reader :method
13
+
14
+ def initialize(exp, parent)
15
+ super
16
+ @receiver = exp[1].is_a?(Sexp) ? (exp[1][1].nil? ? exp[1][2].to_s : exp[1][1].to_s) : nil
17
+ @method = exp[2].to_s
18
+ @full_name = [@receiver, @method].compact.join('.')
19
+ end
20
+
21
+ def <=>(other)
22
+ @full_name <=> other.full_name
23
+ end
24
+ alias :eql? :'<=>'
25
+
26
+ def hash
27
+ @full_name.hash
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,31 @@
1
+ require 'simplabs/excellent/parsing/conditional_context'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Parsing
8
+
9
+ class CaseContext < ConditionalContext #:nodoc:
10
+
11
+ def initialize(exp, parent)
12
+ super
13
+ @has_else_clause = exp.last
14
+ @tests_parameter = contains_parameter?
15
+ end
16
+
17
+ def has_else_clause?
18
+ @has_else_clause
19
+ end
20
+
21
+ def tests_parameter?
22
+ @tests_parameter
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,68 @@
1
+ require 'simplabs/excellent/parsing/scopeable'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Parsing
8
+
9
+ class ClassContext < SexpContext #:nodoc:
10
+
11
+ include FlogMeasure
12
+ include Scopeable
13
+
14
+ attr_reader :methods
15
+ attr_reader :line_count
16
+ attr_reader :base_class_name
17
+
18
+ def initialize(exp, parent)
19
+ super
20
+ @name, @full_name = get_names
21
+ @base_class_name = get_base_class_name
22
+ @methods = []
23
+ @line_count = count_lines
24
+ @attr_accessible = false
25
+ @attr_protected = false
26
+ end
27
+
28
+ def activerecord_model?
29
+ @base_class_name == 'ActiveRecord::Base'
30
+ end
31
+
32
+ def specifies_attr_accessible?
33
+ @attr_accessible
34
+ end
35
+
36
+ def specifies_attr_protected?
37
+ @attr_protected
38
+ end
39
+
40
+ def process_call(exp)
41
+ @attr_accessible = true if exp[2] == :attr_accessible
42
+ @attr_protected = true if exp[2] == :attr_protected
43
+ super
44
+ end
45
+
46
+ private
47
+
48
+ def get_base_class_name
49
+ base = @exp[2]
50
+ base_class_name = ''
51
+ while base.is_a?(Sexp)
52
+ base_class_name = "#{base.last}::#{base_class_name}"
53
+ if base[0] == :colon2
54
+ base = base[1]
55
+ else
56
+ break
57
+ end
58
+ end
59
+ base_class_name = base_class_name.empty? ? nil : base_class_name.sub(/::$/, '')
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,154 @@
1
+ require 'sexp_processor'
2
+ require 'simplabs/excellent/extensions/sexp'
3
+ require 'simplabs/excellent/parsing/if_context'
4
+ require 'simplabs/excellent/parsing/method_context'
5
+ require 'simplabs/excellent/parsing/singleton_method_context'
6
+ require 'simplabs/excellent/parsing/case_context'
7
+ require 'simplabs/excellent/parsing/block_context'
8
+ require 'simplabs/excellent/parsing/class_context'
9
+ require 'simplabs/excellent/parsing/module_context'
10
+ require 'simplabs/excellent/parsing/for_loop_context'
11
+ require 'simplabs/excellent/parsing/while_context'
12
+ require 'simplabs/excellent/parsing/until_context'
13
+ require 'simplabs/excellent/parsing/cvar_context'
14
+ require 'simplabs/excellent/parsing/resbody_context'
15
+ require 'simplabs/excellent/parsing/call_context'
16
+
17
+ module Simplabs
18
+
19
+ module Excellent
20
+
21
+ module Parsing
22
+
23
+ class CodeProcessor < SexpProcessor #:nodoc:
24
+
25
+ def initialize(checks)
26
+ setup_checks(checks)
27
+ setup_processors
28
+ super()
29
+ @require_empty = @warn_on_default = false
30
+ @contexts = []
31
+ @default_method = 'process_default'
32
+ end
33
+
34
+ def process(exp)
35
+ super
36
+ rescue
37
+ #continue on errors
38
+ end
39
+
40
+ def process_class(exp)
41
+ process_default(exp, ClassContext.new(exp, @contexts.last))
42
+ end
43
+
44
+ def process_module(exp)
45
+ process_default(exp, ModuleContext.new(exp, @contexts.last))
46
+ end
47
+
48
+ def process_defn(exp)
49
+ process_default(exp, MethodContext.new(exp, @contexts.last))
50
+ end
51
+
52
+ def process_defs(exp)
53
+ process_default(exp, SingletonMethodContext.new(exp, @contexts.last))
54
+ end
55
+
56
+ def process_cvar(exp)
57
+ process_default(exp, CvarContext.new(exp, @contexts.last))
58
+ end
59
+
60
+ def process_if(exp)
61
+ process_default(exp, IfContext.new(exp, @contexts.last))
62
+ end
63
+
64
+ def process_while(exp)
65
+ process_default(exp, WhileContext.new(exp, @contexts.last))
66
+ end
67
+
68
+ def process_until(exp)
69
+ process_default(exp, UntilContext.new(exp, @contexts.last))
70
+ end
71
+
72
+ def process_for(exp)
73
+ process_default(exp, ForLoopContext.new(exp, @contexts.last))
74
+ end
75
+
76
+ def process_args(exp)
77
+ exp[1..-1].each { |parameter| @contexts.last.parameters << parameter if parameter.is_a?(Symbol) }
78
+ process_default(exp)
79
+ end
80
+
81
+ def process_masgn(exp)
82
+ exp[1][1..-1].each { |parameter| @contexts.last.parameters << parameter[1] if parameter[1].is_a?(Symbol) } if @contexts.last.is_a?(BlockContext)
83
+ process_default(exp)
84
+ end
85
+
86
+ def process_case(exp)
87
+ process_default(exp, CaseContext.new(exp, @contexts.last))
88
+ end
89
+
90
+ def process_iter(exp)
91
+ process_default(exp, BlockContext.new(exp, @contexts.last))
92
+ end
93
+
94
+ def process_call(exp)
95
+ if @contexts.last.is_a?(MethodContext) || @contexts.last.is_a?(BlockContext) || @contexts.last.is_a?(SingletonMethodContext)
96
+ @contexts.last.record_call_to(CallContext.new(exp, @contexts.last))
97
+ end
98
+ process_default(exp)
99
+ end
100
+
101
+ def process_resbody(exp)
102
+ process_default(exp, ResbodyContext.new(exp, @contexts.last))
103
+ end
104
+
105
+ def process_default(exp, context = nil)
106
+ @contexts.push(context) if context
107
+ @contexts.each do |c|
108
+ method = "process_#{exp.node_type}".to_sym
109
+ c.send(method, exp) if c.respond_to?(method)
110
+ end
111
+ exp.children.each { |sub| process(sub) }
112
+ apply_checks(exp)
113
+ @contexts.pop if context
114
+ exp
115
+ end
116
+
117
+ private
118
+
119
+ def apply_checks(exp)
120
+ if exp.is_a?(Sexp)
121
+ checks = @checks[exp.node_type]
122
+ checks.each { |check| check.evaluate_node(@contexts.last) } unless checks.nil?
123
+ end
124
+ end
125
+
126
+ def setup_checks(checks)
127
+ @checks = {}
128
+ checks.each do |check|
129
+ check.interesting_nodes.each do |node|
130
+ @checks[node] ||= []
131
+ @checks[node] << check
132
+ @checks[node].uniq!
133
+ end
134
+ end
135
+ end
136
+
137
+ def setup_processors
138
+ @checks.each_key do |key|
139
+ method = "process_#{key.to_s}".to_sym
140
+ unless self.respond_to?(method)
141
+ self.class.send(:define_method, method) do |exp| # def process_call(exp)
142
+ process_default(exp) # process_default(exp)
143
+ end # end
144
+ end
145
+ end
146
+ end
147
+
148
+ end
149
+
150
+ end
151
+
152
+ end
153
+
154
+ end
@@ -0,0 +1,25 @@
1
+ require 'simplabs/excellent/parsing/sexp_context'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Parsing
8
+
9
+ class ConditionalContext < SexpContext #:nodoc:
10
+
11
+ protected
12
+
13
+ def contains_parameter?
14
+ return false unless @parent.is_a?(MethodContext)
15
+ return @exp[1][1] if @exp[1][0] == :lvar and @parent.has_parameter?(@exp[1][1])
16
+ false
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,28 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Parsing
6
+
7
+ class CvarContext < SexpContext #:nodoc:
8
+
9
+ def initialize(exp, parent)
10
+ super
11
+ @name = exp[1].to_s.sub(/^@+/, '')
12
+ end
13
+
14
+ def full_name
15
+ return @name if @parent.blank?
16
+ full_name = @name
17
+ parent = @parent
18
+ parent = parent.parent until parent.is_a?(ClassContext) || parent.is_a?(ModuleContext)
19
+ full_name = "#{parent.full_name}.#{full_name}"
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end