reek 4.7.3 → 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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +17 -12
- data/.rubocop.yml +18 -3
- data/.simplecov +1 -0
- data/.travis.yml +3 -4
- data/.yardopts +1 -1
- data/CHANGELOG.md +45 -0
- data/Gemfile +4 -4
- data/README.md +134 -104
- data/Rakefile +16 -3
- data/bin/reek +1 -3
- data/docs/API.md +2 -9
- data/docs/Basic-Smell-Options.md +51 -11
- data/docs/Code-Smells.md +1 -1
- data/docs/Command-Line-Options.md +14 -4
- data/docs/Duplicate-Method-Call.md +49 -1
- data/docs/Feature-Envy.md +44 -0
- data/docs/How-To-Write-New-Detectors.md +2 -3
- data/docs/Instance-Variable-Assumption.md +1 -1
- data/docs/{Prima-Donna-Method.md → Missing-Safe-Method.md} +11 -9
- data/docs/Rake-Task.md +1 -1
- data/docs/Reek-4-to-Reek-5-migration.md +193 -0
- data/docs/Reek-Driven-Development.md +1 -1
- data/docs/Uncommunicative-Method-Name.md +45 -6
- data/docs/Uncommunicative-Module-Name.md +49 -7
- data/docs/Uncommunicative-Parameter-Name.md +43 -5
- data/docs/Uncommunicative-Variable-Name.md +73 -2
- data/docs/Unused-Private-Method.md +2 -2
- data/docs/defaults.reek.yml +129 -0
- data/docs/yard_plugin.rb +1 -0
- data/features/command_line_interface/basic_usage.feature +2 -2
- data/features/command_line_interface/options.feature +46 -4
- data/features/command_line_interface/show_progress.feature +4 -4
- data/features/command_line_interface/smell_selection.feature +1 -1
- data/features/command_line_interface/smells_count.feature +6 -6
- data/features/command_line_interface/stdin.feature +30 -8
- data/features/configuration_files/accept_setting.feature +45 -28
- data/features/configuration_files/directory_specific_directives.feature +78 -73
- data/features/configuration_files/exclude_directives.feature +11 -10
- data/features/configuration_files/exclude_paths_directives.feature +4 -4
- data/features/configuration_files/masking_smells.feature +38 -9
- data/features/configuration_files/mix_accept_reject_setting.feature +31 -28
- data/features/configuration_files/reject_setting.feature +52 -41
- data/features/configuration_files/schema_validation.feature +59 -0
- data/features/configuration_files/unused_private_method.feature +18 -16
- data/features/configuration_loading.feature +53 -10
- data/features/configuration_via_source_comments/erroneous_source_comments.feature +2 -2
- data/features/configuration_via_source_comments/well_formed_source_comments.feature +2 -2
- data/features/locales.feature +2 -2
- data/features/rake_task/rake_task.feature +15 -15
- data/features/reports/json.feature +3 -3
- data/features/reports/reports.feature +34 -34
- data/features/reports/yaml.feature +3 -3
- data/features/rspec_matcher.feature +9 -1
- data/features/samples.feature +287 -287
- data/features/step_definitions/reek_steps.rb +4 -0
- data/features/step_definitions/sample_file_steps.rb +9 -4
- data/features/support/env.rb +2 -2
- data/features/todo_list.feature +26 -23
- data/lib/reek/ast/node.rb +40 -55
- data/lib/reek/ast/object_refs.rb +1 -1
- data/lib/reek/ast/reference_collector.rb +2 -4
- data/lib/reek/ast/sexp_extensions/case.rb +1 -1
- data/lib/reek/ast/sexp_extensions/if.rb +8 -1
- data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
- data/lib/reek/ast/sexp_extensions/methods.rb +4 -6
- data/lib/reek/cli/application.rb +4 -3
- data/lib/reek/cli/command/report_command.rb +1 -2
- data/lib/reek/cli/command/todo_list_command.rb +8 -8
- data/lib/reek/cli/options.rb +29 -14
- data/lib/reek/cli/silencer.rb +14 -3
- data/lib/reek/code_comment.rb +14 -16
- data/lib/reek/configuration/app_configuration.rb +32 -28
- data/lib/reek/configuration/configuration_converter.rb +110 -0
- data/lib/reek/configuration/configuration_file_finder.rb +15 -40
- data/lib/reek/configuration/configuration_validator.rb +12 -23
- data/lib/reek/configuration/default_directive.rb +17 -3
- data/lib/reek/configuration/directory_directives.rb +17 -11
- data/lib/reek/configuration/excluded_paths.rb +1 -1
- data/lib/reek/configuration/rake_task_converter.rb +29 -0
- data/lib/reek/configuration/schema.yml +210 -0
- data/lib/reek/configuration/schema_validator.rb +38 -0
- data/lib/reek/context/attribute_context.rb +1 -1
- data/lib/reek/context/code_context.rb +8 -11
- data/lib/reek/context/method_context.rb +7 -12
- data/lib/reek/context/module_context.rb +4 -4
- data/lib/reek/context_builder.rb +11 -11
- data/lib/reek/detector_repository.rb +6 -0
- data/lib/reek/documentation_link.rb +28 -0
- data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +13 -12
- data/lib/reek/errors/bad_detector_in_comment_error.rb +11 -10
- data/lib/reek/errors/base_error.rb +3 -0
- data/lib/reek/errors/config_file_error.rb +11 -0
- data/lib/reek/errors/encoding_error.rb +16 -11
- data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +11 -10
- data/lib/reek/errors/incomprehensible_source_error.rb +20 -22
- data/lib/reek/errors/syntax_error.rb +41 -0
- data/lib/reek/examiner.rb +19 -25
- data/lib/reek/logging_error_handler.rb +7 -5
- data/lib/reek/rake/task.rb +3 -3
- data/lib/reek/report/base_report.rb +8 -12
- data/lib/reek/report/code_climate/code_climate_configuration.rb +1 -1
- data/lib/reek/report/code_climate/code_climate_configuration.yml +6 -10
- data/lib/reek/report/documentation_link_warning_formatter.rb +17 -0
- data/lib/reek/report/heading_formatter.rb +54 -0
- data/lib/reek/report/json_report.rb +1 -1
- data/lib/reek/report/location_formatter.rb +40 -0
- data/lib/reek/report/progress_formatter.rb +79 -0
- data/lib/reek/report/simple_warning_formatter.rb +34 -0
- data/lib/reek/report/text_report.rb +1 -2
- data/lib/reek/report/xml_report.rb +3 -3
- data/lib/reek/report/yaml_report.rb +1 -1
- data/lib/reek/report.rb +15 -10
- data/lib/reek/smell_configuration.rb +2 -2
- data/lib/reek/smell_detectors/attribute.rb +0 -1
- data/lib/reek/smell_detectors/base_detector.rb +9 -12
- data/lib/reek/smell_detectors/boolean_parameter.rb +0 -1
- data/lib/reek/smell_detectors/class_variable.rb +3 -11
- data/lib/reek/smell_detectors/control_parameter.rb +17 -32
- data/lib/reek/smell_detectors/data_clump.rb +3 -4
- data/lib/reek/smell_detectors/duplicate_method_call.rb +6 -7
- data/lib/reek/smell_detectors/feature_envy.rb +1 -1
- data/lib/reek/smell_detectors/instance_variable_assumption.rb +1 -10
- data/lib/reek/smell_detectors/irresponsible_module.rb +0 -1
- data/lib/reek/smell_detectors/long_parameter_list.rb +1 -2
- data/lib/reek/smell_detectors/long_yield_list.rb +2 -3
- data/lib/reek/smell_detectors/manual_dispatch.rb +3 -3
- data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +6 -7
- data/lib/reek/smell_detectors/module_initialize.rb +1 -2
- data/lib/reek/smell_detectors/nested_iterators.rb +6 -6
- data/lib/reek/smell_detectors/nil_check.rb +0 -1
- data/lib/reek/smell_detectors/repeated_conditional.rb +3 -4
- data/lib/reek/smell_detectors/subclassed_from_core_class.rb +0 -1
- data/lib/reek/smell_detectors/too_many_constants.rb +2 -3
- data/lib/reek/smell_detectors/too_many_instance_variables.rb +1 -2
- data/lib/reek/smell_detectors/too_many_methods.rb +1 -2
- data/lib/reek/smell_detectors/too_many_statements.rb +1 -2
- data/lib/reek/smell_detectors/uncommunicative_method_name.rb +2 -3
- data/lib/reek/smell_detectors/uncommunicative_module_name.rb +2 -3
- data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +2 -3
- data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +6 -7
- data/lib/reek/smell_detectors/unused_parameters.rb +0 -1
- data/lib/reek/smell_detectors/unused_private_method.rb +0 -1
- data/lib/reek/smell_detectors/utility_function.rb +2 -3
- data/lib/reek/smell_detectors.rb +1 -2
- data/lib/reek/smell_warning.rb +15 -8
- data/lib/reek/source/source_code.rb +50 -72
- data/lib/reek/source/source_locator.rb +7 -7
- data/lib/reek/spec/should_reek.rb +2 -2
- data/lib/reek/spec/should_reek_of.rb +9 -16
- data/lib/reek/spec/should_reek_only_of.rb +4 -4
- data/lib/reek/spec.rb +6 -6
- data/lib/reek/tree_dresser.rb +5 -5
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +5 -5
- data/samples/checkstyle.xml +1 -1
- data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +29 -0
- data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +30 -0
- data/samples/configuration/full_configuration.reek +8 -4
- data/samples/configuration/full_mask.reek +5 -4
- data/samples/configuration/partial_mask.reek +3 -2
- data/samples/configuration/regular_configuration/.reek.yml +4 -0
- data/samples/paths.rb +5 -4
- data/samples/source_with_hidden_directories/.hidden/hidden.rb +1 -0
- data/samples/source_with_hidden_directories/not_hidden.rb +1 -0
- data/spec/factories/factories.rb +2 -13
- data/spec/reek/ast/node_spec.rb +103 -10
- data/spec/reek/ast/reference_collector_spec.rb +1 -1
- data/spec/reek/ast/sexp_extensions_spec.rb +2 -2
- data/spec/reek/cli/application_spec.rb +50 -38
- data/spec/reek/cli/command/todo_list_command_spec.rb +6 -4
- data/spec/reek/cli/silencer_spec.rb +28 -0
- data/spec/reek/code_comment_spec.rb +31 -38
- data/spec/reek/configuration/app_configuration_spec.rb +46 -33
- data/spec/reek/configuration/configuration_file_finder_spec.rb +133 -49
- data/spec/reek/configuration/default_directive_spec.rb +1 -1
- data/spec/reek/configuration/directory_directives_spec.rb +6 -7
- data/spec/reek/configuration/excluded_paths_spec.rb +6 -6
- data/spec/reek/configuration/rake_task_converter_spec.rb +33 -0
- data/spec/reek/configuration/schema_validator_spec.rb +165 -0
- data/spec/reek/context/code_context_spec.rb +60 -96
- data/spec/reek/context/ghost_context_spec.rb +1 -1
- data/spec/reek/context/root_context_spec.rb +1 -1
- data/spec/reek/documentation_link_spec.rb +20 -0
- data/spec/reek/errors/base_error_spec.rb +13 -0
- data/spec/reek/examiner_spec.rb +100 -30
- data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +82 -80
- data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
- data/spec/reek/report/json_report_spec.rb +13 -46
- data/spec/reek/report/{formatter/location_formatter_spec.rb → location_formatter_spec.rb} +5 -5
- data/spec/reek/report/{formatter/progress_formatter_spec.rb → progress_formatter_spec.rb} +4 -4
- data/spec/reek/report/text_report_spec.rb +4 -4
- data/spec/reek/report/xml_report_spec.rb +3 -3
- data/spec/reek/report/yaml_report_spec.rb +9 -38
- data/spec/reek/report_spec.rb +3 -3
- data/spec/reek/smell_detectors/boolean_parameter_spec.rb +2 -2
- data/spec/reek/smell_detectors/class_variable_spec.rb +26 -32
- data/spec/reek/smell_detectors/control_parameter_spec.rb +34 -4
- data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +3 -3
- data/spec/reek/smell_detectors/feature_envy_spec.rb +47 -2
- data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +9 -9
- data/spec/reek/smell_detectors/module_initialize_spec.rb +14 -0
- data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
- data/spec/reek/smell_detectors/too_many_constants_spec.rb +3 -3
- data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +1 -1
- data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +6 -6
- data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +6 -4
- data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +6 -4
- data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +9 -9
- data/spec/reek/smell_detectors/unused_parameters_spec.rb +3 -3
- data/spec/reek/smell_detectors/unused_private_method_spec.rb +10 -10
- data/spec/reek/smell_detectors/utility_function_spec.rb +5 -5
- data/spec/reek/smell_warning_spec.rb +12 -8
- data/spec/reek/source/source_code_spec.rb +17 -43
- data/spec/reek/source/source_locator_spec.rb +17 -17
- data/spec/reek/spec/should_reek_of_spec.rb +7 -11
- data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
- data/spec/reek/spec/should_reek_spec.rb +3 -3
- data/spec/reek/spec/smell_matcher_spec.rb +3 -3
- data/spec/reek/tree_dresser_spec.rb +12 -17
- data/spec/spec_helper.rb +6 -17
- data/tasks/configuration.rake +8 -5
- metadata +71 -41
- data/defaults.reek +0 -131
- data/features/configuration_files/warn_about_multiple_configuration_files.feature +0 -44
- data/lib/reek/report/formatter/heading_formatter.rb +0 -52
- data/lib/reek/report/formatter/location_formatter.rb +0 -42
- data/lib/reek/report/formatter/progress_formatter.rb +0 -81
- data/lib/reek/report/formatter/simple_warning_formatter.rb +0 -35
- data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +0 -36
- data/lib/reek/report/formatter.rb +0 -33
- data/lib/reek/smell_detectors/syntax.rb +0 -37
- data/samples/configuration/non_public_modifiers_mask.reek +0 -3
- data/samples/smelly_with_inline_mask.rb +0 -8
- data/samples/smelly_with_modifiers.rb +0 -12
- data/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -5
- data/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -6
- data/spec/reek/smell_detectors/syntax_spec.rb +0 -17
- /data/{samples/configuration/more_than_one_configuration_file/regular.reek → .reek.yml} +0 -0
- /data/samples/{clean.rb → clean_source/clean.rb} +0 -0
- /data/samples/{exceptions.reek → configuration/home/home.reek.yml} +0 -0
- /data/samples/configuration/{more_than_one_configuration_file/todo.reek → regular_configuration/empty_sub_directory/.gitignore} +0 -0
- /data/samples/{configuration/single_configuration_file/.reek → no_config_file/.keep} +0 -0
- /data/samples/{inline.rb → smelly_source/inline.rb} +0 -0
- /data/samples/{optparse.rb → smelly_source/optparse.rb} +0 -0
- /data/samples/{redcloth.rb → smelly_source/redcloth.rb} +0 -0
- /data/samples/{smelly.rb → smelly_source/smelly.rb} +0 -0
- /data/samples/{source_with_hidden_directories/uncommunicative_parameter_name.rb → source_with_non_ruby_files/ruby.rb} +0 -0
data/lib/reek/code_comment.rb
CHANGED
|
@@ -17,22 +17,20 @@ module Reek
|
|
|
17
17
|
:reek: # prefix
|
|
18
18
|
(\w+) # smell detector e.g.: UncommunicativeVariableName
|
|
19
19
|
(
|
|
20
|
-
:? # legacy separator
|
|
21
20
|
\s*
|
|
22
21
|
(\{.*?\}) # optional details in hash style e.g.: { max_methods: 30 }
|
|
23
22
|
)?
|
|
24
23
|
/x
|
|
25
24
|
SANITIZE_REGEX = /(#|\n|\s)+/ # Matches '#', newlines and > 1 whitespaces.
|
|
26
|
-
DISABLE_DETECTOR_CONFIGURATION = '{ enabled: false }'
|
|
25
|
+
DISABLE_DETECTOR_CONFIGURATION = '{ enabled: false }'
|
|
27
26
|
MINIMUM_CONTENT_LENGTH = 2
|
|
28
|
-
LEGACY_SEPARATOR = ':'.freeze
|
|
29
27
|
|
|
30
28
|
attr_reader :config
|
|
31
29
|
|
|
32
30
|
#
|
|
33
|
-
# @param comment [String]
|
|
34
|
-
# @param line [Integer]
|
|
35
|
-
# @param source [String]
|
|
31
|
+
# @param comment [String] the original comment as found in the source code
|
|
32
|
+
# @param line [Integer] start of the expression the comment belongs to
|
|
33
|
+
# @param source [String] Path to source file or "string"
|
|
36
34
|
#
|
|
37
35
|
def initialize(comment:, line: nil, source: nil)
|
|
38
36
|
@original_comment = comment
|
|
@@ -80,15 +78,15 @@ module Reek
|
|
|
80
78
|
# This class validates [1], [2] and [3] at the moment but will also validate
|
|
81
79
|
# [4] in the future.
|
|
82
80
|
#
|
|
83
|
-
# :reek:TooManyInstanceVariables
|
|
81
|
+
# @quality :reek:TooManyInstanceVariables { max_instance_variables: 7 }
|
|
84
82
|
class CodeCommentValidator
|
|
85
83
|
#
|
|
86
|
-
# @param detector_name [String]
|
|
84
|
+
# @param detector_name [String] the detector class that was parsed out of the original
|
|
87
85
|
# comment, e.g. "DuplicateMethodCall" or "UnknownSmellDetector"
|
|
88
|
-
# @param original_comment [String]
|
|
89
|
-
# @param line [Integer]
|
|
90
|
-
# @param source [String]
|
|
91
|
-
# @param options [String]
|
|
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
|
|
92
90
|
# extracted from the original comment
|
|
93
91
|
def initialize(detector_name:, original_comment:, line:, source:, options: {})
|
|
94
92
|
@detector_name = detector_name
|
|
@@ -151,24 +149,24 @@ module Reek
|
|
|
151
149
|
line: line
|
|
152
150
|
end
|
|
153
151
|
|
|
154
|
-
# @return [Boolean]
|
|
152
|
+
# @return [Boolean] all keys in code comment are applicable to the detector in question
|
|
155
153
|
def given_keys_legit?
|
|
156
154
|
given_configuration_keys.subset? valid_detector_keys
|
|
157
155
|
end
|
|
158
156
|
|
|
159
|
-
# @return [Set]
|
|
157
|
+
# @return [Set] the configuration keys that are found in the code comment
|
|
160
158
|
def given_configuration_keys
|
|
161
159
|
parsed_options.keys.map(&:to_sym).to_set
|
|
162
160
|
end
|
|
163
161
|
|
|
164
|
-
# @return [String]
|
|
162
|
+
# @return [String] all keys from the code comment that look bad
|
|
165
163
|
def configuration_keys_difference
|
|
166
164
|
given_configuration_keys.difference(valid_detector_keys).
|
|
167
165
|
to_a.map { |key| "'#{key}'" }.
|
|
168
166
|
join(', ')
|
|
169
167
|
end
|
|
170
168
|
|
|
171
|
-
# @return [Set]
|
|
169
|
+
# @return [Set] all keys that are legit for the given detector
|
|
172
170
|
def valid_detector_keys
|
|
173
171
|
detector_class.configuration_keys
|
|
174
172
|
end
|
|
@@ -15,20 +15,30 @@ module Reek
|
|
|
15
15
|
# @public
|
|
16
16
|
class AppConfiguration
|
|
17
17
|
include ConfigurationValidator
|
|
18
|
-
EXCLUDE_PATHS_KEY = 'exclude_paths'
|
|
18
|
+
EXCLUDE_PATHS_KEY = 'exclude_paths'
|
|
19
|
+
DIRECTORIES_KEY = 'directories'
|
|
20
|
+
DETECTORS_KEY = 'detectors'
|
|
19
21
|
|
|
20
|
-
# Instantiate a configuration via
|
|
22
|
+
# Instantiate a configuration via the given path.
|
|
21
23
|
#
|
|
22
|
-
# @param path [Pathname] the path to the config file
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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]
|
|
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(
|
|
71
|
-
|
|
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
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
Error: Found multiple configuration files %s
|
|
24
|
-
while scanning 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
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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, offensive_files, 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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
28
|
-
#
|
|
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
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
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
|
|
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
|