reek 4.4.2 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +10 -1
  3. data/CHANGELOG.md +16 -0
  4. data/CONTRIBUTING.md +52 -23
  5. data/README.md +16 -3
  6. data/ataru_setup.rb +1 -1
  7. data/docs/API.md +1 -4
  8. data/docs/How-reek-works-internally.md +5 -5
  9. data/docs/Style-Guide.md +7 -0
  10. data/features/command_line_interface/options.feature +1 -0
  11. data/features/command_line_interface/show_progress.feature +33 -0
  12. data/features/configuration_files/masking_smells.feature +0 -1
  13. data/features/configuration_via_source_comments/erroneous_source_comments.feature +18 -4
  14. data/features/configuration_via_source_comments/well_formed_source_comments.feature +116 -0
  15. data/features/step_definitions/sample_file_steps.rb +5 -0
  16. data/features/todo_list.feature +42 -14
  17. data/lib/reek.rb +1 -1
  18. data/lib/reek/cli/application.rb +5 -0
  19. data/lib/reek/cli/command/report_command.rb +6 -1
  20. data/lib/reek/cli/command/todo_list_command.rb +1 -2
  21. data/lib/reek/cli/options.rb +19 -3
  22. data/lib/reek/code_comment.rb +83 -11
  23. data/lib/reek/configuration/configuration_validator.rb +2 -2
  24. data/lib/reek/errors/bad_detector_in_comment_error.rb +35 -0
  25. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +34 -0
  26. data/lib/reek/examiner.rb +36 -14
  27. data/lib/reek/report.rb +28 -9
  28. data/lib/reek/report/base_report.rb +77 -0
  29. data/lib/reek/report/code_climate.rb +4 -0
  30. data/lib/reek/report/code_climate/code_climate_fingerprint.rb +48 -0
  31. data/lib/reek/report/code_climate/code_climate_formatter.rb +5 -0
  32. data/lib/reek/report/code_climate/code_climate_report.rb +19 -0
  33. data/lib/reek/report/formatter.rb +5 -56
  34. data/lib/reek/report/{heading_formatter.rb → formatter/heading_formatter.rb} +4 -4
  35. data/lib/reek/report/formatter/location_formatter.rb +41 -0
  36. data/lib/reek/report/formatter/progress_formatter.rb +80 -0
  37. data/lib/reek/report/formatter/simple_warning_formatter.rb +35 -0
  38. data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +35 -0
  39. data/lib/reek/report/html_report.rb +21 -0
  40. data/lib/reek/report/{html_report.html.erb → html_report/html_report.html.erb} +0 -0
  41. data/lib/reek/report/json_report.rb +18 -0
  42. data/lib/reek/report/text_report.rb +68 -0
  43. data/lib/reek/report/xml_report.rb +61 -0
  44. data/lib/reek/report/yaml_report.rb +18 -0
  45. data/lib/reek/smell_detectors.rb +30 -0
  46. data/lib/reek/{smells → smell_detectors}/attribute.rb +3 -3
  47. data/lib/reek/{smells/smell_detector.rb → smell_detectors/base_detector.rb} +3 -3
  48. data/lib/reek/{smells → smell_detectors}/boolean_parameter.rb +3 -3
  49. data/lib/reek/{smells → smell_detectors}/class_variable.rb +3 -3
  50. data/lib/reek/{smells → smell_detectors}/control_parameter.rb +3 -3
  51. data/lib/reek/{smells → smell_detectors}/data_clump.rb +3 -3
  52. data/lib/reek/{smells/smell_repository.rb → smell_detectors/detector_repository.rb} +9 -9
  53. data/lib/reek/{smells → smell_detectors}/duplicate_method_call.rb +3 -3
  54. data/lib/reek/{smells → smell_detectors}/feature_envy.rb +3 -3
  55. data/lib/reek/{smells → smell_detectors}/instance_variable_assumption.rb +3 -3
  56. data/lib/reek/{smells → smell_detectors}/irresponsible_module.rb +3 -3
  57. data/lib/reek/{smells → smell_detectors}/long_parameter_list.rb +3 -3
  58. data/lib/reek/{smells → smell_detectors}/long_yield_list.rb +3 -3
  59. data/lib/reek/{smells → smell_detectors}/manual_dispatch.rb +3 -3
  60. data/lib/reek/{smells → smell_detectors}/module_initialize.rb +3 -3
  61. data/lib/reek/{smells → smell_detectors}/nested_iterators.rb +3 -3
  62. data/lib/reek/{smells → smell_detectors}/nil_check.rb +3 -3
  63. data/lib/reek/{smells → smell_detectors}/prima_donna_method.rb +3 -3
  64. data/lib/reek/{smells → smell_detectors}/repeated_conditional.rb +3 -3
  65. data/lib/reek/{smells → smell_detectors}/smell_configuration.rb +1 -1
  66. data/lib/reek/{smells → smell_detectors}/smell_warning.rb +1 -1
  67. data/lib/reek/{smells → smell_detectors}/subclassed_from_core_class.rb +3 -3
  68. data/lib/reek/{smells → smell_detectors}/too_many_constants.rb +3 -3
  69. data/lib/reek/{smells → smell_detectors}/too_many_instance_variables.rb +3 -3
  70. data/lib/reek/{smells → smell_detectors}/too_many_methods.rb +3 -3
  71. data/lib/reek/{smells → smell_detectors}/too_many_statements.rb +3 -3
  72. data/lib/reek/{smells → smell_detectors}/uncommunicative_method_name.rb +3 -3
  73. data/lib/reek/{smells → smell_detectors}/uncommunicative_module_name.rb +3 -3
  74. data/lib/reek/{smells → smell_detectors}/uncommunicative_parameter_name.rb +3 -3
  75. data/lib/reek/{smells → smell_detectors}/uncommunicative_variable_name.rb +3 -3
  76. data/lib/reek/{smells → smell_detectors}/unused_parameters.rb +3 -3
  77. data/lib/reek/{smells → smell_detectors}/unused_private_method.rb +3 -3
  78. data/lib/reek/{smells → smell_detectors}/utility_function.rb +3 -3
  79. data/lib/reek/spec/should_reek_of.rb +1 -1
  80. data/lib/reek/version.rb +1 -1
  81. data/reek.gemspec +1 -1
  82. data/spec/factories/factories.rb +6 -6
  83. data/spec/reek/cli/command/report_command_spec.rb +3 -1
  84. data/spec/reek/cli/options_spec.rb +12 -2
  85. data/spec/reek/code_comment_spec.rb +18 -6
  86. data/spec/reek/configuration/app_configuration_spec.rb +12 -12
  87. data/spec/reek/configuration/default_directive_spec.rb +1 -1
  88. data/spec/reek/configuration/directory_directives_spec.rb +2 -2
  89. data/spec/reek/examiner_spec.rb +56 -28
  90. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +122 -0
  91. data/spec/reek/report/{code_climate_formatter_spec.rb → code_climate/code_climate_formatter_spec.rb} +6 -2
  92. data/spec/reek/report/{code_climate_report_spec.rb → code_climate/code_climate_report_spec.rb} +7 -5
  93. data/spec/reek/report/{location_formatter_spec.rb → formatter/location_formatter_spec.rb} +4 -4
  94. data/spec/reek/report/formatter/progress_formatter_spec.rb +68 -0
  95. data/spec/reek/report/html_report_spec.rb +1 -1
  96. data/spec/reek/report/json_report_spec.rb +2 -2
  97. data/spec/reek/report/text_report_spec.rb +3 -4
  98. data/spec/reek/report/xml_report_spec.rb +1 -1
  99. data/spec/reek/report/yaml_report_spec.rb +2 -2
  100. data/spec/reek/report_spec.rb +3 -3
  101. data/spec/reek/{smells → smell_detectors}/attribute_spec.rb +2 -2
  102. data/spec/reek/{smells/smell_detector_spec.rb → smell_detectors/base_detector_spec.rb} +5 -5
  103. data/spec/reek/{smells → smell_detectors}/boolean_parameter_spec.rb +2 -2
  104. data/spec/reek/{smells → smell_detectors}/class_variable_spec.rb +2 -2
  105. data/spec/reek/{smells → smell_detectors}/control_parameter_spec.rb +2 -2
  106. data/spec/reek/{smells → smell_detectors}/data_clump_spec.rb +2 -2
  107. data/spec/reek/smell_detectors/detector_repository_spec.rb +22 -0
  108. data/spec/reek/{smells → smell_detectors}/duplicate_method_call_spec.rb +6 -6
  109. data/spec/reek/{smells → smell_detectors}/feature_envy_spec.rb +2 -2
  110. data/spec/reek/{smells → smell_detectors}/instance_variable_assumption_spec.rb +2 -2
  111. data/spec/reek/{smells → smell_detectors}/irresponsible_module_spec.rb +2 -2
  112. data/spec/reek/{smells → smell_detectors}/long_parameter_list_spec.rb +2 -2
  113. data/spec/reek/{smells → smell_detectors}/long_yield_list_spec.rb +2 -2
  114. data/spec/reek/{smells → smell_detectors}/manual_dispatch_spec.rb +2 -2
  115. data/spec/reek/{smells → smell_detectors}/module_initialize_spec.rb +2 -2
  116. data/spec/reek/{smells → smell_detectors}/nested_iterators_spec.rb +4 -4
  117. data/spec/reek/{smells → smell_detectors}/nil_check_spec.rb +2 -2
  118. data/spec/reek/{smells → smell_detectors}/prima_donna_method_spec.rb +2 -2
  119. data/spec/reek/{smells → smell_detectors}/repeated_conditional_spec.rb +2 -2
  120. data/spec/reek/{smells → smell_detectors}/smell_configuration_spec.rb +2 -2
  121. data/spec/reek/{smells → smell_detectors}/smell_warning_spec.rb +3 -3
  122. data/spec/reek/{smells → smell_detectors}/subclassed_from_core_class_spec.rb +2 -2
  123. data/spec/reek/{smells → smell_detectors}/too_many_constants_spec.rb +3 -3
  124. data/spec/reek/{smells → smell_detectors}/too_many_instance_variables_spec.rb +3 -3
  125. data/spec/reek/{smells → smell_detectors}/too_many_methods_spec.rb +3 -3
  126. data/spec/reek/{smells → smell_detectors}/too_many_statements_spec.rb +3 -3
  127. data/spec/reek/{smells → smell_detectors}/uncommunicative_method_name_spec.rb +2 -2
  128. data/spec/reek/{smells → smell_detectors}/uncommunicative_module_name_spec.rb +2 -2
  129. data/spec/reek/{smells → smell_detectors}/uncommunicative_parameter_name_spec.rb +2 -2
  130. data/spec/reek/{smells → smell_detectors}/uncommunicative_variable_name_spec.rb +2 -2
  131. data/spec/reek/{smells → smell_detectors}/unused_parameters_spec.rb +2 -2
  132. data/spec/reek/{smells → smell_detectors}/unused_private_method_spec.rb +4 -4
  133. data/spec/reek/{smells → smell_detectors}/utility_function_spec.rb +3 -3
  134. data/spec/reek/spec/should_reek_of_spec.rb +3 -3
  135. data/tasks/configuration.rake +2 -2
  136. metadata +95 -81
  137. data/features/smells/subclassed_from_core_class.feature +0 -14
  138. data/features/smells/too_many_constants.feature +0 -19
  139. data/lib/reek/errors.rb +0 -32
  140. data/lib/reek/report/location_formatter.rb +0 -39
  141. data/lib/reek/report/report.rb +0 -229
  142. data/lib/reek/smells.rb +0 -30
  143. data/spec/reek/smells/smell_repository_spec.rb +0 -22
@@ -1,14 +0,0 @@
1
- Feature: Smell - SubclassedFromCoreClass
2
- Scenario: Reports smell
3
- Given a file named "my_hash.rb" with:
4
- """
5
- # The MyHash class
6
- class MyHash < Hash
7
- end
8
- """
9
- When I run `reek my_hash.rb`
10
- Then it reports:
11
- """
12
- my_hash.rb -- 1 warning:
13
- [2]:SubclassedFromCoreClass: MyHash inherits from core class 'Hash' [https://github.com/troessner/reek/blob/master/docs/Subclassed-From-Core-Class.md]
14
- """
@@ -1,19 +0,0 @@
1
- Feature: Smell - TooManyConstants
2
- Scenario: Reports smell
3
- Given a file named "my_class.rb" with:
4
- """
5
- class MyClass
6
- A = 1
7
- B = 2
8
- C = 3
9
- D = 4
10
- E = 5
11
- F = 6
12
- end
13
- """
14
- When I run `reek my_class.rb`
15
- Then it reports:
16
- """
17
- my_class.rb -- 1 warning:
18
- [1]:TooManyConstants: MyClass has 6 constants [https://github.com/troessner/reek/blob/master/docs/Too-Many-Constants.md]
19
- """
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
- module Reek
3
- # Gets raised when trying to configure a detector which is unknown to us.
4
- # This might happen for multiple reasons. The users might have a typo in
5
- # his comment or he might use a detector that does not exist anymore.
6
- class BadDetectorInCommentError < RuntimeError
7
- UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-EOS.freeze
8
-
9
- Error: You are trying to configure an unknown smell detector '%s' in one
10
- of your source code comments.
11
- The source is '%s' and the comment belongs to the expression starting in line %d.
12
- Here's the original comment:
13
-
14
- %s
15
-
16
- Please see the Reek docs for:
17
- * how to configure Reek via source code comments: https://github.com/troessner/reek/blob/master/docs/Smell-Suppression.md
18
- * what smell detectors are available: https://github.com/troessner/reek/blob/master/docs/Code-Smells.md
19
- Update the offensive comment (or remove it if no longer applicable) and re-run Reek.
20
-
21
- EOS
22
-
23
- def initialize(detector:, source:, line:, original_comment:)
24
- message = format(UNKNOWN_SMELL_DETECTOR_MESSAGE,
25
- detector,
26
- source,
27
- line,
28
- original_comment)
29
- super message
30
- end
31
- end
32
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
- module Reek
3
- module Report
4
- #
5
- # Formats the location of a warning as an empty string.
6
- #
7
- module BlankLocationFormatter
8
- module_function
9
-
10
- def format(_warning)
11
- ''
12
- end
13
- end
14
-
15
- #
16
- # Formats the location of a warning as an array of line numbers.
17
- #
18
- module DefaultLocationFormatter
19
- module_function
20
-
21
- def format(warning)
22
- "#{warning.lines.sort.inspect}:"
23
- end
24
- end
25
-
26
- #
27
- # Formats the location of a warning as a combination of source file name
28
- # and line number. In this format, it is not possible to show more than
29
- # one line number, so the first number is displayed.
30
- #
31
- module SingleLineLocationFormatter
32
- module_function
33
-
34
- def format(warning)
35
- "#{warning.source}:#{warning.lines.sort.first}: "
36
- end
37
- end
38
- end
39
- end
@@ -1,229 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'json'
3
- require 'pathname'
4
- require 'rainbow'
5
- require_relative 'formatter'
6
- require_relative 'heading_formatter'
7
-
8
- module Reek
9
- # @public
10
- module Report
11
- #
12
- # A report that contains the smells and smell counts following source code analysis.
13
- #
14
- # @abstract Subclass and override {#show} to create a concrete report class.
15
- #
16
- # @public
17
- #
18
- # :reek:TooManyInstanceVariables: { max_instance_variables: 6 }
19
- class Base
20
- NO_WARNINGS_COLOR = :green
21
- WARNINGS_COLOR = :red
22
-
23
- # @public
24
- #
25
- # :reek:BooleanParameter
26
- def initialize(heading_formatter: HeadingFormatter::Quiet,
27
- report_formatter: Formatter, sort_by_issue_count: false,
28
- warning_formatter: SimpleWarningFormatter.new)
29
- @examiners = []
30
- @heading_formatter = heading_formatter.new(report_formatter)
31
- @report_formatter = report_formatter
32
- @sort_by_issue_count = sort_by_issue_count
33
- @total_smell_count = 0
34
- @warning_formatter = warning_formatter
35
-
36
- # TODO: Only used in TextReport and YAMLReport
37
- end
38
-
39
- # Add Examiner to report on. The report will output results for all
40
- # added examiners.
41
- #
42
- # @param [Reek::Examiner] examiner object to report on
43
- #
44
- # @public
45
- def add_examiner(examiner)
46
- self.total_smell_count += examiner.smells_count
47
- examiners << examiner
48
- self
49
- end
50
-
51
- # Render the report results on STDOUT
52
- #
53
- # @public
54
- def show
55
- raise NotImplementedError
56
- end
57
-
58
- def smells?
59
- total_smell_count > 0
60
- end
61
-
62
- def smells
63
- examiners.map(&:smells).flatten
64
- end
65
-
66
- protected
67
-
68
- attr_accessor :total_smell_count
69
-
70
- private
71
-
72
- attr_reader :examiners, :heading_formatter, :report_formatter,
73
- :sort_by_issue_count, :warning_formatter
74
- end
75
-
76
- #
77
- # Generates a sorted, text summary of smells in examiners
78
- #
79
- # @public
80
- class TextReport < Base
81
- # @public
82
- def show
83
- sort_examiners if smells?
84
- display_summary
85
- display_total_smell_count
86
- end
87
-
88
- private
89
-
90
- def smell_summaries
91
- examiners.map { |ex| summarize_single_examiner(ex) }.reject(&:empty?)
92
- end
93
-
94
- def display_summary
95
- smell_summaries.each { |smell| puts smell }
96
- end
97
-
98
- def display_total_smell_count
99
- return unless examiners.size > 1
100
- print total_smell_count_message
101
- end
102
-
103
- def summarize_single_examiner(examiner)
104
- result = heading_formatter.header(examiner)
105
- if examiner.smelly?
106
- formatted_list = report_formatter.format_list(examiner.smells,
107
- formatter: warning_formatter)
108
- result += ":\n#{formatted_list}"
109
- end
110
- result
111
- end
112
-
113
- def sort_examiners
114
- examiners.sort_by!(&:smells_count).reverse! if sort_by_issue_count
115
- end
116
-
117
- def total_smell_count_message
118
- colour = smells? ? WARNINGS_COLOR : NO_WARNINGS_COLOR
119
- Rainbow("#{total_smell_count} total warning#{total_smell_count == 1 ? '' : 's'}\n").color(colour)
120
- end
121
- end
122
-
123
- #
124
- # Displays a list of smells in YAML format
125
- # YAML with empty array for 0 smells
126
- #
127
- # @public
128
- class YAMLReport < Base
129
- # @public
130
- def show(out = $stdout)
131
- out.print smells.map { |smell| warning_formatter.format_hash(smell) }.to_yaml
132
- end
133
- end
134
-
135
- #
136
- # Displays a list of smells in JSON format
137
- # JSON with empty array for 0 smells
138
- #
139
- # @public
140
- class JSONReport < Base
141
- # @public
142
- def show(out = $stdout)
143
- out.print ::JSON.generate smells.map { |smell| warning_formatter.format_hash(smell) }
144
- end
145
- end
146
-
147
- #
148
- # Displays a list of smells in Code Climate engine format
149
- # (https://github.com/codeclimate/spec/blob/master/SPEC.md)
150
- # JSON with empty array for 0 smells
151
- #
152
- class CodeClimateReport < Base
153
- # @public
154
- def show(out = $stdout)
155
- smells.map do |smell|
156
- out.print warning_formatter.format_code_climate_hash(smell)
157
- end
158
- end
159
- end
160
-
161
- #
162
- # Saves the report as a HTML file
163
- #
164
- # @public
165
- class HTMLReport < Base
166
- require 'erb'
167
-
168
- # @public
169
- def show
170
- template_path = Pathname.new("#{__dir__}/html_report.html.erb")
171
- puts ERB.new(template_path.read).result(binding)
172
- end
173
- end
174
-
175
- #
176
- # Generates a list of smells in XML format
177
- #
178
- # @public
179
- class XMLReport < Base
180
- require 'rexml/document'
181
-
182
- # @public
183
- def show
184
- document.write output: $stdout, indent: 2
185
- $stdout.puts
186
- end
187
-
188
- private
189
-
190
- def document
191
- REXML::Document.new.tap do |document|
192
- document << REXML::XMLDecl.new << checkstyle
193
- end
194
- end
195
-
196
- def checkstyle
197
- REXML::Element.new('checkstyle').tap do |checkstyle|
198
- smells.group_by(&:source).each do |source, source_smells|
199
- checkstyle << file(source, source_smells)
200
- end
201
- end
202
- end
203
-
204
- # :reek:FeatureEnvy
205
- # :reek:NestedIterators: { max_allowed_nesting: 2 }
206
- def file(name, smells)
207
- REXML::Element.new('file').tap do |file|
208
- file.add_attribute 'name', File.realpath(name)
209
- smells.each do |smell|
210
- smell.lines.each do |line|
211
- file << error(smell, line)
212
- end
213
- end
214
- end
215
- end
216
-
217
- # :reek:UtilityFunction
218
- def error(smell, line)
219
- REXML::Element.new('error').tap do |error|
220
- error.add_attributes 'column' => 0,
221
- 'line' => line,
222
- 'message' => smell.message,
223
- 'severity' => 'warning',
224
- 'source' => smell.smell_type
225
- end
226
- end
227
- end
228
- end
229
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative 'smells/attribute'
3
- require_relative 'smells/boolean_parameter'
4
- require_relative 'smells/class_variable'
5
- require_relative 'smells/control_parameter'
6
- require_relative 'smells/data_clump'
7
- require_relative 'smells/duplicate_method_call'
8
- require_relative 'smells/feature_envy'
9
- require_relative 'smells/irresponsible_module'
10
- require_relative 'smells/instance_variable_assumption'
11
- require_relative 'smells/long_parameter_list'
12
- require_relative 'smells/long_yield_list'
13
- require_relative 'smells/manual_dispatch'
14
- require_relative 'smells/module_initialize'
15
- require_relative 'smells/nested_iterators'
16
- require_relative 'smells/nil_check'
17
- require_relative 'smells/prima_donna_method'
18
- require_relative 'smells/repeated_conditional'
19
- require_relative 'smells/subclassed_from_core_class'
20
- require_relative 'smells/too_many_instance_variables'
21
- require_relative 'smells/too_many_constants'
22
- require_relative 'smells/too_many_methods'
23
- require_relative 'smells/too_many_statements'
24
- require_relative 'smells/uncommunicative_method_name'
25
- require_relative 'smells/uncommunicative_module_name'
26
- require_relative 'smells/uncommunicative_parameter_name'
27
- require_relative 'smells/uncommunicative_variable_name'
28
- require_relative 'smells/unused_parameters'
29
- require_relative 'smells/unused_private_method'
30
- require_relative 'smells/utility_function'
@@ -1,22 +0,0 @@
1
- require_relative '../../spec_helper'
2
- require_lib 'reek/smells/smell_detector'
3
- require_lib 'reek/smells/smell_repository'
4
-
5
- RSpec.describe Reek::Smells::SmellRepository do
6
- describe '.smell_types' do
7
- let(:smell_types) { described_class.smell_types }
8
-
9
- it 'includes existing smell_types' do
10
- expect(smell_types).to include(Reek::Smells::IrresponsibleModule).
11
- and include(Reek::Smells::TooManyStatements)
12
- end
13
-
14
- it 'excludes the smell detector base class' do
15
- expect(smell_types).not_to include(Reek::Smells::SmellDetector)
16
- end
17
-
18
- it 'returns the smell types in alphabetic order' do
19
- expect(smell_types).to eq(smell_types.sort_by(&:name))
20
- end
21
- end
22
- end