reek 1.0.0 → 1.1.3

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