reek 1.0.0 → 1.1.3

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 +56 -22
  2. data/config/defaults.reek +3 -5
  3. data/lib/reek.rb +1 -1
  4. data/lib/reek/block_context.rb +27 -5
  5. data/lib/reek/class_context.rb +5 -9
  6. data/lib/reek/code_parser.rb +23 -50
  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/object_source.rb +53 -0
  12. data/lib/reek/report.rb +41 -2
  13. data/lib/reek/sexp_formatter.rb +4 -46
  14. data/lib/reek/smells/large_class.rb +27 -8
  15. data/lib/reek/smells/long_parameter_list.rb +1 -1
  16. data/lib/reek/smells/smells.rb +4 -8
  17. data/lib/reek/source.rb +19 -8
  18. data/lib/reek/stop_context.rb +4 -16
  19. data/lib/reek/yield_call_context.rb +1 -3
  20. data/reek.gemspec +11 -9
  21. data/spec/reek/block_context_spec.rb +40 -0
  22. data/spec/reek/class_context_spec.rb +11 -40
  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/singleton_method_context_spec.rb +1 -1
  31. data/spec/reek/smells/duplication_spec.rb +2 -2
  32. data/spec/reek/smells/feature_envy_spec.rb +132 -36
  33. data/spec/reek/smells/large_class_spec.rb +48 -47
  34. data/spec/reek/smells/long_method_spec.rb +1 -1
  35. data/spec/reek/smells/long_parameter_list_spec.rb +4 -11
  36. data/spec/reek/smells/uncommunicative_name_spec.rb +6 -1
  37. data/spec/reek/smells/utility_function_spec.rb +6 -9
  38. data/spec/{samples → slow}/inline_spec.rb +13 -10
  39. data/spec/{samples → slow}/optparse_spec.rb +20 -12
  40. data/spec/{samples → slow}/redcloth_spec.rb +16 -8
  41. data/spec/{integration → slow}/reek_source_spec.rb +0 -0
  42. data/spec/{samples → slow/samples}/inline.rb +0 -0
  43. data/spec/{samples → slow/samples}/optparse.rb +0 -0
  44. data/spec/{samples → slow/samples}/redcloth.rb +0 -0
  45. data/spec/{integration → slow}/script_spec.rb +0 -0
  46. data/spec/slow/source_list_spec.rb +40 -0
  47. data/spec/spec_helper.rb +2 -0
  48. data/tasks/rspec.rake +1 -1
  49. metadata +30 -15
  50. data/spec/reek/sexp_formatter_spec.rb +0 -31
@@ -1,44 +1,78 @@
1
- == 1.0.0 2009-04-05
1
+ == 1.1.3 2009-05-19
2
+
3
+ === Minor Changes
4
+ * No longer depends directly on the sexp_processor gem
5
+
6
+ === Fixes
7
+ * LargeClass now relies only on the given source code (#26)
8
+
9
+ == 1.1.2 2009-05-18
10
+
11
+ === Major Enhancements
12
+ * Switched from ParseTree to ruby_parser for source code parsing
13
+ * 'MyClass.should_not reek' now only possible if ParseTree gem installed
14
+
15
+ == 1.1.1 2009-05-08
16
+
17
+ === Minor enhancements
18
+ * LargeClass now also warns about any class with > 9 instance variables (#6)
19
+ * Now depends on ruby2ruby, to display code better
20
+ * Duplication notices more repeated method calls
21
+ * Smells within blocks are now reported better
22
+
23
+ == 1.1.0 2009-04-10
24
+
25
+ === Minor enhancements
26
+ * Now possible to write 'MyClass.should_not reek' (#33)
27
+
28
+ === Fixes
29
+ * Now counts attr assignments ([]= etc) in feature envy calculations
30
+ * Doesn't attempt to find *.reek files when reading code from stdin
2
31
 
3
- === Major enhancements:
32
+ == 1.0.1 2009-04-06
4
33
 
34
+ === Fixes
35
+ * Dir[...].to_source now creates a Report that can be browsed (#36)
36
+
37
+ == 1.0.0 2009-04-05
38
+
39
+ === Major enhancements
5
40
  * Use *.reek files in source tree to configure Reek's behaviour
6
41
  * Added -f option to configure report format
7
42
  * --sort_order replaced by -f, -c and -s
8
43
  * Matchers provided for rspec; eg. foo.should_not reek
9
44
 
10
- === Minor enhancements:
11
-
45
+ === Minor enhancements
12
46
  * Smells in singleton methods are now analysed
13
47
  * Uncommunicative parameter names in blocks now reported
14
48
  * Modules and blocks now reflected in scope of smell reports
15
49
 
16
- === Fixes:
17
-
50
+ === Fixes
18
51
  * Corrected false reports of long arg lists to yield
19
52
  * A method can now be a UtilityFunction only when it includes a call
20
53
 
21
54
  == 0.3.1 2008-11-17
22
55
 
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
56
+ === Minor enhancements
57
+ * Uncommunicative Name now checks instance variables more thoroughly
58
+ * Uncommunicative Name now warns about names of the form 'x2'
59
+ * Added check for duplicated calls within a method
60
+ * Reduced scope of Feature Envy warnings to cover only overuse of lvars
61
+ * Added rdoc comments explaining what each smell is about
62
+ * Chained iterators are no longer mis-reported as nested
30
63
 
31
64
  == 0.3.0 2008-11-02
32
65
 
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
66
+ === Minor enhancements
67
+ * New smell: first naive checks for Control Couple
68
+ * reek now only checks sources passed on the command line
69
+ * Code snippets can be supplied on the commandline
70
+ * Added headings and warnings count when smells in multiple files
71
+ * Added Reek::RakeTask to run reek from rakefiles
72
+
73
+ === Fixes
74
+ * Fixed: Returns exit status 2 when smells are reported
75
+ * Fixed: no longer claims an empty method is a Utility Function
42
76
 
43
77
  == 0.2.3 2008-09-22
44
78
 
@@ -1,12 +1,10 @@
1
1
  ---
2
2
  LargeClass:
3
3
  max_methods: 25
4
- exclude:
5
- - Array
6
- - Hash
7
- - Module
8
- - String
4
+ exclude: []
5
+
9
6
  enabled: true
7
+ max_instance_variables: 9
10
8
  LongParameterList:
11
9
  max_params: 3
12
10
  exclude: []
@@ -1,5 +1,5 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
2
 
3
3
  module Reek # :doc:
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.3'
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,10 +1,7 @@
1
+ require 'set'
1
2
  require 'reek/code_context'
2
3
 
3
4
  class Class
4
- def non_inherited_methods
5
- instance_methods(false) + private_instance_methods(false)
6
- end
7
-
8
5
  def is_overriding_method?(sym)
9
6
  instance_methods(false).include?(sym) and superclass.instance_methods(true).include?(sym)
10
7
  end
@@ -23,7 +20,7 @@ module Reek
23
20
  @name = name
24
21
  @superclass = superclass
25
22
  @parsed_methods = []
26
- @instance_variables = []
23
+ @instance_variables = Set.new
27
24
  end
28
25
 
29
26
  def myself
@@ -33,12 +30,12 @@ module Reek
33
30
  def find_module(modname)
34
31
  sym = modname.to_s
35
32
  return nil unless myself
36
- myself.const_defined?(sym) ? myself.const_get(sym) : nil
33
+ @myself.const_defined?(sym) ? @myself.const_get(sym) : nil
37
34
  end
38
35
 
39
36
  def is_overriding_method?(name)
40
37
  return false unless myself
41
- myself.is_overriding_method?(name.to_s)
38
+ @myself.is_overriding_method?(name.to_s)
42
39
  end
43
40
 
44
41
  def is_struct?
@@ -46,8 +43,7 @@ module Reek
46
43
  end
47
44
 
48
45
  def num_methods
49
- meths = myself ? myself.non_inherited_methods : @parsed_methods
50
- meths.length
46
+ @parsed_methods.length
51
47
  end
52
48
 
53
49
  def record_instance_variable(sym)
@@ -1,6 +1,5 @@
1
1
  require 'rubygems'
2
- require 'parse_tree'
3
- require 'sexp_processor'
2
+ require 'sexp'
4
3
  require 'reek/block_context'
5
4
  require 'reek/class_context'
6
5
  require 'reek/module_context'
@@ -12,41 +11,24 @@ require 'reek/yield_call_context'
12
11
 
13
12
  module Reek
14
13
 
15
- class CodeParser < SexpProcessor
14
+ class CodeParser
16
15
 
17
- def self.parse_tree_for(code) # :nodoc:
18
- ParseTree.new.parse_tree_for_string(code)
19
- end
20
-
21
- # Creates a new Ruby code checker. Any smells discovered by
22
- # +check_source+ or +check_object+ will be stored in +report+.
16
+ # Creates a new Ruby code checker. Any smells discovered
17
+ # will be stored in +report+.
23
18
  def initialize(report, smells, ctx = StopContext.new)
24
- super()
25
19
  @report = report
26
20
  @smells = smells
27
21
  @element = ctx
28
- @unsupported -= [:cfunc]
29
- @default_method = :process_default
30
- @require_empty = @warn_on_default = false
31
22
  end
32
23
 
33
- # Analyses the given Ruby source +code+ looking for smells.
34
- # Any smells found are saved in the +Report+ object that
35
- # was passed to this object's constructor.
36
- def check_source(code)
37
- check_parse_tree(CodeParser.parse_tree_for(code))
38
- end
39
-
40
- # Analyses the given Ruby object +obj+ looking for smells.
41
- # Any smells found are saved in the +Report+ object that
42
- # was passed to this object's constructor.
43
- def check_object(obj)
44
- check_parse_tree(ParseTree.new.parse_tree(obj))
24
+ def process(exp)
25
+ meth = "process_#{exp[0]}"
26
+ meth = :process_default unless self.respond_to?(meth)
27
+ self.send(meth, exp)
45
28
  end
46
29
 
47
30
  def process_default(exp)
48
- exp[1..-1].each { |sub| process(sub) if Array === sub }
49
- s(exp)
31
+ exp[0..-1].each { |sub| process(sub) if Array === sub }
50
32
  end
51
33
 
52
34
  def process_module(exp)
@@ -54,7 +36,6 @@ module Reek
54
36
  process_default(exp)
55
37
  check_smells(:module)
56
38
  end
57
- s(exp)
58
39
  end
59
40
 
60
41
  def process_class(exp)
@@ -62,7 +43,6 @@ module Reek
62
43
  process_default(exp) unless @element.is_struct?
63
44
  check_smells(:class)
64
45
  end
65
- s(exp)
66
46
  end
67
47
 
68
48
  def process_defn(exp)
@@ -75,23 +55,20 @@ module Reek
75
55
 
76
56
  def process_args(exp)
77
57
  exp[1..-1].each {|sym| @element.record_parameter(sym) }
78
- s(exp)
79
58
  end
80
59
 
81
60
  def process_attrset(exp)
82
61
  @element.record_depends_on_self if /^@/ === exp[1].to_s
83
- s(exp)
84
62
  end
85
63
 
86
64
  def process_lit(exp)
87
65
  val = exp[1]
88
66
  @element.record_depends_on_self if val == :self
89
- s(exp)
90
67
  end
91
68
 
92
69
  def process_iter(exp)
93
70
  process(exp[1])
94
- handle_context(BlockContext, :iter, exp[1..-1])
71
+ handle_context(BlockContext, :iter, exp[2..-1])
95
72
  end
96
73
 
97
74
  def process_dasgn_curr(exp)
@@ -114,20 +91,24 @@ module Reek
114
91
  end
115
92
 
116
93
  def process_fcall(exp)
117
- @element.record_depends_on_self
118
- @element.refs.record_reference_to_self
94
+ @element.record_use_of_self
119
95
  process_default(exp)
120
96
  end
121
97
 
122
98
  def process_cfunc(exp)
123
99
  @element.record_depends_on_self
124
- s(exp)
125
100
  end
126
101
 
127
102
  def process_vcall(exp)
128
- @element.record_depends_on_self
129
- @element.refs.record_reference_to_self
130
- s(exp)
103
+ @element.record_use_of_self
104
+ end
105
+
106
+ def process_attrasgn(exp)
107
+ process_call(exp)
108
+ end
109
+
110
+ def process_op_asgn1(exp)
111
+ process_call(exp)
131
112
  end
132
113
 
133
114
  def process_if(exp)
@@ -140,8 +121,7 @@ module Reek
140
121
 
141
122
  def process_lasgn(exp)
142
123
  @element.record_local_variable(exp[1])
143
- process(exp[2])
144
- s(exp)
124
+ process_default(exp)
145
125
  end
146
126
 
147
127
  def process_iasgn(exp)
@@ -152,11 +132,8 @@ module Reek
152
132
 
153
133
  def process_self(exp)
154
134
  @element.record_depends_on_self
155
- s(exp)
156
135
  end
157
136
 
158
- private
159
-
160
137
  def self.count_statements(exp)
161
138
  stmts = exp[1..-1]
162
139
  ignore = 0
@@ -165,6 +142,8 @@ module Reek
165
142
  stmts.length - ignore
166
143
  end
167
144
 
145
+ private
146
+
168
147
  def self.is_expr?(exp, type)
169
148
  Array === exp and exp[0] == type
170
149
  end
@@ -178,7 +157,6 @@ module Reek
178
157
  process_default(exp)
179
158
  check_smells(type)
180
159
  end
181
- s(exp)
182
160
  end
183
161
 
184
162
  def check_smells(type)
@@ -194,11 +172,6 @@ module Reek
194
172
 
195
173
  def pop(exp)
196
174
  @element = @element.outer
197
- s(exp)
198
- end
199
-
200
- def check_parse_tree(sexp) # :nodoc:
201
- sexp.each { |exp| process(exp) }
202
175
  end
203
176
  end
204
177
  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)