reek 2.2.1 → 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/.rubocop.yml +1 -1
- data/.travis.yml +9 -4
- data/CHANGELOG +8 -0
- data/Gemfile +6 -4
- data/README.md +6 -0
- data/docs/API.md +51 -22
- data/docs/Configuration-Files.md +12 -1
- data/docs/Feature-Envy.md +30 -10
- data/docs/How-reek-works-internally.md +109 -39
- data/docs/RSpec-matchers.md +26 -22
- data/docs/Reek-Driven-Development.md +0 -8
- data/docs/Utility-Function.md +8 -10
- data/features/{ruby_api/api.feature → command_line_interface/basic_usage.feature} +2 -2
- data/features/programmatic_access.feature +21 -2
- data/features/samples.feature +3 -1
- data/lib/reek.rb +2 -2
- data/lib/reek/{core → ast}/ast_node_class_map.rb +8 -8
- data/lib/reek/{sexp/sexp_node.rb → ast/node.rb} +47 -6
- data/lib/reek/{core → ast}/object_refs.rb +2 -1
- data/lib/reek/{core → ast}/reference_collector.rb +2 -1
- data/lib/reek/{sexp → ast}/sexp_extensions.rb +10 -5
- data/lib/reek/{sexp → ast}/sexp_formatter.rb +7 -5
- data/lib/reek/cli/application.rb +1 -0
- data/lib/reek/cli/command.rb +1 -0
- data/lib/reek/cli/input.rb +4 -1
- data/lib/reek/cli/option_interpreter.rb +6 -4
- data/lib/reek/cli/options.rb +2 -1
- data/lib/reek/cli/reek_command.rb +3 -2
- data/lib/reek/cli/silencer.rb +1 -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 +17 -2
- data/lib/reek/configuration/configuration_file_finder.rb +1 -0
- data/lib/reek/{core → context}/code_context.rb +7 -5
- data/lib/reek/{core → context}/method_context.rb +5 -3
- data/lib/reek/{core → context}/module_context.rb +8 -3
- data/lib/reek/{core/stop_context.rb → context/root_context.rb} +4 -2
- data/lib/reek/{core → context}/singleton_method_context.rb +2 -1
- data/lib/reek/examiner.rb +82 -0
- 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.rb +24 -13
- data/lib/reek/smells/attribute.rb +6 -4
- data/lib/reek/smells/boolean_parameter.rb +3 -1
- data/lib/reek/smells/class_variable.rb +3 -1
- data/lib/reek/smells/control_parameter.rb +3 -1
- data/lib/reek/smells/data_clump.rb +3 -1
- data/lib/reek/smells/duplicate_method_call.rb +3 -1
- data/lib/reek/smells/feature_envy.rb +3 -1
- data/lib/reek/smells/irresponsible_module.rb +12 -7
- data/lib/reek/smells/long_parameter_list.rb +5 -3
- data/lib/reek/smells/long_yield_list.rb +3 -1
- data/lib/reek/smells/module_initialize.rb +3 -1
- data/lib/reek/smells/nested_iterators.rb +3 -1
- data/lib/reek/smells/nil_check.rb +3 -1
- data/lib/reek/smells/prima_donna_method.rb +3 -1
- data/lib/reek/smells/repeated_conditional.rb +5 -3
- data/lib/reek/{core → smells}/smell_configuration.rb +3 -1
- data/lib/reek/smells/smell_detector.rb +9 -7
- data/lib/reek/{core → smells}/smell_repository.rb +3 -2
- data/lib/reek/smells/smell_warning.rb +6 -4
- data/lib/reek/smells/too_many_instance_variables.rb +3 -1
- data/lib/reek/smells/too_many_methods.rb +3 -1
- data/lib/reek/smells/too_many_statements.rb +3 -1
- data/lib/reek/smells/uncommunicative_method_name.rb +3 -1
- data/lib/reek/smells/uncommunicative_module_name.rb +3 -1
- data/lib/reek/smells/uncommunicative_parameter_name.rb +3 -1
- data/lib/reek/smells/uncommunicative_variable_name.rb +3 -1
- data/lib/reek/smells/unused_parameters.rb +3 -1
- data/lib/reek/smells/utility_function.rb +5 -2
- data/lib/reek/source/source_code.rb +40 -9
- data/lib/reek/source/source_locator.rb +30 -12
- data/lib/reek/spec/should_reek.rb +5 -4
- data/lib/reek/spec/should_reek_of.rb +3 -2
- data/lib/reek/spec/should_reek_only_of.rb +5 -4
- data/lib/reek/tree_dresser.rb +32 -0
- data/lib/reek/tree_walker.rb +182 -0
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +3 -3
- data/spec/factories/factories.rb +2 -0
- data/spec/reek/{sexp/sexp_node_spec.rb → ast/node_spec.rb} +2 -2
- data/spec/reek/{core → ast}/object_refs_spec.rb +3 -3
- data/spec/reek/{core → ast}/reference_collector_spec.rb +2 -2
- data/spec/reek/{sexp → ast}/sexp_extensions_spec.rb +6 -16
- data/spec/reek/{sexp → ast}/sexp_formatter_spec.rb +2 -2
- data/spec/reek/cli/option_interpreter_spec.rb +2 -1
- data/spec/reek/{core → cli}/warning_collector_spec.rb +3 -3
- data/spec/reek/{core/code_comment_spec.rb → code_comment_spec.rb} +3 -3
- data/spec/reek/configuration/app_configuration_spec.rb +31 -18
- data/spec/reek/{core → context}/code_context_spec.rb +14 -15
- data/spec/reek/{core → context}/method_context_spec.rb +8 -8
- data/spec/reek/{core → context}/module_context_spec.rb +4 -4
- data/spec/reek/context/root_context_spec.rb +14 -0
- data/spec/reek/{core → context}/singleton_method_context_spec.rb +4 -4
- data/spec/reek/{core/examiner_spec.rb → examiner_spec.rb} +3 -42
- data/spec/reek/{cli → report}/html_report_spec.rb +5 -5
- data/spec/reek/report/json_report_spec.rb +20 -0
- data/spec/reek/{cli → report}/text_report_spec.rb +14 -14
- data/spec/reek/{cli → report}/xml_report_spec.rb +7 -7
- data/spec/reek/report/yaml_report_spec.rb +20 -0
- data/spec/reek/smells/attribute_spec.rb +2 -1
- data/spec/reek/smells/boolean_parameter_spec.rb +1 -1
- data/spec/reek/smells/class_variable_spec.rb +5 -5
- data/spec/reek/smells/control_parameter_spec.rb +1 -1
- data/spec/reek/smells/data_clump_spec.rb +1 -1
- data/spec/reek/smells/duplicate_method_call_spec.rb +3 -3
- data/spec/reek/smells/feature_envy_spec.rb +1 -1
- data/spec/reek/smells/irresponsible_module_spec.rb +24 -28
- data/spec/reek/smells/long_parameter_list_spec.rb +2 -2
- data/spec/reek/smells/long_yield_list_spec.rb +2 -2
- data/spec/reek/smells/nested_iterators_spec.rb +1 -1
- data/spec/reek/smells/nil_check_spec.rb +2 -2
- data/spec/reek/smells/prima_donna_method_spec.rb +3 -3
- data/spec/reek/smells/repeated_conditional_spec.rb +6 -6
- data/spec/reek/{core → smells}/smell_configuration_spec.rb +4 -4
- data/spec/reek/smells/smell_detector_shared.rb +2 -2
- data/spec/reek/{core → smells}/smell_repository_spec.rb +5 -4
- data/spec/reek/smells/too_many_instance_variables_spec.rb +1 -1
- data/spec/reek/smells/too_many_methods_spec.rb +4 -4
- data/spec/reek/smells/too_many_statements_spec.rb +2 -2
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +1 -1
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +4 -4
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +2 -2
- data/spec/reek/smells/uncommunicative_variable_name_spec.rb +3 -3
- data/spec/reek/smells/utility_function_spec.rb +23 -1
- data/spec/reek/source/source_code_spec.rb +1 -1
- data/spec/reek/source/source_locator_spec.rb +30 -0
- data/spec/reek/spec/should_reek_of_spec.rb +0 -17
- data/spec/reek/spec/should_reek_spec.rb +0 -25
- data/spec/reek/tree_dresser_spec.rb +16 -0
- data/spec/reek/{core/tree_walker_spec.rb → tree_walker_spec.rb} +5 -5
- data/spec/samples/{simple_configuration.reek → configuration/simple_configuration.reek} +0 -0
- data/spec/samples/configuration/with_excluded_paths.reek +4 -0
- 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/spec_helper.rb +6 -7
- data/tasks/develop.rake +2 -2
- metadata +71 -69
- 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 -191
- data/lib/reek/core/ast_node.rb +0 -38
- data/lib/reek/core/code_comment.rb +0 -37
- data/lib/reek/core/examiner.rb +0 -85
- data/lib/reek/core/tree_dresser.rb +0 -24
- data/lib/reek/core/tree_walker.rb +0 -180
- data/lib/reek/source/source_repository.rb +0 -43
- data/spec/reek/cli/json_report_spec.rb +0 -20
- data/spec/reek/cli/yaml_report_spec.rb +0 -20
- data/spec/reek/core/object_source_spec.rb +0 -18
- data/spec/reek/core/stop_context_spec.rb +0 -14
- data/spec/reek/core/tree_dresser_spec.rb +0 -16
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Reek
|
4
|
+
#
|
5
|
+
# A comment header from an abstract syntax tree; found directly above
|
6
|
+
# module, class and method definitions.
|
7
|
+
#
|
8
|
+
# @api private
|
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
|
@@ -1,13 +1,18 @@
|
|
1
1
|
require_relative './configuration_file_finder'
|
2
2
|
|
3
3
|
module Reek
|
4
|
+
# @api private
|
4
5
|
module Configuration
|
6
|
+
# @api private
|
5
7
|
class ConfigFileException < StandardError; end
|
6
8
|
#
|
7
9
|
# Reek's singleton configuration instance.
|
8
10
|
#
|
11
|
+
# @api private
|
9
12
|
module AppConfiguration
|
10
|
-
|
13
|
+
NON_SMELL_TYPE_KEYS = %w(exclude_paths)
|
14
|
+
EXCLUDE_PATHS_KEY = 'exclude_paths'
|
15
|
+
@configuration = {}
|
11
16
|
@has_been_initialized = false
|
12
17
|
|
13
18
|
class << self
|
@@ -24,7 +29,7 @@ module Reek
|
|
24
29
|
# Let users call this method directly without having initialized AppConfiguration before
|
25
30
|
# and if they do, initialize it without application context
|
26
31
|
initialize_with(nil) unless @has_been_initialized
|
27
|
-
|
32
|
+
for_smell_types.each do |klass_name, config|
|
28
33
|
klass = load_smell_type(klass_name)
|
29
34
|
smell_repository.configure(klass, config) if klass
|
30
35
|
end
|
@@ -49,8 +54,18 @@ module Reek
|
|
49
54
|
@configuration.clear
|
50
55
|
end
|
51
56
|
|
57
|
+
def exclude_paths
|
58
|
+
@exclude_paths ||= @configuration.
|
59
|
+
fetch(EXCLUDE_PATHS_KEY, []).
|
60
|
+
map { |path| path.chomp('/') }
|
61
|
+
end
|
62
|
+
|
52
63
|
private
|
53
64
|
|
65
|
+
def for_smell_types
|
66
|
+
@configuration.reject { |key, _value| NON_SMELL_TYPE_KEYS.include?(key) }
|
67
|
+
end
|
68
|
+
|
54
69
|
def load_smell_type(name)
|
55
70
|
Reek::Smells.const_get(name)
|
56
71
|
rescue NameError
|
@@ -1,12 +1,14 @@
|
|
1
|
+
require_relative '../code_comment'
|
1
2
|
|
2
3
|
module Reek
|
3
|
-
module
|
4
|
+
module Context
|
4
5
|
#
|
5
6
|
# Superclass for all types of source code context. Each instance represents
|
6
7
|
# a code element of some kind, and each provides behaviour relevant to that
|
7
8
|
# code element. CodeContexts form a tree in the same way the code does,
|
8
9
|
# with each context holding a reference to a unique outer context.
|
9
10
|
#
|
11
|
+
# @api private
|
10
12
|
class CodeContext
|
11
13
|
attr_reader :exp
|
12
14
|
|
@@ -21,9 +23,9 @@ module Reek
|
|
21
23
|
#
|
22
24
|
# class Omg; def foo(x); puts x; end; end
|
23
25
|
#
|
24
|
-
# the first time this is instantianted from TreeWalker `context` is a
|
26
|
+
# the first time this is instantianted from TreeWalker `context` is a RootContext:
|
25
27
|
#
|
26
|
-
# #<Reek::
|
28
|
+
# #<Reek::Context::RootContext:0x00000002231098 @name="">
|
27
29
|
#
|
28
30
|
# and `exp` looks like this:
|
29
31
|
#
|
@@ -37,7 +39,7 @@ module Reek
|
|
37
39
|
#
|
38
40
|
# The next time we instantiate a CodeContext via TreeWalker `context` would be:
|
39
41
|
#
|
40
|
-
# Reek::
|
42
|
+
# Reek::Context::ModuleContext
|
41
43
|
#
|
42
44
|
# and `exp` is:
|
43
45
|
#
|
@@ -97,7 +99,7 @@ module Reek
|
|
97
99
|
|
98
100
|
def config
|
99
101
|
@config ||= if @exp
|
100
|
-
|
102
|
+
CodeComment.new(@exp.full_comment || '').config
|
101
103
|
else
|
102
104
|
{}
|
103
105
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
require_relative 'code_context'
|
2
|
-
require_relative 'object_refs'
|
2
|
+
require_relative '../ast/object_refs'
|
3
3
|
|
4
4
|
module Reek
|
5
|
-
module
|
5
|
+
module Context
|
6
6
|
#
|
7
7
|
# The parameters in a method's definition.
|
8
8
|
#
|
9
|
+
# @api private
|
9
10
|
module MethodParameters
|
10
11
|
def default_assignments
|
11
12
|
result = []
|
@@ -19,6 +20,7 @@ module Reek
|
|
19
20
|
#
|
20
21
|
# A context wrapper for any method definition found in a syntax tree.
|
21
22
|
#
|
23
|
+
# @api private
|
22
24
|
class MethodContext < CodeContext
|
23
25
|
attr_reader :parameters
|
24
26
|
attr_reader :refs
|
@@ -29,7 +31,7 @@ module Reek
|
|
29
31
|
@parameters = exp.parameters.dup
|
30
32
|
@parameters.extend MethodParameters
|
31
33
|
@num_statements = 0
|
32
|
-
@refs = ObjectRefs.new
|
34
|
+
@refs = AST::ObjectRefs.new
|
33
35
|
end
|
34
36
|
|
35
37
|
def count_statements(num)
|
@@ -1,20 +1,25 @@
|
|
1
1
|
require_relative 'code_context'
|
2
|
-
require_relative '../
|
2
|
+
require_relative '../ast/sexp_formatter'
|
3
3
|
|
4
4
|
module Reek
|
5
|
-
module
|
5
|
+
module Context
|
6
6
|
#
|
7
7
|
# A context wrapper for any module found in a syntax tree.
|
8
8
|
#
|
9
|
+
# @api private
|
9
10
|
class ModuleContext < CodeContext
|
10
11
|
def initialize(outer, exp)
|
11
12
|
super(outer, exp)
|
12
|
-
@name =
|
13
|
+
@name = AST::SexpFormatter.format(exp.children.first)
|
13
14
|
end
|
14
15
|
|
15
16
|
def node_instance_methods
|
16
17
|
local_nodes(:def)
|
17
18
|
end
|
19
|
+
|
20
|
+
def descriptively_commented?
|
21
|
+
CodeComment.new(exp.leading_comment).descriptive?
|
22
|
+
end
|
18
23
|
end
|
19
24
|
end
|
20
25
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require_relative 'method_context'
|
2
2
|
|
3
3
|
module Reek
|
4
|
-
module
|
4
|
+
module Context
|
5
5
|
#
|
6
6
|
# A context wrapper for any singleton method definition found in a syntax tree.
|
7
7
|
#
|
8
|
+
# @api private
|
8
9
|
class SingletonMethodContext < MethodContext
|
9
10
|
def envious_receivers
|
10
11
|
[]
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# NOTE: tree_walker is required first to ensure unparser is required before
|
2
|
+
# parser. This prevents a potentially incompatible version of parser from being
|
3
|
+
# loaded first. This is only relevant when running bin/reek straight from a
|
4
|
+
# checkout directory without using Bundler.
|
5
|
+
#
|
6
|
+
# See also https://github.com/troessner/reek/pull/468
|
7
|
+
require_relative 'tree_walker'
|
8
|
+
require_relative 'source/source_code'
|
9
|
+
require_relative 'cli/warning_collector'
|
10
|
+
require_relative 'smells/smell_repository'
|
11
|
+
|
12
|
+
module Reek
|
13
|
+
#
|
14
|
+
# Applies all available smell detectors to a source.
|
15
|
+
#
|
16
|
+
class Examiner
|
17
|
+
#
|
18
|
+
# Creates an Examiner which scans the given +source+ for code smells.
|
19
|
+
#
|
20
|
+
# @param source [File, IO, String]
|
21
|
+
# If +source+ is a String it is assumed to be Ruby source code;
|
22
|
+
# if it is a File or IO, it is opened and Ruby source code is read from it;
|
23
|
+
#
|
24
|
+
# @param smell_types_to_filter_by [Array<String>]
|
25
|
+
# List of smell types to filter by.
|
26
|
+
#
|
27
|
+
def initialize(source, smell_types_to_filter_by = [])
|
28
|
+
@source = Source::SourceCode.from(source)
|
29
|
+
@collector = CLI::WarningCollector.new
|
30
|
+
@smell_types = eligible_smell_types(smell_types_to_filter_by)
|
31
|
+
|
32
|
+
run
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# @return [String] description of the source being analysed
|
37
|
+
#
|
38
|
+
def description
|
39
|
+
@description ||= @source.description
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# @return [Array<SmellWarning>] the smells found in the source
|
44
|
+
#
|
45
|
+
def smells
|
46
|
+
@smells ||= @collector.warnings
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# @return [Integer] the number of smells found in the source
|
51
|
+
#
|
52
|
+
def smells_count
|
53
|
+
smells.length
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# @return [Boolean] true if and only if there are code smells in the source.
|
58
|
+
#
|
59
|
+
def smelly?
|
60
|
+
!smells.empty?
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def run
|
66
|
+
smell_repository = Smells::SmellRepository.new(description, @smell_types)
|
67
|
+
syntax_tree = @source.syntax_tree
|
68
|
+
TreeWalker.new(smell_repository).process(syntax_tree) if syntax_tree
|
69
|
+
smell_repository.report_on(@collector)
|
70
|
+
end
|
71
|
+
|
72
|
+
def eligible_smell_types(smell_types_to_filter_by = [])
|
73
|
+
if smell_types_to_filter_by.any?
|
74
|
+
Smells::SmellRepository.smell_types.select do |klass|
|
75
|
+
smell_types_to_filter_by.include? klass.smell_type
|
76
|
+
end
|
77
|
+
else
|
78
|
+
Smells::SmellRepository.smell_types
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative 'location_formatter'
|
2
|
+
|
3
|
+
module Reek
|
4
|
+
module Report
|
5
|
+
#
|
6
|
+
# Formatter handling the formatting of the report at large.
|
7
|
+
# Formatting of the individual warnings is handled by the
|
8
|
+
# passed-in warning formatter.
|
9
|
+
#
|
10
|
+
# @api private
|
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
|
+
# @api private
|
32
|
+
class SimpleWarningFormatter
|
33
|
+
def initialize(location_formatter = BlankLocationFormatter)
|
34
|
+
@location_formatter = location_formatter
|
35
|
+
end
|
36
|
+
|
37
|
+
def format(warning)
|
38
|
+
"#{@location_formatter.format(warning)}#{base_format(warning)}"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def base_format(warning)
|
44
|
+
"#{warning.context} #{warning.message} (#{warning.smell_type})"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Formatter that adds a link to the wiki to the basic message from
|
50
|
+
# SimpleWarningFormatter.
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
class WikiLinkWarningFormatter < SimpleWarningFormatter
|
54
|
+
BASE_URL_FOR_HELP_LINK = 'https://github.com/troessner/reek/wiki/'
|
55
|
+
|
56
|
+
def format(warning)
|
57
|
+
"#{super} " \
|
58
|
+
"[#{explanatory_link(warning)}]"
|
59
|
+
end
|
60
|
+
|
61
|
+
def explanatory_link(warning)
|
62
|
+
"#{BASE_URL_FOR_HELP_LINK}#{class_name_to_param(warning.smell_type)}"
|
63
|
+
end
|
64
|
+
|
65
|
+
def class_name_to_param(name)
|
66
|
+
name.split(/(?=[A-Z])/).join('-')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Reek
|
2
|
+
module Report
|
3
|
+
# @api private
|
4
|
+
module HeadingFormatter
|
5
|
+
#
|
6
|
+
# Base class for heading formatters.
|
7
|
+
# Is responsible for formatting the heading emitted for each examiner
|
8
|
+
#
|
9
|
+
# @abstract Override {#show_header?} to implement a heading formatter.
|
10
|
+
class Base
|
11
|
+
attr_reader :report_formatter
|
12
|
+
|
13
|
+
def initialize(report_formatter)
|
14
|
+
@report_formatter = report_formatter
|
15
|
+
end
|
16
|
+
|
17
|
+
def header(examiner)
|
18
|
+
if show_header?(examiner)
|
19
|
+
report_formatter.header examiner
|
20
|
+
else
|
21
|
+
''
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Lists out each examiner, even if it has no smell
|
28
|
+
#
|
29
|
+
class Verbose < Base
|
30
|
+
def show_header?(_examiner)
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Lists only smelly examiners
|
37
|
+
#
|
38
|
+
class Quiet < Base
|
39
|
+
def show_header?(examiner)
|
40
|
+
examiner.smelly?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Reek
|
2
|
+
module Report
|
3
|
+
#
|
4
|
+
# Formats the location of a warning as an empty string.
|
5
|
+
#
|
6
|
+
# @api private
|
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
|
+
# @api private
|
17
|
+
module DefaultLocationFormatter
|
18
|
+
def self.format(warning)
|
19
|
+
"#{warning.lines.inspect}:"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Formats the location of a warning as a combination of source file name
|
25
|
+
# and line number. In this format, it is not possible to show more than
|
26
|
+
# one line number, so the first number is displayed.
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
module SingleLineLocationFormatter
|
30
|
+
def self.format(warning)
|
31
|
+
"#{warning.source}:#{warning.lines.first}: "
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|