reek 4.8.2 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (213) hide show
  1. checksums.yaml +5 -5
  2. data/{samples/configuration/more_than_one_configuration_file/regular.reek → .reek.yml} +0 -0
  3. data/.rubocop.yml +17 -3
  4. data/.simplecov +1 -0
  5. data/.travis.yml +0 -5
  6. data/.yardopts +1 -1
  7. data/CHANGELOG.md +28 -0
  8. data/Gemfile +1 -1
  9. data/README.md +113 -98
  10. data/Rakefile +16 -3
  11. data/bin/reek +1 -3
  12. data/docs/API.md +2 -9
  13. data/docs/Basic-Smell-Options.md +51 -11
  14. data/docs/Code-Smells.md +1 -1
  15. data/docs/Command-Line-Options.md +14 -4
  16. data/docs/Duplicate-Method-Call.md +49 -1
  17. data/docs/Feature-Envy.md +44 -0
  18. data/docs/How-To-Write-New-Detectors.md +2 -3
  19. data/docs/{Prima-Donna-Method.md → Missing-Safe-Method.md} +11 -9
  20. data/docs/Rake-Task.md +1 -1
  21. data/docs/Reek-4-to-Reek-5-migration.md +193 -0
  22. data/docs/Reek-Driven-Development.md +1 -1
  23. data/docs/Uncommunicative-Method-Name.md +43 -4
  24. data/docs/Uncommunicative-Module-Name.md +48 -6
  25. data/docs/Uncommunicative-Parameter-Name.md +42 -4
  26. data/docs/Uncommunicative-Variable-Name.md +73 -2
  27. data/docs/Unused-Private-Method.md +1 -1
  28. data/docs/defaults.reek.yml +129 -0
  29. data/docs/yard_plugin.rb +1 -0
  30. data/features/command_line_interface/options.feature +46 -4
  31. data/features/command_line_interface/stdin.feature +27 -5
  32. data/features/configuration_files/accept_setting.feature +39 -22
  33. data/features/configuration_files/directory_specific_directives.feature +58 -53
  34. data/features/configuration_files/exclude_directives.feature +8 -7
  35. data/features/configuration_files/masking_smells.feature +35 -6
  36. data/features/configuration_files/mix_accept_reject_setting.feature +24 -21
  37. data/features/configuration_files/reject_setting.feature +45 -34
  38. data/features/configuration_files/schema_validation.feature +59 -0
  39. data/features/configuration_files/unused_private_method.feature +14 -12
  40. data/features/configuration_loading.feature +50 -7
  41. data/features/rake_task/rake_task.feature +5 -5
  42. data/features/reports/json.feature +4 -1
  43. data/features/reports/reports.feature +12 -12
  44. data/features/reports/yaml.feature +3 -0
  45. data/features/rspec_matcher.feature +9 -1
  46. data/features/step_definitions/reek_steps.rb +4 -0
  47. data/features/step_definitions/sample_file_steps.rb +9 -4
  48. data/features/support/env.rb +2 -2
  49. data/features/todo_list.feature +16 -13
  50. data/lib/reek/ast/node.rb +3 -6
  51. data/lib/reek/ast/object_refs.rb +1 -1
  52. data/lib/reek/ast/sexp_extensions/if.rb +1 -1
  53. data/lib/reek/ast/sexp_extensions/methods.rb +1 -1
  54. data/lib/reek/cli/application.rb +4 -3
  55. data/lib/reek/cli/command/report_command.rb +1 -2
  56. data/lib/reek/cli/command/todo_list_command.rb +4 -2
  57. data/lib/reek/cli/options.rb +27 -13
  58. data/lib/reek/cli/silencer.rb +14 -3
  59. data/lib/reek/code_comment.rb +14 -16
  60. data/lib/reek/configuration/app_configuration.rb +32 -28
  61. data/lib/reek/configuration/configuration_converter.rb +110 -0
  62. data/lib/reek/configuration/configuration_file_finder.rb +15 -40
  63. data/lib/reek/configuration/configuration_validator.rb +12 -23
  64. data/lib/reek/configuration/default_directive.rb +17 -3
  65. data/lib/reek/configuration/directory_directives.rb +17 -11
  66. data/lib/reek/configuration/excluded_paths.rb +1 -1
  67. data/lib/reek/configuration/rake_task_converter.rb +29 -0
  68. data/lib/reek/configuration/schema.yml +210 -0
  69. data/lib/reek/configuration/schema_validator.rb +38 -0
  70. data/lib/reek/context/attribute_context.rb +1 -1
  71. data/lib/reek/context/code_context.rb +4 -4
  72. data/lib/reek/context/method_context.rb +2 -2
  73. data/lib/reek/context/module_context.rb +1 -1
  74. data/lib/reek/context_builder.rb +9 -9
  75. data/lib/reek/detector_repository.rb +6 -0
  76. data/lib/reek/documentation_link.rb +2 -2
  77. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +1 -1
  78. data/lib/reek/errors/bad_detector_in_comment_error.rb +1 -1
  79. data/lib/reek/errors/config_file_error.rb +11 -0
  80. data/lib/reek/errors/encoding_error.rb +2 -2
  81. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +1 -1
  82. data/lib/reek/errors/incomprehensible_source_error.rb +2 -2
  83. data/lib/reek/errors/syntax_error.rb +41 -0
  84. data/lib/reek/examiner.rb +9 -19
  85. data/lib/reek/rake/task.rb +3 -3
  86. data/lib/reek/report.rb +15 -10
  87. data/lib/reek/report/base_report.rb +8 -12
  88. data/lib/reek/report/code_climate/code_climate_configuration.yml +5 -9
  89. data/lib/reek/report/documentation_link_warning_formatter.rb +17 -0
  90. data/lib/reek/report/heading_formatter.rb +54 -0
  91. data/lib/reek/report/json_report.rb +1 -1
  92. data/lib/reek/report/location_formatter.rb +40 -0
  93. data/lib/reek/report/progress_formatter.rb +79 -0
  94. data/lib/reek/report/simple_warning_formatter.rb +34 -0
  95. data/lib/reek/report/text_report.rb +1 -2
  96. data/lib/reek/report/xml_report.rb +3 -3
  97. data/lib/reek/report/yaml_report.rb +1 -1
  98. data/lib/reek/smell_configuration.rb +2 -2
  99. data/lib/reek/smell_detectors.rb +1 -2
  100. data/lib/reek/smell_detectors/attribute.rb +0 -1
  101. data/lib/reek/smell_detectors/base_detector.rb +8 -11
  102. data/lib/reek/smell_detectors/boolean_parameter.rb +0 -1
  103. data/lib/reek/smell_detectors/class_variable.rb +0 -1
  104. data/lib/reek/smell_detectors/control_parameter.rb +17 -32
  105. data/lib/reek/smell_detectors/data_clump.rb +3 -4
  106. data/lib/reek/smell_detectors/duplicate_method_call.rb +5 -6
  107. data/lib/reek/smell_detectors/feature_envy.rb +0 -1
  108. data/lib/reek/smell_detectors/instance_variable_assumption.rb +0 -1
  109. data/lib/reek/smell_detectors/irresponsible_module.rb +0 -1
  110. data/lib/reek/smell_detectors/long_parameter_list.rb +1 -2
  111. data/lib/reek/smell_detectors/long_yield_list.rb +2 -3
  112. data/lib/reek/smell_detectors/manual_dispatch.rb +2 -2
  113. data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +6 -7
  114. data/lib/reek/smell_detectors/module_initialize.rb +0 -1
  115. data/lib/reek/smell_detectors/nested_iterators.rb +4 -5
  116. data/lib/reek/smell_detectors/nil_check.rb +0 -1
  117. data/lib/reek/smell_detectors/repeated_conditional.rb +3 -4
  118. data/lib/reek/smell_detectors/subclassed_from_core_class.rb +0 -1
  119. data/lib/reek/smell_detectors/too_many_constants.rb +1 -2
  120. data/lib/reek/smell_detectors/too_many_instance_variables.rb +1 -2
  121. data/lib/reek/smell_detectors/too_many_methods.rb +1 -2
  122. data/lib/reek/smell_detectors/too_many_statements.rb +1 -2
  123. data/lib/reek/smell_detectors/uncommunicative_method_name.rb +2 -3
  124. data/lib/reek/smell_detectors/uncommunicative_module_name.rb +2 -3
  125. data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +2 -3
  126. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +4 -5
  127. data/lib/reek/smell_detectors/unused_parameters.rb +0 -1
  128. data/lib/reek/smell_detectors/unused_private_method.rb +0 -1
  129. data/lib/reek/smell_detectors/utility_function.rb +1 -2
  130. data/lib/reek/smell_warning.rb +10 -8
  131. data/lib/reek/source/source_code.rb +40 -55
  132. data/lib/reek/source/source_locator.rb +7 -7
  133. data/lib/reek/spec.rb +6 -6
  134. data/lib/reek/spec/should_reek.rb +2 -2
  135. data/lib/reek/spec/should_reek_of.rb +9 -16
  136. data/lib/reek/spec/should_reek_only_of.rb +4 -4
  137. data/lib/reek/tree_dresser.rb +5 -5
  138. data/lib/reek/version.rb +1 -1
  139. data/reek.gemspec +3 -3
  140. data/samples/checkstyle.xml +1 -1
  141. data/samples/{clean.rb → clean_source/clean.rb} +0 -0
  142. data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +29 -0
  143. data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +30 -0
  144. data/samples/configuration/full_configuration.reek +8 -4
  145. data/samples/configuration/full_mask.reek +5 -4
  146. data/samples/{exceptions.reek → configuration/home/home.reek.yml} +0 -0
  147. data/samples/configuration/partial_mask.reek +3 -2
  148. data/samples/configuration/regular_configuration/.reek.yml +4 -0
  149. data/samples/configuration/{more_than_one_configuration_file/todo.reek → regular_configuration/empty_sub_directory/.gitignore} +0 -0
  150. data/samples/{configuration/single_configuration_file/.reek → no_config_file/.keep} +0 -0
  151. data/samples/paths.rb +5 -4
  152. data/samples/{inline.rb → smelly_source/inline.rb} +0 -0
  153. data/samples/{optparse.rb → smelly_source/optparse.rb} +0 -0
  154. data/samples/{redcloth.rb → smelly_source/redcloth.rb} +0 -0
  155. data/samples/{smelly.rb → smelly_source/smelly.rb} +0 -0
  156. data/samples/source_with_hidden_directories/.hidden/hidden.rb +1 -0
  157. data/samples/source_with_hidden_directories/not_hidden.rb +1 -0
  158. data/samples/{source_with_hidden_directories/uncommunicative_parameter_name.rb → source_with_non_ruby_files/ruby.rb} +0 -0
  159. data/spec/reek/ast/node_spec.rb +5 -5
  160. data/spec/reek/cli/application_spec.rb +18 -4
  161. data/spec/reek/cli/command/todo_list_command_spec.rb +4 -2
  162. data/spec/reek/cli/silencer_spec.rb +28 -0
  163. data/spec/reek/code_comment_spec.rb +0 -7
  164. data/spec/reek/configuration/app_configuration_spec.rb +44 -31
  165. data/spec/reek/configuration/configuration_file_finder_spec.rb +133 -49
  166. data/spec/reek/configuration/default_directive_spec.rb +1 -1
  167. data/spec/reek/configuration/directory_directives_spec.rb +3 -4
  168. data/spec/reek/configuration/excluded_paths_spec.rb +5 -5
  169. data/spec/reek/configuration/rake_task_converter_spec.rb +33 -0
  170. data/spec/reek/configuration/schema_validator_spec.rb +165 -0
  171. data/spec/reek/context/code_context_spec.rb +1 -1
  172. data/spec/reek/examiner_spec.rb +28 -1
  173. data/spec/reek/report/json_report_spec.rb +13 -46
  174. data/spec/reek/report/{formatter/location_formatter_spec.rb → location_formatter_spec.rb} +5 -5
  175. data/spec/reek/report/{formatter/progress_formatter_spec.rb → progress_formatter_spec.rb} +4 -4
  176. data/spec/reek/report/text_report_spec.rb +4 -4
  177. data/spec/reek/report/xml_report_spec.rb +1 -1
  178. data/spec/reek/report/yaml_report_spec.rb +9 -38
  179. data/spec/reek/report_spec.rb +3 -3
  180. data/spec/reek/smell_detectors/feature_envy_spec.rb +2 -2
  181. data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +9 -9
  182. data/spec/reek/smell_detectors/too_many_constants_spec.rb +3 -3
  183. data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +1 -1
  184. data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +6 -6
  185. data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +6 -4
  186. data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +6 -4
  187. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +6 -6
  188. data/spec/reek/smell_detectors/unused_private_method_spec.rb +1 -1
  189. data/spec/reek/smell_warning_spec.rb +4 -0
  190. data/spec/reek/source/source_code_spec.rb +16 -22
  191. data/spec/reek/source/source_locator_spec.rb +11 -11
  192. data/spec/reek/spec/should_reek_of_spec.rb +0 -4
  193. data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
  194. data/spec/reek/spec/should_reek_spec.rb +1 -1
  195. data/spec/reek/tree_dresser_spec.rb +2 -6
  196. data/spec/spec_helper.rb +3 -5
  197. data/tasks/configuration.rake +8 -5
  198. metadata +56 -35
  199. data/defaults.reek +0 -131
  200. data/features/configuration_files/warn_about_multiple_configuration_files.feature +0 -44
  201. data/lib/reek/report/formatter.rb +0 -33
  202. data/lib/reek/report/formatter/heading_formatter.rb +0 -52
  203. data/lib/reek/report/formatter/location_formatter.rb +0 -42
  204. data/lib/reek/report/formatter/progress_formatter.rb +0 -81
  205. data/lib/reek/report/formatter/simple_warning_formatter.rb +0 -35
  206. data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +0 -23
  207. data/lib/reek/smell_detectors/syntax.rb +0 -37
  208. data/samples/configuration/non_public_modifiers_mask.reek +0 -3
  209. data/samples/smelly_with_inline_mask.rb +0 -8
  210. data/samples/smelly_with_modifiers.rb +0 -12
  211. data/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -5
  212. data/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -6
  213. data/spec/reek/smell_detectors/syntax_spec.rb +0 -17
@@ -15,20 +15,30 @@ module Reek
15
15
  # @public
16
16
  class AppConfiguration
17
17
  include ConfigurationValidator
18
- EXCLUDE_PATHS_KEY = 'exclude_paths'.freeze
18
+ EXCLUDE_PATHS_KEY = 'exclude_paths'
19
+ DIRECTORIES_KEY = 'directories'
20
+ DETECTORS_KEY = 'detectors'
19
21
 
20
- # Instantiate a configuration via given path, or the default path.
22
+ # Instantiate a configuration via the given path.
21
23
  #
22
- # @param path [Pathname] the path to the config file, or nil to use the
23
- # default path.
24
+ # @param path [Pathname] the path to the config file.
24
25
  #
25
26
  # @return [AppConfiguration]
26
27
  #
27
28
  # @public
28
- def self.from_path(path = nil)
29
- allocate.tap do |instance|
30
- instance.instance_eval { find_and_load(path: path) }
31
- end
29
+ def self.from_path(path)
30
+ values = ConfigurationFileFinder.find_and_load(path: path)
31
+ new(values: values)
32
+ end
33
+
34
+ # Instantiate a configuration via the default path.
35
+ #
36
+ # @return [AppConfiguration]
37
+ #
38
+ # @public
39
+ def self.from_default_path
40
+ values = ConfigurationFileFinder.find_and_load(path: nil)
41
+ new(values: values)
32
42
  end
33
43
 
34
44
  # Instantiate a configuration by passing everything in.
@@ -40,21 +50,17 @@ module Reek
40
50
  # @return [AppConfiguration]
41
51
  #
42
52
  # @public
43
- def self.from_hash(hash = {})
44
- allocate.tap do |instance|
45
- instance.instance_eval do
46
- load_values hash
47
- end
48
- end
53
+ def self.from_hash(hash)
54
+ new(values: hash)
49
55
  end
50
56
 
51
57
  def self.default
52
- new
58
+ new(values: {})
53
59
  end
54
60
 
55
61
  # Returns the directive for a given directory.
56
62
  #
57
- # @param source_via [String] - the source of the code inspected
63
+ # @param source_via [String] the source of the code inspected
58
64
  #
59
65
  # @return [Hash] the directory directive for the source with the default directive
60
66
  # reverse-merged into it.
@@ -67,18 +73,22 @@ module Reek
67
73
  excluded_paths.map(&:expand_path).include?(path.expand_path)
68
74
  end
69
75
 
70
- def load_values(configuration_hash)
71
- configuration_hash.each do |key, value|
76
+ def load_values(values)
77
+ values.each do |key, value|
72
78
  if key == EXCLUDE_PATHS_KEY
73
79
  excluded_paths.add value
74
- elsif smell_type?(key)
75
- default_directive.add key, value
76
- else
77
- directory_directives.add key, value
80
+ elsif key == DIRECTORIES_KEY
81
+ directory_directives.add value
82
+ elsif key == DETECTORS_KEY
83
+ default_directive.add value
78
84
  end
79
85
  end
80
86
  end
81
87
 
88
+ def initialize(values: {})
89
+ load_values(values)
90
+ end
91
+
82
92
  private
83
93
 
84
94
  attr_writer :directory_directives, :default_directive, :excluded_paths
@@ -94,12 +104,6 @@ module Reek
94
104
  def excluded_paths
95
105
  @excluded_paths ||= [].extend(ExcludedPaths)
96
106
  end
97
-
98
- def find_and_load(path: nil)
99
- configuration_hash = ConfigurationFileFinder.find_and_load(path: path)
100
-
101
- load_values(configuration_hash)
102
- end
103
107
  end
104
108
  end
105
109
  end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './configuration_validator'
4
+
5
+ module Reek
6
+ module Configuration
7
+ # Responsible for converting marked strings coming from the outside world
8
+ # into proper regexes.
9
+ class ConfigurationConverter
10
+ REGEXABLE_ATTRIBUTES = %w(accept reject exclude).freeze
11
+ include ConfigurationValidator
12
+ attr_reader :configuration
13
+
14
+ # @param configuration [Hash] e.g.
15
+ #
16
+ # detectors => {
17
+ # "UnusedPrivateMethod" => {"exclude"=>["/exclude regexp/"]},
18
+ # "UncommunicativeMethodName"=>{"reject"=>["reject name"], "accept"=>["accept name"]
19
+ # },
20
+ # directories => {
21
+ # "app/controllers" => {
22
+ # "UnusedPrivateMethod" => {"exclude"=>["/exclude regexp/"]},
23
+ # "UncommunicativeMethodName"=>{"reject"=>["reject name"], "accept"=>["accept name"]}
24
+ # }
25
+ # }
26
+ def initialize(configuration)
27
+ @configuration = configuration
28
+ end
29
+
30
+ # Converts all marked strings across the whole configuration to regexes.
31
+ # @return [Hash]
32
+ #
33
+ def convert
34
+ strings_to_regexes_for_detectors
35
+ strings_to_regexes_for_directories
36
+
37
+ configuration
38
+ end
39
+
40
+ private
41
+
42
+ # @param value [String] String that is potentially marked as regex, e.g. "/foobar/".
43
+ # @return [Bool] if the string in question is marked as regex.
44
+ #
45
+ # @quality :reek:UtilityFunction
46
+ def marked_as_regex?(value)
47
+ value.start_with?('/') && value.end_with?('/')
48
+ end
49
+
50
+ # @param value [value] String that is potentially marked as regex, e.g. "/foobar/".
51
+ # @return [Regexp] e.g. /foobar/.
52
+ #
53
+ def to_regex(value)
54
+ marked_as_regex?(value) ? Regexp.new(value[1..-2]) : value
55
+ end
56
+
57
+ # @param detector_configuration [Hash] e.g.
58
+ # { "UnusedPrivateMethod" => {"exclude"=>["/exclude regexp/"] }
59
+ # @return [Array] all the attributes from the detector configuration that potentially contain regexes.
60
+ # Using this example above this would just be "exclude".
61
+ #
62
+ # @quality :reek:UtilityFunction
63
+ def convertible_attributes(detector_configuration)
64
+ detector_configuration.keys & REGEXABLE_ATTRIBUTES
65
+ end
66
+
67
+ # Iterates over our detector configuration and converts all marked strings into regexes.
68
+ # @return nil
69
+ #
70
+ # @quality :reek:DuplicateMethodCall { max_calls: 3 }
71
+ # @quality :reek:NestedIterators { max_allowed_nesting: 3 }
72
+ # @quality :reek:TooManyStatements { max_statements: 6 }
73
+ def strings_to_regexes_for_detectors
74
+ return unless configuration[AppConfiguration::DETECTORS_KEY]
75
+
76
+ configuration[AppConfiguration::DETECTORS_KEY].tap do |detectors|
77
+ detectors.keys.each do |detector|
78
+ convertible_attributes(detectors[detector]).each do |attribute|
79
+ detectors[detector][attribute] = detectors[detector][attribute].map do |item|
80
+ to_regex item
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ # Iterates over our directory configuration and converts all marked strings into regexes.
88
+ # @return nil
89
+ #
90
+ # @quality :reek:DuplicateMethodCall { max_calls: 3 }
91
+ # @quality :reek:NestedIterators { max_allowed_nesting: 4 }
92
+ # @quality :reek:TooManyStatements { max_statements: 7 }
93
+ def strings_to_regexes_for_directories
94
+ return unless configuration[AppConfiguration::DIRECTORIES_KEY]
95
+
96
+ configuration[AppConfiguration::DIRECTORIES_KEY].tap do |directories|
97
+ directories.keys.each do |directory|
98
+ directories[directory].each do |detector, configuration|
99
+ convertible_attributes(configuration).each do |attribute|
100
+ directories[directory][detector][attribute] = directories[directory][detector][attribute].map do |item|
101
+ to_regex item
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -1,35 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'pathname'
4
+ require_relative './configuration_converter'
5
+ require_relative './schema_validator'
6
+ require_relative '../errors/config_file_error'
4
7
 
5
8
  module Reek
6
9
  module Configuration
7
- # Raised when config file is not properly readable.
8
- class ConfigFileException < StandardError; end
9
10
  #
10
11
  # ConfigurationFileFinder is responsible for finding Reek's configuration.
11
12
  #
12
13
  # There are 3 ways of passing `reek` a configuration file:
13
14
  # 1. Using the cli "-c" switch
14
- # 2. Having a file ending with .reek either in your current working
15
+ # 2. Having a file .reek.yml either in your current working
15
16
  # directory or in a parent directory
16
- # 3. Having a file ending with .reek in your HOME directory
17
+ # 3. Having a file .reek.yml in your HOME directory
17
18
  #
18
19
  # The order in which ConfigurationFileFinder tries to find such a
19
20
  # configuration file is exactly like above.
20
21
  module ConfigurationFileFinder
21
- TOO_MANY_CONFIGURATION_FILES_MESSAGE = <<-MESSAGE.freeze
22
-
23
- Error: Found multiple configuration files %<files>s
24
- while scanning directory %<directory>s.
25
-
26
- Reek supports only one configuration file. You have 2 options now:
27
- 1) Remove all offending files.
28
- 2) Be specific about which one you want to load via the -c switch.
29
-
30
- MESSAGE
22
+ DEFAULT_FILE_NAME = '.reek.yml'
31
23
 
32
24
  class << self
25
+ include ConfigurationValidator
33
26
  #
34
27
  # Finds and loads a configuration file from a given path.
35
28
  #
@@ -47,7 +40,7 @@ module Reek
47
40
  #
48
41
  # @return [File|nil]
49
42
  #
50
- # :reek:ControlParameter
43
+ # @quality :reek:ControlParameter
51
44
  def find(path: nil, current: Pathname.pwd, home: Pathname.new(Dir.home))
52
45
  path || find_by_dir(current) || find_in_dir(home)
53
46
  end
@@ -59,19 +52,18 @@ module Reek
59
52
  # @param path [String]
60
53
  # @return [Hash]
61
54
  #
62
- # :reek:TooManyStatements: { max_statements: 6 }
55
+ # @quality :reek:TooManyStatements { max_statements: 6 }
63
56
  def load_from_file(path)
64
57
  return {} unless path
58
+
65
59
  begin
66
60
  configuration = YAML.load_file(path) || {}
67
61
  rescue StandardError => error
68
- raise ConfigFileException, "Invalid configuration file #{path}, error is #{error}"
62
+ raise Errors::ConfigFileException, "Invalid configuration file #{path}, error is #{error}"
69
63
  end
70
64
 
71
- unless configuration.is_a? Hash
72
- raise ConfigFileException, "Invalid configuration file \"#{path}\" -- Not a hash"
73
- end
74
- configuration
65
+ SchemaValidator.new(configuration).validate
66
+ ConfigurationConverter.new(configuration).convert
75
67
  end
76
68
 
77
69
  private
@@ -90,29 +82,12 @@ module Reek
90
82
 
91
83
  #
92
84
  # Checks a given directory for a configuration file and returns it.
93
- # Raises an exception if we find more than one.
94
85
  #
95
86
  # @return [File|nil]
96
87
  #
97
- # :reek:FeatureEnvy
88
+ # @quality :reek:FeatureEnvy
98
89
  def find_in_dir(dir)
99
- found = dir.children.select { |item| item.file? && item.to_s.end_with?('.reek') }.sort
100
- if found.size > 1
101
- escalate_too_many_configuration_files found, dir
102
- else
103
- found.first
104
- end
105
- end
106
-
107
- #
108
- # Writes a proper warning message to STDERR and then exits the program.
109
- #
110
- # @return [undefined]
111
- #
112
- def escalate_too_many_configuration_files(found, directory)
113
- offensive_files = found.map { |file| "'#{file.basename}'" }.join(', ')
114
- warn format(TOO_MANY_CONFIGURATION_FILES_MESSAGE, files: offensive_files, directory: directory)
115
- exit 1
90
+ dir.children.detect { |item| item.file? && item.basename.to_s == DEFAULT_FILE_NAME }
116
91
  end
117
92
  end
118
93
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../errors/config_file_error'
4
+
3
5
  module Reek
4
6
  module Configuration
5
7
  #
@@ -8,37 +10,24 @@ module Reek
8
10
  module ConfigurationValidator
9
11
  private
10
12
 
11
- # :reek:UtilityFunction
13
+ # @quality :reek:UtilityFunction
12
14
  def smell_type?(key)
13
- case key
14
- when Class
15
- true
16
- when String
17
- begin
18
- Reek::SmellDetectors.const_defined? key
19
- rescue NameError
20
- false
21
- end
22
- end
15
+ Reek::SmellDetectors.const_defined? key
16
+ rescue NameError
17
+ false
23
18
  end
24
19
 
25
- # :reek:UtilityFunction
20
+ # @quality :reek:UtilityFunction
26
21
  def key_to_smell_detector(key)
27
- case key
28
- when Class
29
- key
30
- else
31
- Reek::SmellDetectors.const_get key
32
- end
33
- end
34
-
35
- def error_message_for_file_given(pathname)
36
- "Configuration error: `#{pathname}` is supposed to be a directory but is a file"
22
+ Reek::SmellDetectors.const_get key
37
23
  end
38
24
 
39
25
  def with_valid_directory(path)
40
26
  directory = Pathname.new path.to_s.chomp('/')
41
- abort(error_message_for_file_given(directory)) if directory.file?
27
+ if directory.file?
28
+ raise Errors::ConfigFileError,
29
+ "`#{directory}` is supposed to be a directory but is a file"
30
+ end
42
31
  yield directory if block_given?
43
32
  end
44
33
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative './configuration_validator'
4
+
3
5
  module Reek
4
6
  module Configuration
5
7
  #
@@ -8,9 +10,21 @@ module Reek
8
10
  module DefaultDirective
9
11
  include ConfigurationValidator
10
12
 
11
- def add(key, config)
12
- detector = key_to_smell_detector(key)
13
- self[detector] = (self[detector] || {}).merge config
13
+ # Adds the configuration for detectors as default directive.
14
+ #
15
+ # @param detectors_configuration [Hash] the configuration e.g.:
16
+ # {
17
+ # :IrresponsibleModule => {:enabled=>false},
18
+ # :Attribute => {:enabled=>true}
19
+ # }
20
+ #
21
+ # @return [self]
22
+ def add(detectors_configuration)
23
+ detectors_configuration.each do |name, configuration|
24
+ detector = key_to_smell_detector(name)
25
+ self[detector] = (self[detector] || {}).merge configuration
26
+ end
27
+ self
14
28
  end
15
29
  end
16
30
  end
@@ -24,17 +24,23 @@ module Reek
24
24
 
25
25
  # Adds a directive and returns self.
26
26
  #
27
- # @param path [Pathname] the path
28
- # @param config [Hash] the configuration
27
+ # @param directory_config [Hash] the configuration e.g.:
28
+ # {
29
+ # "samples/two_smelly_files" => {:IrresponsibleModule=>{:enabled=>false}},
30
+ # "samples/three_clean_files" => {:Attribute=>{:enabled=>true}}
31
+ # }
29
32
  #
30
33
  # @return [self]
31
34
  #
32
- # :reek:NestedIterators: { max_allowed_nesting: 2 }
33
- def add(path, config)
34
- with_valid_directory(path) do |directory|
35
- self[directory] = config.each_with_object({}) do |(key, value), hash|
36
- abort(error_message_for_invalid_smell_type(key)) unless smell_type?(key)
37
- hash[key_to_smell_detector(key)] = value
35
+ # @quality :reek:NestedIterators { max_allowed_nesting: 3 }
36
+ # @quality :reek:TooManyStatements { max_statements: 6 }
37
+ def add(directory_config)
38
+ directory_config.each do |path, detector_config|
39
+ with_valid_directory(path) do |directory|
40
+ self[directory] = detector_config.each_with_object({}) do |(key, value), hash|
41
+ abort(error_message_for_invalid_smell_type(key)) unless smell_type?(key)
42
+ hash[key_to_smell_detector(key)] = value
43
+ end
38
44
  end
39
45
  end
40
46
  self
@@ -42,8 +48,8 @@ module Reek
42
48
 
43
49
  private
44
50
 
45
- # :reek:DuplicateMethodCall: { max_calls: 2 }
46
- # :reek:FeatureEnvy
51
+ # @quality :reek:DuplicateMethodCall { max_calls: 2 }
52
+ # @quality :reek:FeatureEnvy
47
53
  def best_match_for(source_base_dir)
48
54
  keys.
49
55
  select { |pathname| source_base_dir.to_s.match(/#{Regexp.escape(pathname.to_s)}/) }.
@@ -52,7 +58,7 @@ module Reek
52
58
 
53
59
  def error_message_for_invalid_smell_type(klass)
54
60
  "You are trying to configure smell type #{klass} but we can't find one with that name.\n" \
55
- "Please make sure you spelled it right. (See 'defaults.reek' in the Reek\n" \
61
+ "Please make sure you spelled it right. (See 'docs/defaults.reek' in the Reek\n" \
56
62
  'repository for a list of all available smell types.)'
57
63
  end
58
64
  end