reek 2.0.4 → 3.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/.gitignore +13 -0
- data/.rubocop.yml +47 -0
- data/.travis.yml +31 -0
- data/.yardopts +3 -6
- data/CHANGELOG +24 -0
- data/CONTRIBUTING.md +111 -0
- data/Gemfile +16 -0
- data/README.md +112 -59
- data/bin/reek +2 -2
- data/config/cucumber.yml +3 -0
- data/config/defaults.reek +0 -1
- data/docs/API.md +79 -0
- data/docs/Attribute.md +43 -0
- data/docs/Basic-Smell-Options.md +44 -0
- data/docs/Boolean-Parameter.md +52 -0
- data/docs/Class-Variable.md +40 -0
- data/docs/Code-Smells.md +34 -0
- data/docs/Command-Line-Options.md +84 -0
- data/docs/Configuration-Files.md +49 -0
- data/docs/Control-Couple.md +22 -0
- data/docs/Control-Parameter.md +29 -0
- data/docs/Data-Clump.md +44 -0
- data/docs/Duplicate-Method-Call.md +49 -0
- data/docs/Feature-Envy.md +49 -0
- data/docs/How-reek-works-internally.md +114 -0
- data/docs/Irresponsible-Module.md +39 -0
- data/docs/Large-Class.md +20 -0
- data/docs/Long-Parameter-List.md +38 -0
- data/docs/Long-Yield-List.md +36 -0
- data/docs/Module-Initialize.md +62 -0
- data/docs/Nested-Iterators.md +38 -0
- data/docs/Nil-Check.md +39 -0
- data/docs/Prima-Donna-Method.md +53 -0
- data/docs/RSpec-matchers.md +137 -0
- data/docs/Rake-Task.md +58 -0
- data/docs/Reek-Driven-Development.md +37 -0
- data/docs/Repeated-Conditional.md +44 -0
- data/docs/Simulated-Polymorphism.md +16 -0
- data/docs/Smell-Suppression.md +32 -0
- data/docs/Too-Many-Instance-Variables.md +43 -0
- data/docs/Too-Many-Methods.md +55 -0
- data/docs/Too-Many-Statements.md +50 -0
- data/docs/Uncommunicative-Method-Name.md +24 -0
- data/docs/Uncommunicative-Module-Name.md +23 -0
- data/docs/Uncommunicative-Name.md +16 -0
- data/docs/Uncommunicative-Parameter-Name.md +24 -0
- data/docs/Uncommunicative-Variable-Name.md +24 -0
- data/docs/Unused-Parameters.md +27 -0
- data/docs/Utility-Function.md +44 -0
- data/docs/Versioning-Policy.md +7 -0
- data/docs/YAML-Reports.md +111 -0
- data/docs/yard_plugin.rb +14 -0
- data/features/{ruby_api/api.feature → command_line_interface/basic_usage.feature} +6 -5
- data/features/command_line_interface/options.feature +1 -0
- data/features/command_line_interface/smell_selection.feature +4 -8
- data/features/command_line_interface/smells_count.feature +25 -32
- data/features/command_line_interface/stdin.feature +2 -2
- data/features/configuration_files/masking_smells.feature +30 -41
- data/features/configuration_files/overrides_defaults.feature +5 -3
- data/features/configuration_loading.feature +26 -23
- data/features/programmatic_access.feature +43 -0
- data/features/rake_task/rake_task.feature +25 -22
- data/features/reports/json.feature +24 -26
- data/features/reports/reports.feature +77 -103
- data/features/reports/yaml.feature +26 -20
- data/features/samples.feature +26 -31
- data/features/step_definitions/.rubocop.yml +5 -0
- data/features/step_definitions/reek_steps.rb +16 -28
- data/features/step_definitions/sample_file_steps.rb +158 -0
- data/features/support/env.rb +16 -27
- data/lib/reek/{source → ast}/ast_node_class_map.rb +11 -12
- data/lib/reek/ast/node.rb +132 -0
- data/lib/reek/{core → ast}/object_refs.rb +2 -1
- data/lib/reek/ast/reference_collector.rb +32 -0
- data/lib/reek/{source → ast}/sexp_extensions.rb +43 -7
- data/lib/reek/ast/sexp_formatter.rb +31 -0
- data/lib/reek/cli/application.rb +5 -4
- data/lib/reek/cli/command.rb +2 -1
- data/lib/reek/cli/input.rb +6 -5
- data/lib/reek/cli/option_interpreter.rb +13 -9
- data/lib/reek/cli/options.rb +16 -8
- data/lib/reek/cli/reek_command.rb +4 -3
- data/lib/reek/cli/silencer.rb +14 -0
- data/lib/reek/{core → cli}/warning_collector.rb +2 -1
- data/lib/reek/code_comment.rb +36 -0
- data/lib/reek/configuration/app_configuration.rb +18 -3
- data/lib/reek/configuration/configuration_file_finder.rb +18 -32
- data/lib/reek/{core → context}/code_context.rb +8 -6
- data/lib/reek/{core → context}/method_context.rb +11 -5
- data/lib/reek/{core → context}/module_context.rb +9 -4
- data/lib/reek/{core/stop_context.rb → context/root_context.rb} +4 -2
- data/lib/reek/{core → context}/singleton_method_context.rb +3 -6
- data/lib/reek/examiner.rb +32 -29
- data/lib/reek/report/formatter.rb +70 -0
- data/lib/reek/report/heading_formatter.rb +45 -0
- data/lib/reek/report/location_formatter.rb +35 -0
- data/lib/reek/report/report.rb +198 -0
- data/lib/reek/smells/attribute.rb +41 -11
- data/lib/reek/smells/boolean_parameter.rb +4 -2
- data/lib/reek/smells/class_variable.rb +4 -2
- data/lib/reek/smells/control_parameter.rb +4 -2
- data/lib/reek/smells/data_clump.rb +4 -3
- data/lib/reek/smells/duplicate_method_call.rb +15 -5
- data/lib/reek/smells/feature_envy.rb +8 -2
- data/lib/reek/smells/irresponsible_module.rb +13 -8
- data/lib/reek/smells/long_parameter_list.rb +6 -4
- data/lib/reek/smells/long_yield_list.rb +4 -2
- data/lib/reek/smells/module_initialize.rb +4 -2
- data/lib/reek/smells/nested_iterators.rb +4 -2
- data/lib/reek/smells/nil_check.rb +6 -3
- data/lib/reek/smells/prima_donna_method.rb +21 -12
- data/lib/reek/smells/repeated_conditional.rb +7 -4
- data/lib/reek/{core → smells}/smell_configuration.rb +3 -1
- data/lib/reek/smells/smell_detector.rb +11 -5
- data/lib/reek/{core → smells}/smell_repository.rb +9 -5
- data/lib/reek/smells/smell_warning.rb +101 -0
- data/lib/reek/smells/too_many_instance_variables.rb +4 -2
- data/lib/reek/smells/too_many_methods.rb +4 -2
- data/lib/reek/smells/too_many_statements.rb +4 -2
- data/lib/reek/smells/uncommunicative_method_name.rb +4 -2
- data/lib/reek/smells/uncommunicative_module_name.rb +4 -2
- data/lib/reek/smells/uncommunicative_parameter_name.rb +4 -2
- data/lib/reek/smells/uncommunicative_variable_name.rb +4 -2
- data/lib/reek/smells/unused_parameters.rb +4 -2
- data/lib/reek/smells/utility_function.rb +11 -22
- data/lib/reek/smells.rb +24 -11
- data/lib/reek/source/source_code.rb +69 -12
- data/lib/reek/source/source_locator.rb +34 -19
- data/lib/reek/spec/should_reek.rb +4 -3
- data/lib/reek/spec/should_reek_of.rb +2 -1
- data/lib/reek/spec/should_reek_only_of.rb +4 -3
- data/lib/reek/spec.rb +3 -3
- data/lib/reek/tree_dresser.rb +32 -0
- data/lib/reek/tree_walker.rb +182 -0
- data/lib/reek/version.rb +1 -1
- data/lib/reek.rb +3 -3
- data/reek.gemspec +18 -25
- data/spec/factories/factories.rb +4 -2
- data/spec/gem/updates_spec.rb +1 -1
- data/spec/gem/yard_spec.rb +2 -2
- data/spec/quality/reek_source_spec.rb +3 -3
- data/spec/reek/{source/sexp_node_spec.rb → ast/node_spec.rb} +4 -6
- data/spec/reek/{core → ast}/object_refs_spec.rb +4 -6
- data/spec/reek/{source → ast}/reference_collector_spec.rb +27 -19
- data/spec/reek/{source → ast}/sexp_extensions_spec.rb +7 -19
- data/spec/reek/{source → ast}/sexp_formatter_spec.rb +13 -8
- data/spec/reek/cli/option_interpreter_spec.rb +15 -0
- data/spec/reek/cli/options_spec.rb +19 -0
- data/spec/reek/cli/warning_collector_spec.rb +28 -0
- data/spec/reek/{source/code_comment_spec.rb → code_comment_spec.rb} +19 -17
- data/spec/reek/configuration/app_configuration_spec.rb +37 -26
- data/spec/reek/configuration/configuration_file_finder_spec.rb +67 -21
- data/spec/reek/{core → context}/code_context_spec.rb +18 -21
- data/spec/reek/{core → context}/method_context_spec.rb +33 -27
- data/spec/reek/{core → context}/module_context_spec.rb +5 -7
- data/spec/reek/context/root_context_spec.rb +14 -0
- data/spec/reek/context/singleton_method_context_spec.rb +17 -0
- data/spec/reek/examiner_spec.rb +9 -50
- data/spec/reek/{cli → report}/html_report_spec.rb +6 -9
- data/spec/reek/report/json_report_spec.rb +20 -0
- data/spec/reek/{cli → report}/text_report_spec.rb +16 -19
- data/spec/reek/report/xml_report_spec.rb +34 -0
- data/spec/reek/report/yaml_report_spec.rb +20 -0
- data/spec/reek/smells/attribute_spec.rb +93 -88
- data/spec/reek/smells/behaves_like_variable_detector.rb +1 -1
- data/spec/reek/smells/boolean_parameter_spec.rb +31 -5
- data/spec/reek/smells/class_variable_spec.rb +10 -10
- data/spec/reek/smells/control_parameter_spec.rb +5 -5
- data/spec/reek/smells/data_clump_spec.rb +6 -6
- data/spec/reek/smells/duplicate_method_call_spec.rb +7 -7
- data/spec/reek/smells/feature_envy_spec.rb +12 -13
- data/spec/reek/smells/irresponsible_module_spec.rb +28 -32
- data/spec/reek/smells/long_parameter_list_spec.rb +7 -7
- data/spec/reek/smells/long_yield_list_spec.rb +6 -6
- data/spec/reek/smells/module_initialize_spec.rb +4 -4
- data/spec/reek/smells/nested_iterators_spec.rb +6 -6
- data/spec/reek/smells/nil_check_spec.rb +6 -6
- data/spec/reek/smells/prima_donna_method_spec.rb +8 -5
- data/spec/reek/smells/repeated_conditional_spec.rb +18 -17
- data/spec/reek/{core → smells}/smell_configuration_spec.rb +5 -7
- data/spec/reek/smells/smell_detector_shared.rb +6 -7
- data/spec/reek/smells/smell_repository_spec.rb +29 -0
- data/spec/reek/{smell_warning_spec.rb → smells/smell_warning_spec.rb} +7 -9
- data/spec/reek/smells/too_many_instance_variables_spec.rb +5 -5
- data/spec/reek/smells/too_many_methods_spec.rb +12 -8
- data/spec/reek/smells/too_many_statements_spec.rb +10 -9
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +5 -5
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +8 -8
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +18 -6
- data/spec/reek/smells/uncommunicative_variable_name_spec.rb +10 -7
- data/spec/reek/smells/unused_parameters_spec.rb +4 -4
- data/spec/reek/smells/utility_function_spec.rb +65 -39
- data/spec/reek/source/source_code_spec.rb +10 -10
- data/spec/reek/source/source_locator_spec.rb +30 -0
- data/spec/reek/spec/should_reek_of_spec.rb +9 -25
- data/spec/reek/spec/should_reek_only_of_spec.rb +4 -7
- data/spec/reek/spec/should_reek_spec.rb +4 -32
- data/spec/reek/tree_dresser_spec.rb +16 -0
- data/spec/reek/{core/tree_walker_spec.rb → tree_walker_spec.rb} +5 -7
- data/spec/samples/checkstyle.xml +2 -0
- data/spec/samples/configuration/with_excluded_paths.reek +4 -0
- data/spec/samples/{overrides/masked/lower.reek → masked_by_dotfile/.reek} +3 -1
- data/spec/samples/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb +5 -0
- data/spec/samples/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb +2 -0
- data/spec/samples/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb +6 -0
- data/spec/samples/{ruby20_syntax.rb → unusual_syntax.rb} +8 -0
- data/spec/spec_helper.rb +21 -10
- data/tasks/develop.rake +2 -2
- data/tasks/reek.rake +1 -1
- metadata +158 -141
- data/features/support/hooks.rb +0 -15
- data/lib/reek/cli/report/formatter.rb +0 -69
- data/lib/reek/cli/report/heading_formatter.rb +0 -45
- data/lib/reek/cli/report/location_formatter.rb +0 -34
- data/lib/reek/cli/report/report.rb +0 -131
- data/lib/reek/core/tree_walker.rb +0 -180
- data/lib/reek/smell_warning.rb +0 -87
- data/lib/reek/source/ast_node.rb +0 -38
- data/lib/reek/source/code_comment.rb +0 -37
- data/lib/reek/source/core_extras.rb +0 -46
- data/lib/reek/source/reference_collector.rb +0 -27
- data/lib/reek/source/sexp_formatter.rb +0 -22
- data/lib/reek/source/sexp_node.rb +0 -79
- data/lib/reek/source/source_file.rb +0 -16
- data/lib/reek/source/source_repository.rb +0 -45
- data/lib/reek/source/tree_dresser.rb +0 -24
- data/lib/reek/source.rb +0 -16
- data/spec/reek/cli/json_report_spec.rb +0 -20
- data/spec/reek/cli/option_interperter_spec.rb +0 -14
- data/spec/reek/cli/yaml_report_spec.rb +0 -23
- data/spec/reek/core/singleton_method_context_spec.rb +0 -9
- data/spec/reek/core/smell_repository_spec.rb +0 -17
- data/spec/reek/core/stop_context_spec.rb +0 -17
- data/spec/reek/core/warning_collector_spec.rb +0 -30
- data/spec/reek/source/object_source_spec.rb +0 -20
- data/spec/reek/source/tree_dresser_spec.rb +0 -18
- data/spec/samples/config/allow_duplication.reek +0 -3
- data/spec/samples/config/deeper_nested_iterators.reek +0 -3
- data/spec/samples/configuration_loading/minimal_dirty.rb +0 -4
- data/spec/samples/configuration_loading/reek-test-run-disable_smells.reek +0 -7
- data/spec/samples/configuration_loading/reek-test-run-enable_smells.reek +0 -7
- data/spec/samples/corrupt_config_file/corrupt.reek +0 -1
- data/spec/samples/corrupt_config_file/dirty.rb +0 -8
- data/spec/samples/demo/demo.rb +0 -8
- data/spec/samples/empty_config_file/dirty.rb +0 -8
- data/spec/samples/empty_config_file/empty.reek +0 -0
- data/spec/samples/inline_config/dirty.rb +0 -16
- data/spec/samples/inline_config/masked.reek +0 -7
- data/spec/samples/mask_some/dirty.rb +0 -8
- data/spec/samples/mask_some/some.reek +0 -8
- data/spec/samples/masked/dirty.rb +0 -8
- data/spec/samples/masked/masked.reek +0 -5
- data/spec/samples/minimal_smelly_and_masked/config.reek +0 -7
- data/spec/samples/minimal_smelly_and_masked/minimal_dirty.rb +0 -4
- data/spec/samples/mixed_results/clean_one.rb +0 -7
- data/spec/samples/mixed_results/clean_three.rb +0 -7
- data/spec/samples/mixed_results/clean_two.rb +0 -7
- data/spec/samples/mixed_results/dirty_one.rb +0 -8
- data/spec/samples/mixed_results/dirty_two.rb +0 -8
- data/spec/samples/not_quite_masked/dirty.rb +0 -8
- data/spec/samples/not_quite_masked/masked.reek +0 -5
- data/spec/samples/not_quite_masked/smelly.rb +0 -3
- data/spec/samples/one_smelly_file/dirty.rb +0 -3
- data/spec/samples/overrides/masked/dirty.rb +0 -8
- data/spec/samples/overrides/upper.reek +0 -5
- data/spec/samples/overrides_defaults/camel_case.rb +0 -14
- data/spec/samples/overrides_defaults/config.reek +0 -6
- data/spec/samples/ruby21_syntax.rb +0 -5
- data/spec/samples/standard_smelly/dirty.rb +0 -8
- data/spec/samples/standard_smelly/minimal_dirty.rb +0 -4
- data/spec/samples/three_smelly_files/dirty_one.rb +0 -3
- data/spec/samples/three_smelly_files/dirty_three.rb +0 -5
- data/spec/samples/three_smelly_files/dirty_two.rb +0 -4
- /data/spec/samples/{simple_configuration.reek → configuration/simple_configuration.reek} +0 -0
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
require 'reek/cli/report/location_formatter'
|
|
2
|
-
|
|
3
|
-
module Reek
|
|
4
|
-
module Cli
|
|
5
|
-
module Report
|
|
6
|
-
#
|
|
7
|
-
# Formatter handling the formatting of the report at large.
|
|
8
|
-
# Formatting of the individual warnings is handled by the
|
|
9
|
-
# passed-in warning formatter.
|
|
10
|
-
#
|
|
11
|
-
module Formatter
|
|
12
|
-
def self.format_list(warnings, formatter = SimpleWarningFormatter.new)
|
|
13
|
-
warnings.map do |warning|
|
|
14
|
-
" #{formatter.format warning}"
|
|
15
|
-
end.join("\n")
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def self.header(examiner)
|
|
19
|
-
count = examiner.smells_count
|
|
20
|
-
result = Rainbow("#{examiner.description} -- ").cyan +
|
|
21
|
-
Rainbow("#{count} warning").yellow
|
|
22
|
-
result += Rainbow('s').yellow unless count == 1
|
|
23
|
-
result
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
#
|
|
28
|
-
# Basic formatter that just shows a simple message for each warning,
|
|
29
|
-
# prepended with the result of the passed-in location formatter.
|
|
30
|
-
#
|
|
31
|
-
class SimpleWarningFormatter
|
|
32
|
-
def initialize(location_formatter = BlankLocationFormatter)
|
|
33
|
-
@location_formatter = location_formatter
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def format(warning)
|
|
37
|
-
"#{@location_formatter.format(warning)}#{base_format(warning)}"
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
def base_format(warning)
|
|
43
|
-
"#{warning.context} #{warning.message} (#{warning.smell_type})"
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
#
|
|
48
|
-
# Formatter that adds a link to the wiki to the basic message from
|
|
49
|
-
# SimpleWarningFormatter.
|
|
50
|
-
#
|
|
51
|
-
class WikiLinkWarningFormatter < SimpleWarningFormatter
|
|
52
|
-
BASE_URL_FOR_HELP_LINK = 'https://github.com/troessner/reek/wiki/'
|
|
53
|
-
|
|
54
|
-
def format(warning)
|
|
55
|
-
"#{super} " \
|
|
56
|
-
"[#{explanatory_link(warning)}]"
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def explanatory_link(warning)
|
|
60
|
-
"#{BASE_URL_FOR_HELP_LINK}#{class_name_to_param(warning.smell_type)}"
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def class_name_to_param(name)
|
|
64
|
-
name.split(/(?=[A-Z])/).join('-')
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
module Reek
|
|
2
|
-
module Cli
|
|
3
|
-
module Report
|
|
4
|
-
module HeadingFormatter
|
|
5
|
-
#
|
|
6
|
-
# Base class for heading formatters.
|
|
7
|
-
# Is responsible for formatting the heading emitted for each examiner
|
|
8
|
-
#
|
|
9
|
-
class Base
|
|
10
|
-
attr_reader :report_formatter
|
|
11
|
-
|
|
12
|
-
def initialize(report_formatter)
|
|
13
|
-
@report_formatter = report_formatter
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def header(examiner)
|
|
17
|
-
if show_header?(examiner)
|
|
18
|
-
report_formatter.header examiner
|
|
19
|
-
else
|
|
20
|
-
''
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
#
|
|
26
|
-
# Lists out each examiner, even if it has no smell
|
|
27
|
-
#
|
|
28
|
-
class Verbose < Base
|
|
29
|
-
def show_header?(_examiner)
|
|
30
|
-
true
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
#
|
|
35
|
-
# Lists only smelly examiners
|
|
36
|
-
#
|
|
37
|
-
class Quiet < Base
|
|
38
|
-
def show_header?(examiner)
|
|
39
|
-
examiner.smelly?
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
module Reek
|
|
2
|
-
module Cli
|
|
3
|
-
module Report
|
|
4
|
-
#
|
|
5
|
-
# Formats the location of a warning as an empty string.
|
|
6
|
-
#
|
|
7
|
-
module BlankLocationFormatter
|
|
8
|
-
def self.format(_warning)
|
|
9
|
-
''
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
# Formats the location of a warning as an array of line numbers.
|
|
15
|
-
#
|
|
16
|
-
module DefaultLocationFormatter
|
|
17
|
-
def self.format(warning)
|
|
18
|
-
"#{warning.lines.inspect}:"
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
#
|
|
23
|
-
# Formats the location of a warning as a combination of source file name
|
|
24
|
-
# and line number. In this format, it is not possible to show more than
|
|
25
|
-
# one line number, so the first number is displayed.
|
|
26
|
-
#
|
|
27
|
-
module SingleLineLocationFormatter
|
|
28
|
-
def self.format(warning)
|
|
29
|
-
"#{warning.source}:#{warning.lines.first}: "
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
require 'rainbow'
|
|
2
|
-
require 'json'
|
|
3
|
-
|
|
4
|
-
module Reek
|
|
5
|
-
module Cli
|
|
6
|
-
module Report
|
|
7
|
-
#
|
|
8
|
-
# A report that contains the smells and smell counts following source code analysis.
|
|
9
|
-
#
|
|
10
|
-
class Base
|
|
11
|
-
DEFAULT_FORMAT = :text
|
|
12
|
-
NO_WARNINGS_COLOR = :green
|
|
13
|
-
WARNINGS_COLOR = :red
|
|
14
|
-
|
|
15
|
-
def initialize(options = {})
|
|
16
|
-
@examiners = []
|
|
17
|
-
@total_smell_count = 0
|
|
18
|
-
@options = options
|
|
19
|
-
@warning_formatter = options.fetch :warning_formatter, SimpleWarningFormatter.new
|
|
20
|
-
@report_formatter = options.fetch :report_formatter, Formatter
|
|
21
|
-
@sort_by_issue_count = options.fetch :sort_by_issue_count, false
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def add_examiner(examiner)
|
|
25
|
-
@total_smell_count += examiner.smells_count
|
|
26
|
-
@examiners << examiner
|
|
27
|
-
self
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def smells?
|
|
31
|
-
@total_smell_count > 0
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def smells
|
|
35
|
-
@examiners.map(&:smells).flatten
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
#
|
|
40
|
-
# Generates a sorted, text summary of smells in examiners
|
|
41
|
-
#
|
|
42
|
-
class TextReport < Base
|
|
43
|
-
def show
|
|
44
|
-
sort_examiners if smells?
|
|
45
|
-
display_summary
|
|
46
|
-
display_total_smell_count
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def smells
|
|
50
|
-
@examiners.each_with_object([]) do |examiner, result|
|
|
51
|
-
result << summarize_single_examiner(examiner)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
private
|
|
56
|
-
|
|
57
|
-
def display_summary
|
|
58
|
-
smells.reject(&:empty?).each { |smell| puts smell }
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def display_total_smell_count
|
|
62
|
-
return unless @examiners.size > 1
|
|
63
|
-
print total_smell_count_message
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def summarize_single_examiner(examiner)
|
|
67
|
-
result = heading_formatter.header(examiner)
|
|
68
|
-
if examiner.smelly?
|
|
69
|
-
formatted_list = @report_formatter.format_list(examiner.smells,
|
|
70
|
-
@warning_formatter)
|
|
71
|
-
result += ":\n#{formatted_list}"
|
|
72
|
-
end
|
|
73
|
-
result
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def sort_examiners
|
|
77
|
-
@examiners.sort_by!(&:smells_count).reverse! if @sort_by_issue_count
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def total_smell_count_message
|
|
81
|
-
colour = smells? ? WARNINGS_COLOR : NO_WARNINGS_COLOR
|
|
82
|
-
s = @total_smell_count == 1 ? '' : 's'
|
|
83
|
-
Rainbow("#{@total_smell_count} total warning#{s}\n").color(colour)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def heading_formatter
|
|
87
|
-
@heading_formatter ||=
|
|
88
|
-
@options.fetch(:heading_formatter, HeadingFormatter::Quiet).new(@report_formatter)
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
#
|
|
93
|
-
# Displays a list of smells in YAML format
|
|
94
|
-
# YAML with empty array for 0 smells
|
|
95
|
-
class YamlReport < Base
|
|
96
|
-
def show
|
|
97
|
-
print smells.map(&:yaml_hash).to_yaml
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
#
|
|
102
|
-
# Displays a list of smells in JSON format
|
|
103
|
-
# JSON with empty array for 0 smells
|
|
104
|
-
class JsonReport < Base
|
|
105
|
-
def show
|
|
106
|
-
print ::JSON.generate(
|
|
107
|
-
smells.map do |smell|
|
|
108
|
-
smell.yaml_hash(@warning_formatter)
|
|
109
|
-
end
|
|
110
|
-
)
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
#
|
|
115
|
-
# Saves the report as a HTML file
|
|
116
|
-
#
|
|
117
|
-
class HtmlReport < Base
|
|
118
|
-
require 'erb'
|
|
119
|
-
|
|
120
|
-
def show
|
|
121
|
-
path = File.expand_path('../../../../../assets/html_output.html.erb',
|
|
122
|
-
__FILE__)
|
|
123
|
-
File.open('reek.html', 'w+') do |file|
|
|
124
|
-
file.puts ERB.new(File.read(path)).result(binding)
|
|
125
|
-
end
|
|
126
|
-
print("Html file saved\n")
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
end
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
require 'reek/core/method_context'
|
|
2
|
-
require 'reek/core/module_context'
|
|
3
|
-
require 'reek/core/stop_context'
|
|
4
|
-
require 'reek/core/singleton_method_context'
|
|
5
|
-
|
|
6
|
-
module Reek
|
|
7
|
-
module Core
|
|
8
|
-
#
|
|
9
|
-
# Traverses a Sexp abstract syntax tree and fires events whenever
|
|
10
|
-
# it encounters specific node types.
|
|
11
|
-
#
|
|
12
|
-
# SMELL: This class is responsible for counting statements and for feeding
|
|
13
|
-
# each context to the smell repository.
|
|
14
|
-
class TreeWalker
|
|
15
|
-
def initialize(smell_repository = Core::SmellRepository.new)
|
|
16
|
-
@smell_repository = smell_repository
|
|
17
|
-
@element = StopContext.new
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def process(exp)
|
|
21
|
-
context_processor = "process_#{exp.type}"
|
|
22
|
-
if context_processor_exists?(context_processor)
|
|
23
|
-
send(context_processor, exp)
|
|
24
|
-
else
|
|
25
|
-
process_default exp
|
|
26
|
-
end
|
|
27
|
-
@element
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def process_default(exp)
|
|
31
|
-
exp.children.each do |child|
|
|
32
|
-
process(child) if child.is_a? AST::Node
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def process_module(exp)
|
|
37
|
-
inside_new_context(ModuleContext, exp) do
|
|
38
|
-
process_default(exp)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
alias_method :process_class, :process_module
|
|
43
|
-
|
|
44
|
-
def process_def(exp)
|
|
45
|
-
inside_new_context(MethodContext, exp) do
|
|
46
|
-
count_clause(exp.body)
|
|
47
|
-
process_default(exp)
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def process_defs(exp)
|
|
52
|
-
inside_new_context(SingletonMethodContext, exp) do
|
|
53
|
-
count_clause(exp.body)
|
|
54
|
-
process_default(exp)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def process_args(_) end
|
|
59
|
-
|
|
60
|
-
#
|
|
61
|
-
# Recording of calls to methods and self
|
|
62
|
-
#
|
|
63
|
-
|
|
64
|
-
def process_send(exp)
|
|
65
|
-
@element.record_call_to(exp)
|
|
66
|
-
process_default(exp)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
alias_method :process_attrasgn, :process_send
|
|
70
|
-
alias_method :process_op_asgn, :process_send
|
|
71
|
-
|
|
72
|
-
def process_ivar(exp)
|
|
73
|
-
@element.record_use_of_self
|
|
74
|
-
process_default(exp)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
alias_method :process_ivasgn, :process_ivar
|
|
78
|
-
|
|
79
|
-
def process_self(_)
|
|
80
|
-
@element.record_use_of_self
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
alias_method :process_zsuper, :process_self
|
|
84
|
-
|
|
85
|
-
#
|
|
86
|
-
# Statement counting
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
def process_block(exp)
|
|
90
|
-
count_clause(exp.block)
|
|
91
|
-
process_default(exp)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def process_begin(exp)
|
|
95
|
-
count_statement_list(exp.children)
|
|
96
|
-
@element.count_statements(-1)
|
|
97
|
-
process_default(exp)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
alias_method :process_kwbegin, :process_begin
|
|
101
|
-
|
|
102
|
-
def process_if(exp)
|
|
103
|
-
count_clause(exp[2])
|
|
104
|
-
count_clause(exp[3])
|
|
105
|
-
@element.count_statements(-1)
|
|
106
|
-
process_default(exp)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def process_while(exp)
|
|
110
|
-
count_clause(exp[2])
|
|
111
|
-
@element.count_statements(-1)
|
|
112
|
-
process_default(exp)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
alias_method :process_until, :process_while
|
|
116
|
-
|
|
117
|
-
def process_for(exp)
|
|
118
|
-
count_clause(exp[3])
|
|
119
|
-
@element.count_statements(-1)
|
|
120
|
-
process_default(exp)
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def process_rescue(exp)
|
|
124
|
-
count_clause(exp[1])
|
|
125
|
-
@element.count_statements(-1)
|
|
126
|
-
process_default(exp)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def process_resbody(exp)
|
|
130
|
-
count_statement_list(exp[2..-1].compact)
|
|
131
|
-
process_default(exp)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def process_case(exp)
|
|
135
|
-
count_clause(exp.else_body)
|
|
136
|
-
@element.count_statements(-1)
|
|
137
|
-
process_default(exp)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def process_when(exp)
|
|
141
|
-
count_clause(exp.body)
|
|
142
|
-
process_default(exp)
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
private
|
|
146
|
-
|
|
147
|
-
def context_processor_exists?(name)
|
|
148
|
-
respond_to?(name)
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def count_clause(sexp)
|
|
152
|
-
@element.count_statements(1) if sexp
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def count_statement_list(statement_list)
|
|
156
|
-
@element.count_statements statement_list.length
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def inside_new_context(klass, exp)
|
|
160
|
-
scope = klass.new(@element, exp)
|
|
161
|
-
push(scope) do
|
|
162
|
-
yield
|
|
163
|
-
check_smells(exp.type)
|
|
164
|
-
end
|
|
165
|
-
scope
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def check_smells(type)
|
|
169
|
-
@smell_repository.examine(@element, type)
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def push(scope)
|
|
173
|
-
orig = @element
|
|
174
|
-
@element = scope
|
|
175
|
-
yield
|
|
176
|
-
@element = orig
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
end
|
data/lib/reek/smell_warning.rb
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
require 'forwardable'
|
|
2
|
-
|
|
3
|
-
module Reek
|
|
4
|
-
#
|
|
5
|
-
# Reports a warning that a smell has been found.
|
|
6
|
-
#
|
|
7
|
-
class SmellWarning
|
|
8
|
-
include Comparable
|
|
9
|
-
extend Forwardable
|
|
10
|
-
attr_accessor :smell_detector, :context, :lines, :message, :parameters
|
|
11
|
-
def_delegators :smell_detector, :smell_category, :smell_type, :source
|
|
12
|
-
|
|
13
|
-
def initialize(smell_detector, options = {})
|
|
14
|
-
self.smell_detector = smell_detector
|
|
15
|
-
self.context = options.fetch(:context, '').to_s
|
|
16
|
-
self.lines = options.fetch(:lines)
|
|
17
|
-
self.message = options.fetch(:message)
|
|
18
|
-
self.parameters = options.fetch(:parameters, {})
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def smell_classes
|
|
22
|
-
[smell_detector.smell_category, smell_detector.smell_type]
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def hash
|
|
26
|
-
sort_key.hash
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def <=>(other)
|
|
30
|
-
sort_key <=> other.sort_key
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def eql?(other)
|
|
34
|
-
(self <=> other) == 0
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def matches?(klass, other_parameters = {})
|
|
38
|
-
smell_classes.include?(klass.to_s) && common_parameters_equal?(other_parameters)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def report_on(listener)
|
|
42
|
-
listener.found_smell(self)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def yaml_hash(warning_formatter = nil)
|
|
46
|
-
result = {
|
|
47
|
-
'smell_category' => smell_detector.smell_category,
|
|
48
|
-
'smell_type' => smell_detector.smell_type,
|
|
49
|
-
'source' => smell_detector.source,
|
|
50
|
-
'context' => context,
|
|
51
|
-
'lines' => lines,
|
|
52
|
-
'message' => message
|
|
53
|
-
}
|
|
54
|
-
if warning_formatter.respond_to?(:explanatory_link)
|
|
55
|
-
result.merge!('wiki_link' => warning_formatter.explanatory_link(smell_detector))
|
|
56
|
-
end
|
|
57
|
-
parameters.each do |key, value|
|
|
58
|
-
result[key.to_s] = value
|
|
59
|
-
end
|
|
60
|
-
result
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
protected
|
|
64
|
-
|
|
65
|
-
def sort_key
|
|
66
|
-
[context, message, smell_category]
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def common_parameters_equal?(other_parameters)
|
|
70
|
-
other_parameters.keys.each do |key|
|
|
71
|
-
unless parameters.key?(key)
|
|
72
|
-
raise ArgumentError, "The parameter #{key} you want to check for doesn't exist"
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Why not check for strict parameter equality instead of just the common ones?
|
|
77
|
-
#
|
|
78
|
-
# In `self`, `parameters` might look like this: {:name=>"@other.thing", :count=>2}
|
|
79
|
-
# Coming from specs, 'other_parameters' might look like this, e.g.:
|
|
80
|
-
# {:name=>"@other.thing"}
|
|
81
|
-
# So in this spec we are just specifying the "name" parameter but not the "count".
|
|
82
|
-
# In order to allow for this kind of leniency we just test for common parameter equality,
|
|
83
|
-
# not for a strict one.
|
|
84
|
-
parameters.values_at(*other_parameters.keys) == other_parameters.values
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
data/lib/reek/source/ast_node.rb
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
require 'parser'
|
|
2
|
-
|
|
3
|
-
module Reek
|
|
4
|
-
module Source
|
|
5
|
-
# Base class for AST nodes extended with utility methods. Contains some
|
|
6
|
-
# methods to ease the transition from Sexp to AST::Node.
|
|
7
|
-
class AstNode < Parser::AST::Node
|
|
8
|
-
def initialize(type, children = [], options = {})
|
|
9
|
-
@comments = options.fetch(:comments, [])
|
|
10
|
-
super
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def comments
|
|
14
|
-
@comments.map(&:text).join("\n")
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# @deprecated
|
|
18
|
-
def [](index)
|
|
19
|
-
elements[index]
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def line
|
|
23
|
-
loc.line
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# @deprecated
|
|
27
|
-
def first
|
|
28
|
-
type
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
def elements
|
|
34
|
-
[type, *children]
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
require 'yaml'
|
|
2
|
-
|
|
3
|
-
module Reek
|
|
4
|
-
module Source
|
|
5
|
-
#
|
|
6
|
-
# A comment header from an abstract syntax tree; found directly above
|
|
7
|
-
# module, class and method definitions.
|
|
8
|
-
#
|
|
9
|
-
class CodeComment
|
|
10
|
-
CONFIG_REGEX = /:reek:(\w+)(:\s*\{.*?\})?/
|
|
11
|
-
|
|
12
|
-
def initialize(text)
|
|
13
|
-
@text = text.gsub(CONFIG_REGEX) do
|
|
14
|
-
add_to_config($1, $2)
|
|
15
|
-
''
|
|
16
|
-
end.gsub(/#/, '').gsub(/\n/, '').strip
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def config
|
|
20
|
-
@config ||= Hash.new { |hash, key| hash[key] = {} }
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def descriptive?
|
|
24
|
-
@text.split(/\s+/).length >= 2
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
protected
|
|
28
|
-
|
|
29
|
-
def add_to_config(smell, options)
|
|
30
|
-
options ||= ': { enabled: false }'
|
|
31
|
-
config.merge! YAML.load(smell.gsub(/(?:^|_)(.)/) { $1.upcase } + options)
|
|
32
|
-
# TODO: extend this to all configs -------------------^
|
|
33
|
-
# TODO: extend to allow configuration of whole smell class, not just subclass
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
require 'reek/source/source_code'
|
|
2
|
-
|
|
3
|
-
#
|
|
4
|
-
# Extensions to +File+ needed by Reek.
|
|
5
|
-
#
|
|
6
|
-
class File
|
|
7
|
-
#
|
|
8
|
-
# Creates a new +Source+ that assumes this File contains Ruby source
|
|
9
|
-
# code and prepares it to be examined for code smells.
|
|
10
|
-
#
|
|
11
|
-
# @return [Reek::Source::SourceFile]
|
|
12
|
-
#
|
|
13
|
-
def to_reek_source
|
|
14
|
-
Reek::Source::SourceFile.new(path)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
#
|
|
19
|
-
# Extensions to +IO+ needed by Reek.
|
|
20
|
-
#
|
|
21
|
-
class IO
|
|
22
|
-
#
|
|
23
|
-
# Creates a new +Source+ that assumes this IO stream contains Ruby source
|
|
24
|
-
# code and prepares it to be examined for code smells.
|
|
25
|
-
#
|
|
26
|
-
# @return [Reek::Source::SourceCode]
|
|
27
|
-
#
|
|
28
|
-
def to_reek_source(description = 'io')
|
|
29
|
-
Reek::Source::SourceCode.new(readlines.join, description)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
#
|
|
34
|
-
# Extensions to +String+ needed by Reek.
|
|
35
|
-
#
|
|
36
|
-
class String
|
|
37
|
-
#
|
|
38
|
-
# Creates a new +Source+ that assumes this string contains Ruby source
|
|
39
|
-
# code and prepares it to be examined for code smells.
|
|
40
|
-
#
|
|
41
|
-
# @return [Reek::Source::SourceCode]
|
|
42
|
-
#
|
|
43
|
-
def to_reek_source
|
|
44
|
-
Reek::Source::SourceCode.new(self, 'string')
|
|
45
|
-
end
|
|
46
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module Reek
|
|
3
|
-
module Source
|
|
4
|
-
#
|
|
5
|
-
# Locates references to the current object within a portion
|
|
6
|
-
# of an abstract syntax tree.
|
|
7
|
-
#
|
|
8
|
-
class ReferenceCollector
|
|
9
|
-
STOP_NODES = [:class, :module, :def, :defs]
|
|
10
|
-
|
|
11
|
-
def initialize(ast)
|
|
12
|
-
@ast = ast
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def num_refs_to_self
|
|
16
|
-
result = 0
|
|
17
|
-
[:self, :zsuper, :ivar, :ivasgn].each do |node_type|
|
|
18
|
-
@ast.look_for(node_type, STOP_NODES) { result += 1 }
|
|
19
|
-
end
|
|
20
|
-
@ast.look_for(:send, STOP_NODES) do |call|
|
|
21
|
-
result += 1 unless call.receiver
|
|
22
|
-
end
|
|
23
|
-
result
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|