reek 3.4.0 → 3.4.1

Sign up to get free protection for your applications and to get access to all the features.
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