reek 2.2.1 → 3.0.0

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