reek 3.1 → 3.2

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/{CHANGELOG → CHANGELOG.md} +150 -123
  4. data/README.md +61 -21
  5. data/Rakefile +2 -1
  6. data/bin/reek +1 -0
  7. data/config/defaults.reek +2 -2
  8. data/docs/Attribute.md +9 -13
  9. data/docs/Basic-Smell-Options.md +2 -2
  10. data/docs/Command-Line-Options.md +2 -2
  11. data/docs/Too-Many-Instance-Variables.md +1 -1
  12. data/features/samples.feature +22 -31
  13. data/features/step_definitions/sample_file_steps.rb +2 -2
  14. data/features/support/env.rb +1 -0
  15. data/lib/reek.rb +1 -0
  16. data/lib/reek/ast/ast_node_class_map.rb +5 -1
  17. data/lib/reek/ast/node.rb +4 -2
  18. data/lib/reek/ast/object_refs.rb +9 -5
  19. data/lib/reek/ast/reference_collector.rb +4 -2
  20. data/lib/reek/cli/application.rb +12 -9
  21. data/lib/reek/cli/command.rb +4 -0
  22. data/lib/reek/cli/input.rb +4 -4
  23. data/lib/reek/cli/option_interpreter.rb +11 -7
  24. data/lib/reek/cli/options.rb +42 -40
  25. data/lib/reek/cli/reek_command.rb +3 -3
  26. data/lib/reek/cli/warning_collector.rb +7 -3
  27. data/lib/reek/code_comment.rb +5 -1
  28. data/lib/reek/configuration/app_configuration.rb +4 -4
  29. data/lib/reek/context/code_context.rb +19 -17
  30. data/lib/reek/examiner.rb +8 -6
  31. data/lib/reek/rake/task.rb +13 -22
  32. data/lib/reek/report/formatter.rb +5 -1
  33. data/lib/reek/report/report.rb +46 -44
  34. data/lib/reek/smells/attribute.rb +42 -24
  35. data/lib/reek/smells/control_parameter.rb +21 -13
  36. data/lib/reek/smells/data_clump.rb +17 -9
  37. data/lib/reek/smells/duplicate_method_call.rb +12 -6
  38. data/lib/reek/smells/long_parameter_list.rb +2 -2
  39. data/lib/reek/smells/long_yield_list.rb +4 -4
  40. data/lib/reek/smells/nested_iterators.rb +4 -2
  41. data/lib/reek/smells/nil_check.rb +6 -2
  42. data/lib/reek/smells/repeated_conditional.rb +2 -2
  43. data/lib/reek/smells/smell_configuration.rb +15 -7
  44. data/lib/reek/smells/smell_detector.rb +23 -10
  45. data/lib/reek/smells/smell_repository.rb +9 -16
  46. data/lib/reek/smells/smell_warning.rb +6 -6
  47. data/lib/reek/smells/too_many_instance_variables.rb +4 -4
  48. data/lib/reek/smells/too_many_methods.rb +2 -2
  49. data/lib/reek/smells/too_many_statements.rb +4 -4
  50. data/lib/reek/smells/uncommunicative_method_name.rb +5 -5
  51. data/lib/reek/smells/uncommunicative_module_name.rb +5 -5
  52. data/lib/reek/smells/uncommunicative_parameter_name.rb +8 -4
  53. data/lib/reek/smells/uncommunicative_variable_name.rb +8 -4
  54. data/lib/reek/source/source_code.rb +6 -2
  55. data/lib/reek/source/source_locator.rb +4 -4
  56. data/lib/reek/spec/should_reek.rb +9 -4
  57. data/lib/reek/spec/should_reek_of.rb +8 -5
  58. data/lib/reek/spec/should_reek_only_of.rb +12 -8
  59. data/lib/reek/tree_dresser.rb +6 -2
  60. data/lib/reek/tree_walker.rb +28 -22
  61. data/lib/reek/version.rb +1 -1
  62. data/reek.gemspec +6 -5
  63. data/spec/gem/yard_spec.rb +6 -9
  64. data/spec/reek/code_comment_spec.rb +1 -1
  65. data/spec/reek/report/xml_report_spec.rb +11 -21
  66. data/spec/reek/smells/attribute_spec.rb +73 -57
  67. data/spec/reek/smells/too_many_instance_variables_spec.rb +26 -12
  68. data/spec/reek/source/source_locator_spec.rb +2 -2
  69. data/spec/samples/checkstyle.xml +12 -1
  70. data/spec/spec_helper.rb +1 -0
  71. metadata +20 -7
  72. data/spec/samples/unusual_syntax.rb +0 -21
@@ -47,13 +47,13 @@ module Reek
47
47
  # @return [Array<SmellWarning>]
48
48
  #
49
49
  def examine_context(ctx)
50
- @reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
51
- @accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
50
+ reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
51
+ accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
52
52
  name = ctx.name.to_s
53
- return [] if @accept_names.include?(ctx.full_name)
53
+ return [] if accept_names.include?(ctx.full_name)
54
54
  var = name.gsub(/^[@\*\&]*/, '')
55
- return [] if @accept_names.include?(var)
56
- return [] unless @reject_names.find { |patt| patt =~ var }
55
+ return [] if accept_names.include?(var)
56
+ return [] unless reject_names.find { |patt| patt =~ var }
57
57
  [SmellWarning.new(self,
58
58
  context: ctx.full_name,
59
59
  lines: [ctx.exp.line],
@@ -52,15 +52,15 @@ module Reek
52
52
  #
53
53
  # :reek:Duplication { allow_calls: [ to_s ] }
54
54
  def examine_context(ctx)
55
- @reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
56
- @accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
55
+ reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
56
+ accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
57
57
  exp = ctx.exp
58
58
  full_name = ctx.full_name
59
59
  name = exp.simple_name.to_s
60
- return [] if @accept_names.include?(full_name)
60
+ return [] if accept_names.include?(full_name)
61
61
  var = name.gsub(/^[@\*\&]*/, '')
62
- return [] if @accept_names.include?(var)
63
- return [] unless @reject_names.find { |patt| patt =~ var }
62
+ return [] if accept_names.include?(var)
63
+ return [] unless reject_names.find { |patt| patt =~ var }
64
64
  [SmellWarning.new(self,
65
65
  context: full_name,
66
66
  lines: [exp.line],
@@ -47,8 +47,8 @@ module Reek
47
47
  # @return [Array<SmellWarning>]
48
48
  #
49
49
  def examine_context(ctx)
50
- @reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
51
- @accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
50
+ self.reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
51
+ self.accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
52
52
  context_expression = ctx.exp
53
53
  context_expression.parameter_names.select do |name|
54
54
  bad_name?(name) && ctx.uses_param?(name)
@@ -63,9 +63,13 @@ module Reek
63
63
 
64
64
  def bad_name?(name)
65
65
  var = name.to_s.gsub(/^[@\*\&]*/, '')
66
- return false if var == '*' || @accept_names.include?(var)
67
- @reject_names.find { |patt| patt =~ var }
66
+ return false if var == '*' || accept_names.include?(var)
67
+ reject_names.find { |patt| patt =~ var }
68
68
  end
69
+
70
+ private
71
+
72
+ private_attr_accessor :accept_names, :reject_names
69
73
  end
70
74
  end
71
75
  end
@@ -51,8 +51,8 @@ module Reek
51
51
  # @return [Array<SmellWarning>]
52
52
  #
53
53
  def examine_context(ctx)
54
- @reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
55
- @accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
54
+ self.reject_names = value(REJECT_KEY, ctx, DEFAULT_REJECT_SET)
55
+ self.accept_names = value(ACCEPT_KEY, ctx, DEFAULT_ACCEPT_SET)
56
56
  variable_names(ctx.exp).select do |name, _lines|
57
57
  bad_name?(name, ctx)
58
58
  end.map do |name, lines|
@@ -66,8 +66,8 @@ module Reek
66
66
 
67
67
  def bad_name?(name, _ctx)
68
68
  var = name.to_s.gsub(/^[@\*\&]*/, '')
69
- return false if @accept_names.include?(var)
70
- @reject_names.find { |patt| patt =~ var }
69
+ return false if accept_names.include?(var)
70
+ reject_names.find { |patt| patt =~ var }
71
71
  end
72
72
 
73
73
  def variable_names(exp)
@@ -121,6 +121,10 @@ module Reek
121
121
  var = varname.to_sym
122
122
  accumulator[var].push(exp.line)
123
123
  end
124
+
125
+ private
126
+
127
+ private_attr_accessor :accept_names, :reject_names
124
128
  end
125
129
  end
126
130
  end
@@ -20,7 +20,7 @@ module Reek
20
20
 
21
21
  # Initializer.
22
22
  #
23
- # code - ruby code as String
23
+ # code - Ruby code as String
24
24
  # description - 'STDIN', 'string' or a filepath as String
25
25
  # parser - the parser to use for generating AST's out of the given source
26
26
  def initialize(code, description, parser = Parser::Ruby22)
@@ -82,7 +82,7 @@ module Reek
82
82
  @syntax_tree ||=
83
83
  begin
84
84
  begin
85
- ast, comments = @parser.parse_with_comments(@source, @description)
85
+ ast, comments = parser.parse_with_comments(source, description)
86
86
  rescue Racc::ParseError, Parser::SyntaxError => error
87
87
  $stderr.puts "#{description}: #{error.class.name}: #{error}"
88
88
  end
@@ -92,6 +92,10 @@ module Reek
92
92
  TreeDresser.new.dress(ast, comment_map)
93
93
  end
94
94
  end
95
+
96
+ private
97
+
98
+ private_attr_reader :parser, :source
95
99
  end
96
100
  end
97
101
  end
@@ -12,15 +12,15 @@ module Reek
12
12
  #
13
13
  # paths - a list of paths as Strings
14
14
  def initialize(paths, configuration: Configuration::AppConfiguration.new)
15
- self.paths = paths.flat_map do |string|
15
+ @paths = paths.flat_map do |string|
16
16
  path = Pathname.new(string)
17
17
  current_directory?(path) ? path.entries : path
18
18
  end
19
- self.configuration = configuration
19
+ @configuration = configuration
20
20
  end
21
21
 
22
22
  # Traverses all paths we initialized the SourceLocator with, finds
23
- # all relevant ruby files and returns them as a list.
23
+ # all relevant Ruby files and returns them as a list.
24
24
  #
25
25
  # @return [Array<Pathname>] - Ruby paths found
26
26
  def sources
@@ -29,7 +29,7 @@ module Reek
29
29
 
30
30
  private
31
31
 
32
- attr_accessor :paths, :configuration
32
+ private_attr_reader :configuration, :paths
33
33
 
34
34
  def source_paths
35
35
  paths.each_with_object([]) do |given_path, relevant_paths|
@@ -13,18 +13,23 @@ module Reek
13
13
  end
14
14
 
15
15
  def matches?(actual)
16
- @examiner = Examiner.new(actual, configuration: @configuration)
17
- @examiner.smelly?
16
+ self.examiner = Examiner.new(actual, configuration: configuration)
17
+ examiner.smelly?
18
18
  end
19
19
 
20
20
  def failure_message
21
- "Expected #{@examiner.description} to reek, but it didn't"
21
+ "Expected #{examiner.description} to reek, but it didn't"
22
22
  end
23
23
 
24
24
  def failure_message_when_negated
25
- rpt = Report::Formatter.format_list(@examiner.smells)
25
+ rpt = Report::Formatter.format_list(examiner.smells)
26
26
  "Expected no smells, but got:\n#{rpt}"
27
27
  end
28
+
29
+ private
30
+
31
+ private_attr_reader :configuration
32
+ private_attr_accessor :examiner
28
33
  end
29
34
  end
30
35
  end
@@ -17,21 +17,24 @@ module Reek
17
17
  end
18
18
 
19
19
  def matches?(actual)
20
- @examiner = Examiner.new(actual, configuration: @configuration)
21
- @all_smells = @examiner.smells
22
- @all_smells.any? { |warning| warning.matches?(@smell_category, @smell_details) }
20
+ self.examiner = Examiner.new(actual, configuration: configuration)
21
+ self.all_smells = examiner.smells
22
+ all_smells.any? { |warning| warning.matches?(smell_category, smell_details) }
23
23
  end
24
24
 
25
25
  def failure_message
26
- "Expected #{@examiner.description} to reek of #{@smell_category}, but it didn't"
26
+ "Expected #{examiner.description} to reek of #{smell_category}, but it didn't"
27
27
  end
28
28
 
29
29
  def failure_message_when_negated
30
- "Expected #{@examiner.description} not to reek of #{@smell_category}, but it did"
30
+ "Expected #{examiner.description} not to reek of #{smell_category}, but it did"
31
31
  end
32
32
 
33
33
  private
34
34
 
35
+ private_attr_reader :configuration, :smell_category, :smell_details
36
+ private_attr_accessor :all_smells, :examiner
37
+
35
38
  def normalize(smell_category_or_type)
36
39
  # In theory, users can give us many different types of input (see the documentation for
37
40
  # reek_of below), however we're basically ignoring all of those subleties and just
@@ -10,24 +10,28 @@ module Reek
10
10
  # @api private
11
11
  class ShouldReekOnlyOf < ShouldReekOf
12
12
  def matches?(actual)
13
- matches_examiner?(Examiner.new(actual, configuration: @configuration))
13
+ matches_examiner?(Examiner.new(actual, configuration: configuration))
14
14
  end
15
15
 
16
16
  def matches_examiner?(examiner)
17
- @examiner = examiner
18
- @warnings = @examiner.smells
19
- return false if @warnings.empty?
20
- @warnings.all? { |warning| warning.matches?(@smell_category) }
17
+ self.examiner = examiner
18
+ self.warnings = examiner.smells
19
+ return false if warnings.empty?
20
+ warnings.all? { |warning| warning.matches?(smell_category) }
21
21
  end
22
22
 
23
23
  def failure_message
24
- rpt = Report::Formatter.format_list(@warnings)
25
- "Expected #{@examiner.description} to reek only of #{@smell_category}, but got:\n#{rpt}"
24
+ rpt = Report::Formatter.format_list(warnings)
25
+ "Expected #{examiner.description} to reek only of #{smell_category}, but got:\n#{rpt}"
26
26
  end
27
27
 
28
28
  def failure_message_when_negated
29
- "Expected #{@examiner.description} not to reek only of #{@smell_category}, but it did"
29
+ "Expected #{examiner.description} not to reek only of #{smell_category}, but it did"
30
30
  end
31
+
32
+ private
33
+
34
+ private_attr_accessor :warnings
31
35
  end
32
36
  end
33
37
  end
@@ -25,8 +25,12 @@ module Reek
25
25
  type = sexp.type
26
26
  children = sexp.children.map { |child| dress(child, comment_map, sexp) }
27
27
  comments = comment_map[sexp]
28
- @klass_map.klass_for(type).new(type, children,
29
- location: sexp.loc, comments: comments, parent: parent)
28
+ klass_map.klass_for(type).new(type, children,
29
+ location: sexp.loc, comments: comments, parent: parent)
30
30
  end
31
+
32
+ private
33
+
34
+ private_attr_reader :klass_map
31
35
  end
32
36
  end
@@ -24,14 +24,20 @@ module Reek
24
24
  end
25
25
 
26
26
  def walk
27
- @result ||= process(@exp)
28
- @result.each do |element|
29
- @smell_repository.examine(element)
27
+ result.each do |element|
28
+ smell_repository.examine(element)
30
29
  end
31
30
  end
32
31
 
33
32
  private
34
33
 
34
+ private_attr_accessor :element
35
+ private_attr_reader :exp, :smell_repository
36
+
37
+ def result
38
+ @result ||= process(exp)
39
+ end
40
+
35
41
  def process(exp)
36
42
  context_processor = "process_#{exp.type}"
37
43
  if context_processor_exists?(context_processor)
@@ -39,7 +45,7 @@ module Reek
39
45
  else
40
46
  process_default exp
41
47
  end
42
- @element
48
+ element
43
49
  end
44
50
 
45
51
  def process_module(exp)
@@ -86,28 +92,28 @@ module Reek
86
92
 
87
93
  def process_send(exp)
88
94
  if visibility_modifier? exp
89
- @element.track_visibility(exp.method_name, exp.arg_names)
95
+ element.track_visibility(exp.method_name, exp.arg_names)
90
96
  end
91
- @element.record_call_to(exp)
97
+ element.record_call_to(exp)
92
98
  process_default(exp)
93
99
  end
94
100
 
95
101
  def process_attrasgn(exp)
96
- @element.record_call_to(exp)
102
+ element.record_call_to(exp)
97
103
  process_default(exp)
98
104
  end
99
105
 
100
106
  alias_method :process_op_asgn, :process_attrasgn
101
107
 
102
108
  def process_ivar(exp)
103
- @element.record_use_of_self
109
+ element.record_use_of_self
104
110
  process_default(exp)
105
111
  end
106
112
 
107
113
  alias_method :process_ivasgn, :process_ivar
108
114
 
109
115
  def process_self(_)
110
- @element.record_use_of_self
116
+ element.record_use_of_self
111
117
  end
112
118
 
113
119
  alias_method :process_zsuper, :process_self
@@ -123,7 +129,7 @@ module Reek
123
129
 
124
130
  def process_begin(exp)
125
131
  count_statement_list(exp.children)
126
- @element.count_statements(-1)
132
+ element.count_statements(-1)
127
133
  process_default(exp)
128
134
  end
129
135
 
@@ -132,13 +138,13 @@ module Reek
132
138
  def process_if(exp)
133
139
  count_clause(exp[2])
134
140
  count_clause(exp[3])
135
- @element.count_statements(-1)
141
+ element.count_statements(-1)
136
142
  process_default(exp)
137
143
  end
138
144
 
139
145
  def process_while(exp)
140
146
  count_clause(exp[2])
141
- @element.count_statements(-1)
147
+ element.count_statements(-1)
142
148
  process_default(exp)
143
149
  end
144
150
 
@@ -146,13 +152,13 @@ module Reek
146
152
 
147
153
  def process_for(exp)
148
154
  count_clause(exp[3])
149
- @element.count_statements(-1)
155
+ element.count_statements(-1)
150
156
  process_default(exp)
151
157
  end
152
158
 
153
159
  def process_rescue(exp)
154
160
  count_clause(exp[1])
155
- @element.count_statements(-1)
161
+ element.count_statements(-1)
156
162
  process_default(exp)
157
163
  end
158
164
 
@@ -163,7 +169,7 @@ module Reek
163
169
 
164
170
  def process_case(exp)
165
171
  count_clause(exp.else_body)
166
- @element.count_statements(-1)
172
+ element.count_statements(-1)
167
173
  process_default(exp)
168
174
  end
169
175
 
@@ -177,16 +183,16 @@ module Reek
177
183
  end
178
184
 
179
185
  def count_clause(sexp)
180
- @element.count_statements(1) if sexp
186
+ element.count_statements(1) if sexp
181
187
  end
182
188
 
183
189
  def count_statement_list(statement_list)
184
- @element.count_statements statement_list.length
190
+ element.count_statements statement_list.length
185
191
  end
186
192
 
187
193
  def inside_new_context(klass, exp)
188
- scope = klass.new(@element, exp)
189
- @element.append_child_context(scope)
194
+ scope = klass.new(element, exp)
195
+ element.append_child_context(scope)
190
196
  push(scope) do
191
197
  yield
192
198
  end
@@ -194,10 +200,10 @@ module Reek
194
200
  end
195
201
 
196
202
  def push(scope)
197
- orig = @element
198
- @element = scope
203
+ orig = element
204
+ self.element = scope
199
205
  yield
200
- @element = orig
206
+ self.element = orig
201
207
  end
202
208
 
203
209
  # FIXME: Move to SendNode?
@@ -3,6 +3,6 @@ module Reek
3
3
  # This module holds the Reek version informations
4
4
  #
5
5
  module Version
6
- STRING = '3.1'
6
+ STRING = '3.2'
7
7
  end
8
8
  end
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.license = 'MIT'
16
16
  s.email = ['timo.roessner@googlemail.com']
17
- s.extra_rdoc_files = ['CHANGELOG', 'License.txt']
17
+ s.extra_rdoc_files = ['CHANGELOG.md', 'License.txt']
18
18
  s.files = `git ls-files -z`.split("\0")
19
19
  s.executables = s.files.grep(%r{^bin/}).map { |path| File.basename(path) }
20
20
  s.homepage = 'https://github.com/troessner/reek/wiki'
@@ -22,12 +22,13 @@ Gem::Specification.new do |s|
22
22
  s.required_ruby_version = '>= 2.0.0'
23
23
  s.summary = 'Code smell detector for Ruby'
24
24
 
25
- s.add_runtime_dependency 'parser', '~> 2.2.2.5'
26
- s.add_runtime_dependency 'rainbow', '~> 2.0'
27
- s.add_runtime_dependency 'unparser', '~> 0.2.2'
25
+ s.add_runtime_dependency 'parser', '~> 2.2.2.5'
26
+ s.add_runtime_dependency 'private_attr', '~> 1.1'
27
+ s.add_runtime_dependency 'rainbow', '~> 2.0'
28
+ s.add_runtime_dependency 'unparser', '~> 0.2.2'
28
29
 
29
30
  s.add_development_dependency 'activesupport', '~> 4.2'
30
- s.add_development_dependency 'aruba', '~> 0.7.2'
31
+ s.add_development_dependency 'aruba', '~> 0.8.0'
31
32
  s.add_development_dependency 'ataru', '~> 0.2.0'
32
33
  s.add_development_dependency 'bundler', '~> 1.1'
33
34
  s.add_development_dependency 'cucumber', '~> 2.0'