reek 4.8.1 → 5.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +17 -3
  3. data/.simplecov +1 -0
  4. data/.travis.yml +0 -5
  5. data/.yardopts +1 -1
  6. data/CHANGELOG.md +44 -0
  7. data/Gemfile +1 -1
  8. data/README.md +115 -100
  9. data/Rakefile +16 -3
  10. data/bin/reek +1 -3
  11. data/docs/API.md +2 -9
  12. data/docs/Basic-Smell-Options.md +51 -11
  13. data/docs/Code-Smells.md +1 -1
  14. data/docs/Command-Line-Options.md +14 -4
  15. data/docs/Duplicate-Method-Call.md +49 -1
  16. data/docs/Feature-Envy.md +44 -0
  17. data/docs/How-To-Write-New-Detectors.md +2 -3
  18. data/docs/Instance-Variable-Assumption.md +1 -1
  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 +45 -6
  24. data/docs/Uncommunicative-Module-Name.md +49 -7
  25. data/docs/Uncommunicative-Parameter-Name.md +43 -5
  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/basic_usage.feature +2 -2
  31. data/features/command_line_interface/options.feature +46 -4
  32. data/features/command_line_interface/show_progress.feature +4 -4
  33. data/features/command_line_interface/smell_selection.feature +1 -1
  34. data/features/command_line_interface/smells_count.feature +6 -6
  35. data/features/command_line_interface/stdin.feature +30 -8
  36. data/features/configuration_files/accept_setting.feature +45 -28
  37. data/features/configuration_files/directory_specific_directives.feature +78 -73
  38. data/features/configuration_files/exclude_directives.feature +11 -10
  39. data/features/configuration_files/exclude_paths_directives.feature +4 -4
  40. data/features/configuration_files/masking_smells.feature +38 -9
  41. data/features/configuration_files/mix_accept_reject_setting.feature +31 -28
  42. data/features/configuration_files/reject_setting.feature +52 -41
  43. data/features/configuration_files/schema_validation.feature +59 -0
  44. data/features/configuration_files/unused_private_method.feature +18 -16
  45. data/features/configuration_loading.feature +53 -10
  46. data/features/configuration_via_source_comments/erroneous_source_comments.feature +2 -2
  47. data/features/configuration_via_source_comments/well_formed_source_comments.feature +2 -2
  48. data/features/locales.feature +2 -2
  49. data/features/rake_task/rake_task.feature +15 -15
  50. data/features/reports/json.feature +3 -3
  51. data/features/reports/reports.feature +34 -34
  52. data/features/reports/yaml.feature +3 -3
  53. data/features/rspec_matcher.feature +9 -1
  54. data/features/samples.feature +287 -287
  55. data/features/step_definitions/reek_steps.rb +4 -0
  56. data/features/step_definitions/sample_file_steps.rb +9 -4
  57. data/features/support/env.rb +2 -2
  58. data/features/todo_list.feature +26 -23
  59. data/lib/reek/ast/node.rb +3 -6
  60. data/lib/reek/ast/object_refs.rb +1 -1
  61. data/lib/reek/ast/sexp_extensions/case.rb +3 -1
  62. data/lib/reek/ast/sexp_extensions/if.rb +2 -2
  63. data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
  64. data/lib/reek/ast/sexp_extensions/methods.rb +1 -1
  65. data/lib/reek/cli/application.rb +4 -3
  66. data/lib/reek/cli/command/report_command.rb +1 -2
  67. data/lib/reek/cli/command/todo_list_command.rb +4 -2
  68. data/lib/reek/cli/options.rb +29 -14
  69. data/lib/reek/cli/silencer.rb +14 -3
  70. data/lib/reek/code_comment.rb +14 -16
  71. data/lib/reek/configuration/app_configuration.rb +32 -28
  72. data/lib/reek/configuration/configuration_converter.rb +110 -0
  73. data/lib/reek/configuration/configuration_file_finder.rb +15 -40
  74. data/lib/reek/configuration/configuration_validator.rb +12 -23
  75. data/lib/reek/configuration/default_directive.rb +17 -3
  76. data/lib/reek/configuration/directory_directives.rb +17 -11
  77. data/lib/reek/configuration/excluded_paths.rb +1 -1
  78. data/lib/reek/configuration/rake_task_converter.rb +29 -0
  79. data/lib/reek/configuration/schema.yml +210 -0
  80. data/lib/reek/configuration/schema_validator.rb +38 -0
  81. data/lib/reek/context/attribute_context.rb +1 -1
  82. data/lib/reek/context/code_context.rb +4 -4
  83. data/lib/reek/context/method_context.rb +2 -2
  84. data/lib/reek/context/module_context.rb +1 -1
  85. data/lib/reek/context_builder.rb +9 -9
  86. data/lib/reek/detector_repository.rb +6 -0
  87. data/lib/reek/documentation_link.rb +28 -0
  88. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +4 -3
  89. data/lib/reek/errors/bad_detector_in_comment_error.rb +4 -3
  90. data/lib/reek/errors/config_file_error.rb +11 -0
  91. data/lib/reek/errors/encoding_error.rb +2 -2
  92. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +4 -3
  93. data/lib/reek/errors/incomprehensible_source_error.rb +2 -2
  94. data/lib/reek/errors/syntax_error.rb +41 -0
  95. data/lib/reek/examiner.rb +9 -19
  96. data/lib/reek/rake/task.rb +3 -3
  97. data/lib/reek/report/base_report.rb +8 -12
  98. data/lib/reek/report/code_climate/code_climate_configuration.yml +6 -10
  99. data/lib/reek/report/documentation_link_warning_formatter.rb +17 -0
  100. data/lib/reek/report/heading_formatter.rb +54 -0
  101. data/lib/reek/report/json_report.rb +1 -1
  102. data/lib/reek/report/location_formatter.rb +40 -0
  103. data/lib/reek/report/progress_formatter.rb +79 -0
  104. data/lib/reek/report/simple_warning_formatter.rb +34 -0
  105. data/lib/reek/report/text_report.rb +1 -2
  106. data/lib/reek/report/xml_report.rb +3 -3
  107. data/lib/reek/report/yaml_report.rb +1 -1
  108. data/lib/reek/report.rb +15 -10
  109. data/lib/reek/smell_configuration.rb +2 -2
  110. data/lib/reek/smell_detectors/attribute.rb +0 -1
  111. data/lib/reek/smell_detectors/base_detector.rb +8 -11
  112. data/lib/reek/smell_detectors/boolean_parameter.rb +0 -1
  113. data/lib/reek/smell_detectors/class_variable.rb +0 -1
  114. data/lib/reek/smell_detectors/control_parameter.rb +17 -32
  115. data/lib/reek/smell_detectors/data_clump.rb +3 -4
  116. data/lib/reek/smell_detectors/duplicate_method_call.rb +5 -6
  117. data/lib/reek/smell_detectors/feature_envy.rb +1 -1
  118. data/lib/reek/smell_detectors/instance_variable_assumption.rb +0 -1
  119. data/lib/reek/smell_detectors/irresponsible_module.rb +0 -1
  120. data/lib/reek/smell_detectors/long_parameter_list.rb +1 -2
  121. data/lib/reek/smell_detectors/long_yield_list.rb +2 -3
  122. data/lib/reek/smell_detectors/manual_dispatch.rb +2 -2
  123. data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +6 -7
  124. data/lib/reek/smell_detectors/module_initialize.rb +0 -1
  125. data/lib/reek/smell_detectors/nested_iterators.rb +4 -5
  126. data/lib/reek/smell_detectors/nil_check.rb +0 -1
  127. data/lib/reek/smell_detectors/repeated_conditional.rb +3 -4
  128. data/lib/reek/smell_detectors/subclassed_from_core_class.rb +0 -1
  129. data/lib/reek/smell_detectors/too_many_constants.rb +1 -2
  130. data/lib/reek/smell_detectors/too_many_instance_variables.rb +1 -2
  131. data/lib/reek/smell_detectors/too_many_methods.rb +1 -2
  132. data/lib/reek/smell_detectors/too_many_statements.rb +1 -2
  133. data/lib/reek/smell_detectors/uncommunicative_method_name.rb +2 -3
  134. data/lib/reek/smell_detectors/uncommunicative_module_name.rb +2 -3
  135. data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +2 -3
  136. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +4 -5
  137. data/lib/reek/smell_detectors/unused_parameters.rb +0 -1
  138. data/lib/reek/smell_detectors/unused_private_method.rb +0 -1
  139. data/lib/reek/smell_detectors/utility_function.rb +1 -2
  140. data/lib/reek/smell_detectors.rb +1 -2
  141. data/lib/reek/smell_warning.rb +15 -8
  142. data/lib/reek/source/source_code.rb +40 -55
  143. data/lib/reek/source/source_locator.rb +7 -7
  144. data/lib/reek/spec/should_reek.rb +2 -2
  145. data/lib/reek/spec/should_reek_of.rb +9 -16
  146. data/lib/reek/spec/should_reek_only_of.rb +4 -4
  147. data/lib/reek/spec.rb +6 -6
  148. data/lib/reek/tree_dresser.rb +5 -5
  149. data/lib/reek/version.rb +1 -1
  150. data/reek.gemspec +4 -4
  151. data/samples/checkstyle.xml +1 -1
  152. data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +29 -0
  153. data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +30 -0
  154. data/samples/configuration/full_configuration.reek +8 -4
  155. data/samples/configuration/full_mask.reek +5 -4
  156. data/samples/configuration/partial_mask.reek +3 -2
  157. data/samples/configuration/regular_configuration/.reek.yml +4 -0
  158. data/samples/paths.rb +5 -4
  159. data/samples/source_with_hidden_directories/.hidden/hidden.rb +1 -0
  160. data/samples/source_with_hidden_directories/not_hidden.rb +1 -0
  161. data/spec/reek/ast/node_spec.rb +5 -5
  162. data/spec/reek/cli/application_spec.rb +18 -4
  163. data/spec/reek/cli/command/todo_list_command_spec.rb +4 -2
  164. data/spec/reek/cli/silencer_spec.rb +28 -0
  165. data/spec/reek/code_comment_spec.rb +0 -7
  166. data/spec/reek/configuration/app_configuration_spec.rb +44 -31
  167. data/spec/reek/configuration/configuration_file_finder_spec.rb +141 -49
  168. data/spec/reek/configuration/default_directive_spec.rb +1 -1
  169. data/spec/reek/configuration/directory_directives_spec.rb +3 -4
  170. data/spec/reek/configuration/excluded_paths_spec.rb +5 -5
  171. data/spec/reek/configuration/rake_task_converter_spec.rb +33 -0
  172. data/spec/reek/configuration/schema_validator_spec.rb +165 -0
  173. data/spec/reek/context/code_context_spec.rb +1 -1
  174. data/spec/reek/documentation_link_spec.rb +20 -0
  175. data/spec/reek/examiner_spec.rb +28 -1
  176. data/spec/reek/report/json_report_spec.rb +13 -46
  177. data/spec/reek/report/{formatter/location_formatter_spec.rb → location_formatter_spec.rb} +5 -5
  178. data/spec/reek/report/{formatter/progress_formatter_spec.rb → progress_formatter_spec.rb} +4 -4
  179. data/spec/reek/report/text_report_spec.rb +4 -4
  180. data/spec/reek/report/xml_report_spec.rb +1 -1
  181. data/spec/reek/report/yaml_report_spec.rb +9 -38
  182. data/spec/reek/report_spec.rb +3 -3
  183. data/spec/reek/smell_detectors/feature_envy_spec.rb +47 -2
  184. data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +9 -9
  185. data/spec/reek/smell_detectors/too_many_constants_spec.rb +3 -3
  186. data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +1 -1
  187. data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +6 -6
  188. data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +6 -4
  189. data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +6 -4
  190. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +6 -6
  191. data/spec/reek/smell_detectors/unused_private_method_spec.rb +1 -1
  192. data/spec/reek/smell_warning_spec.rb +4 -0
  193. data/spec/reek/source/source_code_spec.rb +16 -22
  194. data/spec/reek/source/source_locator_spec.rb +11 -11
  195. data/spec/reek/spec/should_reek_of_spec.rb +0 -4
  196. data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
  197. data/spec/reek/spec/should_reek_spec.rb +1 -1
  198. data/spec/reek/tree_dresser_spec.rb +2 -6
  199. data/spec/spec_helper.rb +3 -5
  200. data/tasks/configuration.rake +8 -5
  201. metadata +64 -36
  202. data/.codeclimate.yml +0 -21
  203. data/defaults.reek +0 -131
  204. data/features/configuration_files/warn_about_multiple_configuration_files.feature +0 -44
  205. data/lib/reek/report/formatter/heading_formatter.rb +0 -52
  206. data/lib/reek/report/formatter/location_formatter.rb +0 -42
  207. data/lib/reek/report/formatter/progress_formatter.rb +0 -81
  208. data/lib/reek/report/formatter/simple_warning_formatter.rb +0 -35
  209. data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +0 -36
  210. data/lib/reek/report/formatter.rb +0 -33
  211. data/lib/reek/smell_detectors/syntax.rb +0 -37
  212. data/samples/configuration/non_public_modifiers_mask.reek +0 -3
  213. data/samples/smelly_with_inline_mask.rb +0 -8
  214. data/samples/smelly_with_modifiers.rb +0 -12
  215. data/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -5
  216. data/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -6
  217. data/spec/reek/smell_detectors/syntax_spec.rb +0 -17
  218. /data/{samples/configuration/more_than_one_configuration_file/regular.reek → .reek.yml} +0 -0
  219. /data/samples/{clean.rb → clean_source/clean.rb} +0 -0
  220. /data/samples/{exceptions.reek → configuration/home/home.reek.yml} +0 -0
  221. /data/samples/configuration/{more_than_one_configuration_file/todo.reek → regular_configuration/empty_sub_directory/.gitignore} +0 -0
  222. /data/samples/{configuration/single_configuration_file/.reek → no_config_file/.keep} +0 -0
  223. /data/samples/{inline.rb → smelly_source/inline.rb} +0 -0
  224. /data/samples/{optparse.rb → smelly_source/optparse.rb} +0 -0
  225. /data/samples/{redcloth.rb → smelly_source/redcloth.rb} +0 -0
  226. /data/samples/{smelly.rb → smelly_source/smelly.rb} +0 -0
  227. /data/samples/{source_with_hidden_directories/uncommunicative_parameter_name.rb → source_with_non_ruby_files/ruby.rb} +0 -0
@@ -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::ConfigFileError, "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
@@ -10,7 +10,7 @@ module Reek
10
10
  module ExcludedPaths
11
11
  include ConfigurationValidator
12
12
 
13
- # :reek:NestedIterators: { max_allowed_nesting: 2 }
13
+ # @quality :reek:NestedIterators { max_allowed_nesting: 2 }
14
14
  def add(paths)
15
15
  paths.each do |path|
16
16
  with_valid_directory(path) { |directory| self << directory }
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reek
4
+ module Configuration
5
+ # Responsible for converting configuration values coming from the outside world
6
+ # to whatever we want to use internally.
7
+ module RakeTaskConverter
8
+ class << self
9
+ REGEXABLE_ATTRIBUTES = %w(accept reject exclude).freeze
10
+
11
+ # Converts marked strings like "/foobar/" into regexes.
12
+ #
13
+ # @param configuration [Hash] e.g.
14
+ # {"enabled"=>true, "exclude"=>[], "reject"=>[/^[a-z]$/, /[0-9]$/, /[A-Z]/], "accept"=>[]}
15
+ # @return [Hash]
16
+ #
17
+ # @quality :reek:NestedIterators { max_allowed_nesting: 2 }
18
+ def convert(configuration)
19
+ (configuration.keys & REGEXABLE_ATTRIBUTES).each do |attribute|
20
+ configuration[attribute] = configuration[attribute].map do |item|
21
+ item.is_a?(Regexp) ? item.inspect : item
22
+ end
23
+ end
24
+ configuration
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,210 @@
1
+ ---
2
+ type: map
3
+ mapping:
4
+ "detectors":
5
+ type: map
6
+ mapping: &all_detectors
7
+ Attribute:
8
+ type: map
9
+ mapping: &detector_base
10
+ "enabled":
11
+ type: bool
12
+ "exclude":
13
+ type: seq
14
+ sequence:
15
+ - type: str
16
+ BooleanParameter:
17
+ type: map
18
+ mapping:
19
+ <<: *detector_base
20
+ ClassVariable:
21
+ type: map
22
+ mapping:
23
+ <<: *detector_base
24
+ ControlParameter:
25
+ type: map
26
+ mapping:
27
+ <<: *detector_base
28
+ DataClump:
29
+ type: map
30
+ mapping:
31
+ <<: *detector_base
32
+ max_copies:
33
+ type: number
34
+ min_clump_size:
35
+ type: number
36
+ DuplicateMethodCall:
37
+ type: map
38
+ mapping:
39
+ <<: *detector_base
40
+ max_calls:
41
+ type: number
42
+ allow_calls:
43
+ type: seq
44
+ sequence:
45
+ - type: str
46
+ FeatureEnvy:
47
+ type: map
48
+ mapping:
49
+ <<: *detector_base
50
+ InstanceVariableAssumption:
51
+ type: map
52
+ mapping:
53
+ <<: *detector_base
54
+ IrresponsibleModule:
55
+ type: map
56
+ mapping:
57
+ <<: *detector_base
58
+ LongParameterList:
59
+ type: map
60
+ mapping:
61
+ <<: *detector_base
62
+ max_params:
63
+ type: number
64
+ overrides:
65
+ type: map
66
+ mapping:
67
+ initialize:
68
+ type: map
69
+ mapping:
70
+ max_params:
71
+ type: number
72
+ LongYieldList:
73
+ type: map
74
+ mapping:
75
+ <<: *detector_base
76
+ max_params:
77
+ type: number
78
+ ManualDispatch:
79
+ type: map
80
+ mapping:
81
+ <<: *detector_base
82
+ MissingSafeMethod:
83
+ type: map
84
+ mapping:
85
+ <<: *detector_base
86
+ ModuleInitialize:
87
+ type: map
88
+ mapping:
89
+ <<: *detector_base
90
+ NestedIterators:
91
+ type: map
92
+ mapping:
93
+ <<: *detector_base
94
+ max_allowed_nesting:
95
+ type: number
96
+ ignore_iterators:
97
+ type: seq
98
+ sequence:
99
+ - type: str
100
+ NilCheck:
101
+ type: map
102
+ mapping:
103
+ <<: *detector_base
104
+ RepeatedConditional:
105
+ type: map
106
+ mapping:
107
+ <<: *detector_base
108
+ max_ifs:
109
+ type: number
110
+ SubclassedFromCoreClass:
111
+ type: map
112
+ mapping:
113
+ <<: *detector_base
114
+ Syntax:
115
+ type: map
116
+ mapping:
117
+ <<: *detector_base
118
+ TooManyConstants:
119
+ type: map
120
+ mapping:
121
+ <<: *detector_base
122
+ max_constants:
123
+ type: number
124
+ TooManyInstanceVariables:
125
+ type: map
126
+ mapping:
127
+ <<: *detector_base
128
+ max_instance_variables:
129
+ type: number
130
+ TooManyMethods:
131
+ type: map
132
+ mapping:
133
+ <<: *detector_base
134
+ max_methods:
135
+ type: number
136
+ TooManyStatements:
137
+ type: map
138
+ mapping:
139
+ <<: *detector_base
140
+ max_statements:
141
+ type: number
142
+ UncommunicativeMethodName:
143
+ type: map
144
+ mapping:
145
+ <<: *detector_base
146
+ reject: &reject_settings
147
+ type: seq
148
+ sequence:
149
+ - type: str
150
+ accept: &accept_settings
151
+ type: seq
152
+ sequence:
153
+ - type: str
154
+ UncommunicativeModuleName:
155
+ type: map
156
+ mapping:
157
+ <<: *detector_base
158
+ reject: *reject_settings
159
+ accept: *accept_settings
160
+ UncommunicativeParameterName:
161
+ type: map
162
+ mapping:
163
+ <<: *detector_base
164
+ reject: *reject_settings
165
+ accept: *accept_settings
166
+ UncommunicativeVariableName:
167
+ type: map
168
+ mapping:
169
+ <<: *detector_base
170
+ reject: *reject_settings
171
+ accept: *accept_settings
172
+ UnusedParameters:
173
+ type: map
174
+ mapping:
175
+ <<: *detector_base
176
+ UnusedPrivateMethod:
177
+ type: map
178
+ mapping:
179
+ <<: *detector_base
180
+ UtilityFunction:
181
+ type: map
182
+ mapping:
183
+ <<: *detector_base
184
+ public_methods_only:
185
+ type: bool
186
+
187
+ "directories":
188
+ type: map
189
+ mapping:
190
+ # For any given key that is not matched somewhere else we'll apply the schema below.
191
+ # So this will just slurp in every directory we throw at it and then validate everything under
192
+ # it against all detectors - for instance:
193
+ #
194
+ # directories:
195
+ # "web_app/app/controllers":
196
+ # NestedIterators:
197
+ # enabled: false
198
+ # "web_app/app/helpers":
199
+ # UtilityFunction:
200
+ # enabled: false
201
+ #
202
+ # For details check out: http://www.kuwata-lab.com/kwalify/ruby/users-guide.02.html#tips-default
203
+ =:
204
+ type: map
205
+ mapping: *all_detectors
206
+
207
+ "exclude_paths":
208
+ type: seq
209
+ sequence:
210
+ - type: str
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require_relative '../cli/silencer'
5
+ Reek::CLI::Silencer.without_warnings { require 'kwalify' }
6
+ require_relative '../errors/config_file_error'
7
+
8
+ module Reek
9
+ module Configuration
10
+ #
11
+ # Schema validator module.
12
+ #
13
+ class SchemaValidator
14
+ SCHEMA_FILE_PATH = File.expand_path('./schema.yml', __dir__)
15
+
16
+ def initialize(configuration)
17
+ @configuration = configuration
18
+ @validator = CLI::Silencer.without_warnings do
19
+ schema_file = Kwalify::Yaml.load_file(SCHEMA_FILE_PATH)
20
+ Kwalify::Validator.new(schema_file)
21
+ end
22
+ end
23
+
24
+ def validate
25
+ errors = CLI::Silencer.without_warnings { @validator.validate @configuration }
26
+ return if !errors || errors.empty?
27
+ raise Errors::ConfigFileError, error_message(errors)
28
+ end
29
+
30
+ private
31
+
32
+ # :reek:UtilityFunction
33
+ def error_message(errors)
34
+ "We found some problems with your configuration file: #{CLI::Silencer.silently { errors.join(', ') }}"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -7,7 +7,7 @@ module Reek
7
7
  #
8
8
  # A context wrapper for attribute definitions found in a syntax tree.
9
9
  #
10
- # :reek:Attribute
10
+ # @quality :reek:Attribute
11
11
  class AttributeContext < CodeContext
12
12
  attr_accessor :visibility
13
13
 
@@ -14,8 +14,8 @@ module Reek
14
14
  # code element. CodeContexts form a tree in the same way the code does,
15
15
  # with each context holding a reference to a unique outer context.
16
16
  #
17
- # :reek:TooManyMethods: { max_methods: 19 }
18
- # :reek:TooManyInstanceVariables: { max_instance_variables: 8 }
17
+ # @quality :reek:TooManyMethods { max_methods: 19 }
18
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 8 }
19
19
  class CodeContext
20
20
  include Enumerable
21
21
  extend Forwardable
@@ -110,8 +110,8 @@ module Reek
110
110
  self
111
111
  end
112
112
 
113
- # :reek:TooManyStatements: { max_statements: 6 }
114
- # :reek:FeatureEnvy
113
+ # @quality :reek:TooManyStatements { max_statements: 6 }
114
+ # @quality :reek:FeatureEnvy
115
115
  def record_call_to(exp)
116
116
  receiver = exp.receiver
117
117
  type = receiver ? receiver.type : :self