reek 2.2.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|