reek 3.4.0 → 3.4.1

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/features/step_definitions/reek_steps.rb +19 -16
  4. data/lib/reek/ast/sexp_extensions.rb +17 -8
  5. data/lib/reek/cli/application.rb +21 -12
  6. data/lib/reek/cli/option_interpreter.rb +1 -6
  7. data/lib/reek/cli/options.rb +32 -22
  8. data/lib/reek/report/location_formatter.rb +11 -5
  9. data/lib/reek/smells/smell_warning.rb +10 -2
  10. data/lib/reek/smells/uncommunicative_method_name.rb +25 -21
  11. data/lib/reek/smells/uncommunicative_parameter_name.rb +14 -15
  12. data/lib/reek/spec/should_reek_of.rb +44 -9
  13. data/lib/reek/version.rb +1 -1
  14. data/reek.gemspec +2 -2
  15. data/spec/reek/ast/node_spec.rb +1 -1
  16. data/spec/reek/ast/object_refs_spec.rb +1 -1
  17. data/spec/reek/ast/reference_collector_spec.rb +1 -1
  18. data/spec/reek/ast/sexp_extensions_spec.rb +49 -2
  19. data/spec/reek/ast/sexp_formatter_spec.rb +1 -1
  20. data/spec/reek/cli/option_interpreter_spec.rb +7 -7
  21. data/spec/reek/cli/options_spec.rb +27 -19
  22. data/spec/reek/cli/warning_collector_spec.rb +2 -2
  23. data/spec/reek/code_comment_spec.rb +1 -1
  24. data/spec/reek/configuration/app_configuration_spec.rb +4 -4
  25. data/spec/reek/configuration/default_directive_spec.rb +1 -1
  26. data/spec/reek/configuration/directory_directives_spec.rb +1 -1
  27. data/spec/reek/configuration/excluded_paths_spec.rb +1 -1
  28. data/spec/reek/context/code_context_spec.rb +2 -2
  29. data/spec/reek/context/method_context_spec.rb +1 -1
  30. data/spec/reek/context/module_context_spec.rb +2 -2
  31. data/spec/reek/context/root_context_spec.rb +1 -1
  32. data/spec/reek/context/singleton_method_context_spec.rb +1 -1
  33. data/spec/reek/examiner_spec.rb +1 -1
  34. data/spec/reek/report/html_report_spec.rb +2 -2
  35. data/spec/reek/report/json_report_spec.rb +3 -3
  36. data/spec/reek/report/location_formatter_spec.rb +30 -0
  37. data/spec/reek/report/text_report_spec.rb +4 -4
  38. data/spec/reek/report/xml_report_spec.rb +2 -2
  39. data/spec/reek/report/yaml_report_spec.rb +3 -3
  40. data/spec/reek/smells/attribute_spec.rb +1 -1
  41. data/spec/reek/smells/boolean_parameter_spec.rb +1 -1
  42. data/spec/reek/smells/class_variable_spec.rb +2 -2
  43. data/spec/reek/smells/control_parameter_spec.rb +1 -1
  44. data/spec/reek/smells/data_clump_spec.rb +1 -1
  45. data/spec/reek/smells/duplicate_method_call_spec.rb +3 -3
  46. data/spec/reek/smells/feature_envy_spec.rb +2 -2
  47. data/spec/reek/smells/irresponsible_module_spec.rb +2 -2
  48. data/spec/reek/smells/long_parameter_list_spec.rb +2 -2
  49. data/spec/reek/smells/long_yield_list_spec.rb +2 -2
  50. data/spec/reek/smells/module_initialize_spec.rb +1 -1
  51. data/spec/reek/smells/nested_iterators_spec.rb +1 -1
  52. data/spec/reek/smells/nil_check_spec.rb +2 -2
  53. data/spec/reek/smells/prima_donna_method_spec.rb +1 -1
  54. data/spec/reek/smells/repeated_conditional_spec.rb +3 -3
  55. data/spec/reek/smells/smell_configuration_spec.rb +1 -1
  56. data/spec/reek/smells/smell_repository_spec.rb +2 -2
  57. data/spec/reek/smells/smell_warning_spec.rb +1 -1
  58. data/spec/reek/smells/too_many_instance_variables_spec.rb +1 -1
  59. data/spec/reek/smells/too_many_methods_spec.rb +1 -1
  60. data/spec/reek/smells/too_many_statements_spec.rb +1 -1
  61. data/spec/reek/smells/uncommunicative_method_name_spec.rb +2 -2
  62. data/spec/reek/smells/uncommunicative_module_name_spec.rb +2 -2
  63. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +2 -2
  64. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +1 -1
  65. data/spec/reek/smells/unused_parameters_spec.rb +1 -1
  66. data/spec/reek/smells/utility_function_spec.rb +2 -2
  67. data/spec/reek/source/source_code_spec.rb +1 -1
  68. data/spec/reek/source/source_locator_spec.rb +2 -2
  69. data/spec/reek/spec/should_reek_of_spec.rb +90 -37
  70. data/spec/reek/spec/should_reek_only_of_spec.rb +1 -1
  71. data/spec/reek/spec/should_reek_spec.rb +1 -1
  72. data/spec/reek/tree_dresser_spec.rb +2 -2
  73. data/spec/reek/tree_walker_spec.rb +2 -2
  74. data/spec/spec_helper.rb +6 -0
  75. metadata +7 -6
@@ -18,14 +18,9 @@ module Reek
18
18
  #
19
19
  # See {file:docs/Uncommunicative-Parameter-Name.md} for details.
20
20
  class UncommunicativeParameterName < SmellDetector
21
- # The name of the config field that lists the regexps of
22
- # smelly names to be reported.
23
21
  REJECT_KEY = 'reject'
24
22
  DEFAULT_REJECT_PATTERNS = [/^.$/, /[0-9]$/, /[A-Z]/, /^_/]
25
23
 
26
- # The name of the config field that lists the specific names that are
27
- # to be treated as exceptions; these names will not be reported as
28
- # uncommunicative.
29
24
  ACCEPT_KEY = 'accept'
30
25
  DEFAULT_ACCEPT_NAMES = []
31
26
 
@@ -45,14 +40,15 @@ module Reek
45
40
  #
46
41
  # @return [Array<SmellWarning>]
47
42
  #
48
- def examine_context(ctx)
49
- context_expression = ctx.exp
50
- context_expression.parameter_names.select do |name|
51
- bad_name?(ctx, name) && ctx.uses_param?(name)
43
+ def examine_context(context)
44
+ expression = context.exp
45
+ expression.parameter_names.select do |name|
46
+ sanitized_name = sanitize name
47
+ uncommunicative_parameter_name?(name: sanitized_name, context: context)
52
48
  end.map do |name|
53
49
  smell_warning(
54
- context: ctx,
55
- lines: [context_expression.line],
50
+ context: context,
51
+ lines: [expression.line],
56
52
  message: "has the parameter name '#{name}'",
57
53
  parameters: { name: name.to_s })
58
54
  end
@@ -60,10 +56,13 @@ module Reek
60
56
 
61
57
  private
62
58
 
63
- def bad_name?(ctx, name)
64
- sanitized_name = sanitize name
65
- return false if sanitized_name == '*' || accept_names(ctx).include?(sanitized_name)
66
- reject_patterns(ctx).any? { |pattern| sanitized_name.match pattern }
59
+ def uncommunicative_parameter_name?(name: raise, context: raise)
60
+ !acceptable_name?(name: name, context: context) && context.uses_param?(name)
61
+ end
62
+
63
+ def acceptable_name?(name: raise, context: raise)
64
+ accept_names(context).any? { |accept_name| name == accept_name } ||
65
+ reject_patterns(context).none? { |pattern| name.match pattern }
67
66
  end
68
67
 
69
68
  def reject_patterns(context)
@@ -7,6 +7,12 @@ module Reek
7
7
  # code smell.
8
8
  #
9
9
  class ShouldReekOf
10
+ attr_reader :failure_message, :failure_message_when_negated
11
+
12
+ private_attr_reader :configuration, :smell_category, :smell_details
13
+ private_attr_writer :failure_message, :failure_message_when_negated
14
+ private_attr_accessor :examiner
15
+
10
16
  def initialize(smell_category,
11
17
  smell_details = {},
12
18
  configuration = Configuration::AppConfiguration.default)
@@ -17,22 +23,51 @@ module Reek
17
23
 
18
24
  def matches?(actual)
19
25
  self.examiner = Examiner.new(actual, configuration: configuration)
20
- self.all_smells = examiner.smells
21
- all_smells.any? { |warning| warning.matches?(smell_category, smell_details) }
26
+ set_failure_messages
27
+ matching_smell_types? && matching_smell_details?
22
28
  end
23
29
 
24
- def failure_message
25
- "Expected #{examiner.description} to reek of #{smell_category}, but it didn't"
30
+ private
31
+
32
+ def set_failure_messages
33
+ # We set the failure messages for non-matching smell type unconditionally since we
34
+ # need that in any case for "failure_message_when_negated" below.
35
+ # Depending on the existence of matching smell type we check for matching
36
+ # smell details and then overwrite our failure messages conditionally.
37
+ set_failure_messages_for_smell_type
38
+ set_failure_messages_for_smell_details if matching_smell_types? && !matching_smell_details?
26
39
  end
27
40
 
28
- def failure_message_when_negated
29
- "Expected #{examiner.description} not to reek of #{smell_category}, but it did"
41
+ def matching_smell_types
42
+ @matching_smell_types ||= examiner.smells.
43
+ select { |warning| warning.matches_smell_type?(smell_category) }
30
44
  end
31
45
 
32
- private
46
+ def matching_smell_types?
47
+ matching_smell_types.any?
48
+ end
33
49
 
34
- private_attr_reader :configuration, :smell_category, :smell_details
35
- private_attr_accessor :all_smells, :examiner
50
+ def matching_smell_details?
51
+ matching_smell_types.any? { |warning| warning.matches_smell_details?(smell_details) }
52
+ end
53
+
54
+ def set_failure_messages_for_smell_type
55
+ self.failure_message = "Expected #{origin} to reek of #{smell_category}, "\
56
+ 'but it didn\'t'
57
+ self.failure_message_when_negated = "Expected #{origin} not to reek "\
58
+ "of #{smell_category}, but it did"
59
+ end
60
+
61
+ def set_failure_messages_for_smell_details
62
+ self.failure_message = "Expected #{origin} to reek of #{smell_category} "\
63
+ "(which it did) with smell details #{smell_details}, which it didn't"
64
+ self.failure_message_when_negated = "Expected #{origin} not to reek of "\
65
+ "#{smell_category} with smell details #{smell_details}, but it did"
66
+ end
67
+
68
+ def origin
69
+ examiner.description
70
+ end
36
71
 
37
72
  # :reek:UtilityFunction
38
73
  def normalize(smell_category_or_type)
@@ -6,6 +6,6 @@ module Reek
6
6
  # @public
7
7
  module Version
8
8
  # @public
9
- STRING = '3.4.0'
9
+ STRING = '3.4.1'
10
10
  end
11
11
  end
@@ -28,12 +28,12 @@ Gem::Specification.new do |s|
28
28
  s.add_runtime_dependency 'unparser', '~> 0.2.2'
29
29
 
30
30
  s.add_development_dependency 'activesupport', '~> 4.2'
31
- s.add_development_dependency 'aruba', '~> 0.8.0'
31
+ s.add_development_dependency 'aruba', '~> 0.9.0'
32
32
  s.add_development_dependency 'ataru', '~> 0.2.0'
33
33
  s.add_development_dependency 'bundler', '~> 1.1'
34
34
  s.add_development_dependency 'cucumber', '~> 2.0'
35
35
  s.add_development_dependency 'factory_girl', '~> 4.0'
36
36
  s.add_development_dependency 'rake', '~> 10.0'
37
37
  s.add_development_dependency 'rspec', '~> 3.0'
38
- s.add_development_dependency 'rubocop', '~> 0.33.0'
38
+ s.add_development_dependency 'rubocop', '~> 0.34.0'
39
39
  end
@@ -1,5 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/ast/node'
2
+ require_lib 'reek/ast/node'
3
3
 
4
4
  RSpec.describe Reek::AST::Node do
5
5
  context 'format' do
@@ -1,5 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/ast/object_refs'
2
+ require_lib 'reek/ast/object_refs'
3
3
 
4
4
  RSpec.describe Reek::AST::ObjectRefs do
5
5
  let(:refs) { Reek::AST::ObjectRefs.new }
@@ -1,5 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/ast/reference_collector'
2
+ require_lib 'reek/ast/reference_collector'
3
3
 
4
4
  RSpec.describe Reek::AST::ReferenceCollector do
5
5
  context 'counting refs to self' do
@@ -1,5 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/ast/sexp_extensions'
2
+ require_lib 'reek/ast/sexp_extensions'
3
3
 
4
4
  RSpec.describe Reek::AST::SexpExtensions::DefNode do
5
5
  context 'with no parameters' do
@@ -231,6 +231,22 @@ RSpec.describe Reek::AST::SexpExtensions::DefsNode do
231
231
  end
232
232
  end
233
233
 
234
+ RSpec.describe Reek::AST::SexpExtensions::LvarNode do
235
+ let(:node) { sexp(:lvar, :foo) }
236
+
237
+ describe '#simple_name' do
238
+ it 'returns the lvar’s name' do
239
+ expect(node.simple_name).to eq(:foo)
240
+ end
241
+ end
242
+
243
+ describe '#var_name' do
244
+ it 'returns the lvar’s name' do
245
+ expect(node.var_name).to eq(:foo)
246
+ end
247
+ end
248
+ end
249
+
234
250
  RSpec.describe Reek::AST::SexpExtensions::SendNode do
235
251
  context 'with no parameters' do
236
252
  let(:node) { sexp(:send, nil, :hello) }
@@ -238,6 +254,26 @@ RSpec.describe Reek::AST::SexpExtensions::SendNode do
238
254
  it 'has no argument names' do
239
255
  expect(node.arg_names).to eq []
240
256
  end
257
+
258
+ it 'is not considered to be a writable attr' do
259
+ expect(sexp(:send, nil, :attr).attr_with_writable_flag?).to be_falsey
260
+ end
261
+ end
262
+
263
+ context 'when it’s ‘new’ with no parameters and no receiver' do
264
+ let(:bare_new) { sexp(:send, nil, :new) }
265
+
266
+ it 'is not considered to be a module creation call' do
267
+ expect(bare_new.module_creation_call?).to be_falsey
268
+ end
269
+
270
+ it 'is not considered to have a module creation receiver' do
271
+ expect(bare_new.module_creation_receiver?).to be_falsey
272
+ end
273
+
274
+ it 'is considered to be an object creation call' do
275
+ expect(bare_new.object_creation_call?).to be_truthy
276
+ end
241
277
  end
242
278
 
243
279
  context 'with 1 literal parameter' do
@@ -264,6 +300,10 @@ RSpec.describe Reek::AST::SexpExtensions::BlockNode do
264
300
  it 'has no parameter names' do
265
301
  expect(node.parameter_names).to eq []
266
302
  end
303
+
304
+ it 'has a name' do
305
+ expect(node.simple_name).to eq(:block)
306
+ end
267
307
  end
268
308
 
269
309
  context 'with 1 parameter' do
@@ -337,7 +377,14 @@ RSpec.describe Reek::AST::SexpExtensions::CasgnNode do
337
377
  end
338
378
 
339
379
  it 'does not define a module' do
340
- expect(subject.defines_module?).to eq(false)
380
+ expect(subject.defines_module?).to be_falsey
381
+ end
382
+ end
383
+
384
+ context 'with implicit receiver to new' do
385
+ it 'does not define a module' do
386
+ exp = sexp(:casgn, nil, :Foo, sexp(:send, nil, :new))
387
+ expect(exp.defines_module?).to be_falsey
341
388
  end
342
389
  end
343
390
  end
@@ -1,5 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/ast/sexp_formatter'
2
+ require_lib 'reek/ast/sexp_formatter'
3
3
 
4
4
  RSpec.describe Reek::AST::SexpFormatter do
5
5
  describe '::format' do
@@ -1,19 +1,19 @@
1
1
  require_relative '../../spec_helper'
2
2
 
3
- require_relative '../../../lib/reek/cli/options'
4
- require_relative '../../../lib/reek/report/report'
3
+ require_lib 'reek/cli/options'
4
+ require_lib 'reek/cli/option_interpreter'
5
+ require_lib 'reek/report/report'
5
6
 
6
7
  RSpec.describe Reek::CLI::OptionInterpreter do
7
8
  describe '#reporter' do
8
- let(:instance) { Reek::CLI::OptionInterpreter.new(options) }
9
-
10
9
  context 'with a valid set of options' do
11
10
  let(:options) do
12
- OpenStruct.new(report_format: :text,
13
- location_format: :plain)
11
+ Reek::CLI::Options.new.parse
14
12
  end
13
+ subject { described_class.new(options) }
14
+
15
15
  it 'returns an object of the correct subclass of Report::Base' do
16
- expect(instance.reporter).to be_instance_of Reek::Report::TextReport
16
+ expect(subject.reporter).to be_instance_of Reek::Report::TextReport
17
17
  end
18
18
  end
19
19
  end
@@ -1,28 +1,36 @@
1
1
  require_relative '../../spec_helper'
2
-
3
- require_relative '../../../lib/reek/cli/options'
2
+ require_lib 'reek/cli/options'
4
3
 
5
4
  RSpec.describe Reek::CLI::Options do
6
- describe '#parse' do
7
- context 'with no arguments passed' do
8
- let(:options) { Reek::CLI::Options.new.parse }
9
- it 'enables colors when stdout is a TTY' do
10
- allow($stdout).to receive_messages(tty?: false)
11
- expect(options.colored).to be false
12
- end
5
+ describe '#initialize' do
6
+ it 'sets a valid default value for report_format' do
7
+ expect(subject.report_format).to eq :text
8
+ end
13
9
 
14
- it 'does not enable colors when stdout is not a TTY' do
15
- allow($stdout).to receive_messages(tty?: true)
16
- expect(options.colored).to be true
17
- end
10
+ it 'sets a valid default value for location_format' do
11
+ expect(subject.location_format).to eq :numbers
12
+ end
13
+
14
+ it 'enables colors when stdout is a TTY' do
15
+ allow($stdout).to receive_messages(tty?: false)
16
+ expect(subject.colored).to be false
17
+ end
18
18
 
19
- it 'sets a valid default value for report_format' do
20
- expect(options.report_format).to eq :text
21
- end
19
+ it 'does not enable colors when stdout is not a TTY' do
20
+ allow($stdout).to receive_messages(tty?: true)
21
+ expect(subject.colored).to be true
22
+ end
23
+ end
24
+
25
+ describe 'parse' do
26
+ it 'raises on invalid argument in ARGV' do
27
+ options = described_class.new
28
+ options.argv = ['-z']
29
+ expect { options.parse }.to raise_error(OptionParser::InvalidOption)
30
+ end
22
31
 
23
- it 'sets a valid default value for location_format' do
24
- expect(options.location_format).to eq :numbers
25
- end
32
+ it 'returns self' do
33
+ expect(subject.parse).to be_a(described_class)
26
34
  end
27
35
  end
28
36
  end
@@ -1,6 +1,6 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/cli/warning_collector'
3
- require_relative '../../../lib/reek/smells/smell_warning'
2
+ require_lib 'reek/cli/warning_collector'
3
+ require_lib 'reek/smells/smell_warning'
4
4
 
5
5
  RSpec.describe Reek::CLI::WarningCollector do
6
6
  let(:collector) { described_class.new }
@@ -1,5 +1,5 @@
1
1
  require_relative '../spec_helper'
2
- require_relative '../../lib/reek/code_comment'
2
+ require_lib 'reek/code_comment'
3
3
 
4
4
  RSpec.describe Reek::CodeComment do
5
5
  context 'with an empty comment' do
@@ -1,9 +1,9 @@
1
1
  require 'pathname'
2
2
  require_relative '../../spec_helper'
3
- require_relative '../../../lib/reek/configuration/app_configuration'
4
- require_relative '../../../lib/reek/configuration/directory_directives'
5
- require_relative '../../../lib/reek/configuration/default_directive'
6
- require_relative '../../../lib/reek/configuration/excluded_paths'
3
+ require_lib 'reek/configuration/app_configuration'
4
+ require_lib 'reek/configuration/directory_directives'
5
+ require_lib 'reek/configuration/default_directive'
6
+ require_lib 'reek/configuration/excluded_paths'
7
7
 
8
8
  RSpec.describe Reek::Configuration::AppConfiguration do
9
9
  describe '#new' do
@@ -1,5 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/configuration/default_directive'
2
+ require_lib 'reek/configuration/default_directive'
3
3
 
4
4
  RSpec.describe Reek::Configuration::DefaultDirective do
5
5
  describe '#add' do
@@ -1,5 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/configuration/directory_directives'
2
+ require_lib 'reek/configuration/directory_directives'
3
3
 
4
4
  RSpec.describe Reek::Configuration::DirectoryDirectives do
5
5
  describe '#directive_for' do
@@ -1,5 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/configuration/excluded_paths'
2
+ require_lib 'reek/configuration/excluded_paths'
3
3
 
4
4
  RSpec.describe Reek::Configuration::ExcludedPaths do
5
5
  describe '#add' do
@@ -1,6 +1,6 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/context/method_context'
3
- require_relative '../../../lib/reek/context/module_context'
2
+ require_lib 'reek/context/method_context'
3
+ require_lib 'reek/context/module_context'
4
4
 
5
5
  RSpec.describe Reek::Context::CodeContext do
6
6
  context 'name recognition' do
@@ -1,5 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/context/method_context'
2
+ require_lib 'reek/context/method_context'
3
3
 
4
4
  RSpec.describe Reek::Context::MethodContext do
5
5
  let(:method_context) { Reek::Context::MethodContext.new(nil, exp) }
@@ -1,6 +1,6 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/context/module_context'
3
- require_relative '../../../lib/reek/context/root_context'
2
+ require_lib 'reek/context/module_context'
3
+ require_lib 'reek/context/root_context'
4
4
 
5
5
  RSpec.describe Reek::Context::ModuleContext do
6
6
  it 'should report module name for smell in method' do
@@ -1,5 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/context/root_context'
2
+ require_lib 'reek/context/root_context'
3
3
 
4
4
  RSpec.describe Reek::Context::RootContext do
5
5
  context 'full_name' do
@@ -1,5 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/reek/context/singleton_method_context'
2
+ require_lib 'reek/context/singleton_method_context'
3
3
 
4
4
  RSpec.describe Reek::Context::SingletonMethodContext do
5
5
  let(:smc) do