simplabs-excellent 1.0.1 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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