reek 4.8.1 → 5.0.2

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 (227) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +17 -3
  3. data/.simplecov +1 -0
  4. data/.travis.yml +0 -5
  5. data/.yardopts +1 -1
  6. data/CHANGELOG.md +44 -0
  7. data/Gemfile +1 -1
  8. data/README.md +115 -100
  9. data/Rakefile +16 -3
  10. data/bin/reek +1 -3
  11. data/docs/API.md +2 -9
  12. data/docs/Basic-Smell-Options.md +51 -11
  13. data/docs/Code-Smells.md +1 -1
  14. data/docs/Command-Line-Options.md +14 -4
  15. data/docs/Duplicate-Method-Call.md +49 -1
  16. data/docs/Feature-Envy.md +44 -0
  17. data/docs/How-To-Write-New-Detectors.md +2 -3
  18. data/docs/Instance-Variable-Assumption.md +1 -1
  19. data/docs/{Prima-Donna-Method.md → Missing-Safe-Method.md} +11 -9
  20. data/docs/Rake-Task.md +1 -1
  21. data/docs/Reek-4-to-Reek-5-migration.md +193 -0
  22. data/docs/Reek-Driven-Development.md +1 -1
  23. data/docs/Uncommunicative-Method-Name.md +45 -6
  24. data/docs/Uncommunicative-Module-Name.md +49 -7
  25. data/docs/Uncommunicative-Parameter-Name.md +43 -5
  26. data/docs/Uncommunicative-Variable-Name.md +73 -2
  27. data/docs/Unused-Private-Method.md +1 -1
  28. data/docs/defaults.reek.yml +129 -0
  29. data/docs/yard_plugin.rb +1 -0
  30. data/features/command_line_interface/basic_usage.feature +2 -2
  31. data/features/command_line_interface/options.feature +46 -4
  32. data/features/command_line_interface/show_progress.feature +4 -4
  33. data/features/command_line_interface/smell_selection.feature +1 -1
  34. data/features/command_line_interface/smells_count.feature +6 -6
  35. data/features/command_line_interface/stdin.feature +30 -8
  36. data/features/configuration_files/accept_setting.feature +45 -28
  37. data/features/configuration_files/directory_specific_directives.feature +78 -73
  38. data/features/configuration_files/exclude_directives.feature +11 -10
  39. data/features/configuration_files/exclude_paths_directives.feature +4 -4
  40. data/features/configuration_files/masking_smells.feature +38 -9
  41. data/features/configuration_files/mix_accept_reject_setting.feature +31 -28
  42. data/features/configuration_files/reject_setting.feature +52 -41
  43. data/features/configuration_files/schema_validation.feature +59 -0
  44. data/features/configuration_files/unused_private_method.feature +18 -16
  45. data/features/configuration_loading.feature +53 -10
  46. data/features/configuration_via_source_comments/erroneous_source_comments.feature +2 -2
  47. data/features/configuration_via_source_comments/well_formed_source_comments.feature +2 -2
  48. data/features/locales.feature +2 -2
  49. data/features/rake_task/rake_task.feature +15 -15
  50. data/features/reports/json.feature +3 -3
  51. data/features/reports/reports.feature +34 -34
  52. data/features/reports/yaml.feature +3 -3
  53. data/features/rspec_matcher.feature +9 -1
  54. data/features/samples.feature +287 -287
  55. data/features/step_definitions/reek_steps.rb +4 -0
  56. data/features/step_definitions/sample_file_steps.rb +9 -4
  57. data/features/support/env.rb +2 -2
  58. data/features/todo_list.feature +26 -23
  59. data/lib/reek/ast/node.rb +3 -6
  60. data/lib/reek/ast/object_refs.rb +1 -1
  61. data/lib/reek/ast/sexp_extensions/case.rb +3 -1
  62. data/lib/reek/ast/sexp_extensions/if.rb +2 -2
  63. data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
  64. data/lib/reek/ast/sexp_extensions/methods.rb +1 -1
  65. data/lib/reek/cli/application.rb +4 -3
  66. data/lib/reek/cli/command/report_command.rb +1 -2
  67. data/lib/reek/cli/command/todo_list_command.rb +4 -2
  68. data/lib/reek/cli/options.rb +29 -14
  69. data/lib/reek/cli/silencer.rb +14 -3
  70. data/lib/reek/code_comment.rb +14 -16
  71. data/lib/reek/configuration/app_configuration.rb +32 -28
  72. data/lib/reek/configuration/configuration_converter.rb +110 -0
  73. data/lib/reek/configuration/configuration_file_finder.rb +15 -40
  74. data/lib/reek/configuration/configuration_validator.rb +12 -23
  75. data/lib/reek/configuration/default_directive.rb +17 -3
  76. data/lib/reek/configuration/directory_directives.rb +17 -11
  77. data/lib/reek/configuration/excluded_paths.rb +1 -1
  78. data/lib/reek/configuration/rake_task_converter.rb +29 -0
  79. data/lib/reek/configuration/schema.yml +210 -0
  80. data/lib/reek/configuration/schema_validator.rb +38 -0
  81. data/lib/reek/context/attribute_context.rb +1 -1
  82. data/lib/reek/context/code_context.rb +4 -4
  83. data/lib/reek/context/method_context.rb +2 -2
  84. data/lib/reek/context/module_context.rb +1 -1
  85. data/lib/reek/context_builder.rb +9 -9
  86. data/lib/reek/detector_repository.rb +6 -0
  87. data/lib/reek/documentation_link.rb +28 -0
  88. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +4 -3
  89. data/lib/reek/errors/bad_detector_in_comment_error.rb +4 -3
  90. data/lib/reek/errors/config_file_error.rb +11 -0
  91. data/lib/reek/errors/encoding_error.rb +2 -2
  92. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +4 -3
  93. data/lib/reek/errors/incomprehensible_source_error.rb +2 -2
  94. data/lib/reek/errors/syntax_error.rb +41 -0
  95. data/lib/reek/examiner.rb +9 -19
  96. data/lib/reek/rake/task.rb +3 -3
  97. data/lib/reek/report/base_report.rb +8 -12
  98. data/lib/reek/report/code_climate/code_climate_configuration.yml +6 -10
  99. data/lib/reek/report/documentation_link_warning_formatter.rb +17 -0
  100. data/lib/reek/report/heading_formatter.rb +54 -0
  101. data/lib/reek/report/json_report.rb +1 -1
  102. data/lib/reek/report/location_formatter.rb +40 -0
  103. data/lib/reek/report/progress_formatter.rb +79 -0
  104. data/lib/reek/report/simple_warning_formatter.rb +34 -0
  105. data/lib/reek/report/text_report.rb +1 -2
  106. data/lib/reek/report/xml_report.rb +3 -3
  107. data/lib/reek/report/yaml_report.rb +1 -1
  108. data/lib/reek/report.rb +15 -10
  109. data/lib/reek/smell_configuration.rb +2 -2
  110. data/lib/reek/smell_detectors/attribute.rb +0 -1
  111. data/lib/reek/smell_detectors/base_detector.rb +8 -11
  112. data/lib/reek/smell_detectors/boolean_parameter.rb +0 -1
  113. data/lib/reek/smell_detectors/class_variable.rb +0 -1
  114. data/lib/reek/smell_detectors/control_parameter.rb +17 -32
  115. data/lib/reek/smell_detectors/data_clump.rb +3 -4
  116. data/lib/reek/smell_detectors/duplicate_method_call.rb +5 -6
  117. data/lib/reek/smell_detectors/feature_envy.rb +1 -1
  118. data/lib/reek/smell_detectors/instance_variable_assumption.rb +0 -1
  119. data/lib/reek/smell_detectors/irresponsible_module.rb +0 -1
  120. data/lib/reek/smell_detectors/long_parameter_list.rb +1 -2
  121. data/lib/reek/smell_detectors/long_yield_list.rb +2 -3
  122. data/lib/reek/smell_detectors/manual_dispatch.rb +2 -2
  123. data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +6 -7
  124. data/lib/reek/smell_detectors/module_initialize.rb +0 -1
  125. data/lib/reek/smell_detectors/nested_iterators.rb +4 -5
  126. data/lib/reek/smell_detectors/nil_check.rb +0 -1
  127. data/lib/reek/smell_detectors/repeated_conditional.rb +3 -4
  128. data/lib/reek/smell_detectors/subclassed_from_core_class.rb +0 -1
  129. data/lib/reek/smell_detectors/too_many_constants.rb +1 -2
  130. data/lib/reek/smell_detectors/too_many_instance_variables.rb +1 -2
  131. data/lib/reek/smell_detectors/too_many_methods.rb +1 -2
  132. data/lib/reek/smell_detectors/too_many_statements.rb +1 -2
  133. data/lib/reek/smell_detectors/uncommunicative_method_name.rb +2 -3
  134. data/lib/reek/smell_detectors/uncommunicative_module_name.rb +2 -3
  135. data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +2 -3
  136. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +4 -5
  137. data/lib/reek/smell_detectors/unused_parameters.rb +0 -1
  138. data/lib/reek/smell_detectors/unused_private_method.rb +0 -1
  139. data/lib/reek/smell_detectors/utility_function.rb +1 -2
  140. data/lib/reek/smell_detectors.rb +1 -2
  141. data/lib/reek/smell_warning.rb +15 -8
  142. data/lib/reek/source/source_code.rb +40 -55
  143. data/lib/reek/source/source_locator.rb +7 -7
  144. data/lib/reek/spec/should_reek.rb +2 -2
  145. data/lib/reek/spec/should_reek_of.rb +9 -16
  146. data/lib/reek/spec/should_reek_only_of.rb +4 -4
  147. data/lib/reek/spec.rb +6 -6
  148. data/lib/reek/tree_dresser.rb +5 -5
  149. data/lib/reek/version.rb +1 -1
  150. data/reek.gemspec +4 -4
  151. data/samples/checkstyle.xml +1 -1
  152. data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +29 -0
  153. data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +30 -0
  154. data/samples/configuration/full_configuration.reek +8 -4
  155. data/samples/configuration/full_mask.reek +5 -4
  156. data/samples/configuration/partial_mask.reek +3 -2
  157. data/samples/configuration/regular_configuration/.reek.yml +4 -0
  158. data/samples/paths.rb +5 -4
  159. data/samples/source_with_hidden_directories/.hidden/hidden.rb +1 -0
  160. data/samples/source_with_hidden_directories/not_hidden.rb +1 -0
  161. data/spec/reek/ast/node_spec.rb +5 -5
  162. data/spec/reek/cli/application_spec.rb +18 -4
  163. data/spec/reek/cli/command/todo_list_command_spec.rb +4 -2
  164. data/spec/reek/cli/silencer_spec.rb +28 -0
  165. data/spec/reek/code_comment_spec.rb +0 -7
  166. data/spec/reek/configuration/app_configuration_spec.rb +44 -31
  167. data/spec/reek/configuration/configuration_file_finder_spec.rb +141 -49
  168. data/spec/reek/configuration/default_directive_spec.rb +1 -1
  169. data/spec/reek/configuration/directory_directives_spec.rb +3 -4
  170. data/spec/reek/configuration/excluded_paths_spec.rb +5 -5
  171. data/spec/reek/configuration/rake_task_converter_spec.rb +33 -0
  172. data/spec/reek/configuration/schema_validator_spec.rb +165 -0
  173. data/spec/reek/context/code_context_spec.rb +1 -1
  174. data/spec/reek/documentation_link_spec.rb +20 -0
  175. data/spec/reek/examiner_spec.rb +28 -1
  176. data/spec/reek/report/json_report_spec.rb +13 -46
  177. data/spec/reek/report/{formatter/location_formatter_spec.rb → location_formatter_spec.rb} +5 -5
  178. data/spec/reek/report/{formatter/progress_formatter_spec.rb → progress_formatter_spec.rb} +4 -4
  179. data/spec/reek/report/text_report_spec.rb +4 -4
  180. data/spec/reek/report/xml_report_spec.rb +1 -1
  181. data/spec/reek/report/yaml_report_spec.rb +9 -38
  182. data/spec/reek/report_spec.rb +3 -3
  183. data/spec/reek/smell_detectors/feature_envy_spec.rb +47 -2
  184. data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +9 -9
  185. data/spec/reek/smell_detectors/too_many_constants_spec.rb +3 -3
  186. data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +1 -1
  187. data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +6 -6
  188. data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +6 -4
  189. data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +6 -4
  190. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +6 -6
  191. data/spec/reek/smell_detectors/unused_private_method_spec.rb +1 -1
  192. data/spec/reek/smell_warning_spec.rb +4 -0
  193. data/spec/reek/source/source_code_spec.rb +16 -22
  194. data/spec/reek/source/source_locator_spec.rb +11 -11
  195. data/spec/reek/spec/should_reek_of_spec.rb +0 -4
  196. data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
  197. data/spec/reek/spec/should_reek_spec.rb +1 -1
  198. data/spec/reek/tree_dresser_spec.rb +2 -6
  199. data/spec/spec_helper.rb +3 -5
  200. data/tasks/configuration.rake +8 -5
  201. metadata +64 -36
  202. data/.codeclimate.yml +0 -21
  203. data/defaults.reek +0 -131
  204. data/features/configuration_files/warn_about_multiple_configuration_files.feature +0 -44
  205. data/lib/reek/report/formatter/heading_formatter.rb +0 -52
  206. data/lib/reek/report/formatter/location_formatter.rb +0 -42
  207. data/lib/reek/report/formatter/progress_formatter.rb +0 -81
  208. data/lib/reek/report/formatter/simple_warning_formatter.rb +0 -35
  209. data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +0 -36
  210. data/lib/reek/report/formatter.rb +0 -33
  211. data/lib/reek/smell_detectors/syntax.rb +0 -37
  212. data/samples/configuration/non_public_modifiers_mask.reek +0 -3
  213. data/samples/smelly_with_inline_mask.rb +0 -8
  214. data/samples/smelly_with_modifiers.rb +0 -12
  215. data/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -5
  216. data/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -6
  217. data/spec/reek/smell_detectors/syntax_spec.rb +0 -17
  218. /data/{samples/configuration/more_than_one_configuration_file/regular.reek → .reek.yml} +0 -0
  219. /data/samples/{clean.rb → clean_source/clean.rb} +0 -0
  220. /data/samples/{exceptions.reek → configuration/home/home.reek.yml} +0 -0
  221. /data/samples/configuration/{more_than_one_configuration_file/todo.reek → regular_configuration/empty_sub_directory/.gitignore} +0 -0
  222. /data/samples/{configuration/single_configuration_file/.reek → no_config_file/.keep} +0 -0
  223. /data/samples/{inline.rb → smelly_source/inline.rb} +0 -0
  224. /data/samples/{optparse.rb → smelly_source/optparse.rb} +0 -0
  225. /data/samples/{redcloth.rb → smelly_source/redcloth.rb} +0 -0
  226. /data/samples/{smelly.rb → smelly_source/smelly.rb} +0 -0
  227. /data/samples/{source_with_hidden_directories/uncommunicative_parameter_name.rb → source_with_non_ruby_files/ruby.rb} +0 -0
@@ -6,6 +6,10 @@ When /^I pass "([^\"]*)" to reek *(.*)$/ do |stdin, args|
6
6
  reek_with_pipe(stdin, args)
7
7
  end
8
8
 
9
+ When /^I pass a stdin to reek *(.*) with:$/ do |args, stdin|
10
+ reek_with_pipe(stdin, args)
11
+ end
12
+
9
13
  Then /^it reports nothing$/ do
10
14
  expect(last_command_started).to have_output_on_stdout('')
11
15
  end
@@ -1,7 +1,7 @@
1
1
  require_relative '../../samples/paths'
2
2
 
3
3
  Given(/^the smelly file '(.+)'$/) do |filename|
4
- write_file(filename, SAMPLES_PATH.join(filename).read)
4
+ write_file(filename, SAMPLES_DIR.join('smelly_source').join(filename).read)
5
5
  end
6
6
 
7
7
  Given(/^the clean file 'clean.rb'$/) do
@@ -28,13 +28,18 @@ Given(/^a directory called 'smelly' containing two smelly files$/) do
28
28
  end
29
29
 
30
30
  Given(/^the smelly file '(.+)' in a subdirectory$/) do |filename|
31
- contents = SAMPLES_PATH.join(filename).read
31
+ contents = SAMPLES_DIR.join('smelly_source').join(filename).read
32
32
 
33
33
  write_file("subdir/#{filename}", contents)
34
34
  end
35
35
 
36
36
  Given(/^a configuration file '(.+)'$/) do |filename|
37
- write_file(filename, CONFIG_PATH.join(filename).read)
37
+ write_file(filename, CONFIGURATION_DIR.join(filename).read)
38
+ end
39
+
40
+ Given(/^our default configuration file$/) do
41
+ default_configuration = File.read SAMPLES_DIR.join('..').join('docs').join('defaults.reek.yml')
42
+ write_file('defaults.reek', default_configuration)
38
43
  end
39
44
 
40
45
  When(/^I run "reek (.*?)" in a subdirectory$/) do |args|
@@ -44,7 +49,7 @@ When(/^I run "reek (.*?)" in a subdirectory$/) do |args|
44
49
  end
45
50
 
46
51
  Given(/^a configuration file '(.+)' in a subdirectory$/) do |filename|
47
- contents = CONFIG_PATH.join(filename).read
52
+ contents = CONFIGURATION_DIR.join(filename).read
48
53
 
49
54
  write_file("subdir/#{filename}", contents)
50
55
  end
@@ -13,11 +13,11 @@ end
13
13
  #
14
14
  class ReekWorld
15
15
  def reek(args)
16
- run_simple("reek --no-color #{args}", false)
16
+ run_simple("reek --no-color --no-documentation #{args}", false)
17
17
  end
18
18
 
19
19
  def reek_with_pipe(stdin, args)
20
- run "reek --no-color #{args}"
20
+ run "reek --no-color --no-documentation #{args}"
21
21
  type(stdin)
22
22
  close_input
23
23
  end
@@ -15,8 +15,8 @@ Feature: Auto-generate a todo file
15
15
  And it reports:
16
16
  """
17
17
  smelly.rb -- 2 warnings:
18
- [4]:UncommunicativeMethodName: Smelly#x has the name 'x' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Method-Name.md]
19
- [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
18
+ [4]:UncommunicativeMethodName: Smelly#x has the name 'x'
19
+ [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y'
20
20
  """
21
21
  When I run reek --todo smelly.rb
22
22
  Then it succeeds
@@ -29,13 +29,14 @@ Feature: Auto-generate a todo file
29
29
  And the file ".todo.reek" should contain:
30
30
  """
31
31
  ---
32
- UncommunicativeMethodName:
33
- exclude:
34
- - Smelly#x
35
- UncommunicativeVariableName:
36
- exclude:
37
- - Smelly#x
38
- """
32
+ detectors:
33
+ UncommunicativeMethodName:
34
+ exclude:
35
+ - Smelly#x
36
+ UncommunicativeVariableName:
37
+ exclude:
38
+ - Smelly#x
39
+ """
39
40
  When I run reek -c .todo.reek smelly.rb
40
41
  Then it succeeds
41
42
 
@@ -54,20 +55,21 @@ Feature: Auto-generate a todo file
54
55
  And a file named ".todo.reek" with:
55
56
  """
56
57
  ---
57
- # smelly.rb reeks of UncommunicativeMethodName and UncommunicativeVariableName
58
- # so the configuration below will partially mask this
59
- UncommunicativeMethodName:
60
- enabled: false
58
+ detectors:
59
+ # smelly.rb reeks of UncommunicativeMethodName and UncommunicativeVariableName
60
+ # so the configuration below will partially mask this
61
+ UncommunicativeMethodName:
62
+ enabled: false
61
63
  """
62
- When I run `reek -c .todo.reek smelly.rb`
64
+ When I run reek -c .todo.reek smelly.rb
63
65
  Then it reports:
64
66
  """
65
67
  smelly.rb -- 1 warning:
66
- [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
68
+ [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y'
67
69
  """
68
- When I run `reek --todo smelly.rb`
70
+ When I run reek --todo smelly.rb
69
71
  Then it succeeds
70
- When I run `reek -c .todo.reek smelly.rb`
72
+ When I run reek -c .todo.reek smelly.rb
71
73
  Then it reports nothing
72
74
 
73
75
  Scenario: Ignore existing other configuration files that are passed explicitly
@@ -77,16 +79,17 @@ Feature: Auto-generate a todo file
77
79
  ---
78
80
  # smelly.rb reeks of UncommunicativeMethodName and UncommunicativeVariableName
79
81
  # so the configuration below will partially mask this
80
- UncommunicativeMethodName:
81
- enabled: false
82
+ detectors:
83
+ UncommunicativeMethodName:
84
+ enabled: false
82
85
  """
83
- When I run `reek -c config.reek smelly.rb`
86
+ When I run reek -c config.reek smelly.rb
84
87
  Then it reports:
85
88
  """
86
89
  smelly.rb -- 1 warning:
87
- [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md]
90
+ [5]:UncommunicativeVariableName: Smelly#x has the variable name 'y'
88
91
  """
89
- When I run `reek -c config.reek --todo smelly.rb`
92
+ When I run reek -c config.reek --todo smelly.rb
90
93
  Then it succeeds
91
- When I run `reek -c .todo.reek smelly.rb`
94
+ When I run reek -c .todo.reek smelly.rb
92
95
  Then it reports nothing
data/lib/reek/ast/node.rb CHANGED
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../cli/silencer'
4
-
5
- Reek::CLI::Silencer.silently do
6
- require 'parser'
7
- end
4
+ Reek::CLI::Silencer.without_warnings { require 'parser' }
8
5
 
9
6
  module Reek
10
7
  module AST
@@ -72,8 +69,8 @@ module Reek
72
69
  each_node(target_type).any?
73
70
  end
74
71
 
75
- # :reek:DuplicateMethodCall { max_calls: 2 } is ok for lines.first
76
- # :reek:FeatureEnvy
72
+ # @quality :reek:DuplicateMethodCall { max_calls: 2 } is ok for lines.first
73
+ # @quality :reek:FeatureEnvy
77
74
  def format_to_ruby
78
75
  if location
79
76
  lines = location.expression.source.split("\n").map(&:strip)
@@ -50,7 +50,7 @@ module Reek
50
50
  end
51
51
 
52
52
  def self_is_max?
53
- refs.empty? || most_popular.keys.include?(:self)
53
+ refs.empty? || most_popular.key?(:self)
54
54
  end
55
55
 
56
56
  private
@@ -10,7 +10,9 @@ module Reek
10
10
  end
11
11
 
12
12
  def body_nodes(type, ignoring = [])
13
- children[1..-1].compact.flat_map { |child| child.each_node(type, ignoring).to_a }
13
+ children[1..-1].compact.flat_map do |child|
14
+ child.each_node(type, ignoring | type).to_a
15
+ end
14
16
  end
15
17
 
16
18
  def else_body
@@ -9,13 +9,13 @@ module Reek
9
9
  children.first
10
10
  end
11
11
 
12
- # :reek:FeatureEnvy
12
+ # @quality :reek:FeatureEnvy
13
13
  def body_nodes(type, ignoring = [])
14
14
  children[1..-1].compact.flat_map do |child|
15
15
  if ignoring.include? child.type
16
16
  []
17
17
  else
18
- child.each_node(type, ignoring).to_a
18
+ child.each_node(type, ignoring | type).to_a
19
19
  end
20
20
  end
21
21
  end
@@ -10,7 +10,7 @@ module Reek
10
10
  end
11
11
 
12
12
  def body_nodes(type, ignoring = [])
13
- children[1].each_node type, ignoring
13
+ children[1].each_node type, (ignoring | type)
14
14
  end
15
15
  end
16
16
 
@@ -57,7 +57,7 @@ module Reek
57
57
  end
58
58
 
59
59
  def depends_on_instance?
60
- ReferenceCollector.new(self).num_refs_to_self > 0
60
+ ReferenceCollector.new(self).num_refs_to_self.positive?
61
61
  end
62
62
  end
63
63
 
@@ -6,6 +6,7 @@ require_relative '../configuration/app_configuration'
6
6
  require_relative '../source/source_locator'
7
7
  require_relative 'command/report_command'
8
8
  require_relative 'command/todo_list_command'
9
+ require_relative '../errors/config_file_error'
9
10
 
10
11
  module Reek
11
12
  module CLI
@@ -42,7 +43,7 @@ module Reek
42
43
 
43
44
  def configure_app_configuration(config_file)
44
45
  Configuration::AppConfiguration.from_path(config_file)
45
- rescue Reek::Configuration::ConfigFileException => error
46
+ rescue Errors::ConfigFileError => error
46
47
  warn "Error: #{error}"
47
48
  exit Status::DEFAULT_ERROR_EXIT_CODE
48
49
  end
@@ -68,7 +69,7 @@ module Reek
68
69
  options.argv
69
70
  end
70
71
 
71
- # :reek:UtilityFunction
72
+ # @quality :reek:UtilityFunction
72
73
  def input_was_piped?
73
74
  !$stdin.tty?
74
75
  end
@@ -88,7 +89,7 @@ module Reek
88
89
  end
89
90
 
90
91
  def source_from_pipe
91
- [$stdin]
92
+ [Source::SourceCode.from($stdin, origin: options.stdin_filename)]
92
93
  end
93
94
 
94
95
  def disable_progress_output_unless_verbose
@@ -38,7 +38,6 @@ module Reek
38
38
  @reporter ||=
39
39
  report_class.new(
40
40
  warning_formatter: warning_formatter,
41
- report_formatter: Report::Formatter,
42
41
  sort_by_issue_count: sort_by_issue_count,
43
42
  heading_formatter: heading_formatter,
44
43
  progress_formatter: progress_formatter.new(sources.length))
@@ -53,7 +52,7 @@ module Reek
53
52
  end
54
53
 
55
54
  def warning_formatter_class
56
- Report.warning_formatter_class(options.show_links ? :wiki_links : :simple)
55
+ Report.warning_formatter_class(options.show_links ? :documentation_links : :simple)
57
56
  end
58
57
 
59
58
  def location_formatter
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'base_command'
4
4
  require_relative '../../examiner'
5
+ require_relative '../../configuration/app_configuration'
5
6
 
6
7
  module Reek
7
8
  module CLI
@@ -11,14 +12,15 @@ module Reek
11
12
  # file that can serve as a todo list.
12
13
  #
13
14
  class TodoListCommand < BaseCommand
14
- FILE_NAME = '.todo.reek'.freeze
15
+ FILE_NAME = '.todo.reek'
15
16
 
16
17
  def execute
17
18
  if smells.empty?
18
19
  puts "\n'.todo.reek' not generated because "\
19
20
  'there were no smells found!'
20
21
  else
21
- File.write FILE_NAME, groups.to_yaml
22
+ File.write FILE_NAME,
23
+ { Configuration::AppConfiguration::DETECTORS_KEY => groups }.to_yaml
22
24
  puts "\n'.todo.reek' generated! You can now use "\
23
25
  'this as a starting point for your configuration.'
24
26
  end
@@ -4,6 +4,8 @@ require 'optparse'
4
4
  require 'rainbow'
5
5
  require_relative '../version'
6
6
  require_relative 'status'
7
+ require_relative '../detector_repository'
8
+ require_relative '../documentation_link'
7
9
 
8
10
  module Reek
9
11
  module CLI
@@ -12,9 +14,9 @@ module Reek
12
14
  #
13
15
  # See {file:docs/Command-Line-Options.md} for details.
14
16
  #
15
- # :reek:TooManyInstanceVariables: { max_instance_variables: 12 }
16
- # :reek:TooManyMethods: { max_methods: 18 }
17
- # :reek:Attribute: { enabled: false }
17
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 12 }
18
+ # @quality :reek:TooManyMethods { max_methods: 18 }
19
+ # @quality :reek:Attribute { enabled: false }
18
20
  #
19
21
  class Options
20
22
  attr_reader :argv, :parser, :smells_to_detect
@@ -26,6 +28,7 @@ module Reek
26
28
  :show_empty,
27
29
  :show_links,
28
30
  :sorting,
31
+ :stdin_filename,
29
32
  :success_exit_code,
30
33
  :failure_exit_code,
31
34
  :generate_todo_list,
@@ -64,12 +67,12 @@ module Reek
64
67
  # processing by a machine, but will be viewed by a human. This means
65
68
  # features like coloring can be safely enabled by default.
66
69
  #
67
- # :reek:UtilityFunction
70
+ # @quality :reek:UtilityFunction
68
71
  def tty_output?
69
72
  $stdout.tty?
70
73
  end
71
74
 
72
- # :reek:TooManyStatements: { max_statements: 7 }
75
+ # @quality :reek:TooManyStatements { max_statements: 7 }
73
76
  def set_up_parser
74
77
  set_banner
75
78
  set_configuration_options
@@ -91,12 +94,12 @@ module Reek
91
94
  #{program_name} -s lib
92
95
  cat my_class.rb | #{program_name}
93
96
 
94
- See https://wiki.github.com/troessner/reek for detailed help.
97
+ See https://github.com/troessner/reek for detailed help.
95
98
 
96
99
  BANNER
97
100
  end
98
101
 
99
- # :reek:TooManyStatements: { max_statements: 6 }
102
+ # @quality :reek:TooManyStatements { max_statements: 7 }
100
103
  def set_configuration_options
101
104
  parser.separator 'Configuration:'
102
105
  parser.on('-c', '--config FILE', 'Read configuration options from FILE') do |file|
@@ -104,11 +107,16 @@ module Reek
104
107
  end
105
108
  parser.on('--smell SMELL',
106
109
  'Only look for a specific smell.',
107
- 'Call it like this: reek --smell PrimaDonnaMethod source.rb',
108
- 'Check out https://github.com/troessner/reek/blob/master/docs/Code-Smells.md '\
110
+ 'Call it like this: reek --smell MissingSafeMethod source.rb',
111
+ "Check out #{DocumentationLink.build('Code Smells')} "\
109
112
  'for a list of smells') do |smell|
110
113
  smells_to_detect << smell
111
114
  end
115
+ parser.on('--stdin-filename FILE',
116
+ 'When passing code in via pipe, assume this filename when '\
117
+ 'checking file or directory rules in the config.') do |file|
118
+ self.stdin_filename = file
119
+ end
112
120
  end
113
121
 
114
122
  def set_generate_todo_list_options
@@ -128,7 +136,7 @@ module Reek
128
136
  end
129
137
  end
130
138
 
131
- # :reek:TooManyStatements: { max_statements: 7 }
139
+ # @quality :reek:TooManyStatements { max_statements: 7 }
132
140
  def set_report_formatting_options
133
141
  parser.separator "\nText format options:"
134
142
  set_up_color_option
@@ -150,8 +158,8 @@ module Reek
150
158
  'Show headings for smell-free source files (default: false)') do |show_empty|
151
159
  self.show_empty = show_empty
152
160
  end
153
- parser.on('-U', '--[no-]wiki-links',
154
- 'Show link to related wiki page for each smell (default: true)') do |show_links|
161
+ parser.on('-U', '--[no-]documentation',
162
+ 'Show link to related documentation page for each smell (default: true)') do |show_links|
155
163
  self.show_links = show_links
156
164
  end
157
165
  end
@@ -191,7 +199,7 @@ module Reek
191
199
  end
192
200
  end
193
201
 
194
- # :reek:DuplicateMethodCall: { max_calls: 2 }
202
+ # @quality :reek:DuplicateMethodCall { max_calls: 2 }
195
203
  def set_exit_codes
196
204
  parser.separator "\nExit codes:"
197
205
  parser.on('--success-exit-code CODE',
@@ -206,13 +214,20 @@ module Reek
206
214
  end
207
215
  end
208
216
 
209
- # :reek:TooManyStatements: { max_statements: 7 }
217
+ # @quality :reek:TooManyStatements { max_statements: 12 }
210
218
  def set_utility_options
211
219
  parser.separator "\nUtility options:"
212
220
  parser.on_tail('-h', '--help', 'Show this message') do
213
221
  puts parser
214
222
  exit
215
223
  end
224
+ parser.on_tail('-l', '--list', 'List all available smell detectors') do
225
+ puts "All available smell detectors:\n\n"
226
+ puts DetectorRepository.available_detector_names
227
+ puts "\nCheck out #{DocumentationLink.build('Code Smells')} "\
228
+ 'for a details on each detector'
229
+ exit
230
+ end
216
231
  parser.on_tail('-v', '--version', 'Show version') do
217
232
  puts "#{parser.program_name} #{Reek::Version::STRING}\n"
218
233
  exit
@@ -8,17 +8,28 @@ module Reek
8
8
  module Silencer
9
9
  module_function
10
10
 
11
- # :reek:TooManyStatements: { max_statements: 7 }
11
+ # @quality :reek:TooManyStatements { max_statements: 9 }
12
12
  def silently
13
13
  old_verbose = $VERBOSE
14
+ old_stderr = $stderr
15
+ old_stdout = $stdout
16
+
14
17
  $VERBOSE = false
15
18
  $stderr = StringIO.new
16
19
  $stdout = StringIO.new
17
20
  yield
18
21
  ensure
19
22
  $VERBOSE = old_verbose
20
- $stderr = STDERR
21
- $stdout = STDOUT
23
+ $stderr = old_stderr
24
+ $stdout = old_stdout
25
+ end
26
+
27
+ def without_warnings
28
+ old_verbose = $VERBOSE
29
+ $VERBOSE = false
30
+ yield
31
+ ensure
32
+ $VERBOSE = old_verbose
22
33
  end
23
34
  end
24
35
  end
@@ -17,22 +17,20 @@ module Reek
17
17
  :reek: # prefix
18
18
  (\w+) # smell detector e.g.: UncommunicativeVariableName
19
19
  (
20
- :? # legacy separator
21
20
  \s*
22
21
  (\{.*?\}) # optional details in hash style e.g.: { max_methods: 30 }
23
22
  )?
24
23
  /x
25
24
  SANITIZE_REGEX = /(#|\n|\s)+/ # Matches '#', newlines and > 1 whitespaces.
26
- DISABLE_DETECTOR_CONFIGURATION = '{ enabled: false }'.freeze
25
+ DISABLE_DETECTOR_CONFIGURATION = '{ enabled: false }'
27
26
  MINIMUM_CONTENT_LENGTH = 2
28
- LEGACY_SEPARATOR = ':'.freeze
29
27
 
30
28
  attr_reader :config
31
29
 
32
30
  #
33
- # @param comment [String] - the original comment as found in the source code
34
- # @param line [Integer] - start of the expression the comment belongs to
35
- # @param source [String] - Path to source file or "string"
31
+ # @param comment [String] the original comment as found in the source code
32
+ # @param line [Integer] start of the expression the comment belongs to
33
+ # @param source [String] Path to source file or "string"
36
34
  #
37
35
  def initialize(comment:, line: nil, source: nil)
38
36
  @original_comment = comment
@@ -80,15 +78,15 @@ module Reek
80
78
  # This class validates [1], [2] and [3] at the moment but will also validate
81
79
  # [4] in the future.
82
80
  #
83
- # :reek:TooManyInstanceVariables: { max_instance_variables: 7 }
81
+ # @quality :reek:TooManyInstanceVariables { max_instance_variables: 7 }
84
82
  class CodeCommentValidator
85
83
  #
86
- # @param detector_name [String] - the detector class that was parsed out of the original
84
+ # @param detector_name [String] the detector class that was parsed out of the original
87
85
  # comment, e.g. "DuplicateMethodCall" or "UnknownSmellDetector"
88
- # @param original_comment [String] - the original comment as found in the source code
89
- # @param line [Integer] - start of the expression the comment belongs to
90
- # @param source [String] - path to source file or "string"
91
- # @param options [String] - the configuration options as String for the detector that were
86
+ # @param original_comment [String] the original comment as found in the source code
87
+ # @param line [Integer] start of the expression the comment belongs to
88
+ # @param source [String] path to source file or "string"
89
+ # @param options [String] the configuration options as String for the detector that were
92
90
  # extracted from the original comment
93
91
  def initialize(detector_name:, original_comment:, line:, source:, options: {})
94
92
  @detector_name = detector_name
@@ -151,24 +149,24 @@ module Reek
151
149
  line: line
152
150
  end
153
151
 
154
- # @return [Boolean] - all keys in code comment are applicable to the detector in question
152
+ # @return [Boolean] all keys in code comment are applicable to the detector in question
155
153
  def given_keys_legit?
156
154
  given_configuration_keys.subset? valid_detector_keys
157
155
  end
158
156
 
159
- # @return [Set] - the configuration keys that are found in the code comment
157
+ # @return [Set] the configuration keys that are found in the code comment
160
158
  def given_configuration_keys
161
159
  parsed_options.keys.map(&:to_sym).to_set
162
160
  end
163
161
 
164
- # @return [String] - all keys from the code comment that look bad
162
+ # @return [String] all keys from the code comment that look bad
165
163
  def configuration_keys_difference
166
164
  given_configuration_keys.difference(valid_detector_keys).
167
165
  to_a.map { |key| "'#{key}'" }.
168
166
  join(', ')
169
167
  end
170
168
 
171
- # @return [Set] - all keys that are legit for the given detector
169
+ # @return [Set] all keys that are legit for the given detector
172
170
  def valid_detector_keys
173
171
  detector_class.configuration_keys
174
172
  end
@@ -15,20 +15,30 @@ module Reek
15
15
  # @public
16
16
  class AppConfiguration
17
17
  include ConfigurationValidator
18
- EXCLUDE_PATHS_KEY = 'exclude_paths'.freeze
18
+ EXCLUDE_PATHS_KEY = 'exclude_paths'
19
+ DIRECTORIES_KEY = 'directories'
20
+ DETECTORS_KEY = 'detectors'
19
21
 
20
- # Instantiate a configuration via given path, or the default path.
22
+ # Instantiate a configuration via the given path.
21
23
  #
22
- # @param path [Pathname] the path to the config file, or nil to use the
23
- # default path.
24
+ # @param path [Pathname] the path to the config file.
24
25
  #
25
26
  # @return [AppConfiguration]
26
27
  #
27
28
  # @public
28
- def self.from_path(path = nil)
29
- allocate.tap do |instance|
30
- instance.instance_eval { find_and_load(path: path) }
31
- end
29
+ def self.from_path(path)
30
+ values = ConfigurationFileFinder.find_and_load(path: path)
31
+ new(values: values)
32
+ end
33
+
34
+ # Instantiate a configuration via the default path.
35
+ #
36
+ # @return [AppConfiguration]
37
+ #
38
+ # @public
39
+ def self.from_default_path
40
+ values = ConfigurationFileFinder.find_and_load(path: nil)
41
+ new(values: values)
32
42
  end
33
43
 
34
44
  # Instantiate a configuration by passing everything in.
@@ -40,21 +50,17 @@ module Reek
40
50
  # @return [AppConfiguration]
41
51
  #
42
52
  # @public
43
- def self.from_hash(hash = {})
44
- allocate.tap do |instance|
45
- instance.instance_eval do
46
- load_values hash
47
- end
48
- end
53
+ def self.from_hash(hash)
54
+ new(values: hash)
49
55
  end
50
56
 
51
57
  def self.default
52
- new
58
+ new(values: {})
53
59
  end
54
60
 
55
61
  # Returns the directive for a given directory.
56
62
  #
57
- # @param source_via [String] - the source of the code inspected
63
+ # @param source_via [String] the source of the code inspected
58
64
  #
59
65
  # @return [Hash] the directory directive for the source with the default directive
60
66
  # reverse-merged into it.
@@ -67,18 +73,22 @@ module Reek
67
73
  excluded_paths.map(&:expand_path).include?(path.expand_path)
68
74
  end
69
75
 
70
- def load_values(configuration_hash)
71
- configuration_hash.each do |key, value|
76
+ def load_values(values)
77
+ values.each do |key, value|
72
78
  if key == EXCLUDE_PATHS_KEY
73
79
  excluded_paths.add value
74
- elsif smell_type?(key)
75
- default_directive.add key, value
76
- else
77
- directory_directives.add key, value
80
+ elsif key == DIRECTORIES_KEY
81
+ directory_directives.add value
82
+ elsif key == DETECTORS_KEY
83
+ default_directive.add value
78
84
  end
79
85
  end
80
86
  end
81
87
 
88
+ def initialize(values: {})
89
+ load_values(values)
90
+ end
91
+
82
92
  private
83
93
 
84
94
  attr_writer :directory_directives, :default_directive, :excluded_paths
@@ -94,12 +104,6 @@ module Reek
94
104
  def excluded_paths
95
105
  @excluded_paths ||= [].extend(ExcludedPaths)
96
106
  end
97
-
98
- def find_and_load(path: nil)
99
- configuration_hash = ConfigurationFileFinder.find_and_load(path: path)
100
-
101
- load_values(configuration_hash)
102
- end
103
107
  end
104
108
  end
105
109
  end