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.
- checksums.yaml +5 -5
- data/.rubocop.yml +17 -3
- data/.simplecov +1 -0
- data/.travis.yml +0 -5
- data/.yardopts +1 -1
- data/CHANGELOG.md +44 -0
- data/Gemfile +1 -1
- data/README.md +115 -100
- 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 +1 -1
- 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 +3 -6
- data/lib/reek/ast/object_refs.rb +1 -1
- data/lib/reek/ast/sexp_extensions/case.rb +3 -1
- data/lib/reek/ast/sexp_extensions/if.rb +2 -2
- data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
- data/lib/reek/ast/sexp_extensions/methods.rb +1 -1
- 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 +4 -2
- 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 +4 -4
- data/lib/reek/context/method_context.rb +2 -2
- data/lib/reek/context/module_context.rb +1 -1
- data/lib/reek/context_builder.rb +9 -9
- 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 +4 -3
- data/lib/reek/errors/bad_detector_in_comment_error.rb +4 -3
- data/lib/reek/errors/config_file_error.rb +11 -0
- data/lib/reek/errors/encoding_error.rb +2 -2
- data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +4 -3
- data/lib/reek/errors/incomprehensible_source_error.rb +2 -2
- data/lib/reek/errors/syntax_error.rb +41 -0
- data/lib/reek/examiner.rb +9 -19
- 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.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 +8 -11
- data/lib/reek/smell_detectors/boolean_parameter.rb +0 -1
- data/lib/reek/smell_detectors/class_variable.rb +0 -1
- 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 +5 -6
- data/lib/reek/smell_detectors/feature_envy.rb +1 -1
- data/lib/reek/smell_detectors/instance_variable_assumption.rb +0 -1
- 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 +2 -2
- data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +6 -7
- data/lib/reek/smell_detectors/module_initialize.rb +0 -1
- data/lib/reek/smell_detectors/nested_iterators.rb +4 -5
- 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 +1 -2
- 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 +4 -5
- 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 +1 -2
- data/lib/reek/smell_detectors.rb +1 -2
- data/lib/reek/smell_warning.rb +15 -8
- data/lib/reek/source/source_code.rb +40 -55
- 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 +4 -4
- 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/reek/ast/node_spec.rb +5 -5
- data/spec/reek/cli/application_spec.rb +18 -4
- data/spec/reek/cli/command/todo_list_command_spec.rb +4 -2
- data/spec/reek/cli/silencer_spec.rb +28 -0
- data/spec/reek/code_comment_spec.rb +0 -7
- data/spec/reek/configuration/app_configuration_spec.rb +44 -31
- data/spec/reek/configuration/configuration_file_finder_spec.rb +141 -49
- data/spec/reek/configuration/default_directive_spec.rb +1 -1
- data/spec/reek/configuration/directory_directives_spec.rb +3 -4
- data/spec/reek/configuration/excluded_paths_spec.rb +5 -5
- 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 +1 -1
- data/spec/reek/documentation_link_spec.rb +20 -0
- data/spec/reek/examiner_spec.rb +28 -1
- 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 +1 -1
- data/spec/reek/report/yaml_report_spec.rb +9 -38
- data/spec/reek/report_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/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 +6 -6
- data/spec/reek/smell_detectors/unused_private_method_spec.rb +1 -1
- data/spec/reek/smell_warning_spec.rb +4 -0
- data/spec/reek/source/source_code_spec.rb +16 -22
- data/spec/reek/source/source_locator_spec.rb +11 -11
- data/spec/reek/spec/should_reek_of_spec.rb +0 -4
- data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
- data/spec/reek/spec/should_reek_spec.rb +1 -1
- data/spec/reek/tree_dresser_spec.rb +2 -6
- data/spec/spec_helper.rb +3 -5
- data/tasks/configuration.rake +8 -5
- metadata +64 -36
- data/.codeclimate.yml +0 -21
- 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
|
@@ -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 %<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
|
|
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
|
|
62
|
+
raise Errors::ConfigFileError, "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, 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
|
-
|
|
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
|
|
@@ -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
|
|
@@ -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
|
|
18
|
-
# :reek:TooManyInstanceVariables
|
|
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
|
|
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
|