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.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +9 -4
  4. data/CHANGELOG +8 -0
  5. data/Gemfile +6 -4
  6. data/README.md +6 -0
  7. data/docs/API.md +51 -22
  8. data/docs/Configuration-Files.md +12 -1
  9. data/docs/Feature-Envy.md +30 -10
  10. data/docs/How-reek-works-internally.md +109 -39
  11. data/docs/RSpec-matchers.md +26 -22
  12. data/docs/Reek-Driven-Development.md +0 -8
  13. data/docs/Utility-Function.md +8 -10
  14. data/features/{ruby_api/api.feature → command_line_interface/basic_usage.feature} +2 -2
  15. data/features/programmatic_access.feature +21 -2
  16. data/features/samples.feature +3 -1
  17. data/lib/reek.rb +2 -2
  18. data/lib/reek/{core → ast}/ast_node_class_map.rb +8 -8
  19. data/lib/reek/{sexp/sexp_node.rb → ast/node.rb} +47 -6
  20. data/lib/reek/{core → ast}/object_refs.rb +2 -1
  21. data/lib/reek/{core → ast}/reference_collector.rb +2 -1
  22. data/lib/reek/{sexp → ast}/sexp_extensions.rb +10 -5
  23. data/lib/reek/{sexp → ast}/sexp_formatter.rb +7 -5
  24. data/lib/reek/cli/application.rb +1 -0
  25. data/lib/reek/cli/command.rb +1 -0
  26. data/lib/reek/cli/input.rb +4 -1
  27. data/lib/reek/cli/option_interpreter.rb +6 -4
  28. data/lib/reek/cli/options.rb +2 -1
  29. data/lib/reek/cli/reek_command.rb +3 -2
  30. data/lib/reek/cli/silencer.rb +1 -0
  31. data/lib/reek/{core → cli}/warning_collector.rb +2 -1
  32. data/lib/reek/code_comment.rb +36 -0
  33. data/lib/reek/configuration/app_configuration.rb +17 -2
  34. data/lib/reek/configuration/configuration_file_finder.rb +1 -0
  35. data/lib/reek/{core → context}/code_context.rb +7 -5
  36. data/lib/reek/{core → context}/method_context.rb +5 -3
  37. data/lib/reek/{core → context}/module_context.rb +8 -3
  38. data/lib/reek/{core/stop_context.rb → context/root_context.rb} +4 -2
  39. data/lib/reek/{core → context}/singleton_method_context.rb +2 -1
  40. data/lib/reek/examiner.rb +82 -0
  41. data/lib/reek/report/formatter.rb +70 -0
  42. data/lib/reek/report/heading_formatter.rb +45 -0
  43. data/lib/reek/report/location_formatter.rb +35 -0
  44. data/lib/reek/report/report.rb +198 -0
  45. data/lib/reek/smells.rb +24 -13
  46. data/lib/reek/smells/attribute.rb +6 -4
  47. data/lib/reek/smells/boolean_parameter.rb +3 -1
  48. data/lib/reek/smells/class_variable.rb +3 -1
  49. data/lib/reek/smells/control_parameter.rb +3 -1
  50. data/lib/reek/smells/data_clump.rb +3 -1
  51. data/lib/reek/smells/duplicate_method_call.rb +3 -1
  52. data/lib/reek/smells/feature_envy.rb +3 -1
  53. data/lib/reek/smells/irresponsible_module.rb +12 -7
  54. data/lib/reek/smells/long_parameter_list.rb +5 -3
  55. data/lib/reek/smells/long_yield_list.rb +3 -1
  56. data/lib/reek/smells/module_initialize.rb +3 -1
  57. data/lib/reek/smells/nested_iterators.rb +3 -1
  58. data/lib/reek/smells/nil_check.rb +3 -1
  59. data/lib/reek/smells/prima_donna_method.rb +3 -1
  60. data/lib/reek/smells/repeated_conditional.rb +5 -3
  61. data/lib/reek/{core → smells}/smell_configuration.rb +3 -1
  62. data/lib/reek/smells/smell_detector.rb +9 -7
  63. data/lib/reek/{core → smells}/smell_repository.rb +3 -2
  64. data/lib/reek/smells/smell_warning.rb +6 -4
  65. data/lib/reek/smells/too_many_instance_variables.rb +3 -1
  66. data/lib/reek/smells/too_many_methods.rb +3 -1
  67. data/lib/reek/smells/too_many_statements.rb +3 -1
  68. data/lib/reek/smells/uncommunicative_method_name.rb +3 -1
  69. data/lib/reek/smells/uncommunicative_module_name.rb +3 -1
  70. data/lib/reek/smells/uncommunicative_parameter_name.rb +3 -1
  71. data/lib/reek/smells/uncommunicative_variable_name.rb +3 -1
  72. data/lib/reek/smells/unused_parameters.rb +3 -1
  73. data/lib/reek/smells/utility_function.rb +5 -2
  74. data/lib/reek/source/source_code.rb +40 -9
  75. data/lib/reek/source/source_locator.rb +30 -12
  76. data/lib/reek/spec/should_reek.rb +5 -4
  77. data/lib/reek/spec/should_reek_of.rb +3 -2
  78. data/lib/reek/spec/should_reek_only_of.rb +5 -4
  79. data/lib/reek/tree_dresser.rb +32 -0
  80. data/lib/reek/tree_walker.rb +182 -0
  81. data/lib/reek/version.rb +1 -1
  82. data/reek.gemspec +3 -3
  83. data/spec/factories/factories.rb +2 -0
  84. data/spec/reek/{sexp/sexp_node_spec.rb → ast/node_spec.rb} +2 -2
  85. data/spec/reek/{core → ast}/object_refs_spec.rb +3 -3
  86. data/spec/reek/{core → ast}/reference_collector_spec.rb +2 -2
  87. data/spec/reek/{sexp → ast}/sexp_extensions_spec.rb +6 -16
  88. data/spec/reek/{sexp → ast}/sexp_formatter_spec.rb +2 -2
  89. data/spec/reek/cli/option_interpreter_spec.rb +2 -1
  90. data/spec/reek/{core → cli}/warning_collector_spec.rb +3 -3
  91. data/spec/reek/{core/code_comment_spec.rb → code_comment_spec.rb} +3 -3
  92. data/spec/reek/configuration/app_configuration_spec.rb +31 -18
  93. data/spec/reek/{core → context}/code_context_spec.rb +14 -15
  94. data/spec/reek/{core → context}/method_context_spec.rb +8 -8
  95. data/spec/reek/{core → context}/module_context_spec.rb +4 -4
  96. data/spec/reek/context/root_context_spec.rb +14 -0
  97. data/spec/reek/{core → context}/singleton_method_context_spec.rb +4 -4
  98. data/spec/reek/{core/examiner_spec.rb → examiner_spec.rb} +3 -42
  99. data/spec/reek/{cli → report}/html_report_spec.rb +5 -5
  100. data/spec/reek/report/json_report_spec.rb +20 -0
  101. data/spec/reek/{cli → report}/text_report_spec.rb +14 -14
  102. data/spec/reek/{cli → report}/xml_report_spec.rb +7 -7
  103. data/spec/reek/report/yaml_report_spec.rb +20 -0
  104. data/spec/reek/smells/attribute_spec.rb +2 -1
  105. data/spec/reek/smells/boolean_parameter_spec.rb +1 -1
  106. data/spec/reek/smells/class_variable_spec.rb +5 -5
  107. data/spec/reek/smells/control_parameter_spec.rb +1 -1
  108. data/spec/reek/smells/data_clump_spec.rb +1 -1
  109. data/spec/reek/smells/duplicate_method_call_spec.rb +3 -3
  110. data/spec/reek/smells/feature_envy_spec.rb +1 -1
  111. data/spec/reek/smells/irresponsible_module_spec.rb +24 -28
  112. data/spec/reek/smells/long_parameter_list_spec.rb +2 -2
  113. data/spec/reek/smells/long_yield_list_spec.rb +2 -2
  114. data/spec/reek/smells/nested_iterators_spec.rb +1 -1
  115. data/spec/reek/smells/nil_check_spec.rb +2 -2
  116. data/spec/reek/smells/prima_donna_method_spec.rb +3 -3
  117. data/spec/reek/smells/repeated_conditional_spec.rb +6 -6
  118. data/spec/reek/{core → smells}/smell_configuration_spec.rb +4 -4
  119. data/spec/reek/smells/smell_detector_shared.rb +2 -2
  120. data/spec/reek/{core → smells}/smell_repository_spec.rb +5 -4
  121. data/spec/reek/smells/too_many_instance_variables_spec.rb +1 -1
  122. data/spec/reek/smells/too_many_methods_spec.rb +4 -4
  123. data/spec/reek/smells/too_many_statements_spec.rb +2 -2
  124. data/spec/reek/smells/uncommunicative_method_name_spec.rb +1 -1
  125. data/spec/reek/smells/uncommunicative_module_name_spec.rb +4 -4
  126. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +2 -2
  127. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +3 -3
  128. data/spec/reek/smells/utility_function_spec.rb +23 -1
  129. data/spec/reek/source/source_code_spec.rb +1 -1
  130. data/spec/reek/source/source_locator_spec.rb +30 -0
  131. data/spec/reek/spec/should_reek_of_spec.rb +0 -17
  132. data/spec/reek/spec/should_reek_spec.rb +0 -25
  133. data/spec/reek/tree_dresser_spec.rb +16 -0
  134. data/spec/reek/{core/tree_walker_spec.rb → tree_walker_spec.rb} +5 -5
  135. data/spec/samples/{simple_configuration.reek → configuration/simple_configuration.reek} +0 -0
  136. data/spec/samples/configuration/with_excluded_paths.reek +4 -0
  137. data/spec/samples/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb +5 -0
  138. data/spec/samples/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb +2 -0
  139. data/spec/samples/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb +6 -0
  140. data/spec/spec_helper.rb +6 -7
  141. data/tasks/develop.rake +2 -2
  142. metadata +71 -69
  143. data/lib/reek/cli/report/formatter.rb +0 -69
  144. data/lib/reek/cli/report/heading_formatter.rb +0 -45
  145. data/lib/reek/cli/report/location_formatter.rb +0 -34
  146. data/lib/reek/cli/report/report.rb +0 -191
  147. data/lib/reek/core/ast_node.rb +0 -38
  148. data/lib/reek/core/code_comment.rb +0 -37
  149. data/lib/reek/core/examiner.rb +0 -85
  150. data/lib/reek/core/tree_dresser.rb +0 -24
  151. data/lib/reek/core/tree_walker.rb +0 -180
  152. data/lib/reek/source/source_repository.rb +0 -43
  153. data/spec/reek/cli/json_report_spec.rb +0 -20
  154. data/spec/reek/cli/yaml_report_spec.rb +0 -20
  155. data/spec/reek/core/object_source_spec.rb +0 -18
  156. data/spec/reek/core/stop_context_spec.rb +0 -14
  157. 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
- @configuration = {}
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
- @configuration.each do |klass_name, config|
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
@@ -13,6 +13,7 @@ module Reek
13
13
  #
14
14
  # The order in which ConfigurationFileFinder tries to find such a
15
15
  # configuration file is exactly like above.
16
+ # @api private
16
17
  module ConfigurationFileFinder
17
18
  module_function
18
19
 
@@ -1,12 +1,14 @@
1
+ require_relative '../code_comment'
1
2
 
2
3
  module Reek
3
- module Core
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 StopContext:
26
+ # the first time this is instantianted from TreeWalker `context` is a RootContext:
25
27
  #
26
- # #<Reek::Core::StopContext:0x00000002231098 @name="">
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::Core::ModuleContext
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
- Core::CodeComment.new(@exp.comments || '').config
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 Core
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 '../sexp/sexp_formatter'
2
+ require_relative '../ast/sexp_formatter'
3
3
 
4
4
  module Reek
5
- module Core
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 = Sexp::SexpFormatter.format(exp.children.first)
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,9 +1,11 @@
1
1
  module Reek
2
- module Core
2
+ # @api private
3
+ module Context
3
4
  #
4
5
  # A context wrapper representing the root of an abstract syntax tree.
5
6
  #
6
- class StopContext
7
+ # @api private
8
+ class RootContext
7
9
  def initialize
8
10
  @name = ''
9
11
  end
@@ -1,10 +1,11 @@
1
1
  require_relative 'method_context'
2
2
 
3
3
  module Reek
4
- module Core
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