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
@@ -15,6 +15,11 @@ Given(/^a directory called 'clean' containing two clean files$/) do
15
15
  write_file('clean/clean_two.rb', contents)
16
16
  end
17
17
 
18
+ Given(/^a directory called 'mixed_files' containing some clean and smelly files$/) do
19
+ write_file('mixed_files/clean.rb', CLEAN_FILE.read)
20
+ write_file('mixed_files/dirty.rb', SMELLY_FILE.read)
21
+ end
22
+
18
23
  Given(/^a directory called 'smelly' containing two smelly files$/) do
19
24
  contents = SMELLY_FILE.read
20
25
 
@@ -40,20 +40,6 @@ Feature:
40
40
  When I run reek -c .todo.reek smelly.rb
41
41
  Then it succeeds
42
42
 
43
- Scenario: Respects a configuration file
44
- Given the smelly file 'smelly.rb'
45
- And a configuration file 'partial_mask.reek'
46
- When I run reek -c partial_mask.reek --todo smelly.rb
47
- Then it succeeds
48
- And a file named ".todo.reek" should exist
49
- And the file ".todo.reek" should contain:
50
- """
51
- ---
52
- UncommunicativeVariableName:
53
- exclude:
54
- - Smelly#x
55
- """
56
-
57
43
  Scenario: Reacts appropiately when there are no smells
58
44
  Given the clean file 'clean.rb'
59
45
  When I run reek --todo clean.rb
@@ -63,3 +49,45 @@ Feature:
63
49
 
64
50
  '.todo.reek' not generated because there were no smells found!
65
51
  """
52
+
53
+ Scenario: Mercilessly overwrite existing .todo.reek files
54
+ Given the smelly file 'smelly.rb'
55
+ And a file named ".todo.reek" with:
56
+ """
57
+ ---
58
+ # smelly.rb reeks of UncommunicativeMethodName and UncommunicativeVariableName
59
+ # so the configuration below will partially mask this
60
+ UncommunicativeMethodName:
61
+ enabled: false
62
+ """
63
+ When I run `reek -c .todo.reek smelly.rb`
64
+ Then it reports:
65
+ """
66
+ smelly.rb -- 1 warning:
67
+ [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
68
+ """
69
+ When I run `reek --todo smelly.rb`
70
+ Then it succeeds
71
+ When I run `reek -c .todo.reek smelly.rb`
72
+ Then it reports nothing
73
+
74
+ Scenario: Ignore existing other configuration files that are passed explicitly
75
+ Given the smelly file 'smelly.rb'
76
+ And a file named "config.reek" with:
77
+ """
78
+ ---
79
+ # smelly.rb reeks of UncommunicativeMethodName and UncommunicativeVariableName
80
+ # so the configuration below will partially mask this
81
+ UncommunicativeMethodName:
82
+ enabled: false
83
+ """
84
+ When I run `reek -c config.reek smelly.rb`
85
+ Then it reports:
86
+ """
87
+ smelly.rb -- 1 warning:
88
+ [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
89
+ """
90
+ When I run `reek -c config.reek --todo smelly.rb`
91
+ Then it succeeds
92
+ When I run `reek -c .todo.reek smelly.rb`
93
+ Then it reports nothing
@@ -4,4 +4,4 @@
4
4
  #
5
5
  require_relative 'reek/version'
6
6
  require_relative 'reek/examiner'
7
- require_relative 'reek/report/report'
7
+ require_relative 'reek/report'
@@ -54,6 +54,7 @@ module Reek
54
54
  def sources
55
55
  if no_source_files_given?
56
56
  if input_was_piped?
57
+ disable_progress_output_unless_verbose
57
58
  source_from_pipe
58
59
  else
59
60
  working_directory_as_source
@@ -89,6 +90,10 @@ module Reek
89
90
  def source_from_pipe
90
91
  [$stdin]
91
92
  end
93
+
94
+ def disable_progress_output_unless_verbose
95
+ options.progress_format = :quiet unless options.show_empty
96
+ end
92
97
  end
93
98
  end
94
99
  end
@@ -37,7 +37,8 @@ module Reek
37
37
  warning_formatter: warning_formatter,
38
38
  report_formatter: Report::Formatter,
39
39
  sort_by_issue_count: sort_by_issue_count,
40
- heading_formatter: heading_formatter)
40
+ heading_formatter: heading_formatter,
41
+ progress_formatter: progress_formatter.new(sources.length))
41
42
  end
42
43
 
43
44
  def report_class
@@ -60,6 +61,10 @@ module Reek
60
61
  Report.heading_formatter(options.show_empty ? :verbose : :quiet)
61
62
  end
62
63
 
64
+ def progress_formatter
65
+ Report.progress_formatter(options.progress_format)
66
+ end
67
+
63
68
  def sort_by_issue_count
64
69
  options.sorting == :smelliness
65
70
  end
@@ -30,8 +30,7 @@ module Reek
30
30
  def scan_for_smells
31
31
  sources.map do |source|
32
32
  Examiner.new(source,
33
- filter_by_smells: smell_names,
34
- configuration: configuration)
33
+ filter_by_smells: smell_names)
35
34
  end.map(&:smells).flatten
36
35
  end
37
36
 
@@ -10,7 +10,8 @@ module Reek
10
10
  #
11
11
  # See {file:docs/Command-Line-Options.md} for details.
12
12
  #
13
- # :reek:TooManyInstanceVariables: { max_instance_variables: 10 }
13
+ # :reek:TooManyInstanceVariables: { max_instance_variables: 11 }
14
+ # :reek:TooManyMethods: { max_methods: 17 }
14
15
  # :reek:Attribute: { enabled: false }
15
16
  #
16
17
  class Options
@@ -22,6 +23,7 @@ module Reek
22
23
  attr_accessor :colored,
23
24
  :config_file,
24
25
  :location_format,
26
+ :progress_format,
25
27
  :report_format,
26
28
  :show_empty,
27
29
  :show_links,
@@ -35,9 +37,10 @@ module Reek
35
37
  @parser = OptionParser.new
36
38
  @report_format = :text
37
39
  @location_format = :numbers
40
+ @progress_format = tty_output? ? :dots : :quiet
38
41
  @show_links = true
39
42
  @smells_to_detect = []
40
- @colored = color_support?
43
+ @colored = tty_output?
41
44
  @success_exit_code = DEFAULT_SUCCESS_EXIT_CODE
42
45
  @failure_exit_code = DEFAULT_FAILURE_EXIT_CODE
43
46
  @generate_todo_list = false
@@ -53,8 +56,12 @@ module Reek
53
56
 
54
57
  private
55
58
 
59
+ # TTY output generally means the output will not undergo further
60
+ # processing by a machine, but will be viewed by a human. This means
61
+ # features like coloring can be safely enabled by default.
62
+ #
56
63
  # :reek:UtilityFunction
57
- def color_support?
64
+ def tty_output?
58
65
  $stdout.tty?
59
66
  end
60
67
 
@@ -113,11 +120,13 @@ module Reek
113
120
  end
114
121
  end
115
122
 
123
+ # :reek:TooManyStatements: { max_statements: 6 }
116
124
  def set_report_formatting_options
117
125
  parser.separator "\nText format options:"
118
126
  set_up_color_option
119
127
  set_up_verbosity_options
120
128
  set_up_location_formatting_options
129
+ set_up_progress_formatting_options
121
130
  set_up_sorting_option
122
131
  end
123
132
 
@@ -149,6 +158,13 @@ module Reek
149
158
  end
150
159
  end
151
160
 
161
+ def set_up_progress_formatting_options
162
+ parser.on('-P', '--[no-]progress',
163
+ 'Show progress of each source as it is examined (default: true)') do |show_progress|
164
+ self.progress_format = show_progress ? :dots : :quiet
165
+ end
166
+ end
167
+
152
168
  def set_up_sorting_option
153
169
  parser.on('--sort-by SORTING', [:smelliness, :none],
154
170
  'Sort reported files by the given criterium:',
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'yaml'
3
- require_relative 'smells/smell_detector'
4
- require_relative 'errors'
4
+
5
+ require_relative 'smell_detectors/base_detector'
6
+ require_relative 'errors/bad_detector_in_comment_error'
7
+ require_relative 'errors/garbage_detector_configuration_in_comment_error'
5
8
 
6
9
  module Reek
7
10
  #
@@ -32,16 +35,17 @@ module Reek
32
35
  #
33
36
  def initialize(comment:, line: nil, source: nil)
34
37
  @original_comment = comment
38
+ @line = line
39
+ @source = source
35
40
  @config = Hash.new { |hash, key| hash[key] = {} }
36
41
 
37
- @original_comment.scan(CONFIGURATION_REGEX) do |detector, _option_string, options|
38
- unless Smells::SmellDetector.valid_detector?(detector)
39
- raise BadDetectorInCommentError, detector: detector,
40
- source: source,
41
- line: line,
42
- original_comment: @original_comment
43
- end
44
- @config.merge! detector => YAML.load(options || DISABLE_DETECTOR_CONFIGURATION)
42
+ @original_comment.scan(CONFIGURATION_REGEX) do |detector_name, _option_string, options|
43
+ CodeCommentValidator.new(detector_name: detector_name,
44
+ original_comment: original_comment,
45
+ line: line,
46
+ source: source,
47
+ options: options).validate
48
+ @config.merge! detector_name => YAML.load(options || DISABLE_DETECTOR_CONFIGURATION)
45
49
  end
46
50
  end
47
51
 
@@ -51,7 +55,7 @@ module Reek
51
55
 
52
56
  private
53
57
 
54
- attr_reader :original_comment
58
+ attr_reader :original_comment, :source, :line
55
59
 
56
60
  def sanitized_comment
57
61
  @sanitized_comment ||= original_comment.
@@ -59,5 +63,73 @@ module Reek
59
63
  gsub(SANITIZE_REGEX, ' ').
60
64
  strip
61
65
  end
66
+
67
+ #
68
+ # A typical configuration via code comment looks like this:
69
+ #
70
+ # :reek:DuplicateMethodCall { enabled: false }
71
+ #
72
+ # There are a lot of ways a user can introduce some errors here:
73
+ #
74
+ # 1.) Unknown smell detector
75
+ # 2.) Garbage in the detector configuration like { thats: a: bad: config }
76
+ # 3.) Unknown configuration keys (e.g. by doing a simple typo: "exclude" vs. "exlude" )
77
+ # 4.) Bad data types given as values for those keys
78
+ # This class validates [1] and [2] at the moment but will also validate [3]
79
+ # and [4] in the future.
80
+ #
81
+ # :reek:TooManyInstanceVariables: { max_instance_variables: 5 }
82
+ class CodeCommentValidator
83
+ #
84
+ # @param detector_name [String] - the detector class that was parsed out of the original
85
+ # comment, e.g. "DuplicateMethodCall" or "UnknownSmellDetector"
86
+ # @param original_comment [String] - the original comment as found in the source code
87
+ # @param line [Integer] - start of the expression the comment belongs to
88
+ # @param source [String] - path to source file or "string"
89
+ # @param options [String] - the configuration options as String for the detector that were
90
+ # extracted from the original comment
91
+ def initialize(detector_name:, original_comment:, line:, source:, options: {})
92
+ @detector_name = detector_name
93
+ @original_comment = original_comment
94
+ @line = line
95
+ @source = source
96
+ @options = options
97
+ end
98
+
99
+ #
100
+ # Method can raise the following errors:
101
+ # * Errors::BadDetectorInCommentError
102
+ # * Errors::GarbageDetectorConfigurationInCommentError
103
+ # @return [undefined]
104
+ def validate
105
+ escalate_bad_detector
106
+ escalate_bad_detector_configuration
107
+ end
108
+
109
+ private
110
+
111
+ attr_reader :detector_name,
112
+ :original_comment,
113
+ :line,
114
+ :source,
115
+ :options
116
+
117
+ def escalate_bad_detector
118
+ return if SmellDetectors::BaseDetector.valid_detector?(detector_name)
119
+ raise Errors::BadDetectorInCommentError, detector_name: detector_name,
120
+ original_comment: original_comment,
121
+ source: source,
122
+ line: line
123
+ end
124
+
125
+ def escalate_bad_detector_configuration
126
+ YAML.load(options || CodeComment::DISABLE_DETECTOR_CONFIGURATION)
127
+ rescue Psych::SyntaxError
128
+ raise Errors::GarbageDetectorConfigurationInCommentError, detector_name: detector_name,
129
+ original_comment: original_comment,
130
+ source: source,
131
+ line: line
132
+ end
133
+ end
62
134
  end
63
135
  end
@@ -14,7 +14,7 @@ module Reek
14
14
  true
15
15
  when String
16
16
  begin
17
- Reek::Smells.const_defined? key
17
+ Reek::SmellDetectors.const_defined? key
18
18
  rescue NameError
19
19
  false
20
20
  end
@@ -27,7 +27,7 @@ module Reek
27
27
  when Class
28
28
  key
29
29
  else
30
- Reek::Smells.const_get key
30
+ Reek::SmellDetectors.const_get key
31
31
  end
32
32
  end
33
33
 
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reek
4
+ module Errors
5
+ # Gets raised when trying to configure a detector which is unknown to us.
6
+ # This might happen for multiple reasons. The users might have a typo in
7
+ # his comment or he might use a detector that does not exist anymore.
8
+ class BadDetectorInCommentError < RuntimeError
9
+ UNKNOWN_SMELL_DETECTOR_MESSAGE = <<-EOS.freeze
10
+
11
+ Error: You are trying to configure an unknown smell detector '%s' in one
12
+ of your source code comments.
13
+ The source is '%s' and the comment belongs to the expression starting in line %d.
14
+ Here's the original comment:
15
+
16
+ %s
17
+
18
+ Please see the Reek docs for:
19
+ * how to configure Reek via source code comments: https://github.com/troessner/reek/blob/master/docs/Smell-Suppression.md
20
+ * what smell detectors are available: https://github.com/troessner/reek/blob/master/docs/Code-Smells.md
21
+ Update the offensive comment (or remove it if no longer applicable) and re-run Reek.
22
+
23
+ EOS
24
+
25
+ def initialize(detector_name:, source:, line:, original_comment:)
26
+ message = format(UNKNOWN_SMELL_DETECTOR_MESSAGE,
27
+ detector_name,
28
+ source,
29
+ line,
30
+ original_comment)
31
+ super message
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reek
4
+ module Errors
5
+ # Gets raised when trying to use a configuration for a detector
6
+ # that can't be parsed into a hash.
7
+ class GarbageDetectorConfigurationInCommentError < RuntimeError
8
+ BAD_DETECTOR_CONFIGURATION_MESSAGE = <<-EOS.freeze
9
+
10
+ Error: You are trying to configure the smell detector '%s'.
11
+ Unfortunately we can not parse the configuration you have given.
12
+ The source is '%s' and the comment belongs to the expression starting in line %d.
13
+ Here's the original comment:
14
+
15
+ %s
16
+
17
+ Please see the Reek docs for:
18
+ * how to configure Reek via source code comments: https://github.com/troessner/reek/blob/master/docs/Smell-Suppression.md
19
+ * what smell detectors are available: https://github.com/troessner/reek/blob/master/docs/Code-Smells.md
20
+ Update the offensive comment (or remove it if no longer applicable) and re-run Reek.
21
+
22
+ EOS
23
+
24
+ def initialize(detector_name:, source:, line:, original_comment:)
25
+ message = format(BAD_DETECTOR_CONFIGURATION_MESSAGE,
26
+ detector_name,
27
+ source,
28
+ line,
29
+ original_comment)
30
+ super message
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
  require_relative 'context_builder'
3
+ require_relative 'errors/bad_detector_in_comment_error'
4
+ require_relative 'errors/garbage_detector_configuration_in_comment_error'
5
+ require_relative 'smell_detectors/detector_repository'
3
6
  require_relative 'source/source_code'
4
- require_relative 'smells/smell_repository'
5
7
 
6
8
  module Reek
7
9
  #
@@ -12,14 +14,30 @@ module Reek
12
14
  INCOMPREHENSIBLE_SOURCE_TEMPLATE = <<-EOS.freeze
13
15
  !!!
14
16
  Source %s can not be processed by Reek.
15
- This is most likely a Reek bug.
16
- It would be great if you could report this back to the Reek team
17
- by opening up a corresponding issue at https://github.com/troessner/reek/issues
18
- Make sure to include the source in question, the Reek version
17
+
18
+ This is most likely either a bug in your Reek configuration (config file or
19
+ source code comments) or a Reek bug.
20
+
21
+ Please double check your Reek configuration taking the original exception
22
+ below into account - you might have misspelled a smell detector for instance.
23
+ (In the future Reek will handle configuration errors more gracefully, something
24
+ we are working on already).
25
+
26
+ If you feel that this is not a problem with your Reek configuration but with
27
+ Reek itself it would be great if you could report this back to the Reek
28
+ team by opening up a corresponding issue at https://github.com/troessner/reek/issues.
29
+
30
+ Please make sure to include the source in question, the Reek version
19
31
  and the original exception below.
20
32
 
33
+ Exception message:
34
+
35
+ %s
36
+
21
37
  Original exception:
38
+
22
39
  %s
40
+
23
41
  !!!
24
42
  EOS
25
43
  #
@@ -39,11 +57,11 @@ module Reek
39
57
  def initialize(source,
40
58
  filter_by_smells: [],
41
59
  configuration: Configuration::AppConfiguration.default,
42
- smell_repository_class: Smells::SmellRepository)
43
- @source = Source::SourceCode.from(source)
44
- @smell_types = smell_repository_class.eligible_smell_types(filter_by_smells)
45
- @smell_repository = smell_repository_class.new(smell_types: @smell_types,
46
- configuration: configuration.directive_for(description))
60
+ detector_repository_class: SmellDetectors::DetectorRepository)
61
+ @source = Source::SourceCode.from(source)
62
+ @smell_types = detector_repository_class.eligible_smell_types(filter_by_smells)
63
+ @detector_repository = detector_repository_class.new(smell_types: @smell_types,
64
+ configuration: configuration.directive_for(description))
47
65
  end
48
66
 
49
67
  # @return [String] origin of the source being analysed
@@ -87,7 +105,7 @@ module Reek
87
105
 
88
106
  private
89
107
 
90
- attr_reader :source, :smell_repository
108
+ attr_reader :source, :detector_repository
91
109
 
92
110
  # Runs the Examiner on the given source to scan for code smells.
93
111
  #
@@ -102,11 +120,15 @@ module Reek
102
120
  return [] unless syntax_tree
103
121
  begin
104
122
  examine_tree
105
- rescue BadDetectorInCommentError => exception
123
+ rescue Errors::BadDetectorInCommentError,
124
+ Errors::GarbageDetectorConfigurationInCommentError => exception
106
125
  warn exception
107
126
  []
108
127
  rescue StandardError => exception
109
- warn format(INCOMPREHENSIBLE_SOURCE_TEMPLATE, origin, exception.inspect)
128
+ warn format(INCOMPREHENSIBLE_SOURCE_TEMPLATE,
129
+ origin,
130
+ exception.message,
131
+ exception.backtrace.join("\n\t"))
110
132
  []
111
133
  end
112
134
  end
@@ -117,7 +139,7 @@ module Reek
117
139
 
118
140
  def examine_tree
119
141
  ContextBuilder.new(syntax_tree).context_tree.flat_map do |element|
120
- smell_repository.examine(element)
142
+ detector_repository.examine(element)
121
143
  end
122
144
  end
123
145
  end