excellent 1.5.4

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 (106) hide show
  1. data/History.txt +69 -0
  2. data/README.rdoc +72 -0
  3. data/VERSION.yml +4 -0
  4. data/bin/excellent +34 -0
  5. data/lib/simplabs/excellent.rb +16 -0
  6. data/lib/simplabs/excellent/checks.rb +33 -0
  7. data/lib/simplabs/excellent/checks/abc_metric_method_check.rb +43 -0
  8. data/lib/simplabs/excellent/checks/assignment_in_conditional_check.rb +39 -0
  9. data/lib/simplabs/excellent/checks/base.rb +62 -0
  10. data/lib/simplabs/excellent/checks/case_missing_else_check.rb +34 -0
  11. data/lib/simplabs/excellent/checks/class_line_count_check.rb +36 -0
  12. data/lib/simplabs/excellent/checks/class_name_check.rb +38 -0
  13. data/lib/simplabs/excellent/checks/control_coupling_check.rb +35 -0
  14. data/lib/simplabs/excellent/checks/cyclomatic_complexity_block_check.rb +48 -0
  15. data/lib/simplabs/excellent/checks/cyclomatic_complexity_check.rb +23 -0
  16. data/lib/simplabs/excellent/checks/cyclomatic_complexity_method_check.rb +48 -0
  17. data/lib/simplabs/excellent/checks/empty_rescue_body_check.rb +31 -0
  18. data/lib/simplabs/excellent/checks/flog_block_check.rb +40 -0
  19. data/lib/simplabs/excellent/checks/flog_check.rb +27 -0
  20. data/lib/simplabs/excellent/checks/flog_class_check.rb +40 -0
  21. data/lib/simplabs/excellent/checks/flog_method_check.rb +40 -0
  22. data/lib/simplabs/excellent/checks/for_loop_check.rb +42 -0
  23. data/lib/simplabs/excellent/checks/global_variable_check.rb +33 -0
  24. data/lib/simplabs/excellent/checks/line_count_check.rb +27 -0
  25. data/lib/simplabs/excellent/checks/method_line_count_check.rb +36 -0
  26. data/lib/simplabs/excellent/checks/method_name_check.rb +38 -0
  27. data/lib/simplabs/excellent/checks/module_line_count_check.rb +36 -0
  28. data/lib/simplabs/excellent/checks/module_name_check.rb +38 -0
  29. data/lib/simplabs/excellent/checks/name_check.rb +27 -0
  30. data/lib/simplabs/excellent/checks/nested_iterators_check.rb +34 -0
  31. data/lib/simplabs/excellent/checks/parameter_number_check.rb +38 -0
  32. data/lib/simplabs/excellent/checks/rails.rb +22 -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/rails/custom_initialize_method_check.rb +37 -0
  36. data/lib/simplabs/excellent/checks/rails/instance_var_in_partial_check.rb +37 -0
  37. data/lib/simplabs/excellent/checks/rails/params_hash_in_view_check.rb +38 -0
  38. data/lib/simplabs/excellent/checks/rails/session_hash_in_view_check.rb +38 -0
  39. data/lib/simplabs/excellent/checks/rails/validations_check.rb +36 -0
  40. data/lib/simplabs/excellent/checks/singleton_variable_check.rb +33 -0
  41. data/lib/simplabs/excellent/command_line_runner.rb +37 -0
  42. data/lib/simplabs/excellent/extensions/sexp.rb +21 -0
  43. data/lib/simplabs/excellent/extensions/string.rb +28 -0
  44. data/lib/simplabs/excellent/formatters.rb +13 -0
  45. data/lib/simplabs/excellent/formatters/base.rb +49 -0
  46. data/lib/simplabs/excellent/formatters/html.rb +153 -0
  47. data/lib/simplabs/excellent/formatters/text.rb +40 -0
  48. data/lib/simplabs/excellent/parsing.rb +10 -0
  49. data/lib/simplabs/excellent/parsing/abc_measure.rb +52 -0
  50. data/lib/simplabs/excellent/parsing/block_context.rb +43 -0
  51. data/lib/simplabs/excellent/parsing/call_context.rb +52 -0
  52. data/lib/simplabs/excellent/parsing/case_context.rb +31 -0
  53. data/lib/simplabs/excellent/parsing/class_context.rb +99 -0
  54. data/lib/simplabs/excellent/parsing/code_processor.rb +165 -0
  55. data/lib/simplabs/excellent/parsing/conditional_context.rb +25 -0
  56. data/lib/simplabs/excellent/parsing/cvar_context.rb +28 -0
  57. data/lib/simplabs/excellent/parsing/cyclomatic_complexity_measure.rb +73 -0
  58. data/lib/simplabs/excellent/parsing/flog_measure.rb +192 -0
  59. data/lib/simplabs/excellent/parsing/for_loop_context.rb +15 -0
  60. data/lib/simplabs/excellent/parsing/gvar_context.rb +21 -0
  61. data/lib/simplabs/excellent/parsing/if_context.rb +38 -0
  62. data/lib/simplabs/excellent/parsing/ivar_context.rb +32 -0
  63. data/lib/simplabs/excellent/parsing/method_context.rb +50 -0
  64. data/lib/simplabs/excellent/parsing/module_context.rb +29 -0
  65. data/lib/simplabs/excellent/parsing/parser.rb +35 -0
  66. data/lib/simplabs/excellent/parsing/resbody_context.rb +39 -0
  67. data/lib/simplabs/excellent/parsing/scopeable.rb +34 -0
  68. data/lib/simplabs/excellent/parsing/sexp_context.rb +125 -0
  69. data/lib/simplabs/excellent/parsing/singleton_method_context.rb +55 -0
  70. data/lib/simplabs/excellent/parsing/until_context.rb +24 -0
  71. data/lib/simplabs/excellent/parsing/while_context.rb +24 -0
  72. data/lib/simplabs/excellent/rake.rb +1 -0
  73. data/lib/simplabs/excellent/rake/excellent_task.rb +61 -0
  74. data/lib/simplabs/excellent/runner.rb +143 -0
  75. data/lib/simplabs/excellent/warning.rb +53 -0
  76. data/spec/checks/abc_metric_method_check_spec.rb +122 -0
  77. data/spec/checks/assignment_in_conditional_check_spec.rb +90 -0
  78. data/spec/checks/case_missing_else_check_spec.rb +42 -0
  79. data/spec/checks/class_line_count_check_spec.rb +62 -0
  80. data/spec/checks/class_name_check_spec.rb +48 -0
  81. data/spec/checks/control_coupling_check_spec.rb +103 -0
  82. data/spec/checks/cyclomatic_complexity_block_check_spec.rb +47 -0
  83. data/spec/checks/cyclomatic_complexity_method_check_spec.rb +210 -0
  84. data/spec/checks/empty_rescue_body_check_spec.rb +170 -0
  85. data/spec/checks/flog_block_check_spec.rb +28 -0
  86. data/spec/checks/flog_class_check_spec.rb +28 -0
  87. data/spec/checks/flog_method_check_spec.rb +46 -0
  88. data/spec/checks/for_loop_check_spec.rb +52 -0
  89. data/spec/checks/global_variable_check_spec.rb +66 -0
  90. data/spec/checks/method_line_count_check_spec.rb +49 -0
  91. data/spec/checks/method_name_check_spec.rb +112 -0
  92. data/spec/checks/module_line_count_check_spec.rb +48 -0
  93. data/spec/checks/module_name_check_spec.rb +61 -0
  94. data/spec/checks/nested_iterators_check_spec.rb +44 -0
  95. data/spec/checks/parameter_number_check_spec.rb +97 -0
  96. data/spec/checks/rails/attr_accessible_check_spec.rb +79 -0
  97. data/spec/checks/rails/attr_protected_check_spec.rb +77 -0
  98. data/spec/checks/rails/custom_initialize_method_check_spec.rb +58 -0
  99. data/spec/checks/rails/instance_var_in_partial_check_spec.rb +40 -0
  100. data/spec/checks/rails/params_hash_in_view_check_spec.rb +40 -0
  101. data/spec/checks/rails/session_hash_in_view_check_spec.rb +40 -0
  102. data/spec/checks/rails/validations_check_spec.rb +81 -0
  103. data/spec/checks/singleton_variable_check_spec.rb +66 -0
  104. data/spec/extensions/string_spec.rb +13 -0
  105. data/spec/spec_helper.rb +13 -0
  106. metadata +189 -0
@@ -0,0 +1,40 @@
1
+ require 'simplabs/excellent/formatters/base'
2
+
3
+ module Simplabs
4
+
5
+ module Excellent
6
+
7
+ module Formatters
8
+
9
+ class Text < Base #:nodoc:
10
+
11
+ def initialize(stream = $stdout)
12
+ super
13
+ @total_warnings = 0
14
+ end
15
+
16
+ def start
17
+ @stream.puts "\n Excellent result:\n"
18
+ end
19
+
20
+ def file(filename)
21
+ @stream.puts "\n #{filename}\n"
22
+ yield self
23
+ end
24
+
25
+ def warning(warning)
26
+ @stream.puts " * Line #{warning.line_number.to_s.lpad(3)}: \e[33m#{warning.message}\e[0m"
27
+ @total_warnings += 1
28
+ end
29
+
30
+ def end
31
+ @stream.puts "\n Found #{@total_warnings} warnings.\n\n"
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,10 @@
1
+ module Simplabs
2
+
3
+ module Excellent
4
+
5
+ module Parsing #:nodoc:
6
+ end
7
+
8
+ end
9
+
10
+ end
@@ -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,52 @@
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
+ record_validation
20
+ record_call
21
+ end
22
+
23
+ def <=>(other)
24
+ @full_name <=> other.full_name
25
+ end
26
+ alias :eql? :'<=>'
27
+
28
+ def hash
29
+ @full_name.hash
30
+ end
31
+
32
+ private
33
+
34
+ def record_call
35
+ parent = @parent
36
+ parent = parent.parent until parent.nil? || parent.is_a?(MethodContext) || parent.is_a?(BlockContext) || parent.is_a?(SingletonMethodContext)
37
+ parent.record_call_to(self) if parent
38
+ end
39
+
40
+ def record_validation
41
+ if ClassContext::VALIDATIONS.include?(@method) && @parent && @parent.is_a?(ClassContext) && @parent.active_record_model?
42
+ @parent.validations << @method
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ 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,99 @@
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
+ VALIDATIONS = %w(
15
+ validates_acceptance_of
16
+ validates_associated
17
+ validates_confirmation_of
18
+ validates_each
19
+ validates_exclusion_of
20
+ validates_format_of
21
+ validates_inclusion_of
22
+ validates_length_of
23
+ validates_numericality_of
24
+ validates_presence_of
25
+ validates_size_of
26
+ validates_uniqueness_of
27
+ )
28
+
29
+ attr_reader :methods
30
+ attr_reader :line_count
31
+ attr_reader :base_class_name
32
+ attr_reader :validations
33
+
34
+ def initialize(exp, parent)
35
+ super
36
+ @name, @full_name = get_names
37
+ @base_class_name = get_base_class_name
38
+ @methods = []
39
+ @line_count = count_lines
40
+ @attr_accessible = false
41
+ @attr_protected = false
42
+ @initializer = false
43
+ @validations = []
44
+ end
45
+
46
+ def active_record_model?
47
+ @base_class_name == 'ActiveRecord::Base'
48
+ end
49
+
50
+ def specifies_attr_accessible?
51
+ @attr_accessible
52
+ end
53
+
54
+ def specifies_attr_protected?
55
+ @attr_protected
56
+ end
57
+
58
+ def defines_initializer?
59
+ @initializer
60
+ end
61
+
62
+ def validating?
63
+ !@validations.empty? || @methods.any?{ |method| %(validate validate_on_create validate_on_update).include?(method.name) }
64
+ end
65
+
66
+ def process_call(exp)
67
+ @attr_accessible = true if exp[2] == :attr_accessible
68
+ @attr_protected = true if exp[2] == :attr_protected
69
+ super
70
+ end
71
+
72
+ def process_defn(exp)
73
+ @initializer = true if exp[2] == :initialize
74
+ super
75
+ end
76
+
77
+ private
78
+
79
+ def get_base_class_name
80
+ base = @exp[2]
81
+ base_class_name = ''
82
+ while base.is_a?(Sexp)
83
+ base_class_name = "#{base.last}::#{base_class_name}"
84
+ if base[0] == :colon2
85
+ base = base[1]
86
+ else
87
+ break
88
+ end
89
+ end
90
+ base_class_name = base_class_name.empty? ? nil : base_class_name.sub(/::$/, '')
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+
99
+ end
@@ -0,0 +1,165 @@
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/gvar_context'
15
+ require 'simplabs/excellent/parsing/ivar_context'
16
+ require 'simplabs/excellent/parsing/resbody_context'
17
+ require 'simplabs/excellent/parsing/call_context'
18
+
19
+ module Simplabs
20
+
21
+ module Excellent
22
+
23
+ module Parsing
24
+
25
+ class CodeProcessor < SexpProcessor #:nodoc:
26
+
27
+ def initialize(checks)
28
+ setup_checks(checks)
29
+ setup_processors
30
+ super()
31
+ @require_empty = @warn_on_default = false
32
+ @contexts = []
33
+ @default_method = 'process_default'
34
+ end
35
+
36
+ def process(exp)
37
+ super
38
+ rescue
39
+ #continue on errors
40
+ end
41
+
42
+ def process_class(exp)
43
+ process_default(exp, ClassContext.new(exp, @contexts.last))
44
+ end
45
+
46
+ def process_module(exp)
47
+ process_default(exp, ModuleContext.new(exp, @contexts.last))
48
+ end
49
+
50
+ def process_defn(exp)
51
+ process_default(exp, MethodContext.new(exp, @contexts.last))
52
+ end
53
+
54
+ def process_defs(exp)
55
+ process_default(exp, SingletonMethodContext.new(exp, @contexts.last))
56
+ end
57
+
58
+ def process_ivar(exp)
59
+ process_default(exp, IvarContext.new(exp, @contexts.last))
60
+ end
61
+
62
+ def process_cvar(exp)
63
+ process_default(exp, CvarContext.new(exp, @contexts.last))
64
+ end
65
+
66
+ def process_gvar(exp)
67
+ process_default(exp, GvarContext.new(exp, @contexts.last))
68
+ end
69
+
70
+ def process_gasgn(exp)
71
+ process_default(exp, GvarContext.new(exp, @contexts.last))
72
+ end
73
+
74
+ def process_if(exp)
75
+ process_default(exp, IfContext.new(exp, @contexts.last))
76
+ end
77
+
78
+ def process_while(exp)
79
+ process_default(exp, WhileContext.new(exp, @contexts.last))
80
+ end
81
+
82
+ def process_until(exp)
83
+ process_default(exp, UntilContext.new(exp, @contexts.last))
84
+ end
85
+
86
+ def process_for(exp)
87
+ process_default(exp, ForLoopContext.new(exp, @contexts.last))
88
+ end
89
+
90
+ def process_args(exp)
91
+ exp[1..-1].each { |parameter| @contexts.last.parameters << parameter if parameter.is_a?(Symbol) }
92
+ process_default(exp)
93
+ end
94
+
95
+ def process_masgn(exp)
96
+ exp[1][1..-1].each { |parameter| @contexts.last.parameters << parameter[1] if parameter[1].is_a?(Symbol) } if @contexts.last.is_a?(BlockContext)
97
+ process_default(exp)
98
+ end
99
+
100
+ def process_case(exp)
101
+ process_default(exp, CaseContext.new(exp, @contexts.last))
102
+ end
103
+
104
+ def process_iter(exp)
105
+ process_default(exp, BlockContext.new(exp, @contexts.last))
106
+ end
107
+
108
+ def process_call(exp)
109
+ process_default(exp, CallContext.new(exp, @contexts.last))
110
+ end
111
+
112
+ def process_resbody(exp)
113
+ process_default(exp, ResbodyContext.new(exp, @contexts.last))
114
+ end
115
+
116
+ def process_default(exp, context = nil)
117
+ @contexts.push(context) if context
118
+ @contexts.each do |c|
119
+ method = "process_#{exp.node_type}".to_sym
120
+ c.send(method, exp) if c.respond_to?(method)
121
+ end
122
+ exp.children.each { |sub| process(sub) }
123
+ apply_checks(exp)
124
+ @contexts.pop if context
125
+ exp
126
+ end
127
+
128
+ private
129
+
130
+ def apply_checks(exp)
131
+ if exp.is_a?(Sexp)
132
+ checks = @checks[exp.node_type] || []
133
+ checks.each { |check| check.evaluate_node(@contexts.last) if check.interesting_files.any? { |pattern| File.basename(exp.file) =~ pattern } }
134
+ end
135
+ end
136
+
137
+ def setup_checks(checks)
138
+ @checks = {}
139
+ checks.each do |check|
140
+ check.interesting_nodes.each do |node|
141
+ @checks[node] ||= []
142
+ @checks[node] << check
143
+ @checks[node].uniq!
144
+ end
145
+ end
146
+ end
147
+
148
+ def setup_processors
149
+ @checks.each_key do |key|
150
+ method = "process_#{key.to_s}".to_sym
151
+ unless self.respond_to?(method)
152
+ self.class.send(:define_method, method) do |exp| # def process_call(exp)
153
+ process_default(exp) # process_default(exp)
154
+ end # end
155
+ end
156
+ end
157
+ end
158
+
159
+ end
160
+
161
+ end
162
+
163
+ end
164
+
165
+ end