reek 4.7.1 → 5.0.0

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 (260) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -12
  3. data/.rubocop.yml +36 -7
  4. data/.simplecov +1 -0
  5. data/.travis.yml +3 -9
  6. data/.yardopts +1 -1
  7. data/CHANGELOG.md +59 -0
  8. data/CONTRIBUTING.md +1 -1
  9. data/Gemfile +5 -5
  10. data/README.md +134 -104
  11. data/Rakefile +16 -3
  12. data/bin/reek +1 -3
  13. data/docs/API.md +2 -9
  14. data/docs/Basic-Smell-Options.md +51 -11
  15. data/docs/Code-Smells.md +1 -1
  16. data/docs/Command-Line-Options.md +14 -4
  17. data/docs/Duplicate-Method-Call.md +49 -1
  18. data/docs/Feature-Envy.md +44 -0
  19. data/docs/How-To-Write-New-Detectors.md +7 -7
  20. data/docs/Instance-Variable-Assumption.md +1 -1
  21. data/docs/{Prima-Donna-Method.md → Missing-Safe-Method.md} +11 -9
  22. data/docs/Rake-Task.md +1 -1
  23. data/docs/Reek-4-to-Reek-5-migration.md +193 -0
  24. data/docs/Reek-Driven-Development.md +1 -1
  25. data/docs/Uncommunicative-Method-Name.md +45 -6
  26. data/docs/Uncommunicative-Module-Name.md +49 -7
  27. data/docs/Uncommunicative-Parameter-Name.md +43 -5
  28. data/docs/Uncommunicative-Variable-Name.md +73 -2
  29. data/docs/Unused-Private-Method.md +3 -3
  30. data/docs/defaults.reek.yml +129 -0
  31. data/docs/yard_plugin.rb +1 -0
  32. data/features/command_line_interface/basic_usage.feature +2 -2
  33. data/features/command_line_interface/options.feature +46 -4
  34. data/features/command_line_interface/show_progress.feature +4 -4
  35. data/features/command_line_interface/smell_selection.feature +1 -1
  36. data/features/command_line_interface/smells_count.feature +6 -6
  37. data/features/command_line_interface/stdin.feature +30 -8
  38. data/features/configuration_files/accept_setting.feature +45 -28
  39. data/features/configuration_files/directory_specific_directives.feature +80 -75
  40. data/features/configuration_files/exclude_directives.feature +11 -10
  41. data/features/configuration_files/exclude_paths_directives.feature +4 -4
  42. data/features/configuration_files/masking_smells.feature +38 -9
  43. data/features/configuration_files/mix_accept_reject_setting.feature +31 -28
  44. data/features/configuration_files/reject_setting.feature +52 -41
  45. data/features/configuration_files/schema_validation.feature +59 -0
  46. data/features/configuration_files/unused_private_method.feature +18 -16
  47. data/features/configuration_loading.feature +53 -10
  48. data/features/configuration_via_source_comments/erroneous_source_comments.feature +3 -3
  49. data/features/configuration_via_source_comments/well_formed_source_comments.feature +2 -2
  50. data/features/locales.feature +32 -0
  51. data/features/rake_task/rake_task.feature +58 -18
  52. data/features/reports/json.feature +3 -3
  53. data/features/reports/reports.feature +34 -34
  54. data/features/reports/yaml.feature +3 -3
  55. data/features/rspec_matcher.feature +40 -0
  56. data/features/samples.feature +287 -287
  57. data/features/step_definitions/reek_steps.rb +2 -2
  58. data/features/step_definitions/sample_file_steps.rb +9 -4
  59. data/features/support/env.rb +2 -11
  60. data/features/todo_list.feature +26 -23
  61. data/lib/reek/ast/builder.rb +1 -1
  62. data/lib/reek/ast/node.rb +40 -58
  63. data/lib/reek/ast/object_refs.rb +1 -1
  64. data/lib/reek/ast/reference_collector.rb +2 -4
  65. data/lib/reek/ast/sexp_extensions/case.rb +1 -1
  66. data/lib/reek/ast/sexp_extensions/if.rb +8 -1
  67. data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
  68. data/lib/reek/ast/sexp_extensions/methods.rb +4 -6
  69. data/lib/reek/ast/sexp_extensions/send.rb +0 -4
  70. data/lib/reek/cli/application.rb +4 -3
  71. data/lib/reek/cli/command/report_command.rb +1 -2
  72. data/lib/reek/cli/command/todo_list_command.rb +8 -8
  73. data/lib/reek/cli/options.rb +31 -16
  74. data/lib/reek/cli/silencer.rb +14 -3
  75. data/lib/reek/code_comment.rb +14 -16
  76. data/lib/reek/configuration/app_configuration.rb +32 -27
  77. data/lib/reek/configuration/configuration_converter.rb +110 -0
  78. data/lib/reek/configuration/configuration_file_finder.rb +16 -41
  79. data/lib/reek/configuration/configuration_validator.rb +12 -23
  80. data/lib/reek/configuration/default_directive.rb +17 -3
  81. data/lib/reek/configuration/directory_directives.rb +17 -11
  82. data/lib/reek/configuration/excluded_paths.rb +1 -1
  83. data/lib/reek/configuration/rake_task_converter.rb +29 -0
  84. data/lib/reek/configuration/schema.yml +210 -0
  85. data/lib/reek/configuration/schema_validator.rb +38 -0
  86. data/lib/reek/context/attribute_context.rb +3 -3
  87. data/lib/reek/context/code_context.rb +47 -43
  88. data/lib/reek/context/ghost_context.rb +0 -2
  89. data/lib/reek/context/method_context.rb +22 -15
  90. data/lib/reek/context/module_context.rb +5 -9
  91. data/lib/reek/context/root_context.rb +0 -4
  92. data/lib/reek/context/send_context.rb +2 -2
  93. data/lib/reek/context_builder.rb +43 -44
  94. data/lib/reek/detector_repository.rb +11 -11
  95. data/lib/reek/documentation_link.rb +28 -0
  96. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +14 -13
  97. data/lib/reek/errors/bad_detector_in_comment_error.rb +12 -11
  98. data/lib/reek/errors/base_error.rb +3 -0
  99. data/lib/reek/errors/config_file_error.rb +11 -0
  100. data/lib/reek/errors/encoding_error.rb +43 -0
  101. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +13 -12
  102. data/lib/reek/errors/incomprehensible_source_error.rb +22 -24
  103. data/lib/reek/errors/syntax_error.rb +41 -0
  104. data/lib/reek/examiner.rb +24 -26
  105. data/lib/reek/logging_error_handler.rb +7 -5
  106. data/lib/reek/rake/task.rb +8 -4
  107. data/lib/reek/report/base_report.rb +8 -12
  108. data/lib/reek/report/code_climate/code_climate_configuration.rb +1 -1
  109. data/lib/reek/report/code_climate/code_climate_configuration.yml +6 -10
  110. data/lib/reek/report/documentation_link_warning_formatter.rb +17 -0
  111. data/lib/reek/report/heading_formatter.rb +54 -0
  112. data/lib/reek/report/json_report.rb +1 -1
  113. data/lib/reek/report/location_formatter.rb +40 -0
  114. data/lib/reek/report/progress_formatter.rb +79 -0
  115. data/lib/reek/report/simple_warning_formatter.rb +34 -0
  116. data/lib/reek/report/text_report.rb +1 -2
  117. data/lib/reek/report/xml_report.rb +3 -3
  118. data/lib/reek/report/yaml_report.rb +1 -1
  119. data/lib/reek/report.rb +15 -10
  120. data/lib/reek/smell_configuration.rb +2 -2
  121. data/lib/reek/smell_detectors/attribute.rb +5 -11
  122. data/lib/reek/smell_detectors/base_detector.rb +30 -26
  123. data/lib/reek/smell_detectors/boolean_parameter.rb +3 -5
  124. data/lib/reek/smell_detectors/class_variable.rb +5 -14
  125. data/lib/reek/smell_detectors/control_parameter.rb +18 -33
  126. data/lib/reek/smell_detectors/data_clump.rb +15 -9
  127. data/lib/reek/smell_detectors/duplicate_method_call.rb +23 -17
  128. data/lib/reek/smell_detectors/feature_envy.rb +9 -7
  129. data/lib/reek/smell_detectors/instance_variable_assumption.rb +14 -23
  130. data/lib/reek/smell_detectors/irresponsible_module.rb +5 -12
  131. data/lib/reek/smell_detectors/long_parameter_list.rb +10 -7
  132. data/lib/reek/smell_detectors/long_yield_list.rb +10 -7
  133. data/lib/reek/smell_detectors/manual_dispatch.rb +4 -5
  134. data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +20 -20
  135. data/lib/reek/smell_detectors/module_initialize.rb +3 -5
  136. data/lib/reek/smell_detectors/nested_iterators.rb +16 -24
  137. data/lib/reek/smell_detectors/nil_check.rb +8 -15
  138. data/lib/reek/smell_detectors/repeated_conditional.rb +13 -11
  139. data/lib/reek/smell_detectors/subclassed_from_core_class.rb +7 -8
  140. data/lib/reek/smell_detectors/too_many_constants.rb +10 -9
  141. data/lib/reek/smell_detectors/too_many_instance_variables.rb +10 -6
  142. data/lib/reek/smell_detectors/too_many_methods.rb +11 -7
  143. data/lib/reek/smell_detectors/too_many_statements.rb +10 -6
  144. data/lib/reek/smell_detectors/uncommunicative_method_name.rb +10 -11
  145. data/lib/reek/smell_detectors/uncommunicative_module_name.rb +14 -18
  146. data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +17 -22
  147. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +26 -27
  148. data/lib/reek/smell_detectors/unused_parameters.rb +4 -6
  149. data/lib/reek/smell_detectors/unused_private_method.rb +11 -18
  150. data/lib/reek/smell_detectors/utility_function.rb +12 -16
  151. data/lib/reek/smell_detectors.rb +1 -2
  152. data/lib/reek/smell_warning.rb +15 -8
  153. data/lib/reek/source/source_code.rb +57 -58
  154. data/lib/reek/source/source_locator.rb +8 -8
  155. data/lib/reek/spec/should_reek.rb +2 -2
  156. data/lib/reek/spec/should_reek_of.rb +9 -16
  157. data/lib/reek/spec/should_reek_only_of.rb +4 -4
  158. data/lib/reek/spec.rb +6 -6
  159. data/lib/reek/tree_dresser.rb +8 -8
  160. data/lib/reek/version.rb +1 -1
  161. data/reek.gemspec +5 -7
  162. data/samples/checkstyle.xml +1 -1
  163. data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +29 -0
  164. data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +30 -0
  165. data/samples/configuration/full_configuration.reek +8 -4
  166. data/samples/configuration/full_mask.reek +5 -4
  167. data/samples/configuration/partial_mask.reek +3 -2
  168. data/samples/configuration/regular_configuration/.reek.yml +4 -0
  169. data/samples/paths.rb +5 -4
  170. data/samples/source_with_hidden_directories/.hidden/hidden.rb +1 -0
  171. data/samples/source_with_hidden_directories/not_hidden.rb +1 -0
  172. data/spec/factories/factories.rb +2 -13
  173. data/spec/reek/ast/node_spec.rb +103 -10
  174. data/spec/reek/ast/reference_collector_spec.rb +1 -1
  175. data/spec/reek/ast/sexp_extensions_spec.rb +14 -34
  176. data/spec/reek/cli/application_spec.rb +52 -42
  177. data/spec/reek/cli/command/todo_list_command_spec.rb +6 -4
  178. data/spec/reek/cli/silencer_spec.rb +28 -0
  179. data/spec/reek/code_comment_spec.rb +31 -38
  180. data/spec/reek/configuration/app_configuration_spec.rb +46 -33
  181. data/spec/reek/configuration/configuration_file_finder_spec.rb +133 -51
  182. data/spec/reek/configuration/default_directive_spec.rb +1 -1
  183. data/spec/reek/configuration/directory_directives_spec.rb +6 -7
  184. data/spec/reek/configuration/excluded_paths_spec.rb +6 -6
  185. data/spec/reek/configuration/rake_task_converter_spec.rb +33 -0
  186. data/spec/reek/configuration/schema_validator_spec.rb +165 -0
  187. data/spec/reek/context/code_context_spec.rb +70 -106
  188. data/spec/reek/context/ghost_context_spec.rb +9 -9
  189. data/spec/reek/context/method_context_spec.rb +2 -2
  190. data/spec/reek/context/module_context_spec.rb +3 -3
  191. data/spec/reek/context/root_context_spec.rb +1 -1
  192. data/spec/reek/context_builder_spec.rb +20 -0
  193. data/spec/reek/documentation_link_spec.rb +20 -0
  194. data/spec/reek/errors/base_error_spec.rb +13 -0
  195. data/spec/reek/examiner_spec.rb +136 -29
  196. data/spec/reek/rake/task_spec.rb +25 -2
  197. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +82 -80
  198. data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
  199. data/spec/reek/report/json_report_spec.rb +13 -46
  200. data/spec/reek/report/{formatter/location_formatter_spec.rb → location_formatter_spec.rb} +5 -5
  201. data/spec/reek/report/{formatter/progress_formatter_spec.rb → progress_formatter_spec.rb} +4 -4
  202. data/spec/reek/report/text_report_spec.rb +4 -4
  203. data/spec/reek/report/xml_report_spec.rb +3 -3
  204. data/spec/reek/report/yaml_report_spec.rb +9 -38
  205. data/spec/reek/report_spec.rb +3 -3
  206. data/spec/reek/smell_detectors/base_detector_spec.rb +4 -5
  207. data/spec/reek/smell_detectors/boolean_parameter_spec.rb +2 -2
  208. data/spec/reek/smell_detectors/class_variable_spec.rb +26 -32
  209. data/spec/reek/smell_detectors/control_parameter_spec.rb +34 -4
  210. data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +3 -3
  211. data/spec/reek/smell_detectors/feature_envy_spec.rb +47 -2
  212. data/spec/reek/smell_detectors/irresponsible_module_spec.rb +0 -11
  213. data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +10 -10
  214. data/spec/reek/smell_detectors/module_initialize_spec.rb +14 -0
  215. data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
  216. data/spec/reek/smell_detectors/too_many_constants_spec.rb +3 -3
  217. data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +1 -1
  218. data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +6 -6
  219. data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +6 -4
  220. data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +36 -15
  221. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +9 -9
  222. data/spec/reek/smell_detectors/unused_parameters_spec.rb +3 -3
  223. data/spec/reek/smell_detectors/unused_private_method_spec.rb +21 -10
  224. data/spec/reek/smell_detectors/utility_function_spec.rb +57 -5
  225. data/spec/reek/smell_warning_spec.rb +12 -8
  226. data/spec/reek/source/source_code_spec.rb +22 -21
  227. data/spec/reek/source/source_locator_spec.rb +17 -17
  228. data/spec/reek/spec/should_reek_of_spec.rb +25 -29
  229. data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
  230. data/spec/reek/spec/should_reek_spec.rb +8 -8
  231. data/spec/reek/spec/smell_matcher_spec.rb +23 -23
  232. data/spec/reek/tree_dresser_spec.rb +12 -17
  233. data/spec/spec_helper.rb +6 -17
  234. data/tasks/configuration.rake +8 -5
  235. metadata +74 -41
  236. data/defaults.reek +0 -131
  237. data/features/configuration_files/warn_about_multiple_configuration_files.feature +0 -44
  238. data/lib/reek/report/formatter/heading_formatter.rb +0 -52
  239. data/lib/reek/report/formatter/location_formatter.rb +0 -42
  240. data/lib/reek/report/formatter/progress_formatter.rb +0 -81
  241. data/lib/reek/report/formatter/simple_warning_formatter.rb +0 -35
  242. data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +0 -36
  243. data/lib/reek/report/formatter.rb +0 -33
  244. data/lib/reek/smell_detectors/syntax.rb +0 -37
  245. data/samples/configuration/non_public_modifiers_mask.reek +0 -3
  246. data/samples/smelly_with_inline_mask.rb +0 -8
  247. data/samples/smelly_with_modifiers.rb +0 -12
  248. data/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -5
  249. data/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -6
  250. data/spec/reek/smell_detectors/syntax_spec.rb +0 -17
  251. /data/{samples/configuration/more_than_one_configuration_file/regular.reek → .reek.yml} +0 -0
  252. /data/samples/{clean.rb → clean_source/clean.rb} +0 -0
  253. /data/samples/{exceptions.reek → configuration/home/home.reek.yml} +0 -0
  254. /data/samples/configuration/{more_than_one_configuration_file/todo.reek → regular_configuration/empty_sub_directory/.gitignore} +0 -0
  255. /data/samples/{configuration/single_configuration_file/.reek → no_config_file/.keep} +0 -0
  256. /data/samples/{inline.rb → smelly_source/inline.rb} +0 -0
  257. /data/samples/{optparse.rb → smelly_source/optparse.rb} +0 -0
  258. /data/samples/{redcloth.rb → smelly_source/redcloth.rb} +0 -0
  259. /data/samples/{smelly.rb → smelly_source/smelly.rb} +0 -0
  260. /data/samples/{source_with_hidden_directories/uncommunicative_parameter_name.rb → source_with_non_ruby_files/ruby.rb} +0 -0
data/lib/reek/examiner.rb CHANGED
@@ -3,6 +3,8 @@
3
3
  require_relative 'context_builder'
4
4
  require_relative 'detector_repository'
5
5
  require_relative 'errors/incomprehensible_source_error'
6
+ require_relative 'errors/encoding_error'
7
+ require_relative 'errors/syntax_error'
6
8
  require_relative 'source/source_code'
7
9
 
8
10
  module Reek
@@ -10,6 +12,7 @@ module Reek
10
12
  # Applies all available smell detectors to a source.
11
13
  #
12
14
  # @public
15
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 5 }
13
16
  class Examiner
14
17
  # Handles no errors
15
18
  class NullHandler
@@ -38,26 +41,17 @@ module Reek
38
41
  detector_repository_class: DetectorRepository,
39
42
  error_handler: NullHandler.new)
40
43
  @source = Source::SourceCode.from(source)
44
+ @origin = @source.origin
41
45
  @smell_types = detector_repository_class.eligible_smell_types(filter_by_smells)
42
46
  @detector_repository = detector_repository_class.new(smell_types: @smell_types,
43
- configuration: configuration.directive_for(description))
47
+ configuration: configuration.directive_for(@origin))
44
48
  @error_handler = error_handler
45
49
  end
46
50
 
47
51
  # @return [String] origin of the source being analysed
48
52
  #
49
53
  # @public
50
- def origin
51
- @origin ||= source.origin
52
- end
53
-
54
- # @return [String] description of the source being analysed
55
- #
56
- # @public
57
- # @deprecated Use origin
58
- def description
59
- origin
60
- end
54
+ attr_reader :origin
61
55
 
62
56
  #
63
57
  # @return [Array<SmellWarning>] the smells found in the source
@@ -95,18 +89,26 @@ module Reek
95
89
  #
96
90
  # @return [Array<SmellWarning>] the smells found in the source
97
91
  #
98
- # :reek:TooManyStatements { max_statements: 8 }
99
92
  def run
100
- if source.valid_syntax? && syntax_tree
101
- begin
102
- examine_tree
103
- rescue Errors::BaseError => exception
104
- raise unless @error_handler.handle exception
105
- []
106
- end
107
- else
108
- SmellDetectors::Syntax.smells_from_source(source)
93
+ wrap_exceptions do
94
+ examine_tree
109
95
  end
96
+ rescue StandardError => exception
97
+ raise unless @error_handler.handle exception
98
+ []
99
+ end
100
+
101
+ # @quality :reek:TooManyStatements { max_statements: 6 }
102
+ def wrap_exceptions
103
+ yield
104
+ rescue Errors::BaseError
105
+ raise
106
+ rescue EncodingError
107
+ raise Errors::EncodingError, origin: origin
108
+ rescue Parser::SyntaxError
109
+ raise Errors::SyntaxError, origin: origin
110
+ rescue StandardError
111
+ raise Errors::IncomprehensibleSourceError, origin: origin
110
112
  end
111
113
 
112
114
  def syntax_tree
@@ -117,10 +119,6 @@ module Reek
117
119
  ContextBuilder.new(syntax_tree).context_tree.flat_map do |element|
118
120
  detector_repository.examine(element)
119
121
  end
120
- rescue Errors::BaseError
121
- raise
122
- rescue StandardError => exception
123
- raise Errors::IncomprehensibleSourceError, origin: origin, original_exception: exception
124
122
  end
125
123
  end
126
124
  end
@@ -1,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'errors/bad_detector_configuration_key_in_comment_error'
4
- require_relative 'errors/bad_detector_in_comment_error'
5
- require_relative 'errors/garbage_detector_configuration_in_comment_error'
6
- require_relative 'errors/incomprehensible_source_error'
3
+ require_relative 'errors/base_error'
7
4
 
8
5
  module Reek
9
6
  # Handles errors by logging to stderr
10
7
  class LoggingErrorHandler
11
8
  def handle(exception)
12
- warn exception
9
+ case exception
10
+ when Errors::BaseError
11
+ warn exception.long_message
12
+ else
13
+ warn exception
14
+ end
13
15
  true
14
16
  end
15
17
  end
@@ -33,8 +33,8 @@ module Reek
33
33
  #
34
34
  # @public
35
35
  #
36
- # :reek:TooManyInstanceVariables: { max_instance_variables: 6 }
37
- # :reek:Attribute
36
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 6 }
37
+ # @quality :reek:Attribute
38
38
  class Task < ::Rake::TaskLib
39
39
  # Name of Reek task. Defaults to :reek.
40
40
  # @public
@@ -73,10 +73,14 @@ module Reek
73
73
  @name = name
74
74
  @reek_opts = ENV['REEK_OPTS'] || ''
75
75
  @fail_on_error = true
76
- @source_files = FileList[ENV['REEK_SRC'] || 'lib/**/*.rb']
77
76
  @verbose = false
78
77
 
79
78
  yield self if block_given?
79
+
80
+ if (reek_src = ENV['REEK_SRC'])
81
+ @source_files = FileList[reek_src]
82
+ end
83
+ @source_files ||= FileList['lib/**/*.rb']
80
84
  define_task
81
85
  end
82
86
 
@@ -110,7 +114,7 @@ module Reek
110
114
  reject(&:empty?)
111
115
  end
112
116
 
113
- # :reek:UtilityFunction
117
+ # @quality :reek:UtilityFunction
114
118
  def sys_call_failed?
115
119
  !$CHILD_STATUS.success?
116
120
  end
@@ -4,8 +4,6 @@ require 'json'
4
4
  require 'pathname'
5
5
  require 'rainbow'
6
6
 
7
- require_relative 'formatter'
8
-
9
7
  module Reek
10
8
  # @public
11
9
  module Report
@@ -16,22 +14,20 @@ module Reek
16
14
  #
17
15
  # @public
18
16
  #
19
- # :reek:TooManyInstanceVariables: { max_instance_variables: 7 }
17
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 7 }
20
18
  class BaseReport
21
19
  NO_WARNINGS_COLOR = :green
22
20
  WARNINGS_COLOR = :red
23
21
 
24
22
  # @public
25
23
  #
26
- # :reek:BooleanParameter
27
- def initialize(heading_formatter: Formatter::QuietHeadingFormatter,
28
- report_formatter: Formatter,
24
+ # @quality :reek:BooleanParameter
25
+ def initialize(heading_formatter: QuietHeadingFormatter,
29
26
  sort_by_issue_count: false,
30
- warning_formatter: Formatter::SimpleWarningFormatter.new,
31
- progress_formatter: Formatter::ProgressFormatter::Quiet.new(0))
27
+ warning_formatter: SimpleWarningFormatter.new,
28
+ progress_formatter: ProgressFormatter::Quiet.new(0))
32
29
  @examiners = []
33
- @heading_formatter = heading_formatter.new(report_formatter)
34
- @report_formatter = report_formatter
30
+ @heading_formatter = heading_formatter.new
35
31
  @progress_formatter = progress_formatter
36
32
  @sort_by_issue_count = sort_by_issue_count
37
33
  @total_smell_count = 0
@@ -58,7 +54,7 @@ module Reek
58
54
  end
59
55
 
60
56
  def smells?
61
- total_smell_count > 0
57
+ total_smell_count.positive?
62
58
  end
63
59
 
64
60
  def smells
@@ -71,7 +67,7 @@ module Reek
71
67
 
72
68
  private
73
69
 
74
- attr_reader :examiners, :heading_formatter, :report_formatter,
70
+ attr_reader :examiners, :heading_formatter,
75
71
  :sort_by_issue_count, :warning_formatter, :progress_formatter
76
72
  end
77
73
  end
@@ -5,7 +5,7 @@ module Reek
5
5
  # loads the smell type metadata to present in Code Climate
6
6
  module CodeClimateConfiguration
7
7
  def self.load
8
- config_file = File.expand_path('../code_climate_configuration.yml', __FILE__)
8
+ config_file = File.expand_path('code_climate_configuration.yml', __dir__)
9
9
  YAML.load_file config_file
10
10
  end
11
11
  end
@@ -284,7 +284,7 @@ InstanceVariableAssumption:
284
284
  would report:
285
285
 
286
286
  ```Bash
287
- [1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar [https://github.com/troessner/reek/blob/master/docs/Instance-Variable-Assumption.md]
287
+ [1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar
288
288
  ```
289
289
 
290
290
  Note that this example would trigger this smell warning as well:
@@ -565,10 +565,10 @@ NilCheck:
565
565
  test.rb -- 1 warning:
566
566
  [3]:Klass#nil_checker performs a nil-check. (NilCheck)
567
567
  ```
568
- PrimaDonnaMethod:
568
+ MissingSafeMethod:
569
569
  remediation_points: 250_000
570
570
  content: |
571
- A candidate method for the `Prima Donna Method` smell are methods whose names end with an exclamation mark.
571
+ A candidate method for the `Missing Safe Method` smell are methods whose names end with an exclamation mark.
572
572
 
573
573
  An exclamation mark in method names means (the explanation below is taken from [here](http://dablog.rubypal.com/2007/8/15/bang-methods-or-danger-will-rubyist) ):
574
574
 
@@ -576,7 +576,7 @@ PrimaDonnaMethod:
576
576
  The ! in method names that end with ! means, “This method is dangerous”—or, more precisely, this method is the “dangerous” version of an otherwise equivalent method, with the same name minus the !. “Danger” is relative; the ! doesn’t mean anything at all unless the method name it’s in corresponds to a similar but bang-less method name.
577
577
  So, for example, gsub! is the dangerous version of gsub. exit! is the dangerous version of exit. flatten! is the dangerous version of flatten. And so forth.
578
578
 
579
- Such a method is called `Prima Donna Method` if and only if her non-bang version does not exist and this method is reported as a smell.
579
+ Such a method is called `Missing Safe Method` if and only if her non-bang version does not exist and this method is reported as a smell.
580
580
 
581
581
  ## Example
582
582
 
@@ -590,7 +590,7 @@ PrimaDonnaMethod:
590
590
  end
591
591
  ```
592
592
 
593
- Reek would report `bar!` as `prima donna method` smell but not `foo!`.
593
+ Reek would report `bar!` as `Missing Safe Method` smell but not `foo!`.
594
594
 
595
595
  Reek reports this smell only in a class context, not in a module context in order to allow perfectly legit code like this:
596
596
 
@@ -612,7 +612,7 @@ PrimaDonnaMethod:
612
612
  end
613
613
  ```
614
614
 
615
- In this example, Reek would not report the `prima donna method` smell for the method `foo` of the `Dangerous` module.
615
+ In this example, Reek would not report the `Missing Safe Method` smell for the method `foo` of the `Dangerous` module.
616
616
  RepeatedConditional:
617
617
  remediation_points: 400_000
618
618
  content: |
@@ -860,7 +860,3 @@ SubclassedFromCoreClass:
860
860
  you are gonna have a bad time.
861
861
 
862
862
  Source: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes
863
- Syntax:
864
- remediation_points: 250_000
865
- content: |
866
- Check syntax errors.
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'simple_warning_formatter'
4
+
5
+ module Reek
6
+ module Report
7
+ #
8
+ # Formatter that adds a link to the docs to the basic message from
9
+ # SimpleWarningFormatter.
10
+ #
11
+ class DocumentationLinkWarningFormatter < SimpleWarningFormatter
12
+ def format(warning)
13
+ "#{super} [#{warning.explanatory_link}]"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reek
4
+ module Report
5
+ #
6
+ # Base class for heading formatters.
7
+ # Is responsible for formatting the heading emitted for each examiner
8
+ #
9
+ # @abstract Override {#show_header?} to implement a heading formatter.
10
+ class HeadingFormatterBase
11
+ # @quality :reek:UtilityFunction
12
+ def show_header?(_examiner)
13
+ raise NotImplementedError
14
+ end
15
+
16
+ def header(examiner)
17
+ if show_header?(examiner)
18
+ formatted_header examiner
19
+ else
20
+ ''
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def formatted_header(examiner)
27
+ count = examiner.smells_count
28
+ result = Rainbow("#{examiner.origin} -- ").cyan +
29
+ Rainbow("#{count} warning").yellow
30
+ result += Rainbow('s').yellow unless count == 1
31
+ result
32
+ end
33
+ end
34
+
35
+ #
36
+ # Lists out each examiner, even if it has no smell
37
+ #
38
+ class VerboseHeadingFormatter < HeadingFormatterBase
39
+ def show_header?(_examiner)
40
+ true
41
+ end
42
+ end
43
+
44
+ #
45
+ # Lists only smelly examiners
46
+ #
47
+ class QuietHeadingFormatter < HeadingFormatterBase
48
+ # @quality :reek:UtilityFunction
49
+ def show_header?(examiner)
50
+ examiner.smelly?
51
+ end
52
+ end
53
+ end
54
+ end
@@ -12,7 +12,7 @@ module Reek
12
12
  #
13
13
  class JSONReport < BaseReport
14
14
  def show(out = $stdout)
15
- out.print ::JSON.generate smells.map { |smell| warning_formatter.format_hash(smell) }
15
+ out.print ::JSON.generate smells.map(&:yaml_hash)
16
16
  end
17
17
  end
18
18
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reek
4
+ module Report
5
+ #
6
+ # Formats the location of a warning as an empty string.
7
+ #
8
+ module BlankLocationFormatter
9
+ module_function
10
+
11
+ def format(_warning)
12
+ ''
13
+ end
14
+ end
15
+
16
+ #
17
+ # Formats the location of a warning as an array of line numbers.
18
+ #
19
+ module DefaultLocationFormatter
20
+ module_function
21
+
22
+ def format(warning)
23
+ "#{warning.lines.sort.inspect}:"
24
+ end
25
+ end
26
+
27
+ #
28
+ # Formats the location of a warning as a combination of source file name
29
+ # and line number. In this format, it is not possible to show more than
30
+ # one line number, so the first number is displayed.
31
+ #
32
+ module SingleLineLocationFormatter
33
+ module_function
34
+
35
+ def format(warning)
36
+ "#{warning.source}:#{warning.lines.min}: "
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reek
4
+ module Report
5
+ module ProgressFormatter
6
+ #
7
+ # Base class for progress formatters.
8
+ # Is responsible for formatting the progress emitted for each examiner
9
+ #
10
+ # @abstract Override {#header, #progress, #footer} to implement a progress formatter.
11
+ class Base
12
+ attr_reader :sources_count
13
+
14
+ def initialize(sources_count)
15
+ @sources_count = sources_count
16
+ end
17
+
18
+ def header
19
+ raise NotImplementedError
20
+ end
21
+
22
+ def progress(_examiner)
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def footer
27
+ raise NotImplementedError
28
+ end
29
+ end
30
+
31
+ #
32
+ # Shows the status of each source as either a dot (.) or an S
33
+ #
34
+ class Dots < Base
35
+ NO_WARNINGS_COLOR = :green
36
+ WARNINGS_COLOR = :red
37
+
38
+ def header
39
+ "Inspecting #{sources_count} file(s):\n"
40
+ end
41
+
42
+ def progress(examiner)
43
+ examiner.smelly? ? display_smelly : display_clean
44
+ end
45
+
46
+ def footer
47
+ "\n\n"
48
+ end
49
+
50
+ private
51
+
52
+ def display_clean
53
+ Rainbow('.').color(NO_WARNINGS_COLOR)
54
+ end
55
+
56
+ def display_smelly
57
+ Rainbow('S').color(WARNINGS_COLOR)
58
+ end
59
+ end
60
+
61
+ #
62
+ # Does not show progress
63
+ #
64
+ class Quiet < Base
65
+ def header
66
+ ''
67
+ end
68
+
69
+ def progress(_examiner)
70
+ ''
71
+ end
72
+
73
+ def footer
74
+ ''
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'code_climate/code_climate_formatter'
4
+
5
+ module Reek
6
+ module Report
7
+ #
8
+ # Basic formatter that just shows a simple message for each warning,
9
+ # prepended with the result of the passed-in location formatter.
10
+ #
11
+ class SimpleWarningFormatter
12
+ def initialize(location_formatter: BlankLocationFormatter)
13
+ @location_formatter = location_formatter
14
+ end
15
+
16
+ def format(warning)
17
+ "#{location_formatter.format(warning)}#{warning.base_message}"
18
+ end
19
+
20
+ # @quality :reek:UtilityFunction
21
+ def format_code_climate_hash(warning)
22
+ CodeClimateFormatter.new(warning).render
23
+ end
24
+
25
+ def format_list(warnings)
26
+ warnings.map { |warning| " #{format(warning)}" }.join("\n")
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :location_formatter
32
+ end
33
+ end
34
+ end
@@ -49,8 +49,7 @@ module Reek
49
49
  def summarize_single_examiner(examiner)
50
50
  result = heading_formatter.header(examiner)
51
51
  if examiner.smelly?
52
- formatted_list = report_formatter.format_list(examiner.smells,
53
- formatter: warning_formatter)
52
+ formatted_list = warning_formatter.format_list(examiner.smells)
54
53
  result += ":\n#{formatted_list}"
55
54
  end
56
55
  result
@@ -34,8 +34,8 @@ module Reek
34
34
  end
35
35
  end
36
36
 
37
- # :reek:FeatureEnvy
38
- # :reek:NestedIterators: { max_allowed_nesting: 2 }
37
+ # @quality :reek:FeatureEnvy
38
+ # @quality :reek:NestedIterators { max_allowed_nesting: 2 }
39
39
  def file(name, smells)
40
40
  REXML::Element.new('file').tap do |file|
41
41
  file.add_attribute 'name', File.realpath(name)
@@ -47,7 +47,7 @@ module Reek
47
47
  end
48
48
  end
49
49
 
50
- # :reek:UtilityFunction
50
+ # @quality :reek:UtilityFunction
51
51
  def error(smell, line)
52
52
  REXML::Element.new('error').tap do |error|
53
53
  error.add_attributes 'column' => 0,
@@ -12,7 +12,7 @@ module Reek
12
12
  #
13
13
  class YAMLReport < BaseReport
14
14
  def show(out = $stdout)
15
- out.print smells.map { |smell| warning_formatter.format_hash(smell) }.to_yaml
15
+ out.print smells.map(&:yaml_hash).to_yaml
16
16
  end
17
17
  end
18
18
  end
data/lib/reek/report.rb CHANGED
@@ -6,7 +6,12 @@ require_relative 'report/json_report'
6
6
  require_relative 'report/text_report'
7
7
  require_relative 'report/xml_report'
8
8
  require_relative 'report/yaml_report'
9
- require_relative 'report/formatter'
9
+
10
+ require_relative 'report/heading_formatter'
11
+ require_relative 'report/location_formatter'
12
+ require_relative 'report/progress_formatter'
13
+ require_relative 'report/simple_warning_formatter'
14
+ require_relative 'report/documentation_link_warning_formatter'
10
15
 
11
16
  module Reek
12
17
  # Reek reporting functionality.
@@ -21,24 +26,24 @@ module Reek
21
26
  }.freeze
22
27
 
23
28
  LOCATION_FORMATTERS = {
24
- single_line: Formatter::SingleLineLocationFormatter,
25
- plain: Formatter::BlankLocationFormatter,
26
- numbers: Formatter::DefaultLocationFormatter
29
+ single_line: SingleLineLocationFormatter,
30
+ plain: BlankLocationFormatter,
31
+ numbers: DefaultLocationFormatter
27
32
  }.freeze
28
33
 
29
34
  HEADING_FORMATTERS = {
30
- verbose: Formatter::VerboseHeadingFormatter,
31
- quiet: Formatter::QuietHeadingFormatter
35
+ verbose: VerboseHeadingFormatter,
36
+ quiet: QuietHeadingFormatter
32
37
  }.freeze
33
38
 
34
39
  PROGRESS_FORMATTERS = {
35
- dots: Formatter::ProgressFormatter::Dots,
36
- quiet: Formatter::ProgressFormatter::Quiet
40
+ dots: ProgressFormatter::Dots,
41
+ quiet: ProgressFormatter::Quiet
37
42
  }.freeze
38
43
 
39
44
  WARNING_FORMATTER_CLASSES = {
40
- wiki_links: Formatter::WikiLinkWarningFormatter,
41
- simple: Formatter::SimpleWarningFormatter
45
+ documentation_links: DocumentationLinkWarningFormatter,
46
+ simple: SimpleWarningFormatter
42
47
  }.freeze
43
48
 
44
49
  # Map report format symbol to a report class.
@@ -7,11 +7,11 @@ module Reek
7
7
  class SmellConfiguration
8
8
  # The name of the config field that specifies whether a smell is
9
9
  # enabled. Set to +true+ or +false+.
10
- ENABLED_KEY = 'enabled'.freeze
10
+ ENABLED_KEY = 'enabled'
11
11
 
12
12
  # The name of the config field that sets scope-specific overrides
13
13
  # for other values in the current smell detector's configuration.
14
- OVERRIDES_KEY = 'overrides'.freeze
14
+ OVERRIDES_KEY = 'overrides'
15
15
 
16
16
  def initialize(hash)
17
17
  @options = hash
@@ -16,10 +16,6 @@ module Reek
16
16
  #
17
17
  # TODO: Catch attributes declared "by hand"
18
18
  class Attribute < BaseDetector
19
- def initialize(*args)
20
- super
21
- end
22
-
23
19
  def self.contexts # :nodoc:
24
20
  [:sym]
25
21
  end
@@ -29,10 +25,9 @@ module Reek
29
25
  #
30
26
  # @return [Array<SmellWarning>]
31
27
  #
32
- def sniff(ctx)
33
- attributes_in(ctx).map do |_attribute, line|
28
+ def sniff
29
+ attributes_in_context.map do |_attribute, line|
34
30
  smell_warning(
35
- context: ctx,
36
31
  lines: [line],
37
32
  message: 'is a writable attribute')
38
33
  end
@@ -40,10 +35,9 @@ module Reek
40
35
 
41
36
  private
42
37
 
43
- # :reek:UtilityFunction
44
- def attributes_in(module_ctx)
45
- if module_ctx.visibility == :public
46
- call_node = module_ctx.exp
38
+ def attributes_in_context
39
+ if context.visibility == :public
40
+ call_node = expression
47
41
  [[call_node.name, call_node.line]]
48
42
  else
49
43
  []