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.
Files changed (275) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +13 -0
  3. data/.rubocop.yml +47 -0
  4. data/.travis.yml +31 -0
  5. data/.yardopts +3 -6
  6. data/CHANGELOG +24 -0
  7. data/CONTRIBUTING.md +111 -0
  8. data/Gemfile +16 -0
  9. data/README.md +112 -59
  10. data/bin/reek +2 -2
  11. data/config/cucumber.yml +3 -0
  12. data/config/defaults.reek +0 -1
  13. data/docs/API.md +79 -0
  14. data/docs/Attribute.md +43 -0
  15. data/docs/Basic-Smell-Options.md +44 -0
  16. data/docs/Boolean-Parameter.md +52 -0
  17. data/docs/Class-Variable.md +40 -0
  18. data/docs/Code-Smells.md +34 -0
  19. data/docs/Command-Line-Options.md +84 -0
  20. data/docs/Configuration-Files.md +49 -0
  21. data/docs/Control-Couple.md +22 -0
  22. data/docs/Control-Parameter.md +29 -0
  23. data/docs/Data-Clump.md +44 -0
  24. data/docs/Duplicate-Method-Call.md +49 -0
  25. data/docs/Feature-Envy.md +49 -0
  26. data/docs/How-reek-works-internally.md +114 -0
  27. data/docs/Irresponsible-Module.md +39 -0
  28. data/docs/Large-Class.md +20 -0
  29. data/docs/Long-Parameter-List.md +38 -0
  30. data/docs/Long-Yield-List.md +36 -0
  31. data/docs/Module-Initialize.md +62 -0
  32. data/docs/Nested-Iterators.md +38 -0
  33. data/docs/Nil-Check.md +39 -0
  34. data/docs/Prima-Donna-Method.md +53 -0
  35. data/docs/RSpec-matchers.md +137 -0
  36. data/docs/Rake-Task.md +58 -0
  37. data/docs/Reek-Driven-Development.md +37 -0
  38. data/docs/Repeated-Conditional.md +44 -0
  39. data/docs/Simulated-Polymorphism.md +16 -0
  40. data/docs/Smell-Suppression.md +32 -0
  41. data/docs/Too-Many-Instance-Variables.md +43 -0
  42. data/docs/Too-Many-Methods.md +55 -0
  43. data/docs/Too-Many-Statements.md +50 -0
  44. data/docs/Uncommunicative-Method-Name.md +24 -0
  45. data/docs/Uncommunicative-Module-Name.md +23 -0
  46. data/docs/Uncommunicative-Name.md +16 -0
  47. data/docs/Uncommunicative-Parameter-Name.md +24 -0
  48. data/docs/Uncommunicative-Variable-Name.md +24 -0
  49. data/docs/Unused-Parameters.md +27 -0
  50. data/docs/Utility-Function.md +44 -0
  51. data/docs/Versioning-Policy.md +7 -0
  52. data/docs/YAML-Reports.md +111 -0
  53. data/docs/yard_plugin.rb +14 -0
  54. data/features/{ruby_api/api.feature → command_line_interface/basic_usage.feature} +6 -5
  55. data/features/command_line_interface/options.feature +1 -0
  56. data/features/command_line_interface/smell_selection.feature +4 -8
  57. data/features/command_line_interface/smells_count.feature +25 -32
  58. data/features/command_line_interface/stdin.feature +2 -2
  59. data/features/configuration_files/masking_smells.feature +30 -41
  60. data/features/configuration_files/overrides_defaults.feature +5 -3
  61. data/features/configuration_loading.feature +26 -23
  62. data/features/programmatic_access.feature +43 -0
  63. data/features/rake_task/rake_task.feature +25 -22
  64. data/features/reports/json.feature +24 -26
  65. data/features/reports/reports.feature +77 -103
  66. data/features/reports/yaml.feature +26 -20
  67. data/features/samples.feature +26 -31
  68. data/features/step_definitions/.rubocop.yml +5 -0
  69. data/features/step_definitions/reek_steps.rb +16 -28
  70. data/features/step_definitions/sample_file_steps.rb +158 -0
  71. data/features/support/env.rb +16 -27
  72. data/lib/reek/{source → ast}/ast_node_class_map.rb +11 -12
  73. data/lib/reek/ast/node.rb +132 -0
  74. data/lib/reek/{core → ast}/object_refs.rb +2 -1
  75. data/lib/reek/ast/reference_collector.rb +32 -0
  76. data/lib/reek/{source → ast}/sexp_extensions.rb +43 -7
  77. data/lib/reek/ast/sexp_formatter.rb +31 -0
  78. data/lib/reek/cli/application.rb +5 -4
  79. data/lib/reek/cli/command.rb +2 -1
  80. data/lib/reek/cli/input.rb +6 -5
  81. data/lib/reek/cli/option_interpreter.rb +13 -9
  82. data/lib/reek/cli/options.rb +16 -8
  83. data/lib/reek/cli/reek_command.rb +4 -3
  84. data/lib/reek/cli/silencer.rb +14 -0
  85. data/lib/reek/{core → cli}/warning_collector.rb +2 -1
  86. data/lib/reek/code_comment.rb +36 -0
  87. data/lib/reek/configuration/app_configuration.rb +18 -3
  88. data/lib/reek/configuration/configuration_file_finder.rb +18 -32
  89. data/lib/reek/{core → context}/code_context.rb +8 -6
  90. data/lib/reek/{core → context}/method_context.rb +11 -5
  91. data/lib/reek/{core → context}/module_context.rb +9 -4
  92. data/lib/reek/{core/stop_context.rb → context/root_context.rb} +4 -2
  93. data/lib/reek/{core → context}/singleton_method_context.rb +3 -6
  94. data/lib/reek/examiner.rb +32 -29
  95. data/lib/reek/report/formatter.rb +70 -0
  96. data/lib/reek/report/heading_formatter.rb +45 -0
  97. data/lib/reek/report/location_formatter.rb +35 -0
  98. data/lib/reek/report/report.rb +198 -0
  99. data/lib/reek/smells/attribute.rb +41 -11
  100. data/lib/reek/smells/boolean_parameter.rb +4 -2
  101. data/lib/reek/smells/class_variable.rb +4 -2
  102. data/lib/reek/smells/control_parameter.rb +4 -2
  103. data/lib/reek/smells/data_clump.rb +4 -3
  104. data/lib/reek/smells/duplicate_method_call.rb +15 -5
  105. data/lib/reek/smells/feature_envy.rb +8 -2
  106. data/lib/reek/smells/irresponsible_module.rb +13 -8
  107. data/lib/reek/smells/long_parameter_list.rb +6 -4
  108. data/lib/reek/smells/long_yield_list.rb +4 -2
  109. data/lib/reek/smells/module_initialize.rb +4 -2
  110. data/lib/reek/smells/nested_iterators.rb +4 -2
  111. data/lib/reek/smells/nil_check.rb +6 -3
  112. data/lib/reek/smells/prima_donna_method.rb +21 -12
  113. data/lib/reek/smells/repeated_conditional.rb +7 -4
  114. data/lib/reek/{core → smells}/smell_configuration.rb +3 -1
  115. data/lib/reek/smells/smell_detector.rb +11 -5
  116. data/lib/reek/{core → smells}/smell_repository.rb +9 -5
  117. data/lib/reek/smells/smell_warning.rb +101 -0
  118. data/lib/reek/smells/too_many_instance_variables.rb +4 -2
  119. data/lib/reek/smells/too_many_methods.rb +4 -2
  120. data/lib/reek/smells/too_many_statements.rb +4 -2
  121. data/lib/reek/smells/uncommunicative_method_name.rb +4 -2
  122. data/lib/reek/smells/uncommunicative_module_name.rb +4 -2
  123. data/lib/reek/smells/uncommunicative_parameter_name.rb +4 -2
  124. data/lib/reek/smells/uncommunicative_variable_name.rb +4 -2
  125. data/lib/reek/smells/unused_parameters.rb +4 -2
  126. data/lib/reek/smells/utility_function.rb +11 -22
  127. data/lib/reek/smells.rb +24 -11
  128. data/lib/reek/source/source_code.rb +69 -12
  129. data/lib/reek/source/source_locator.rb +34 -19
  130. data/lib/reek/spec/should_reek.rb +4 -3
  131. data/lib/reek/spec/should_reek_of.rb +2 -1
  132. data/lib/reek/spec/should_reek_only_of.rb +4 -3
  133. data/lib/reek/spec.rb +3 -3
  134. data/lib/reek/tree_dresser.rb +32 -0
  135. data/lib/reek/tree_walker.rb +182 -0
  136. data/lib/reek/version.rb +1 -1
  137. data/lib/reek.rb +3 -3
  138. data/reek.gemspec +18 -25
  139. data/spec/factories/factories.rb +4 -2
  140. data/spec/gem/updates_spec.rb +1 -1
  141. data/spec/gem/yard_spec.rb +2 -2
  142. data/spec/quality/reek_source_spec.rb +3 -3
  143. data/spec/reek/{source/sexp_node_spec.rb → ast/node_spec.rb} +4 -6
  144. data/spec/reek/{core → ast}/object_refs_spec.rb +4 -6
  145. data/spec/reek/{source → ast}/reference_collector_spec.rb +27 -19
  146. data/spec/reek/{source → ast}/sexp_extensions_spec.rb +7 -19
  147. data/spec/reek/{source → ast}/sexp_formatter_spec.rb +13 -8
  148. data/spec/reek/cli/option_interpreter_spec.rb +15 -0
  149. data/spec/reek/cli/options_spec.rb +19 -0
  150. data/spec/reek/cli/warning_collector_spec.rb +28 -0
  151. data/spec/reek/{source/code_comment_spec.rb → code_comment_spec.rb} +19 -17
  152. data/spec/reek/configuration/app_configuration_spec.rb +37 -26
  153. data/spec/reek/configuration/configuration_file_finder_spec.rb +67 -21
  154. data/spec/reek/{core → context}/code_context_spec.rb +18 -21
  155. data/spec/reek/{core → context}/method_context_spec.rb +33 -27
  156. data/spec/reek/{core → context}/module_context_spec.rb +5 -7
  157. data/spec/reek/context/root_context_spec.rb +14 -0
  158. data/spec/reek/context/singleton_method_context_spec.rb +17 -0
  159. data/spec/reek/examiner_spec.rb +9 -50
  160. data/spec/reek/{cli → report}/html_report_spec.rb +6 -9
  161. data/spec/reek/report/json_report_spec.rb +20 -0
  162. data/spec/reek/{cli → report}/text_report_spec.rb +16 -19
  163. data/spec/reek/report/xml_report_spec.rb +34 -0
  164. data/spec/reek/report/yaml_report_spec.rb +20 -0
  165. data/spec/reek/smells/attribute_spec.rb +93 -88
  166. data/spec/reek/smells/behaves_like_variable_detector.rb +1 -1
  167. data/spec/reek/smells/boolean_parameter_spec.rb +31 -5
  168. data/spec/reek/smells/class_variable_spec.rb +10 -10
  169. data/spec/reek/smells/control_parameter_spec.rb +5 -5
  170. data/spec/reek/smells/data_clump_spec.rb +6 -6
  171. data/spec/reek/smells/duplicate_method_call_spec.rb +7 -7
  172. data/spec/reek/smells/feature_envy_spec.rb +12 -13
  173. data/spec/reek/smells/irresponsible_module_spec.rb +28 -32
  174. data/spec/reek/smells/long_parameter_list_spec.rb +7 -7
  175. data/spec/reek/smells/long_yield_list_spec.rb +6 -6
  176. data/spec/reek/smells/module_initialize_spec.rb +4 -4
  177. data/spec/reek/smells/nested_iterators_spec.rb +6 -6
  178. data/spec/reek/smells/nil_check_spec.rb +6 -6
  179. data/spec/reek/smells/prima_donna_method_spec.rb +8 -5
  180. data/spec/reek/smells/repeated_conditional_spec.rb +18 -17
  181. data/spec/reek/{core → smells}/smell_configuration_spec.rb +5 -7
  182. data/spec/reek/smells/smell_detector_shared.rb +6 -7
  183. data/spec/reek/smells/smell_repository_spec.rb +29 -0
  184. data/spec/reek/{smell_warning_spec.rb → smells/smell_warning_spec.rb} +7 -9
  185. data/spec/reek/smells/too_many_instance_variables_spec.rb +5 -5
  186. data/spec/reek/smells/too_many_methods_spec.rb +12 -8
  187. data/spec/reek/smells/too_many_statements_spec.rb +10 -9
  188. data/spec/reek/smells/uncommunicative_method_name_spec.rb +5 -5
  189. data/spec/reek/smells/uncommunicative_module_name_spec.rb +8 -8
  190. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +18 -6
  191. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +10 -7
  192. data/spec/reek/smells/unused_parameters_spec.rb +4 -4
  193. data/spec/reek/smells/utility_function_spec.rb +65 -39
  194. data/spec/reek/source/source_code_spec.rb +10 -10
  195. data/spec/reek/source/source_locator_spec.rb +30 -0
  196. data/spec/reek/spec/should_reek_of_spec.rb +9 -25
  197. data/spec/reek/spec/should_reek_only_of_spec.rb +4 -7
  198. data/spec/reek/spec/should_reek_spec.rb +4 -32
  199. data/spec/reek/tree_dresser_spec.rb +16 -0
  200. data/spec/reek/{core/tree_walker_spec.rb → tree_walker_spec.rb} +5 -7
  201. data/spec/samples/checkstyle.xml +2 -0
  202. data/spec/samples/configuration/with_excluded_paths.reek +4 -0
  203. data/spec/samples/{overrides/masked/lower.reek → masked_by_dotfile/.reek} +3 -1
  204. data/spec/samples/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb +5 -0
  205. data/spec/samples/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb +2 -0
  206. data/spec/samples/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb +6 -0
  207. data/spec/samples/{ruby20_syntax.rb → unusual_syntax.rb} +8 -0
  208. data/spec/spec_helper.rb +21 -10
  209. data/tasks/develop.rake +2 -2
  210. data/tasks/reek.rake +1 -1
  211. metadata +158 -141
  212. data/features/support/hooks.rb +0 -15
  213. data/lib/reek/cli/report/formatter.rb +0 -69
  214. data/lib/reek/cli/report/heading_formatter.rb +0 -45
  215. data/lib/reek/cli/report/location_formatter.rb +0 -34
  216. data/lib/reek/cli/report/report.rb +0 -131
  217. data/lib/reek/core/tree_walker.rb +0 -180
  218. data/lib/reek/smell_warning.rb +0 -87
  219. data/lib/reek/source/ast_node.rb +0 -38
  220. data/lib/reek/source/code_comment.rb +0 -37
  221. data/lib/reek/source/core_extras.rb +0 -46
  222. data/lib/reek/source/reference_collector.rb +0 -27
  223. data/lib/reek/source/sexp_formatter.rb +0 -22
  224. data/lib/reek/source/sexp_node.rb +0 -79
  225. data/lib/reek/source/source_file.rb +0 -16
  226. data/lib/reek/source/source_repository.rb +0 -45
  227. data/lib/reek/source/tree_dresser.rb +0 -24
  228. data/lib/reek/source.rb +0 -16
  229. data/spec/reek/cli/json_report_spec.rb +0 -20
  230. data/spec/reek/cli/option_interperter_spec.rb +0 -14
  231. data/spec/reek/cli/yaml_report_spec.rb +0 -23
  232. data/spec/reek/core/singleton_method_context_spec.rb +0 -9
  233. data/spec/reek/core/smell_repository_spec.rb +0 -17
  234. data/spec/reek/core/stop_context_spec.rb +0 -17
  235. data/spec/reek/core/warning_collector_spec.rb +0 -30
  236. data/spec/reek/source/object_source_spec.rb +0 -20
  237. data/spec/reek/source/tree_dresser_spec.rb +0 -18
  238. data/spec/samples/config/allow_duplication.reek +0 -3
  239. data/spec/samples/config/deeper_nested_iterators.reek +0 -3
  240. data/spec/samples/configuration_loading/minimal_dirty.rb +0 -4
  241. data/spec/samples/configuration_loading/reek-test-run-disable_smells.reek +0 -7
  242. data/spec/samples/configuration_loading/reek-test-run-enable_smells.reek +0 -7
  243. data/spec/samples/corrupt_config_file/corrupt.reek +0 -1
  244. data/spec/samples/corrupt_config_file/dirty.rb +0 -8
  245. data/spec/samples/demo/demo.rb +0 -8
  246. data/spec/samples/empty_config_file/dirty.rb +0 -8
  247. data/spec/samples/empty_config_file/empty.reek +0 -0
  248. data/spec/samples/inline_config/dirty.rb +0 -16
  249. data/spec/samples/inline_config/masked.reek +0 -7
  250. data/spec/samples/mask_some/dirty.rb +0 -8
  251. data/spec/samples/mask_some/some.reek +0 -8
  252. data/spec/samples/masked/dirty.rb +0 -8
  253. data/spec/samples/masked/masked.reek +0 -5
  254. data/spec/samples/minimal_smelly_and_masked/config.reek +0 -7
  255. data/spec/samples/minimal_smelly_and_masked/minimal_dirty.rb +0 -4
  256. data/spec/samples/mixed_results/clean_one.rb +0 -7
  257. data/spec/samples/mixed_results/clean_three.rb +0 -7
  258. data/spec/samples/mixed_results/clean_two.rb +0 -7
  259. data/spec/samples/mixed_results/dirty_one.rb +0 -8
  260. data/spec/samples/mixed_results/dirty_two.rb +0 -8
  261. data/spec/samples/not_quite_masked/dirty.rb +0 -8
  262. data/spec/samples/not_quite_masked/masked.reek +0 -5
  263. data/spec/samples/not_quite_masked/smelly.rb +0 -3
  264. data/spec/samples/one_smelly_file/dirty.rb +0 -3
  265. data/spec/samples/overrides/masked/dirty.rb +0 -8
  266. data/spec/samples/overrides/upper.reek +0 -5
  267. data/spec/samples/overrides_defaults/camel_case.rb +0 -14
  268. data/spec/samples/overrides_defaults/config.reek +0 -6
  269. data/spec/samples/ruby21_syntax.rb +0 -5
  270. data/spec/samples/standard_smelly/dirty.rb +0 -8
  271. data/spec/samples/standard_smelly/minimal_dirty.rb +0 -4
  272. data/spec/samples/three_smelly_files/dirty_one.rb +0 -3
  273. data/spec/samples/three_smelly_files/dirty_three.rb +0 -5
  274. data/spec/samples/three_smelly_files/dirty_two.rb +0 -4
  275. /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
@@ -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
@@ -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