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
@@ -98,7 +98,7 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
98
98
  expect(src).to reek_of(:UtilityFunction, context: 'simple')
99
99
  end
100
100
 
101
- context 'Singleton methods' do
101
+ context 'when examining singleton methods' do
102
102
  ['class', 'module'].each do |scope|
103
103
  it "does not report for #{scope} with `class << self` notation" do
104
104
  src = "#{scope} Alfa; class << self; def bravo(charlie) charlie.to_s; end; end; end"
@@ -111,7 +111,7 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
111
111
  end
112
112
  end
113
113
 
114
- context 'by using `module_function`' do
114
+ context 'when defined by using `module_function`' do
115
115
  it 'does not report UtilityFunction also when using multiple arguments' do
116
116
  src = <<-EOS
117
117
  class Alfa
@@ -194,7 +194,7 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
194
194
  { Reek::SmellDetectors::UtilityFunction::PUBLIC_METHODS_ONLY_KEY => true }
195
195
  end
196
196
 
197
- context 'public methods' do
197
+ context 'when examining public methods' do
198
198
  it 'still reports UtilityFunction' do
199
199
  src = <<-EOS
200
200
  class Alfa
@@ -208,7 +208,7 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
208
208
  end
209
209
  end
210
210
 
211
- context 'private methods' do
211
+ context 'when examining private methods' do
212
212
  it 'does not report UtilityFunction' do
213
213
  src = <<-EOS
214
214
  class Alfa
@@ -221,9 +221,21 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
221
221
 
222
222
  expect(src).not_to reek_of(:UtilityFunction).with_config(config)
223
223
  end
224
+
225
+ it 'does not report UtilityFunction when private is used as a def modifier' do
226
+ src = <<-EOS
227
+ class Alfa
228
+ private def bravo(charlie)
229
+ charlie.delta.echo
230
+ end
231
+ end
232
+ EOS
233
+
234
+ expect(src).not_to reek_of(:UtilityFunction).with_config(config)
235
+ end
224
236
  end
225
237
 
226
- context 'protected methods' do
238
+ context 'when examining protected methods' do
227
239
  it 'does not report UtilityFunction' do
228
240
  src = <<-EOS
229
241
  class Alfa
@@ -236,6 +248,46 @@ RSpec.describe Reek::SmellDetectors::UtilityFunction do
236
248
 
237
249
  expect(src).not_to reek_of(:UtilityFunction).with_config(config)
238
250
  end
251
+
252
+ it 'does not report UtilityFunction when protected is used as a def modifier' do
253
+ src = <<-EOS
254
+ class Alfa
255
+ protected def bravo(charlie)
256
+ charlie.delta.echo
257
+ end
258
+ end
259
+ EOS
260
+
261
+ expect(src).not_to reek_of(:UtilityFunction).with_config(config)
262
+ end
263
+ end
264
+ end
265
+
266
+ describe 'disabling with a comment' do
267
+ it 'disables the method following the comment' do
268
+ src = <<-EOS
269
+ class Alfa
270
+ # :reek:UtilityFunction
271
+ def bravo(charlie)
272
+ charlie.delta.echo
273
+ end
274
+ end
275
+ EOS
276
+
277
+ expect(src).not_to reek_of(:UtilityFunction)
278
+ end
279
+
280
+ it 'disables a method when it has a visibility modifier' do
281
+ src = <<-EOS
282
+ class Alfa
283
+ # :reek:UtilityFunction
284
+ private def bravo(charlie)
285
+ charlie.delta.echo
286
+ end
287
+ end
288
+ EOS
289
+
290
+ expect(src).not_to reek_of(:UtilityFunction)
239
291
  end
240
292
  end
241
293
  end
@@ -7,7 +7,7 @@ RSpec.describe Reek::SmellWarning do
7
7
  let(:utility_function_detector) { build(:smell_detector, smell_type: 'UtilityFunction') }
8
8
  let(:uncommunicative_name_detector) { build(:smell_detector, smell_type: 'UncommunicativeVariableName') }
9
9
 
10
- context 'sort order' do
10
+ describe 'sort order' do
11
11
  shared_examples_for 'first sorts ahead of second' do
12
12
  it 'hash differently' do
13
13
  expect(first.hash).not_to eq(second.hash)
@@ -26,21 +26,21 @@ RSpec.describe Reek::SmellWarning do
26
26
  end
27
27
  end
28
28
 
29
- context 'smells differing only by detector' do
29
+ context 'when smells differ only by detector' do
30
30
  let(:first) { build(:smell_warning, smell_detector: duplication_detector) }
31
31
  let(:second) { build(:smell_warning, smell_detector: feature_envy_detector) }
32
32
 
33
33
  it_behaves_like 'first sorts ahead of second'
34
34
  end
35
35
 
36
- context 'smells differing only by lines' do
36
+ context 'when smells differ only by lines' do
37
37
  let(:first) { build(:smell_warning, smell_detector: feature_envy_detector, lines: [2]) }
38
38
  let(:second) { build(:smell_warning, smell_detector: feature_envy_detector, lines: [3]) }
39
39
 
40
40
  it_behaves_like 'first sorts ahead of second'
41
41
  end
42
42
 
43
- context 'smells differing only by context' do
43
+ context 'when smells differ only by context' do
44
44
  let(:first) { build(:smell_warning, smell_detector: duplication_detector, context: 'first') }
45
45
  let(:second) do
46
46
  build(:smell_warning, smell_detector: duplication_detector, context: 'second')
@@ -49,7 +49,7 @@ RSpec.describe Reek::SmellWarning do
49
49
  it_behaves_like 'first sorts ahead of second'
50
50
  end
51
51
 
52
- context 'smells differing only by message' do
52
+ context 'when smells differ only by message' do
53
53
  let(:first) do
54
54
  build(:smell_warning, smell_detector: duplication_detector,
55
55
  context: 'ctx', message: 'first message')
@@ -62,7 +62,7 @@ RSpec.describe Reek::SmellWarning do
62
62
  it_behaves_like 'first sorts ahead of second'
63
63
  end
64
64
 
65
- context 'smell name takes precedence over message' do
65
+ context 'when smells differ by name and message' do
66
66
  let(:first) do
67
67
  build(:smell_warning, smell_detector: feature_envy_detector, message: 'second message')
68
68
  end
@@ -73,7 +73,7 @@ RSpec.describe Reek::SmellWarning do
73
73
  it_behaves_like 'first sorts ahead of second'
74
74
  end
75
75
 
76
- context 'smells differing everywhere' do
76
+ context 'when smells differ everywhere' do
77
77
  let(:first) do
78
78
  build(:smell_warning, smell_detector: duplication_detector,
79
79
  context: 'Dirty#a',
@@ -97,7 +97,7 @@ RSpec.describe Reek::SmellWarning do
97
97
  end
98
98
  end
99
99
 
100
- context '#yaml_hash' do
100
+ describe '#yaml_hash' do
101
101
  let(:context_name) { 'Module::Class#method/block' }
102
102
  let(:lines) { [24, 513] }
103
103
  let(:message) { 'test message' }
@@ -135,6 +135,10 @@ RSpec.describe Reek::SmellWarning do
135
135
  expect(yaml['source']).to eq source
136
136
  end
137
137
 
138
+ it 'includes the documentation link' do
139
+ expect(yaml['documentation_link']).to eq Reek::DocumentationLink.build('FeatureEnvy')
140
+ end
141
+
138
142
  it 'includes the parameters' do
139
143
  parameters.each do |key, value|
140
144
  expect(yaml[key]).to eq value
@@ -6,59 +6,60 @@ RSpec.describe Reek::Source::SourceCode do
6
6
  describe '#syntax_tree' do
7
7
  it 'associates comments with the AST' do
8
8
  source = "# this is\n# a comment\ndef foo; end"
9
- source_code = described_class.new(code: source, origin: '(string)')
9
+ source_code = described_class.new(source: source, origin: '(string)')
10
10
  result = source_code.syntax_tree
11
11
  expect(result.leading_comment).to eq "# this is\n# a comment"
12
12
  end
13
13
 
14
14
  it 'cleanly processes empty source' do
15
- source_code = described_class.new(code: '', origin: '(string)')
15
+ source_code = described_class.new(source: '', origin: '(string)')
16
16
  result = source_code.syntax_tree
17
- expect(result).to be_nil
17
+ expect(result.type).to eq :empty
18
18
  end
19
19
 
20
20
  it 'cleanly processes empty source with comments' do
21
21
  source = "# this is\n# a comment\n"
22
- source_code = described_class.new(code: source, origin: '(string)')
22
+ source_code = described_class.new(source: source, origin: '(string)')
23
23
  result = source_code.syntax_tree
24
- expect(result).to be_nil
24
+ expect(result.type).to eq :empty
25
25
  end
26
26
 
27
27
  it 'does not crash with sequences incompatible with UTF-8' do
28
28
  source = '"\xFF"'
29
- source_code = described_class.new(code: source, origin: '(string)')
29
+ source_code = described_class.new(source: source, origin: '(string)')
30
30
  result = source_code.syntax_tree
31
31
  expect(result.children.first).to eq "\xFF"
32
32
  end
33
- end
34
33
 
35
- context 'when the parser fails' do
36
- let(:source_name) { 'Test source' }
37
- let(:src) { described_class.new(code: code, origin: source_name, **parser) }
34
+ it 'returns a :lambda node for lambda expressions' do
35
+ source = '->() { }'
36
+ source_code = described_class.new(source: source, origin: '(string)')
37
+ result = source_code.syntax_tree
38
+ expect(result.children.first.type).to eq :lambda
39
+ end
38
40
 
39
- context 'with a Parser::SyntaxError' do
41
+ context 'when the parser fails with a Parser::SyntaxError' do
42
+ let(:src) { described_class.new(source: code) }
40
43
  let(:code) { '== Invalid Syntax ==' }
41
- let(:parser) { {} }
42
44
 
43
- it 'adds a diagnostic' do
44
- expect(src.diagnostics.size).to eq 2
45
+ it 'raises the error' do
46
+ expect { src.syntax_tree }.to raise_error Parser::SyntaxError
45
47
  end
46
48
  end
47
49
 
48
- context 'with a generic error' do
50
+ context 'when the parser fails with a generic error' do
49
51
  let(:code) { '' }
52
+ let(:parser) { instance_double('Parser::Ruby25') }
53
+ let(:src) { described_class.new(source: code, parser: parser) }
50
54
  let(:error_class) { RuntimeError }
51
55
  let(:error_message) { 'An error' }
52
- let(:parser) do
53
- parser = instance_double('Parser::Ruby24')
56
+
57
+ before do
54
58
  allow(parser).to receive(:parse_with_comments).and_raise(error_class, error_message)
55
- {
56
- parser: parser
57
- }
58
59
  end
59
60
 
60
61
  it 'raises the error' do
61
- expect { src }.to raise_error error_class, error_message
62
+ expect { src.syntax_tree }.to raise_error error_class, error_message
62
63
  end
63
64
  end
64
65
  end
@@ -5,15 +5,15 @@ require_lib 'reek/source/source_locator'
5
5
 
6
6
  RSpec.describe Reek::Source::SourceLocator do
7
7
  describe '#sources' do
8
- context 'applied to hidden directories' do
9
- let(:path) { SAMPLES_PATH.join('source_with_hidden_directories') }
8
+ context 'when applied to hidden directories' do
9
+ let(:path) { SAMPLES_DIR.join('source_with_hidden_directories') }
10
10
 
11
11
  let(:expected_paths) do
12
- [path.join('uncommunicative_parameter_name.rb')]
12
+ [path.join('not_hidden.rb')]
13
13
  end
14
14
 
15
15
  let(:paths_that_are_expected_to_be_ignored) do
16
- [path.join('.hidden/uncommunicative_method_name.rb')]
16
+ [path.join('.hidden/hidden.rb')]
17
17
  end
18
18
 
19
19
  it 'does not scan hidden directories' do
@@ -30,21 +30,21 @@ RSpec.describe Reek::Source::SourceLocator do
30
30
  end
31
31
 
32
32
  # rubocop:disable RSpec/NestedGroups
33
- context 'exclude paths' do
33
+ context 'with excluded paths' do
34
34
  let(:configuration) do
35
- test_configuration_for(CONFIG_PATH.join('with_excluded_paths.reek'))
35
+ test_configuration_for(CONFIGURATION_DIR.join('with_excluded_paths.reek'))
36
36
  end
37
37
 
38
38
  let(:options) { instance_double('Reek::CLI::Options', force_exclusion?: false) }
39
39
 
40
40
  context 'when the path is absolute' do
41
41
  let(:path) do
42
- SAMPLES_PATH.join('source_with_exclude_paths',
43
- 'ignore_me',
44
- 'uncommunicative_method_name.rb').expand_path
42
+ SAMPLES_DIR.join('source_with_exclude_paths',
43
+ 'ignore_me',
44
+ 'uncommunicative_method_name.rb').expand_path
45
45
  end
46
46
 
47
- context 'and options.force_exclusion? is true' do
47
+ context 'when options.force_exclusion? is true' do
48
48
  before do
49
49
  allow(options).to receive(:force_exclusion?).and_return(true)
50
50
  end
@@ -55,7 +55,7 @@ RSpec.describe Reek::Source::SourceLocator do
55
55
  end
56
56
  end
57
57
 
58
- context 'and options.force_exclusion? is false' do
58
+ context 'when options.force_exclusion? is false' do
59
59
  before do
60
60
  allow(options).to receive(:force_exclusion?).and_return(false)
61
61
  end
@@ -68,7 +68,7 @@ RSpec.describe Reek::Source::SourceLocator do
68
68
  end
69
69
 
70
70
  context 'when the path is a file name in an excluded directory' do
71
- let(:path) { SAMPLES_PATH.join('source_with_exclude_paths', 'ignore_me', 'uncommunicative_method_name.rb') }
71
+ let(:path) { SAMPLES_DIR.join('source_with_exclude_paths', 'ignore_me', 'uncommunicative_method_name.rb') }
72
72
 
73
73
  context 'when options.force_exclusion? is true' do
74
74
  before do
@@ -94,7 +94,7 @@ RSpec.describe Reek::Source::SourceLocator do
94
94
  end
95
95
 
96
96
  context 'when path is a directory' do
97
- let(:path) { SAMPLES_PATH.join('source_with_exclude_paths') }
97
+ let(:path) { SAMPLES_DIR.join('source_with_exclude_paths') }
98
98
 
99
99
  let(:expected_paths) do
100
100
  [path.join('nested/uncommunicative_parameter_name.rb')]
@@ -120,10 +120,10 @@ RSpec.describe Reek::Source::SourceLocator do
120
120
  end
121
121
  # rubocop:enable RSpec/NestedGroups
122
122
 
123
- context 'non-Ruby paths' do
124
- let(:path) { SAMPLES_PATH.join('source_with_non_ruby_files') }
123
+ context 'with non-Ruby paths' do
124
+ let(:path) { SAMPLES_DIR.join('source_with_non_ruby_files') }
125
125
  let(:expected_sources) do
126
- [path.join('uncommunicative_parameter_name.rb')]
126
+ [path.join('ruby.rb')]
127
127
  end
128
128
  let(:paths_that_are_expected_to_be_ignored) do
129
129
  [
@@ -145,7 +145,7 @@ RSpec.describe Reek::Source::SourceLocator do
145
145
  end
146
146
  end
147
147
 
148
- context 'passing "." or "./" as argument' do
148
+ context 'when passing "." or "./" as argument' do
149
149
  let(:expected_sources) do
150
150
  [Pathname.new('spec/spec_helper.rb'), Pathname.new('lib/reek.rb')]
151
151
  end
@@ -11,50 +11,46 @@ RSpec.describe Reek::Spec::ShouldReekOf do
11
11
  expect(ruby).to reek_of(:DuplicateMethodCall)
12
12
  end
13
13
 
14
- it 'reports duplicate calls by smell detector class' do
15
- expect(ruby).to reek_of(Reek::SmellDetectors::DuplicateMethodCall)
16
- end
17
-
18
14
  it 'does not report any feature envy' do
19
15
  expect(ruby).not_to reek_of(:FeatureEnvy)
20
16
  end
21
17
  end
22
18
 
23
19
  describe 'different sources of input' do
24
- context 'checking code in a string' do
20
+ context 'when checking code in a string' do
25
21
  let(:clean_code) { 'def good() true; end' }
26
22
  let(:matcher) { described_class.new(:UncommunicativeVariableName, name: 'y') }
27
23
  let(:smelly_code) { 'def x() y = 4; end' }
28
24
 
29
25
  it 'matches a smelly String' do
30
- expect(matcher.matches?(smelly_code)).to be_truthy
26
+ expect(matcher).to be_matches(smelly_code)
31
27
  end
32
28
 
33
29
  it 'doesnt match a fragrant String' do
34
- expect(matcher.matches?(clean_code)).to be_falsey
30
+ expect(matcher).not_to be_matches(clean_code)
35
31
  end
36
32
 
37
33
  it 're-calculates matches every time' do
38
34
  matcher.matches? smelly_code
39
- expect(matcher.matches?(clean_code)).to be_falsey
35
+ expect(matcher).not_to be_matches(clean_code)
40
36
  end
41
37
  end
42
38
 
43
- context 'checking code in a File' do
39
+ context 'when checking code in a File' do
44
40
  let(:matcher) { described_class.new(:UncommunicativeMethodName, name: 'x') }
45
41
 
46
42
  it 'matches a smelly file' do
47
- expect(matcher.matches?(SMELLY_FILE)).to be_truthy
43
+ expect(matcher).to be_matches(SMELLY_FILE)
48
44
  end
49
45
 
50
46
  it 'doesnt match a fragrant file' do
51
- expect(matcher.matches?(CLEAN_FILE)).to be_falsey
47
+ expect(matcher).not_to be_matches(CLEAN_FILE)
52
48
  end
53
49
  end
54
50
  end
55
51
 
56
52
  describe 'smell types and smell details' do
57
- context 'passing in smell_details with unknown parameter name' do
53
+ context 'when passing in smell_details with unknown parameter name' do
58
54
  let(:matcher) { described_class.new(:UncommunicativeVariableName, foo: 'y') }
59
55
  let(:smelly_code) { 'def x() y = 4; end' }
60
56
 
@@ -63,23 +59,23 @@ RSpec.describe Reek::Spec::ShouldReekOf do
63
59
  end
64
60
  end
65
61
 
66
- context 'both are matching' do
62
+ context 'when both are matching' do
67
63
  let(:matcher) { described_class.new(:UncommunicativeVariableName, name: 'y') }
68
64
  let(:smelly_code) { 'def x() y = 4; end' }
69
65
 
70
66
  it 'is truthy' do
71
- expect(matcher.matches?(smelly_code)).to be_truthy
67
+ expect(matcher).to be_matches(smelly_code)
72
68
  end
73
69
  end
74
70
 
75
- context 'no smell_type is matching' do
71
+ context 'when no smell_type is matching' do
76
72
  let(:smelly_code) { 'def dummy() y = 4; end' }
77
73
 
78
74
  let(:falsey_matcher) { described_class.new(:FeatureEnvy, name: 'y') }
79
75
  let(:truthy_matcher) { described_class.new(:UncommunicativeVariableName, name: 'y') }
80
76
 
81
77
  it 'is falsey' do
82
- expect(falsey_matcher.matches?(smelly_code)).to be_falsey
78
+ expect(falsey_matcher).not_to be_matches(smelly_code)
83
79
  end
84
80
 
85
81
  it 'sets the proper error message' do
@@ -97,12 +93,12 @@ RSpec.describe Reek::Spec::ShouldReekOf do
97
93
  end
98
94
  end
99
95
 
100
- context 'smell type is matching but smell details are not' do
96
+ context 'when smell type is matching but smell details are not' do
101
97
  let(:smelly_code) { 'def double_thing() @other.thing.foo + @other.thing.foo end' }
102
98
  let(:matcher) { described_class.new(:DuplicateMethodCall, name: 'foo', count: 15) }
103
99
 
104
100
  it 'is falsey' do
105
- expect(matcher.matches?(smelly_code)).to be_falsey
101
+ expect(matcher).not_to be_matches(smelly_code)
106
102
  end
107
103
 
108
104
  it 'sets the proper error message' do
@@ -129,15 +125,15 @@ RSpec.describe Reek::Spec::ShouldReekOf do
129
125
  end
130
126
  end
131
127
 
132
- context 'for a smell that is disabled by default' do
133
- before do
134
- default_config = Reek::SmellDetectors::UnusedPrivateMethod.default_config
135
- expect(default_config[Reek::SmellConfiguration::ENABLED_KEY]).to be_falsy
136
- end
137
-
128
+ context 'with a smell that is disabled by default' do
138
129
  it 'enables the smell detector to match automatically' do
130
+ default_config = Reek::SmellDetectors::UnusedPrivateMethod.default_config
139
131
  src = 'class C; private; def foo; end; end'
140
- expect(src).to reek_of(:UnusedPrivateMethod)
132
+
133
+ aggregate_failures do
134
+ expect(default_config[Reek::SmellConfiguration::ENABLED_KEY]).to be_falsy
135
+ expect(src).to reek_of(:UnusedPrivateMethod)
136
+ end
141
137
  end
142
138
  end
143
139
 
@@ -146,13 +142,13 @@ RSpec.describe Reek::Spec::ShouldReekOf do
146
142
  let(:configured_matcher) { matcher.with_config('accept' => 'x') }
147
143
 
148
144
  it 'uses the passed-in configuration for matching' do
149
- expect(configured_matcher.matches?('def foo; q = 2; end')).to be_truthy
150
- expect(configured_matcher.matches?('def foo; x = 2; end')).to be_falsey
145
+ expect(configured_matcher).to be_matches('def foo; q = 2; end')
146
+ expect(configured_matcher).not_to be_matches('def foo; x = 2; end')
151
147
  end
152
148
 
153
149
  it 'leaves the original matcher intact' do
154
- expect(configured_matcher.matches?('def foo; x = 2; end')).to be_falsey
155
- expect(matcher.matches?('def foo; x = 2; end')).to be_truthy
150
+ expect(configured_matcher).not_to be_matches('def foo; x = 2; end')
151
+ expect(matcher).to be_matches('def foo; x = 2; end')
156
152
  end
157
153
  end
158
154
  end
@@ -21,7 +21,7 @@ RSpec.describe Reek::Spec::ShouldReekOnlyOf do
21
21
  context 'when a match was expected' do
22
22
  let(:source) { 'the_path/to_a/source_file.rb' }
23
23
 
24
- before { allow(examiner).to receive(:description).and_return(source) }
24
+ before { allow(examiner).to receive(:origin).and_return(source) }
25
25
 
26
26
  it 'reports the source' do
27
27
  expect(matcher.failure_message).to match(source)
@@ -91,7 +91,7 @@ RSpec.describe Reek::Spec::ShouldReekOnlyOf do
91
91
 
92
92
  it 'reports the source when no match was expected' do
93
93
  source = 'the_path/to_a/source_file.rb'
94
- allow(examiner).to receive(:description).and_return(source)
94
+ allow(examiner).to receive(:origin).and_return(source)
95
95
  expect(matcher.failure_message_when_negated).to match(source)
96
96
  end
97
97
  end
@@ -8,11 +8,11 @@ RSpec.describe Reek::Spec::ShouldReek do
8
8
  let(:smelly_code) { 'def x() y = 4; end' }
9
9
 
10
10
  it 'matches a smelly String' do
11
- expect(matcher.matches?(smelly_code)).to be_truthy
11
+ expect(matcher).to be_matches(smelly_code)
12
12
  end
13
13
 
14
14
  it 'doesnt match a fragrant String' do
15
- expect(matcher.matches?(clean_code)).to be_falsey
15
+ expect(matcher).not_to be_matches(clean_code)
16
16
  end
17
17
 
18
18
  it 'reports the smells when should_not fails' do
@@ -22,15 +22,15 @@ RSpec.describe Reek::Spec::ShouldReek do
22
22
  end
23
23
 
24
24
  describe 'checking code in a File' do
25
- context 'matcher without masking' do
25
+ context 'without masking' do
26
26
  let(:matcher) { described_class.new }
27
27
 
28
28
  it 'matches a smelly File' do
29
- expect(matcher.matches?(SMELLY_FILE)).to be_truthy
29
+ expect(matcher).to be_matches(SMELLY_FILE)
30
30
  end
31
31
 
32
32
  it 'doesnt match a fragrant File' do
33
- expect(matcher.matches?(CLEAN_FILE)).to be_falsey
33
+ expect(matcher).not_to be_matches(CLEAN_FILE)
34
34
  end
35
35
 
36
36
  it 'reports the smells when should_not fails' do
@@ -39,13 +39,13 @@ RSpec.describe Reek::Spec::ShouldReek do
39
39
  end
40
40
  end
41
41
 
42
- context 'matcher without masking' do
43
- let(:path) { CONFIG_PATH.join('full_mask.reek') }
42
+ context 'with masking' do
43
+ let(:path) { CONFIGURATION_DIR.join('full_mask.reek') }
44
44
  let(:configuration) { test_configuration_for(path) }
45
45
  let(:matcher) { described_class.new(configuration: configuration) }
46
46
 
47
47
  it 'masks smells using the relevant configuration' do
48
- expect(matcher.matches?(SMELLY_FILE)).to be_falsey
48
+ expect(matcher).not_to be_matches(SMELLY_FILE)
49
49
  end
50
50
  end
51
51
  end
@@ -10,35 +10,35 @@ RSpec.describe Reek::Spec::SmellMatcher do
10
10
  end
11
11
  let(:matcher) { described_class.new(smell_warning) }
12
12
 
13
- context '#matches?' do
13
+ describe '#matches?' do
14
14
  it 'matches on class symbol' do
15
- expect(matcher.matches?(:UncommunicativeVariableName)).to be_truthy
15
+ expect(matcher).to be_matches(:UncommunicativeVariableName)
16
16
  end
17
17
 
18
18
  it 'matches on class symbol and params' do
19
- expect(matcher.matches?(:UncommunicativeVariableName,
20
- test: 'something')).to be_truthy
19
+ expect(matcher).to be_matches(:UncommunicativeVariableName,
20
+ test: 'something')
21
21
  end
22
22
 
23
23
  it 'matches on class symbol, params and attributes' do
24
- expect(matcher.matches?(:UncommunicativeVariableName,
25
- test: 'something',
26
- message: "has the variable name '@s'")).to be_truthy
24
+ expect(matcher).to be_matches(:UncommunicativeVariableName,
25
+ test: 'something',
26
+ message: "has the variable name '@s'")
27
27
  end
28
28
 
29
29
  it 'does not match on different class symbol' do
30
- expect(matcher.matches?(:FeatureEnvy)).to be_falsy
30
+ expect(matcher).not_to be_matches(:FeatureEnvy)
31
31
  end
32
32
 
33
33
  it 'does not match on different params' do
34
- expect(matcher.matches?(:UncommunicativeVariableName,
35
- test: 'something else')).to be_falsy
34
+ expect(matcher).not_to be_matches(:UncommunicativeVariableName,
35
+ test: 'something else')
36
36
  end
37
37
 
38
38
  it 'does not match on different attributes' do
39
- expect(matcher.matches?(:UncommunicativeVariableName,
40
- test: 'something',
41
- message: 'nothing')).to be_falsy
39
+ expect(matcher).not_to be_matches(:UncommunicativeVariableName,
40
+ test: 'something',
41
+ message: 'nothing')
42
42
  end
43
43
 
44
44
  it 'raises error on uncomparable attribute' do
@@ -50,33 +50,33 @@ RSpec.describe Reek::Spec::SmellMatcher do
50
50
  end
51
51
  end
52
52
 
53
- context '#matches_smell_type?' do
53
+ describe '#matches_smell_type?' do
54
54
  it 'matches on class symbol' do
55
- expect(matcher.matches_smell_type?(:UncommunicativeVariableName)).to be_truthy
55
+ expect(matcher).to be_matches_smell_type(:UncommunicativeVariableName)
56
56
  end
57
57
 
58
58
  it 'does not match on different class symbol' do
59
- expect(matcher.matches_smell_type?(:FeatureEnvy)).to be_falsy
59
+ expect(matcher).not_to be_matches_smell_type(:FeatureEnvy)
60
60
  end
61
61
  end
62
62
 
63
- context '#matches_attributes?' do
63
+ describe '#matches_attributes?' do
64
64
  it 'matches on params' do
65
- expect(matcher.matches_attributes?(test: 'something')).to be_truthy
65
+ expect(matcher).to be_matches_attributes(test: 'something')
66
66
  end
67
67
 
68
68
  it 'matches on class symbol, params and attributes' do
69
- expect(matcher.matches_attributes?(test: 'something',
70
- message: "has the variable name '@s'")).to be_truthy
69
+ expect(matcher).to be_matches_attributes(test: 'something',
70
+ message: "has the variable name '@s'")
71
71
  end
72
72
 
73
73
  it 'does not match on different params' do
74
- expect(matcher.matches_attributes?(test: 'something else')).to be_falsy
74
+ expect(matcher).not_to be_matches_attributes(test: 'something else')
75
75
  end
76
76
 
77
77
  it 'does not match on different attributes' do
78
- expect(matcher.matches_attributes?(test: 'something',
79
- message: 'nothing')).to be_falsy
78
+ expect(matcher).not_to be_matches_attributes(test: 'something',
79
+ message: 'nothing')
80
80
  end
81
81
 
82
82
  it 'raises error on uncomparable attribute' do