reek 4.7.1 → 5.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 (260) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +17 -12
  3. data/.rubocop.yml +36 -7
  4. data/.simplecov +1 -0
  5. data/.travis.yml +3 -9
  6. data/.yardopts +1 -1
  7. data/CHANGELOG.md +59 -0
  8. data/CONTRIBUTING.md +1 -1
  9. data/Gemfile +5 -5
  10. data/README.md +134 -104
  11. data/Rakefile +16 -3
  12. data/bin/reek +1 -3
  13. data/docs/API.md +2 -9
  14. data/docs/Basic-Smell-Options.md +51 -11
  15. data/docs/Code-Smells.md +1 -1
  16. data/docs/Command-Line-Options.md +14 -4
  17. data/docs/Duplicate-Method-Call.md +49 -1
  18. data/docs/Feature-Envy.md +44 -0
  19. data/docs/How-To-Write-New-Detectors.md +7 -7
  20. data/docs/Instance-Variable-Assumption.md +1 -1
  21. data/docs/{Prima-Donna-Method.md → Missing-Safe-Method.md} +11 -9
  22. data/docs/Rake-Task.md +1 -1
  23. data/docs/Reek-4-to-Reek-5-migration.md +193 -0
  24. data/docs/Reek-Driven-Development.md +1 -1
  25. data/docs/Uncommunicative-Method-Name.md +45 -6
  26. data/docs/Uncommunicative-Module-Name.md +49 -7
  27. data/docs/Uncommunicative-Parameter-Name.md +43 -5
  28. data/docs/Uncommunicative-Variable-Name.md +73 -2
  29. data/docs/Unused-Private-Method.md +3 -3
  30. data/docs/defaults.reek.yml +129 -0
  31. data/docs/yard_plugin.rb +1 -0
  32. data/features/command_line_interface/basic_usage.feature +2 -2
  33. data/features/command_line_interface/options.feature +46 -4
  34. data/features/command_line_interface/show_progress.feature +4 -4
  35. data/features/command_line_interface/smell_selection.feature +1 -1
  36. data/features/command_line_interface/smells_count.feature +6 -6
  37. data/features/command_line_interface/stdin.feature +30 -8
  38. data/features/configuration_files/accept_setting.feature +45 -28
  39. data/features/configuration_files/directory_specific_directives.feature +80 -75
  40. data/features/configuration_files/exclude_directives.feature +11 -10
  41. data/features/configuration_files/exclude_paths_directives.feature +4 -4
  42. data/features/configuration_files/masking_smells.feature +38 -9
  43. data/features/configuration_files/mix_accept_reject_setting.feature +31 -28
  44. data/features/configuration_files/reject_setting.feature +52 -41
  45. data/features/configuration_files/schema_validation.feature +59 -0
  46. data/features/configuration_files/unused_private_method.feature +18 -16
  47. data/features/configuration_loading.feature +53 -10
  48. data/features/configuration_via_source_comments/erroneous_source_comments.feature +3 -3
  49. data/features/configuration_via_source_comments/well_formed_source_comments.feature +2 -2
  50. data/features/locales.feature +32 -0
  51. data/features/rake_task/rake_task.feature +58 -18
  52. data/features/reports/json.feature +3 -3
  53. data/features/reports/reports.feature +34 -34
  54. data/features/reports/yaml.feature +3 -3
  55. data/features/rspec_matcher.feature +40 -0
  56. data/features/samples.feature +287 -287
  57. data/features/step_definitions/reek_steps.rb +2 -2
  58. data/features/step_definitions/sample_file_steps.rb +9 -4
  59. data/features/support/env.rb +2 -11
  60. data/features/todo_list.feature +26 -23
  61. data/lib/reek/ast/builder.rb +1 -1
  62. data/lib/reek/ast/node.rb +40 -58
  63. data/lib/reek/ast/object_refs.rb +1 -1
  64. data/lib/reek/ast/reference_collector.rb +2 -4
  65. data/lib/reek/ast/sexp_extensions/case.rb +1 -1
  66. data/lib/reek/ast/sexp_extensions/if.rb +8 -1
  67. data/lib/reek/ast/sexp_extensions/logical_operators.rb +1 -1
  68. data/lib/reek/ast/sexp_extensions/methods.rb +4 -6
  69. data/lib/reek/ast/sexp_extensions/send.rb +0 -4
  70. data/lib/reek/cli/application.rb +4 -3
  71. data/lib/reek/cli/command/report_command.rb +1 -2
  72. data/lib/reek/cli/command/todo_list_command.rb +8 -8
  73. data/lib/reek/cli/options.rb +31 -16
  74. data/lib/reek/cli/silencer.rb +14 -3
  75. data/lib/reek/code_comment.rb +14 -16
  76. data/lib/reek/configuration/app_configuration.rb +32 -27
  77. data/lib/reek/configuration/configuration_converter.rb +110 -0
  78. data/lib/reek/configuration/configuration_file_finder.rb +16 -41
  79. data/lib/reek/configuration/configuration_validator.rb +12 -23
  80. data/lib/reek/configuration/default_directive.rb +17 -3
  81. data/lib/reek/configuration/directory_directives.rb +17 -11
  82. data/lib/reek/configuration/excluded_paths.rb +1 -1
  83. data/lib/reek/configuration/rake_task_converter.rb +29 -0
  84. data/lib/reek/configuration/schema.yml +210 -0
  85. data/lib/reek/configuration/schema_validator.rb +38 -0
  86. data/lib/reek/context/attribute_context.rb +3 -3
  87. data/lib/reek/context/code_context.rb +47 -43
  88. data/lib/reek/context/ghost_context.rb +0 -2
  89. data/lib/reek/context/method_context.rb +22 -15
  90. data/lib/reek/context/module_context.rb +5 -9
  91. data/lib/reek/context/root_context.rb +0 -4
  92. data/lib/reek/context/send_context.rb +2 -2
  93. data/lib/reek/context_builder.rb +43 -44
  94. data/lib/reek/detector_repository.rb +11 -11
  95. data/lib/reek/documentation_link.rb +28 -0
  96. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +14 -13
  97. data/lib/reek/errors/bad_detector_in_comment_error.rb +12 -11
  98. data/lib/reek/errors/base_error.rb +3 -0
  99. data/lib/reek/errors/config_file_error.rb +11 -0
  100. data/lib/reek/errors/encoding_error.rb +43 -0
  101. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +13 -12
  102. data/lib/reek/errors/incomprehensible_source_error.rb +22 -24
  103. data/lib/reek/errors/syntax_error.rb +41 -0
  104. data/lib/reek/examiner.rb +24 -26
  105. data/lib/reek/logging_error_handler.rb +7 -5
  106. data/lib/reek/rake/task.rb +8 -4
  107. data/lib/reek/report/base_report.rb +8 -12
  108. data/lib/reek/report/code_climate/code_climate_configuration.rb +1 -1
  109. data/lib/reek/report/code_climate/code_climate_configuration.yml +6 -10
  110. data/lib/reek/report/documentation_link_warning_formatter.rb +17 -0
  111. data/lib/reek/report/heading_formatter.rb +54 -0
  112. data/lib/reek/report/json_report.rb +1 -1
  113. data/lib/reek/report/location_formatter.rb +40 -0
  114. data/lib/reek/report/progress_formatter.rb +79 -0
  115. data/lib/reek/report/simple_warning_formatter.rb +34 -0
  116. data/lib/reek/report/text_report.rb +1 -2
  117. data/lib/reek/report/xml_report.rb +3 -3
  118. data/lib/reek/report/yaml_report.rb +1 -1
  119. data/lib/reek/report.rb +15 -10
  120. data/lib/reek/smell_configuration.rb +2 -2
  121. data/lib/reek/smell_detectors/attribute.rb +5 -11
  122. data/lib/reek/smell_detectors/base_detector.rb +30 -26
  123. data/lib/reek/smell_detectors/boolean_parameter.rb +3 -5
  124. data/lib/reek/smell_detectors/class_variable.rb +5 -14
  125. data/lib/reek/smell_detectors/control_parameter.rb +18 -33
  126. data/lib/reek/smell_detectors/data_clump.rb +15 -9
  127. data/lib/reek/smell_detectors/duplicate_method_call.rb +23 -17
  128. data/lib/reek/smell_detectors/feature_envy.rb +9 -7
  129. data/lib/reek/smell_detectors/instance_variable_assumption.rb +14 -23
  130. data/lib/reek/smell_detectors/irresponsible_module.rb +5 -12
  131. data/lib/reek/smell_detectors/long_parameter_list.rb +10 -7
  132. data/lib/reek/smell_detectors/long_yield_list.rb +10 -7
  133. data/lib/reek/smell_detectors/manual_dispatch.rb +4 -5
  134. data/lib/reek/smell_detectors/{prima_donna_method.rb → missing_safe_method.rb} +20 -20
  135. data/lib/reek/smell_detectors/module_initialize.rb +3 -5
  136. data/lib/reek/smell_detectors/nested_iterators.rb +16 -24
  137. data/lib/reek/smell_detectors/nil_check.rb +8 -15
  138. data/lib/reek/smell_detectors/repeated_conditional.rb +13 -11
  139. data/lib/reek/smell_detectors/subclassed_from_core_class.rb +7 -8
  140. data/lib/reek/smell_detectors/too_many_constants.rb +10 -9
  141. data/lib/reek/smell_detectors/too_many_instance_variables.rb +10 -6
  142. data/lib/reek/smell_detectors/too_many_methods.rb +11 -7
  143. data/lib/reek/smell_detectors/too_many_statements.rb +10 -6
  144. data/lib/reek/smell_detectors/uncommunicative_method_name.rb +10 -11
  145. data/lib/reek/smell_detectors/uncommunicative_module_name.rb +14 -18
  146. data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +17 -22
  147. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +26 -27
  148. data/lib/reek/smell_detectors/unused_parameters.rb +4 -6
  149. data/lib/reek/smell_detectors/unused_private_method.rb +11 -18
  150. data/lib/reek/smell_detectors/utility_function.rb +12 -16
  151. data/lib/reek/smell_detectors.rb +1 -2
  152. data/lib/reek/smell_warning.rb +15 -8
  153. data/lib/reek/source/source_code.rb +57 -58
  154. data/lib/reek/source/source_locator.rb +8 -8
  155. data/lib/reek/spec/should_reek.rb +2 -2
  156. data/lib/reek/spec/should_reek_of.rb +9 -16
  157. data/lib/reek/spec/should_reek_only_of.rb +4 -4
  158. data/lib/reek/spec.rb +6 -6
  159. data/lib/reek/tree_dresser.rb +8 -8
  160. data/lib/reek/version.rb +1 -1
  161. data/reek.gemspec +5 -7
  162. data/samples/checkstyle.xml +1 -1
  163. data/samples/configuration/accepts_rejects_and_excludes_for_detectors.reek.yml +29 -0
  164. data/samples/configuration/accepts_rejects_and_excludes_for_directory_directives.reek.yml +30 -0
  165. data/samples/configuration/full_configuration.reek +8 -4
  166. data/samples/configuration/full_mask.reek +5 -4
  167. data/samples/configuration/partial_mask.reek +3 -2
  168. data/samples/configuration/regular_configuration/.reek.yml +4 -0
  169. data/samples/paths.rb +5 -4
  170. data/samples/source_with_hidden_directories/.hidden/hidden.rb +1 -0
  171. data/samples/source_with_hidden_directories/not_hidden.rb +1 -0
  172. data/spec/factories/factories.rb +2 -13
  173. data/spec/reek/ast/node_spec.rb +103 -10
  174. data/spec/reek/ast/reference_collector_spec.rb +1 -1
  175. data/spec/reek/ast/sexp_extensions_spec.rb +14 -34
  176. data/spec/reek/cli/application_spec.rb +52 -42
  177. data/spec/reek/cli/command/todo_list_command_spec.rb +6 -4
  178. data/spec/reek/cli/silencer_spec.rb +28 -0
  179. data/spec/reek/code_comment_spec.rb +31 -38
  180. data/spec/reek/configuration/app_configuration_spec.rb +46 -33
  181. data/spec/reek/configuration/configuration_file_finder_spec.rb +133 -51
  182. data/spec/reek/configuration/default_directive_spec.rb +1 -1
  183. data/spec/reek/configuration/directory_directives_spec.rb +6 -7
  184. data/spec/reek/configuration/excluded_paths_spec.rb +6 -6
  185. data/spec/reek/configuration/rake_task_converter_spec.rb +33 -0
  186. data/spec/reek/configuration/schema_validator_spec.rb +165 -0
  187. data/spec/reek/context/code_context_spec.rb +70 -106
  188. data/spec/reek/context/ghost_context_spec.rb +9 -9
  189. data/spec/reek/context/method_context_spec.rb +2 -2
  190. data/spec/reek/context/module_context_spec.rb +3 -3
  191. data/spec/reek/context/root_context_spec.rb +1 -1
  192. data/spec/reek/context_builder_spec.rb +20 -0
  193. data/spec/reek/documentation_link_spec.rb +20 -0
  194. data/spec/reek/errors/base_error_spec.rb +13 -0
  195. data/spec/reek/examiner_spec.rb +136 -29
  196. data/spec/reek/rake/task_spec.rb +25 -2
  197. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +82 -80
  198. data/spec/reek/report/code_climate/code_climate_formatter_spec.rb +6 -6
  199. data/spec/reek/report/json_report_spec.rb +13 -46
  200. data/spec/reek/report/{formatter/location_formatter_spec.rb → location_formatter_spec.rb} +5 -5
  201. data/spec/reek/report/{formatter/progress_formatter_spec.rb → progress_formatter_spec.rb} +4 -4
  202. data/spec/reek/report/text_report_spec.rb +4 -4
  203. data/spec/reek/report/xml_report_spec.rb +3 -3
  204. data/spec/reek/report/yaml_report_spec.rb +9 -38
  205. data/spec/reek/report_spec.rb +3 -3
  206. data/spec/reek/smell_detectors/base_detector_spec.rb +4 -5
  207. data/spec/reek/smell_detectors/boolean_parameter_spec.rb +2 -2
  208. data/spec/reek/smell_detectors/class_variable_spec.rb +26 -32
  209. data/spec/reek/smell_detectors/control_parameter_spec.rb +34 -4
  210. data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +3 -3
  211. data/spec/reek/smell_detectors/feature_envy_spec.rb +47 -2
  212. data/spec/reek/smell_detectors/irresponsible_module_spec.rb +0 -11
  213. data/spec/reek/smell_detectors/{prima_donna_method_spec.rb → missing_safe_method_spec.rb} +10 -10
  214. data/spec/reek/smell_detectors/module_initialize_spec.rb +14 -0
  215. data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
  216. data/spec/reek/smell_detectors/too_many_constants_spec.rb +3 -3
  217. data/spec/reek/smell_detectors/too_many_instance_variables_spec.rb +1 -1
  218. data/spec/reek/smell_detectors/uncommunicative_method_name_spec.rb +6 -6
  219. data/spec/reek/smell_detectors/uncommunicative_module_name_spec.rb +6 -4
  220. data/spec/reek/smell_detectors/uncommunicative_parameter_name_spec.rb +36 -15
  221. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +9 -9
  222. data/spec/reek/smell_detectors/unused_parameters_spec.rb +3 -3
  223. data/spec/reek/smell_detectors/unused_private_method_spec.rb +21 -10
  224. data/spec/reek/smell_detectors/utility_function_spec.rb +57 -5
  225. data/spec/reek/smell_warning_spec.rb +12 -8
  226. data/spec/reek/source/source_code_spec.rb +22 -21
  227. data/spec/reek/source/source_locator_spec.rb +17 -17
  228. data/spec/reek/spec/should_reek_of_spec.rb +25 -29
  229. data/spec/reek/spec/should_reek_only_of_spec.rb +2 -2
  230. data/spec/reek/spec/should_reek_spec.rb +8 -8
  231. data/spec/reek/spec/smell_matcher_spec.rb +23 -23
  232. data/spec/reek/tree_dresser_spec.rb +12 -17
  233. data/spec/spec_helper.rb +6 -17
  234. data/tasks/configuration.rake +8 -5
  235. metadata +74 -41
  236. data/defaults.reek +0 -131
  237. data/features/configuration_files/warn_about_multiple_configuration_files.feature +0 -44
  238. data/lib/reek/report/formatter/heading_formatter.rb +0 -52
  239. data/lib/reek/report/formatter/location_formatter.rb +0 -42
  240. data/lib/reek/report/formatter/progress_formatter.rb +0 -81
  241. data/lib/reek/report/formatter/simple_warning_formatter.rb +0 -35
  242. data/lib/reek/report/formatter/wiki_link_warning_formatter.rb +0 -36
  243. data/lib/reek/report/formatter.rb +0 -33
  244. data/lib/reek/smell_detectors/syntax.rb +0 -37
  245. data/samples/configuration/non_public_modifiers_mask.reek +0 -3
  246. data/samples/smelly_with_inline_mask.rb +0 -8
  247. data/samples/smelly_with_modifiers.rb +0 -12
  248. data/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb +0 -5
  249. data/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb +0 -6
  250. data/spec/reek/smell_detectors/syntax_spec.rb +0 -17
  251. /data/{samples/configuration/more_than_one_configuration_file/regular.reek → .reek.yml} +0 -0
  252. /data/samples/{clean.rb → clean_source/clean.rb} +0 -0
  253. /data/samples/{exceptions.reek → configuration/home/home.reek.yml} +0 -0
  254. /data/samples/configuration/{more_than_one_configuration_file/todo.reek → regular_configuration/empty_sub_directory/.gitignore} +0 -0
  255. /data/samples/{configuration/single_configuration_file/.reek → no_config_file/.keep} +0 -0
  256. /data/samples/{inline.rb → smelly_source/inline.rb} +0 -0
  257. /data/samples/{optparse.rb → smelly_source/optparse.rb} +0 -0
  258. /data/samples/{redcloth.rb → smelly_source/redcloth.rb} +0 -0
  259. /data/samples/{smelly.rb → smelly_source/smelly.rb} +0 -0
  260. /data/samples/{source_with_hidden_directories/uncommunicative_parameter_name.rb → source_with_non_ruby_files/ruby.rb} +0 -0
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../examiner'
4
4
  require_relative 'smell_matcher'
5
+ require_relative '../configuration/app_configuration'
5
6
 
6
7
  module Reek
7
8
  module Spec
@@ -14,12 +15,13 @@ module Reek
14
15
 
15
16
  attr_reader :failure_message, :failure_message_when_negated
16
17
 
17
- def initialize(smell_type_or_class,
18
+ def initialize(smell_type,
18
19
  smell_details = {},
19
20
  configuration = Configuration::AppConfiguration.default)
20
- @smell_type = normalize smell_type_or_class
21
+ @smell_type = smell_type.to_s
21
22
  @smell_details = smell_details
22
- configuration.load_values(smell_type => { SmellConfiguration::ENABLED_KEY => true })
23
+ configuration.load_values(Configuration::AppConfiguration::DETECTORS_KEY =>
24
+ { smell_type => { SmellConfiguration::ENABLED_KEY => true } })
23
25
  @configuration = configuration
24
26
  end
25
27
 
@@ -34,7 +36,8 @@ module Reek
34
36
 
35
37
  def with_config(config_hash)
36
38
  new_configuration = Configuration::AppConfiguration.default
37
- new_configuration.load_values(smell_type => config_hash)
39
+ new_configuration.load_values(Configuration::AppConfiguration::DETECTORS_KEY =>
40
+ { smell_type => config_hash })
38
41
  self.class.new(smell_type, smell_details, new_configuration)
39
42
  end
40
43
 
@@ -82,7 +85,7 @@ module Reek
82
85
  "#{smell_type} with smell details #{smell_details}, but it did"
83
86
  end
84
87
 
85
- # :reek:FeatureEnvy
88
+ # @quality :reek:FeatureEnvy
86
89
  def all_relevant_smell_details_formatted
87
90
  matching_smell_types.each_with_object([]).with_index do |(smell, accumulator), index|
88
91
  accumulator << "#{index + 1}.)\n"
@@ -93,17 +96,7 @@ module Reek
93
96
  end
94
97
 
95
98
  def origin
96
- examiner.description
97
- end
98
-
99
- # :reek:UtilityFunction
100
- def normalize(smell_type_or_class)
101
- case smell_type_or_class
102
- when Class
103
- smell_type_or_class.smell_type
104
- else
105
- smell_type_or_class.to_s
106
- end
99
+ examiner.origin
107
100
  end
108
101
  end
109
102
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../examiner'
4
- require_relative '../report/formatter'
4
+ require_relative '../report/simple_warning_formatter'
5
5
  require_relative 'should_reek_of'
6
6
  require_relative 'smell_matcher'
7
7
 
@@ -24,12 +24,12 @@ module Reek
24
24
  end
25
25
 
26
26
  def failure_message
27
- rpt = Report::Formatter.format_list(warnings)
28
- "Expected #{examiner.description} to reek only of #{smell_type}, but got:\n#{rpt}"
27
+ rpt = Report::SimpleWarningFormatter.new.format_list(warnings)
28
+ "Expected #{examiner.origin} to reek only of #{smell_type}, but got:\n#{rpt}"
29
29
  end
30
30
 
31
31
  def failure_message_when_negated
32
- "Expected #{examiner.description} not to reek only of #{smell_type}, but it did"
32
+ "Expected #{examiner.origin} not to reek only of #{smell_type}, but it did"
33
33
  end
34
34
 
35
35
  private
data/lib/reek/spec.rb CHANGED
@@ -65,7 +65,7 @@ module Reek
65
65
  # raise an ArgumentError to give you a hint that you passed something that doesn't make
66
66
  # much sense.
67
67
  #
68
- # @param smell_type [Symbol, String, Class] The "smell type" to check for.
68
+ # @param smell_type [Symbol, String] The "smell type" to check for.
69
69
  # @param smell_details [Hash] A hash containing "smell warning" parameters
70
70
  #
71
71
  # @example Without smell_details
@@ -84,7 +84,7 @@ module Reek
84
84
  #
85
85
  # @public
86
86
  #
87
- # :reek:UtilityFunction
87
+ # @quality :reek:UtilityFunction
88
88
  def reek_of(smell_type,
89
89
  smell_details = {},
90
90
  configuration = Configuration::AppConfiguration.default)
@@ -99,11 +99,11 @@ module Reek
99
99
  # "reek_only_of" will fail in that case.
100
100
  # 2.) "reek_only_of" doesn't support the additional smell_details hash.
101
101
  #
102
- # @param smell_type [Symbol, String, Class] The "smell type" to check for.
102
+ # @param smell_type [Symbol, String] The "smell type" to check for.
103
103
  #
104
104
  # @public
105
105
  #
106
- # :reek:UtilityFunction
106
+ # @quality :reek:UtilityFunction
107
107
  def reek_only_of(smell_type, configuration = Configuration::AppConfiguration.default)
108
108
  ShouldReekOnlyOf.new(smell_type, configuration)
109
109
  end
@@ -113,8 +113,8 @@ module Reek
113
113
  #
114
114
  # @public
115
115
  #
116
- # :reek:UtilityFunction
117
- def reek(configuration = Configuration::AppConfiguration.default)
116
+ # @quality :reek:UtilityFunction
117
+ def reek(configuration = Configuration::AppConfiguration.from_default_path)
118
118
  ShouldReek.new(configuration: configuration)
119
119
  end
120
120
  end
@@ -31,21 +31,21 @@ module Reek
31
31
  # time the nodes will contain type-dependent mixins, e.g. this:
32
32
  # (const nil :Klazz)
33
33
  # will be of type Reek::AST::Node with Reek::AST::SexpExtensions::ConstNode mixed in.
34
- # @param sexp [Parser::AST::Node] - the given sexp
35
- # @param comment_map [Hash] - see the documentation for SourceCode#syntax_tree
36
- # @param parent [Parser::AST::Node] - the parent sexp
34
+ #
35
+ # @param sexp [Parser::AST::Node] the given sexp
36
+ # @param comment_map [Hash] see the documentation for SourceCode#syntax_tree
37
37
  #
38
38
  # @return an instance of Reek::AST::Node with type-dependent sexp extensions mixed in.
39
39
  #
40
- # :reek:FeatureEnvy
41
- # :reek:TooManyStatements: { max_statements: 6 }
42
- def dress(sexp, comment_map, parent: nil)
40
+ # @quality :reek:FeatureEnvy
41
+ # @quality :reek:TooManyStatements { max_statements: 6 }
42
+ def dress(sexp, comment_map)
43
43
  return sexp unless sexp.is_a? ::Parser::AST::Node
44
44
  type = sexp.type
45
- children = sexp.children.map { |child| dress(child, comment_map, parent: sexp) }
45
+ children = sexp.children.map { |child| dress(child, comment_map) }
46
46
  comments = comment_map[sexp]
47
47
  klass_map.klass_for(type).new(type, children,
48
- location: sexp.loc, comments: comments, parent: parent)
48
+ location: sexp.loc, comments: comments)
49
49
  end
50
50
 
51
51
  private
data/lib/reek/version.rb CHANGED
@@ -8,6 +8,6 @@ module Reek
8
8
  # @public
9
9
  module Version
10
10
  # @public
11
- STRING = '4.7.1'.freeze
11
+ STRING = '5.0.0'
12
12
  end
13
13
  end
data/reek.gemspec CHANGED
@@ -1,5 +1,3 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
1
  require File.join(File.dirname(__FILE__), 'lib/reek/version')
4
2
 
5
3
  Gem::Specification.new do |s|
@@ -7,7 +5,6 @@ Gem::Specification.new do |s|
7
5
  s.version = Reek::Version::STRING
8
6
 
9
7
  s.authors = ['Kevin Rutherford', 'Timo Roessner', 'Matijs van Zuijlen', 'Piotr Szotkowski']
10
- s.default_executable = 'reek'
11
8
  s.description =
12
9
  'Reek is a tool that examines Ruby classes, modules and methods and reports ' \
13
10
  'any code smells it finds.'
@@ -17,12 +14,13 @@ Gem::Specification.new do |s|
17
14
  s.extra_rdoc_files = ['CHANGELOG.md', 'License.txt']
18
15
  s.files = `git ls-files -z`.split("\0")
19
16
  s.executables = s.files.grep(%r{^bin/}).map { |path| File.basename(path) }
20
- s.homepage = 'https://github.com/troessner/reek/wiki'
17
+ s.homepage = 'https://github.com/troessner/reek'
21
18
  s.rdoc_options = %w(--main README.md -x assets/|bin/|config/|features/|spec/|tasks/)
22
- s.required_ruby_version = '>= 2.1.0'
19
+ s.required_ruby_version = '>= 2.3.0'
23
20
  s.summary = 'Code smell detector for Ruby'
24
21
 
25
22
  s.add_runtime_dependency 'codeclimate-engine-rb', '~> 0.4.0'
26
- s.add_runtime_dependency 'parser', '< 2.5', '>= 2.4.0.0'
27
- s.add_runtime_dependency 'rainbow', '~> 2.0'
23
+ s.add_runtime_dependency 'kwalify', '~> 0.7.0'
24
+ s.add_runtime_dependency 'parser', '< 2.6', '>= 2.5.0.0'
25
+ s.add_runtime_dependency 'rainbow', '>= 2.0', '< 4.0'
28
26
  end
@@ -1,6 +1,6 @@
1
1
  <?xml version='1.0'?>
2
2
  <checkstyle>
3
- <file name='samples/smelly.rb'>
3
+ <file name='samples/smelly_source/smelly.rb'>
4
4
  <error column='0' line='4' message='has the name &apos;x&apos;' severity='warning' source='UncommunicativeMethodName'/>
5
5
  <error column='0' line='5' message='has the variable name &apos;y&apos;' severity='warning' source='UncommunicativeVariableName'/>
6
6
  </file>
@@ -0,0 +1,29 @@
1
+ ---
2
+ detectors:
3
+ UnusedPrivateMethod:
4
+ exclude:
5
+ - "/exclude regexp/"
6
+ UncommunicativeMethodName:
7
+ reject:
8
+ - "reject name"
9
+ accept:
10
+ - "accept name"
11
+ UncommunicativeModuleName:
12
+ reject:
13
+ - "reject name 1"
14
+ - "reject name 2"
15
+ accept:
16
+ - "accept name 1"
17
+ - "accept name 2"
18
+ UncommunicativeParameterName:
19
+ reject:
20
+ - "reject name"
21
+ - "/reject regexp/"
22
+ accept:
23
+ - "accept name"
24
+ - "/accept regexp/"
25
+ UncommunicativeVariableName:
26
+ reject:
27
+ - "/^reject regexp$/"
28
+ accept:
29
+ - "/accept(.*)regexp/"
@@ -0,0 +1,30 @@
1
+ ---
2
+ directories:
3
+ "app/controllers":
4
+ UnusedPrivateMethod:
5
+ exclude:
6
+ - "/exclude regexp/"
7
+ UncommunicativeMethodName:
8
+ reject:
9
+ - "reject name"
10
+ accept:
11
+ - "accept name"
12
+ UncommunicativeModuleName:
13
+ reject:
14
+ - "reject name 1"
15
+ - "reject name 2"
16
+ accept:
17
+ - "accept name 1"
18
+ - "accept name 2"
19
+ UncommunicativeParameterName:
20
+ reject:
21
+ - "reject name"
22
+ - "/reject regexp/"
23
+ accept:
24
+ - "accept name"
25
+ - "/accept regexp/"
26
+ UncommunicativeVariableName:
27
+ reject:
28
+ - "/^reject regexp$/"
29
+ accept:
30
+ - "/accept(.*)regexp/"
@@ -1,9 +1,13 @@
1
1
  ---
2
- IrresponsibleModule:
3
- enabled: false
4
- "samples/three_clean_files/":
5
- UtilityFunction:
2
+ detectors:
3
+ IrresponsibleModule:
6
4
  enabled: false
5
+
6
+ directories:
7
+ "samples/three_clean_files/":
8
+ UtilityFunction:
9
+ enabled: false
10
+
7
11
  exclude_paths:
8
12
  - "samples/two_smelly_files/"
9
13
  - "samples/source_with_non_ruby_files/"
@@ -1,5 +1,6 @@
1
1
  ---
2
- UncommunicativeMethodName:
3
- enabled: false
4
- UncommunicativeVariableName:
5
- enabled: false
2
+ detectors:
3
+ UncommunicativeMethodName:
4
+ enabled: false
5
+ UncommunicativeVariableName:
6
+ enabled: false
@@ -1,3 +1,4 @@
1
1
  ---
2
- UncommunicativeMethodName:
3
- enabled: false
2
+ detectors:
3
+ UncommunicativeMethodName:
4
+ enabled: false
@@ -0,0 +1,4 @@
1
+ ---
2
+ UncommunicativeVariableName:
3
+ accept:
4
+ - md5
data/samples/paths.rb CHANGED
@@ -1,4 +1,5 @@
1
- SAMPLES_PATH = Pathname.new(__dir__).relative_path_from(Pathname.pwd)
2
- CONFIG_PATH = SAMPLES_PATH.join('configuration')
3
- CLEAN_FILE = SAMPLES_PATH.join('clean.rb')
4
- SMELLY_FILE = SAMPLES_PATH.join('smelly.rb')
1
+ SAMPLES_DIR = Pathname.new(__dir__).relative_path_from(Pathname.pwd)
2
+ CONFIGURATION_DIR = SAMPLES_DIR.join('configuration')
3
+ CLEAN_FILE = SAMPLES_DIR.join('clean_source').join('clean.rb')
4
+ SMELLY_FILE = SAMPLES_DIR.join('smelly_source').join('smelly.rb')
5
+
@@ -3,18 +3,7 @@ require_relative '../../lib/reek/smell_detectors/base_detector'
3
3
  require_relative '../../lib/reek/smell_warning'
4
4
  require_relative '../../lib/reek/cli/options'
5
5
 
6
- FactoryGirl.define do
7
- factory :method_context, class: Reek::Context::MethodContext do
8
- skip_create
9
- transient do
10
- source 'def foo; end'
11
- end
12
-
13
- initialize_with do
14
- new(nil, Reek::Source::SourceCode.from(source).syntax_tree)
15
- end
16
- end
17
-
6
+ FactoryBot.define do
18
7
  factory :smell_detector, class: Reek::SmellDetectors::BaseDetector do
19
8
  skip_create
20
9
  transient do
@@ -33,7 +22,7 @@ FactoryGirl.define do
33
22
  source 'dummy_file'
34
23
  lines [42]
35
24
  message 'smell warning message'
36
- parameters { {} }
25
+ parameters({})
37
26
 
38
27
  initialize_with do
39
28
  new(smell_detector,
@@ -2,6 +2,92 @@ require_relative '../../spec_helper'
2
2
  require_lib 'reek/ast/node'
3
3
 
4
4
  RSpec.describe Reek::AST::Node do
5
+ describe '#each_node' do
6
+ context 'with an empty module' do
7
+ let(:ast) do
8
+ src = 'module Emptiness; end'
9
+ Reek::Source::SourceCode.from(src).syntax_tree
10
+ end
11
+
12
+ it 'yields no calls' do
13
+ ast.each_node(:send, []) { |exp| raise "#{exp} yielded by empty module!" }
14
+ end
15
+
16
+ it 'yields one module' do
17
+ mods = 0
18
+ ast.each_node(:module, []) { |_exp| mods += 1 }
19
+ expect(mods).to eq(1)
20
+ end
21
+
22
+ it "yields the module's full AST" do
23
+ ast.each_node(:module, []) do |exp|
24
+ expect(exp).to eq(sexp(:module, sexp(:const, nil, :Emptiness), nil))
25
+ end
26
+ end
27
+
28
+ it 'returns an enumerator when no block is passed' do
29
+ expect(ast.each_node(:if)).to be_instance_of Enumerator
30
+ end
31
+ end
32
+
33
+ context 'with a nested element' do
34
+ let(:ast) do
35
+ src = "module Loneliness; def calloo; puts('hello') end; end"
36
+ Reek::Source::SourceCode.from(src).syntax_tree
37
+ end
38
+
39
+ it 'yields no ifs' do
40
+ ast.each_node(:if) { |exp| raise "#{exp} yielded by empty module!" }
41
+ end
42
+
43
+ it 'yields one module' do
44
+ expect(ast.each_node(:module).to_a.length).to eq(1)
45
+ end
46
+
47
+ it "yields the module's full AST" do
48
+ ast.each_node(:module) do |exp|
49
+ expect(exp).to eq sexp(:module,
50
+ sexp(:const, nil, :Loneliness),
51
+ sexp(:def, :calloo,
52
+ sexp(:args),
53
+ sexp(:send, nil, :puts, sexp(:str, 'hello'))))
54
+ end
55
+ end
56
+
57
+ it 'yields one method' do
58
+ expect(ast.each_node(:def).to_a.length).to eq(1)
59
+ end
60
+
61
+ it "yields the method's full AST" do
62
+ ast.each_node(:def, []) { |exp| expect(exp.children.first).to eq(:calloo) }
63
+ end
64
+
65
+ it 'ignores the call inside the method if the traversal is pruned' do
66
+ expect(ast.each_node(:send, [:def]).to_a).to be_empty
67
+ end
68
+ end
69
+
70
+ it 'finds 3 ifs in a class' do
71
+ src = <<-EOS
72
+ class Scrunch
73
+ def first
74
+ return @field == :sym ? 0 : 3;
75
+ end
76
+ def second
77
+ if @field == :sym
78
+ @other += " quarts"
79
+ end
80
+ end
81
+ def third
82
+ raise 'flu!' unless @field == :sym
83
+ end
84
+ end
85
+ EOS
86
+ ast = Reek::Source::SourceCode.from(src).syntax_tree
87
+ expect(ast.each_node(:if).to_a.length).to eq(3)
88
+ end
89
+ end
90
+
5
91
  describe '#format_to_ruby' do
6
92
  it 'returns #to_s if location is not present' do
7
93
  ast = sexp(:self)
@@ -56,7 +142,7 @@ RSpec.describe Reek::AST::Node do
56
142
  end
57
143
  end
58
144
 
59
- context 'hash' do
145
+ describe '#hash' do
60
146
  it 'hashes equal for equal sexps' do
61
147
  node1 = sexp(:def, :jim, sexp(:args), sexp(:send, sexp(:int, 4), :+, sexp(:send, nil, :fred)))
62
148
  node2 = sexp(:def, :jim, sexp(:args), sexp(:send, sexp(:int, 4), :+, sexp(:send, nil, :fred)))
@@ -77,8 +163,8 @@ RSpec.describe Reek::AST::Node do
77
163
  end
78
164
 
79
165
  describe '#line' do
80
- context 'source from file' do
81
- let(:file) { SAMPLES_PATH.join('smelly.rb') }
166
+ context 'with source from a file' do
167
+ let(:file) { SMELLY_FILE }
82
168
  let(:ast) { Reek::Source::SourceCode.from(file).syntax_tree }
83
169
 
84
170
  it 'returns the right line number' do
@@ -86,8 +172,8 @@ RSpec.describe Reek::AST::Node do
86
172
  end
87
173
  end
88
174
 
89
- context 'source from string' do
90
- let(:source) { File.read(SAMPLES_PATH.join('smelly.rb')) }
175
+ context 'with source from a string' do
176
+ let(:source) { File.read(SMELLY_FILE) }
91
177
  let(:ast) { Reek::Source::SourceCode.from(source).syntax_tree }
92
178
 
93
179
  it 'returns the right line number' do
@@ -97,17 +183,17 @@ RSpec.describe Reek::AST::Node do
97
183
  end
98
184
 
99
185
  describe '#source' do
100
- context 'source from file' do
101
- let(:file) { SAMPLES_PATH.join('smelly.rb') }
186
+ context 'with source from a file' do
187
+ let(:file) { SMELLY_FILE }
102
188
  let(:ast) { Reek::Source::SourceCode.from(file).syntax_tree }
103
189
 
104
190
  it 'returns the file name' do
105
- expect(ast.source).to eq(SAMPLES_PATH.join('smelly.rb').to_s)
191
+ expect(ast.source).to eq(SMELLY_FILE.to_s)
106
192
  end
107
193
  end
108
194
 
109
- context 'source from string' do
110
- let(:source) { File.read(SAMPLES_PATH.join('smelly.rb')) }
195
+ context 'with source from a string' do
196
+ let(:source) { File.read SMELLY_FILE }
111
197
  let(:ast) { Reek::Source::SourceCode.from(source).syntax_tree }
112
198
 
113
199
  it 'returns "string"' do
@@ -115,4 +201,11 @@ RSpec.describe Reek::AST::Node do
115
201
  end
116
202
  end
117
203
  end
204
+
205
+ describe '#name' do
206
+ it 'returns #to_s if no override is present' do
207
+ ast = sexp(:foo)
208
+ expect(ast.name).to eq ast.to_s
209
+ end
210
+ end
118
211
  end
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
2
2
  require_lib 'reek/ast/reference_collector'
3
3
 
4
4
  RSpec.describe Reek::AST::ReferenceCollector do
5
- context 'counting refs to self' do
5
+ describe '#num_refs_to_self' do
6
6
  def refs_to_self(src)
7
7
  syntax_tree = Reek::Source::SourceCode.from(src).syntax_tree
8
8
  described_class.new(syntax_tree).num_refs_to_self
@@ -107,7 +107,7 @@ RSpec.describe Reek::AST::SexpExtensions::DefNode do
107
107
  end
108
108
 
109
109
  it 'finds nodes in the body with #body_nodes' do
110
- expect(node.body_nodes([:first])).to eq [sexp(:first)]
110
+ expect(node.body_nodes([:first]).to_a).to eq [sexp(:first)]
111
111
  end
112
112
  end
113
113
 
@@ -303,12 +303,8 @@ RSpec.describe Reek::AST::SexpExtensions::SendNode do
303
303
  context 'with no parameters' do
304
304
  let(:node) { sexp(:send, nil, :hello) }
305
305
 
306
- it 'has no argument names' do
307
- expect(node.arg_names).to eq []
308
- end
309
-
310
306
  it 'is not considered to be a writable attr' do
311
- expect(sexp(:send, nil, :attr).attr_with_writable_flag?).to be_falsey
307
+ expect(sexp(:send, nil, :attr)).not_to be_attr_with_writable_flag
312
308
  end
313
309
  end
314
310
 
@@ -316,15 +312,15 @@ RSpec.describe Reek::AST::SexpExtensions::SendNode do
316
312
  let(:bare_new) { sexp(:send, nil, :new) }
317
313
 
318
314
  it 'is not considered to be a module creation call' do
319
- expect(bare_new.module_creation_call?).to be_falsey
315
+ expect(bare_new).not_to be_module_creation_call
320
316
  end
321
317
 
322
318
  it 'is not considered to have a module creation receiver' do
323
- expect(bare_new.module_creation_receiver?).to be_falsey
319
+ expect(bare_new).not_to be_module_creation_receiver
324
320
  end
325
321
 
326
322
  it 'is considered to be an object creation call' do
327
- expect(bare_new.object_creation_call?).to be_truthy
323
+ expect(bare_new).to be_object_creation_call
328
324
  end
329
325
  end
330
326
 
@@ -332,31 +328,15 @@ RSpec.describe Reek::AST::SexpExtensions::SendNode do
332
328
  let(:node) { Reek::Source::SourceCode.from('(foo ? bar : baz).new').syntax_tree }
333
329
 
334
330
  it 'is not considered to be a module creation call' do
335
- expect(node.module_creation_call?).to be_falsey
331
+ expect(node).not_to be_module_creation_call
336
332
  end
337
333
 
338
334
  it 'is not considered to have a module creation receiver' do
339
- expect(node.module_creation_receiver?).to be_falsey
335
+ expect(node).not_to be_module_creation_receiver
340
336
  end
341
337
 
342
338
  it 'is considered to be an object creation call' do
343
- expect(node.object_creation_call?).to be_truthy
344
- end
345
- end
346
-
347
- context 'with 1 literal parameter' do
348
- let(:node) { sexp(:send, nil, :hello, sexp(:lit, :param)) }
349
-
350
- it 'has 1 argument name' do
351
- expect(node.arg_names).to eq [:param]
352
- end
353
- end
354
-
355
- context 'with 2 literal parameters' do
356
- let(:node) { sexp(:send, nil, :hello, sexp(:lit, :x), sexp(:lit, :y)) }
357
-
358
- it 'has 2 argument names' do
359
- expect(node.arg_names).to eq [:x, :y]
339
+ expect(node).to be_object_creation_call
360
340
  end
361
341
  end
362
342
  end
@@ -394,7 +374,7 @@ end
394
374
  RSpec.describe Reek::AST::SexpExtensions::LambdaNode do
395
375
  let(:node) { sexp(:lambda) }
396
376
 
397
- context '#name' do
377
+ describe '#name' do
398
378
  it 'returns :lambda' do
399
379
  expect(node.name).to eq 'lambda'
400
380
  end
@@ -466,14 +446,14 @@ RSpec.describe Reek::AST::SexpExtensions::CasgnNode do
466
446
  context 'with single assignment' do
467
447
  it 'does not define a module' do
468
448
  exp = sexp(:casgn, nil, :Foo)
469
- expect(exp.defines_module?).to be_falsey
449
+ expect(exp).not_to be_defines_module
470
450
  end
471
451
  end
472
452
 
473
453
  context 'with implicit receiver to new' do
474
454
  it 'does not define a module' do
475
455
  exp = sexp(:casgn, nil, :Foo, sexp(:send, nil, :new))
476
- expect(exp.defines_module?).to be_falsey
456
+ expect(exp).not_to be_defines_module
477
457
  end
478
458
  end
479
459
 
@@ -481,7 +461,7 @@ RSpec.describe Reek::AST::SexpExtensions::CasgnNode do
481
461
  it 'does not define a module' do
482
462
  exp = Reek::Source::SourceCode.from('Foo = Class.new(Bar)').syntax_tree
483
463
 
484
- expect(exp.defines_module?).to be_truthy
464
+ expect(exp).to be_defines_module
485
465
  end
486
466
  end
487
467
 
@@ -489,7 +469,7 @@ RSpec.describe Reek::AST::SexpExtensions::CasgnNode do
489
469
  it 'does not define a module' do
490
470
  exp = Reek::Source::SourceCode.from('C = ->{}').syntax_tree
491
471
 
492
- expect(exp.defines_module?).to be_falsey
472
+ expect(exp).not_to be_defines_module
493
473
  end
494
474
  end
495
475
 
@@ -497,7 +477,7 @@ RSpec.describe Reek::AST::SexpExtensions::CasgnNode do
497
477
  it 'does not define a module' do
498
478
  exp = Reek::Source::SourceCode.from('C = "hello"').syntax_tree
499
479
 
500
- expect(exp.defines_module?).to be_falsey
480
+ expect(exp).not_to be_defines_module
501
481
  end
502
482
  end
503
483
  end