kevinrutherford-reek 1.0.1 → 1.1.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 (50) hide show
  1. data/History.txt +42 -22
  2. data/config/defaults.reek +1 -0
  3. data/lib/reek.rb +1 -1
  4. data/lib/reek/block_context.rb +27 -5
  5. data/lib/reek/class_context.rb +5 -4
  6. data/lib/reek/code_parser.rb +26 -11
  7. data/lib/reek/method_context.rb +18 -12
  8. data/lib/reek/module_context.rb +1 -1
  9. data/lib/reek/name.rb +8 -1
  10. data/lib/reek/object_refs.rb +2 -3
  11. data/lib/reek/report.rb +11 -4
  12. data/lib/reek/sexp_formatter.rb +4 -46
  13. data/lib/reek/smells/large_class.rb +26 -7
  14. data/lib/reek/smells/long_parameter_list.rb +1 -1
  15. data/lib/reek/smells/smells.rb +4 -8
  16. data/lib/reek/source.rb +31 -4
  17. data/lib/reek/spec.rb +6 -0
  18. data/lib/reek/stop_context.rb +4 -16
  19. data/lib/reek/yield_call_context.rb +1 -3
  20. data/reek.gemspec +8 -5
  21. data/spec/reek/block_context_spec.rb +40 -0
  22. data/spec/reek/class_context_spec.rb +11 -0
  23. data/spec/reek/code_context_spec.rb +2 -1
  24. data/spec/reek/code_parser_spec.rb +0 -10
  25. data/spec/reek/config_spec.rb +2 -2
  26. data/spec/reek/method_context_spec.rb +14 -0
  27. data/spec/reek/name_spec.rb +13 -0
  28. data/spec/reek/object_refs_spec.rb +11 -9
  29. data/spec/reek/report_spec.rb +1 -1
  30. data/spec/reek/sexp_formatter_spec.rb +5 -4
  31. data/spec/reek/singleton_method_context_spec.rb +1 -1
  32. data/spec/reek/smells/duplication_spec.rb +2 -2
  33. data/spec/reek/smells/feature_envy_spec.rb +132 -36
  34. data/spec/reek/smells/large_class_spec.rb +48 -47
  35. data/spec/reek/smells/long_method_spec.rb +1 -1
  36. data/spec/reek/smells/long_parameter_list_spec.rb +4 -11
  37. data/spec/reek/smells/uncommunicative_name_spec.rb +6 -1
  38. data/spec/reek/smells/utility_function_spec.rb +6 -9
  39. data/spec/{samples → slow}/inline_spec.rb +14 -10
  40. data/spec/{samples → slow}/optparse_spec.rb +20 -12
  41. data/spec/{samples → slow}/redcloth_spec.rb +16 -8
  42. data/spec/{integration → slow}/reek_source_spec.rb +0 -0
  43. data/spec/{samples → slow/samples}/inline.rb +0 -0
  44. data/spec/{samples → slow/samples}/optparse.rb +0 -0
  45. data/spec/{samples → slow/samples}/redcloth.rb +0 -0
  46. data/spec/{integration → slow}/script_spec.rb +0 -0
  47. data/spec/{reek/source_spec.rb → slow/source_list_spec.rb} +3 -3
  48. data/spec/spec_helper.rb +2 -0
  49. data/tasks/rspec.rake +1 -1
  50. metadata +24 -12
data/History.txt CHANGED
@@ -1,44 +1,64 @@
1
- == 1.0.0 2009-04-05
1
+ == 1.1.1 2009-05-08
2
+
3
+ === Minor enhancements
4
+ * LargeClass now also warns about any class with > 9 instance variables (#6)
5
+ * Now depends on ruby2ruby, to display code better
6
+ * Duplication notices more repeated method calls
7
+ * Smells within blocks are now reported better
8
+
9
+ == 1.1.0 2009-04-10
10
+
11
+ === Minor enhancements
12
+ * Now possible to write 'MyClass.should_not reek' (#33)
13
+
14
+ === Fixes
15
+ * Now counts attr assignments ([]= etc) in feature envy calculations
16
+ * Doesn't attempt to find *.reek files when reading code from stdin
17
+
18
+ == 1.0.1 2009-04-06
19
+
20
+ === Fixes
21
+ * Dir[...].to_source now creates a Report that can be browsed (#36)
2
22
 
3
- === Major enhancements:
23
+ == 1.0.0 2009-04-05
4
24
 
25
+ === Major enhancements
5
26
  * Use *.reek files in source tree to configure Reek's behaviour
6
27
  * Added -f option to configure report format
7
28
  * --sort_order replaced by -f, -c and -s
8
29
  * Matchers provided for rspec; eg. foo.should_not reek
9
30
 
10
- === Minor enhancements:
11
-
31
+ === Minor enhancements
12
32
  * Smells in singleton methods are now analysed
13
33
  * Uncommunicative parameter names in blocks now reported
14
34
  * Modules and blocks now reflected in scope of smell reports
15
35
 
16
- === Fixes:
17
-
36
+ === Fixes
18
37
  * Corrected false reports of long arg lists to yield
19
38
  * A method can now be a UtilityFunction only when it includes a call
20
39
 
21
40
  == 0.3.1 2008-11-17
22
41
 
23
- * Minor enhancements:
24
- * Uncommunicative Name now checks instance variables more thoroughly
25
- * Uncommunicative Name now warns about names of the form 'x2'
26
- * Added check for duplicated calls within a method
27
- * Reduced scope of Feature Envy warnings to cover only overuse of lvars
28
- * Added rdoc comments explaining what each smell is about
29
- * Chained iterators are no longer mis-reported as nested
42
+ === Minor enhancements
43
+ * Uncommunicative Name now checks instance variables more thoroughly
44
+ * Uncommunicative Name now warns about names of the form 'x2'
45
+ * Added check for duplicated calls within a method
46
+ * Reduced scope of Feature Envy warnings to cover only overuse of lvars
47
+ * Added rdoc comments explaining what each smell is about
48
+ * Chained iterators are no longer mis-reported as nested
30
49
 
31
50
  == 0.3.0 2008-11-02
32
51
 
33
- * Minor enhancements:
34
- * New smell: first naive checks for Control Couple
35
- * reek now only checks sources passed on the command line
36
- * Code snippets can be supplied on the commandline
37
- * Added headings and warnings count when smells in multiple files
38
- * Added Reek::RakeTask to run reek from rakefiles
39
- * Tweaks:
40
- * Fixed: Returns exit status 2 when smells are reported
41
- * Fixed: no longer claims an empty method is a Utility Function
52
+ === Minor enhancements
53
+ * New smell: first naive checks for Control Couple
54
+ * reek now only checks sources passed on the command line
55
+ * Code snippets can be supplied on the commandline
56
+ * Added headings and warnings count when smells in multiple files
57
+ * Added Reek::RakeTask to run reek from rakefiles
58
+
59
+ === Fixes
60
+ * Fixed: Returns exit status 2 when smells are reported
61
+ * Fixed: no longer claims an empty method is a Utility Function
42
62
 
43
63
  == 0.2.3 2008-09-22
44
64
 
data/config/defaults.reek CHANGED
@@ -7,6 +7,7 @@ LargeClass:
7
7
  - Module
8
8
  - String
9
9
  enabled: true
10
+ max_instance_variables: 9
10
11
  LongParameterList:
11
12
  max_params: 3
12
13
  exclude: []
data/lib/reek.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
2
 
3
3
  module Reek # :doc:
4
- VERSION = '1.0.1'
4
+ VERSION = '1.1.1'
5
5
  end
@@ -1,13 +1,35 @@
1
+ require 'set'
1
2
  require 'reek/code_context'
2
3
 
3
4
  module Reek
5
+
6
+ module ParameterSet
7
+ def names
8
+ return @names if @names
9
+ return (@names = []) if empty?
10
+ arg = slice(1)
11
+ case slice(0)
12
+ when :masgn
13
+ @names = arg[1..-1].map {|lasgn| Name.new(lasgn[1]) }
14
+ when :lasgn
15
+ @names = [Name.new(arg)]
16
+ end
17
+ end
18
+
19
+ def include?(name)
20
+ names.include?(name)
21
+ end
22
+ end
23
+
4
24
  class BlockContext < CodeContext
5
25
 
6
26
  def initialize(outer, exp)
7
27
  super
8
- @parameters = []
9
- @local_variables = []
10
28
  @name = Name.new('block')
29
+ @parameters = exp[0] if exp
30
+ @parameters ||= []
31
+ @parameters.extend(ParameterSet)
32
+ @local_variables = Set.new
11
33
  end
12
34
 
13
35
  def inside_a_block?
@@ -22,8 +44,8 @@ module Reek
22
44
  @outer.inside_a_block?
23
45
  end
24
46
 
25
- def record_parameter(sym)
26
- @parameters << Name.new(sym)
47
+ def record_local_variable(sym)
48
+ @local_variables << Name.new(sym)
27
49
  end
28
50
 
29
51
  def outer_name
@@ -31,7 +53,7 @@ module Reek
31
53
  end
32
54
 
33
55
  def variable_names
34
- @parameters + @local_variables
56
+ @parameters.names + @local_variables.to_a
35
57
  end
36
58
  end
37
59
  end
@@ -1,3 +1,4 @@
1
+ require 'set'
1
2
  require 'reek/code_context'
2
3
 
3
4
  class Class
@@ -23,7 +24,7 @@ module Reek
23
24
  @name = name
24
25
  @superclass = superclass
25
26
  @parsed_methods = []
26
- @instance_variables = []
27
+ @instance_variables = Set.new
27
28
  end
28
29
 
29
30
  def myself
@@ -33,12 +34,12 @@ module Reek
33
34
  def find_module(modname)
34
35
  sym = modname.to_s
35
36
  return nil unless myself
36
- myself.const_defined?(sym) ? myself.const_get(sym) : nil
37
+ @myself.const_defined?(sym) ? @myself.const_get(sym) : nil
37
38
  end
38
39
 
39
40
  def is_overriding_method?(name)
40
41
  return false unless myself
41
- myself.is_overriding_method?(name.to_s)
42
+ @myself.is_overriding_method?(name.to_s)
42
43
  end
43
44
 
44
45
  def is_struct?
@@ -46,7 +47,7 @@ module Reek
46
47
  end
47
48
 
48
49
  def num_methods
49
- meths = myself ? myself.non_inherited_methods : @parsed_methods
50
+ meths = myself ? @myself.non_inherited_methods : @parsed_methods
50
51
  meths.length
51
52
  end
52
53
 
@@ -14,8 +14,16 @@ module Reek
14
14
 
15
15
  class CodeParser < SexpProcessor
16
16
 
17
+ def self.unify(sexp) # :nodoc:
18
+ unifier = Unifier.new
19
+ unifier.processors.each do |proc|
20
+ proc.unsupported.delete :cfunc # HACK
21
+ end
22
+ return unifier.process(sexp[0])
23
+ end
24
+
17
25
  def self.parse_tree_for(code) # :nodoc:
18
- ParseTree.new.parse_tree_for_string(code)
26
+ unify(ParseTree.new.parse_tree_for_string(code))
19
27
  end
20
28
 
21
29
  # Creates a new Ruby code checker. Any smells discovered by
@@ -41,11 +49,12 @@ module Reek
41
49
  # Any smells found are saved in the +Report+ object that
42
50
  # was passed to this object's constructor.
43
51
  def check_object(obj)
44
- check_parse_tree(ParseTree.new.parse_tree(obj))
52
+ sexp = CodeParser.unify(ParseTree.new.parse_tree(obj))
53
+ check_parse_tree(sexp)
45
54
  end
46
55
 
47
56
  def process_default(exp)
48
- exp[1..-1].each { |sub| process(sub) if Array === sub }
57
+ exp[0..-1].each { |sub| process(sub) if Array === sub }
49
58
  s(exp)
50
59
  end
51
60
 
@@ -91,7 +100,7 @@ module Reek
91
100
 
92
101
  def process_iter(exp)
93
102
  process(exp[1])
94
- handle_context(BlockContext, :iter, exp[1..-1])
103
+ handle_context(BlockContext, :iter, exp[2..-1])
95
104
  end
96
105
 
97
106
  def process_dasgn_curr(exp)
@@ -114,8 +123,7 @@ module Reek
114
123
  end
115
124
 
116
125
  def process_fcall(exp)
117
- @element.record_depends_on_self
118
- @element.refs.record_reference_to_self
126
+ @element.record_use_of_self
119
127
  process_default(exp)
120
128
  end
121
129
 
@@ -125,11 +133,18 @@ module Reek
125
133
  end
126
134
 
127
135
  def process_vcall(exp)
128
- @element.record_depends_on_self
129
- @element.refs.record_reference_to_self
136
+ @element.record_use_of_self
130
137
  s(exp)
131
138
  end
132
139
 
140
+ def process_attrasgn(exp)
141
+ process_call(exp)
142
+ end
143
+
144
+ def process_op_asgn1(exp)
145
+ process_call(exp)
146
+ end
147
+
133
148
  def process_if(exp)
134
149
  handle_context(IfContext, :if, exp)
135
150
  end
@@ -155,8 +170,6 @@ module Reek
155
170
  s(exp)
156
171
  end
157
172
 
158
- private
159
-
160
173
  def self.count_statements(exp)
161
174
  stmts = exp[1..-1]
162
175
  ignore = 0
@@ -165,6 +178,8 @@ module Reek
165
178
  stmts.length - ignore
166
179
  end
167
180
 
181
+ private
182
+
168
183
  def self.is_expr?(exp, type)
169
184
  Array === exp and exp[0] == type
170
185
  end
@@ -198,7 +213,7 @@ module Reek
198
213
  end
199
214
 
200
215
  def check_parse_tree(sexp) # :nodoc:
201
- sexp.each { |exp| process(exp) }
216
+ process(sexp)
202
217
  end
203
218
  end
204
219
  end
@@ -32,22 +32,28 @@ module Reek
32
32
  def has_parameter(sym)
33
33
  @parameters.include?(sym.to_s)
34
34
  end
35
-
35
+
36
36
  def record_call_to(exp)
37
37
  @calls[exp] += 1
38
38
  receiver, meth = exp[1..2]
39
- if receiver.nil?
40
- record_depends_on_self
41
- else
42
- case receiver[0]
43
- when :lvar
44
- @refs.record_ref(receiver) unless meth == :new
45
- when :ivar
46
- record_depends_on_self
47
- @refs.record_reference_to_self
48
- end
39
+ receiver ||= [:self]
40
+ case receiver[0]
41
+ when :lvar
42
+ @refs.record_ref(receiver) unless meth == :new
43
+ when :self
44
+ record_use_of_self
49
45
  end
50
46
  end
47
+
48
+ def record_use_of_self
49
+ record_depends_on_self
50
+ @refs.record_reference_to_self
51
+ end
52
+
53
+ def record_instance_variable(sym)
54
+ record_use_of_self
55
+ @outer.record_instance_variable(sym)
56
+ end
51
57
 
52
58
  def record_depends_on_self
53
59
  @depends_on_self = true
@@ -58,7 +64,7 @@ module Reek
58
64
  end
59
65
 
60
66
  def self.is_block_arg?(param)
61
- Array === param and param[0] == :block
67
+ (Array === param and param[0] == :block) or (param.to_s =~ /^\&/)
62
68
  end
63
69
 
64
70
  def record_parameter(param)
@@ -20,7 +20,7 @@ module Reek
20
20
  def find_module(modname)
21
21
  return nil unless myself
22
22
  sym = modname.to_s
23
- myself.const_defined?(sym) ? myself.const_get(sym) : nil
23
+ @myself.const_defined?(sym) ? @myself.const_get(sym) : nil
24
24
  end
25
25
 
26
26
  def outer_name
data/lib/reek/name.rb CHANGED
@@ -3,7 +3,9 @@ module Reek
3
3
  include Comparable
4
4
 
5
5
  def self.resolve(exp, context)
6
- return [context, new(exp)] unless Array === exp
6
+ unless Array === exp
7
+ return resolve_string(exp.to_s, context)
8
+ end
7
9
  name = exp[1]
8
10
  case exp[0]
9
11
  when :colon2
@@ -17,6 +19,11 @@ module Reek
17
19
  end
18
20
  end
19
21
 
22
+ def self.resolve_string(str, context)
23
+ return [context, new(str)] unless str =~ /::/
24
+ resolve(ParseTree.new.parse_tree_for_string(str)[0], context)
25
+ end
26
+
20
27
  def initialize(sym)
21
28
  @name = sym.to_s
22
29
  end
@@ -6,7 +6,6 @@ module Reek
6
6
  class ObjectRefs # :nodoc:
7
7
  def initialize
8
8
  @refs = Hash.new(0)
9
- record_reference_to_self
10
9
  end
11
10
 
12
11
  def record_reference_to_self
@@ -34,8 +33,8 @@ module Reek
34
33
  end
35
34
 
36
35
  # TODO
37
- # Should be moved to Hash; but Hash has 58 methods, and there's currently
38
- # no way to turn off that report; which would therefore make the tests fail
36
+ # Should be moved to Hash
37
+ #
39
38
  def max_keys
40
39
  max = max_refs
41
40
  @refs.reject {|key,val| val != max}.keys
data/lib/reek/report.rb CHANGED
@@ -35,6 +35,12 @@ module Reek
35
35
  @report.to_a[index]
36
36
  end
37
37
 
38
+ # Creates a formatted report of all the +Smells::SmellWarning+ objects recorded in
39
+ # this report, with a heading.
40
+ def full_report(desc)
41
+ "\"#{desc}\" -- #{length} warnings:\n#{to_s}\n"
42
+ end
43
+
38
44
  # Creates a formatted report of all the +Smells::SmellWarning+ objects recorded in
39
45
  # this report.
40
46
  def to_s
@@ -64,11 +70,12 @@ module Reek
64
70
  @sources.inject(0) {|sum, src| sum + src.report.length }
65
71
  end
66
72
 
73
+ def smelly_sources
74
+ @sources.select {|src| src.smelly? }
75
+ end
76
+
67
77
  def to_s
68
- @sources.select {|src| src.smelly? }.map do |src|
69
- warnings = src.report
70
- "\"#{src}\" -- #{warnings.length} warnings:\n#{warnings.to_s}\n"
71
- end.join("\n")
78
+ smelly_sources.map { |src| src.full_report }.join("\n")
72
79
  end
73
80
  end
74
81
  end
@@ -1,52 +1,10 @@
1
+ require 'ruby2ruby'
2
+
1
3
  module Reek
2
4
  class SexpFormatter
3
5
  def self.format(sexp)
4
- return sexp.to_s unless Array === sexp
5
- first = sexp[1]
6
- case sexp[0]
7
- when :array
8
- format_all(sexp, ', ')
9
- when :call
10
- meth, args = sexp[2..3]
11
- result = format(first)
12
- if meth.to_s == '[]'
13
- result += (args.nil? ? '[]' : "[#{format(args)}]")
14
- else
15
- result += ".#{meth}" + (args ? "(#{format(args)})" : '')
16
- end
17
- result
18
- when :colon2
19
- format_all(sexp, '::')
20
- when :const, :cvar, :dvar
21
- format(first)
22
- when :dot2
23
- format_all(sexp, '..')
24
- when :dstr
25
- '"' + format_all(sexp, '') + '"'
26
- when :evstr
27
- "\#\{#{format(first)}\}"
28
- when :fcall, :vcall
29
- args = sexp[2]
30
- result = first.to_s
31
- result += "(#{format(args)})" if args
32
- result
33
- when :iter
34
- 'block'
35
- when :lasgn
36
- format_all(sexp, '=')
37
- when :nth_ref
38
- "$#{first}"
39
- when :str
40
- first
41
- when :xstr
42
- "`#{first}`"
43
- else
44
- sexp[-1].to_s
45
- end
46
- end
47
-
48
- def self.format_all(sexp, glue)
49
- sexp[1..-1].map {|arg| format(arg)}.join(glue)
6
+ sexp = YAML::load(YAML::dump(sexp))
7
+ Ruby2Ruby.new.process(sexp)
50
8
  end
51
9
  end
52
10
  end