reek 3.1 → 3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/{CHANGELOG → CHANGELOG.md} +150 -123
  4. data/README.md +61 -21
  5. data/Rakefile +2 -1
  6. data/bin/reek +1 -0
  7. data/config/defaults.reek +2 -2
  8. data/docs/Attribute.md +9 -13
  9. data/docs/Basic-Smell-Options.md +2 -2
  10. data/docs/Command-Line-Options.md +2 -2
  11. data/docs/Too-Many-Instance-Variables.md +1 -1
  12. data/features/samples.feature +22 -31
  13. data/features/step_definitions/sample_file_steps.rb +2 -2
  14. data/features/support/env.rb +1 -0
  15. data/lib/reek.rb +1 -0
  16. data/lib/reek/ast/ast_node_class_map.rb +5 -1
  17. data/lib/reek/ast/node.rb +4 -2
  18. data/lib/reek/ast/object_refs.rb +9 -5
  19. data/lib/reek/ast/reference_collector.rb +4 -2
  20. data/lib/reek/cli/application.rb +12 -9
  21. data/lib/reek/cli/command.rb +4 -0
  22. data/lib/reek/cli/input.rb +4 -4
  23. data/lib/reek/cli/option_interpreter.rb +11 -7
  24. data/lib/reek/cli/options.rb +42 -40
  25. data/lib/reek/cli/reek_command.rb +3 -3
  26. data/lib/reek/cli/warning_collector.rb +7 -3
  27. data/lib/reek/code_comment.rb +5 -1
  28. data/lib/reek/configuration/app_configuration.rb +4 -4
  29. data/lib/reek/context/code_context.rb +19 -17
  30. data/lib/reek/examiner.rb +8 -6
  31. data/lib/reek/rake/task.rb +13 -22
  32. data/lib/reek/report/formatter.rb +5 -1
  33. data/lib/reek/report/report.rb +46 -44
  34. data/lib/reek/smells/attribute.rb +42 -24
  35. data/lib/reek/smells/control_parameter.rb +21 -13
  36. data/lib/reek/smells/data_clump.rb +17 -9
  37. data/lib/reek/smells/duplicate_method_call.rb +12 -6
  38. data/lib/reek/smells/long_parameter_list.rb +2 -2
  39. data/lib/reek/smells/long_yield_list.rb +4 -4
  40. data/lib/reek/smells/nested_iterators.rb +4 -2
  41. data/lib/reek/smells/nil_check.rb +6 -2
  42. data/lib/reek/smells/repeated_conditional.rb +2 -2
  43. data/lib/reek/smells/smell_configuration.rb +15 -7
  44. data/lib/reek/smells/smell_detector.rb +23 -10
  45. data/lib/reek/smells/smell_repository.rb +9 -16
  46. data/lib/reek/smells/smell_warning.rb +6 -6
  47. data/lib/reek/smells/too_many_instance_variables.rb +4 -4
  48. data/lib/reek/smells/too_many_methods.rb +2 -2
  49. data/lib/reek/smells/too_many_statements.rb +4 -4
  50. data/lib/reek/smells/uncommunicative_method_name.rb +5 -5
  51. data/lib/reek/smells/uncommunicative_module_name.rb +5 -5
  52. data/lib/reek/smells/uncommunicative_parameter_name.rb +8 -4
  53. data/lib/reek/smells/uncommunicative_variable_name.rb +8 -4
  54. data/lib/reek/source/source_code.rb +6 -2
  55. data/lib/reek/source/source_locator.rb +4 -4
  56. data/lib/reek/spec/should_reek.rb +9 -4
  57. data/lib/reek/spec/should_reek_of.rb +8 -5
  58. data/lib/reek/spec/should_reek_only_of.rb +12 -8
  59. data/lib/reek/tree_dresser.rb +6 -2
  60. data/lib/reek/tree_walker.rb +28 -22
  61. data/lib/reek/version.rb +1 -1
  62. data/reek.gemspec +6 -5
  63. data/spec/gem/yard_spec.rb +6 -9
  64. data/spec/reek/code_comment_spec.rb +1 -1
  65. data/spec/reek/report/xml_report_spec.rb +11 -21
  66. data/spec/reek/smells/attribute_spec.rb +73 -57
  67. data/spec/reek/smells/too_many_instance_variables_spec.rb +26 -12
  68. data/spec/reek/source/source_locator_spec.rb +2 -2
  69. data/spec/samples/checkstyle.xml +12 -1
  70. data/spec/spec_helper.rb +1 -0
  71. metadata +20 -7
  72. data/spec/samples/unusual_syntax.rb +0 -21
@@ -1,14 +1,11 @@
1
+ require 'open3'
1
2
  require_relative '../spec_helper'
2
- require 'tempfile'
3
3
 
4
4
  RSpec.describe 'yardoc' do
5
- before :each do
6
- stderr_file = Tempfile.new('yardoc')
7
- stderr_file.close
8
- @stdout = `yardoc 2> #{stderr_file.path}`
9
- @stderr = IO.read(stderr_file.path)
10
- end
11
- it 'raises no warnings' do
12
- expect(@stderr).to eq('')
5
+ it 'executes successfully with no warnings' do
6
+ stdout, stderr, status = Open3.capture3('yardoc')
7
+ expect(stdout).to_not include('[warn]')
8
+ expect(stderr).to be_empty
9
+ expect(status).to be_success
13
10
  end
14
11
  end
@@ -38,7 +38,7 @@ RSpec.describe Reek::CodeComment do
38
38
  expect(config['Duplication']).to include('enabled')
39
39
  expect(config['Duplication']['enabled']).to be_falsey
40
40
  end
41
- it 'parses hashed options with ruby names' do
41
+ it 'parses hashed options with Ruby names' do
42
42
  comment = '# :reek:nested_iterators: { enabled: true }'
43
43
  config = described_class.new(comment).config
44
44
  expect(config).to include('NestedIterators')
@@ -1,35 +1,25 @@
1
- require 'pathname'
2
1
  require_relative '../../spec_helper'
3
2
  require_relative '../../../lib/reek/examiner'
4
3
  require_relative '../../../lib/reek/report/report'
5
- require_relative '../../../lib/reek/report/formatter'
6
4
 
7
5
  RSpec.describe Reek::Report::XMLReport do
8
- let(:instance) { Reek::Report::XMLReport.new }
6
+ let(:xml_report) { Reek::Report::XMLReport.new }
9
7
 
10
8
  context 'empty source' do
11
- let(:examiner) { Reek::Examiner.new('') }
12
-
13
- before do
14
- instance.add_examiner examiner
15
- end
16
-
17
- it 'prints empty checkstyle xml' do
18
- expect { instance.show }.to output("<?xml version='1.0'?>\n<checkstyle/>\n").to_stdout
9
+ it 'prints empty checkstyle XML' do
10
+ xml_report.add_examiner Reek::Examiner.new('')
11
+ xml = "<?xml version='1.0'?>\n<checkstyle/>\n"
12
+ expect { xml_report.show }.to output(xml).to_stdout
19
13
  end
20
14
  end
21
15
 
22
16
  context 'source with voliations' do
23
- let(:examiner) { Reek::Examiner.new('def simple(a) a[0] end') }
24
-
25
- before do
26
- allow(File).to receive(:realpath).and_return('/some/path')
27
- instance.add_examiner examiner
28
- end
29
-
30
- it 'prints non-empty checkstyle xml' do
31
- sample_path = SAMPLES_PATH.join('checkstyle.xml')
32
- expect { instance.show }.to output(sample_path.read).to_stdout
17
+ it 'prints non-empty checkstyle XML' do
18
+ path = SAMPLES_PATH.join('two_smelly_files/dirty_one.rb')
19
+ xml_report.add_examiner Reek::Examiner.new(path)
20
+ xml = SAMPLES_PATH.join('checkstyle.xml').read
21
+ xml = xml.gsub(path.to_s, path.expand_path.to_s)
22
+ expect { xml_report.show }.to output(xml).to_stdout
33
23
  end
34
24
  end
35
25
  end
@@ -1,16 +1,8 @@
1
1
  require_relative '../../spec_helper'
2
2
  require_relative '../../../lib/reek/smells/attribute'
3
- require_relative '../../../lib/reek/smells/smell_configuration'
4
3
  require_relative 'smell_detector_shared'
5
4
 
6
5
  RSpec.describe Reek::Smells::Attribute do
7
- let(:config) do
8
- {
9
- Reek::Smells::Attribute => { Reek::Smells::SmellConfiguration::ENABLED_KEY => true }
10
- }
11
- end
12
- let(:configuration) { test_configuration_for(config) }
13
-
14
6
  before(:each) do
15
7
  @source_name = 'dummy_source'
16
8
  @detector = build(:smell_detector, smell_type: :Attribute, source: @source_name)
@@ -20,99 +12,123 @@ RSpec.describe Reek::Smells::Attribute do
20
12
 
21
13
  context 'with no attributes' do
22
14
  it 'records nothing' do
23
- expect('
15
+ src = <<-EOS
24
16
  class Klass
25
17
  end
26
- ').to_not reek_of(:Attribute, {}, configuration)
18
+ EOS
19
+ expect(src).to_not reek_of(:Attribute)
27
20
  end
28
21
  end
29
22
 
30
23
  context 'with attributes' do
31
- it 'records nothing' do
32
- expect('
24
+ it 'records nothing for attribute readers' do
25
+ src = <<-EOS
33
26
  class Klass
34
- attr :super_private, :super_private2
35
- private :super_private, :super_private2
36
- private
37
- attr :super_thing
38
- public
39
- attr :super_thing2
40
- private
41
- attr :super_thing2
27
+ attr :my_attr
28
+ attr_reader :my_attr2
42
29
  end
43
- ').to_not reek_of(:Attribute, {}, configuration)
30
+ EOS
31
+ expect(src).to_not reek_of(:Attribute)
44
32
  end
45
33
 
46
- it 'records attr attribute in a module' do
47
- expect('
34
+ it 'records writer attribute' do
35
+ src = <<-EOS
36
+ class Klass
37
+ attr_writer :my_attr
38
+ end
39
+ EOS
40
+ expect(src).to reek_of(:Attribute, name: 'my_attr')
41
+ end
42
+
43
+ it 'records attr_writer attribute in a module' do
44
+ src = <<-EOS
48
45
  module Mod
49
- attr :my_attr
46
+ attr_writer :my_attr
50
47
  end
51
- ').to reek_of(:Attribute, { name: 'my_attr' }, configuration)
48
+ EOS
49
+ expect(src).to reek_of(:Attribute, name: 'my_attr')
52
50
  end
53
51
 
54
- it 'records attr attribute' do
55
- expect('
52
+ it 'records accessor attribute' do
53
+ src = <<-EOS
56
54
  class Klass
57
- attr :my_attr
55
+ attr_accessor :my_attr
58
56
  end
59
- ').to reek_of(:Attribute, { name: 'my_attr' }, configuration)
57
+ EOS
58
+ expect(src).to reek_of(:Attribute, name: 'my_attr')
60
59
  end
61
60
 
62
- it 'records reader attribute' do
63
- expect('
61
+ it 'records attr defining a writer' do
62
+ src = <<-EOS
64
63
  class Klass
65
- attr_reader :my_attr
64
+ attr :my_attr, true
66
65
  end
67
- ').to reek_of(:Attribute, { name: 'my_attr' }, configuration)
66
+ EOS
67
+ expect(src).to reek_of(:Attribute, name: 'my_attr')
68
68
  end
69
69
 
70
- it 'records writer attribute' do
71
- expect('
70
+ it "doesn't record protected attributes" do
71
+ src = '
72
72
  class Klass
73
- attr_writer :my_attr
73
+ protected
74
+ attr_writer :attr1
75
+ attr_accessor :attr2
76
+ attr :attr3
77
+ attr :attr4, true
78
+ attr_reader :attr5
74
79
  end
75
- ').to reek_of(:Attribute, { name: 'my_attr' }, configuration)
80
+ '
81
+ expect(src).to_not reek_of(:Attribute)
76
82
  end
77
83
 
78
- it 'records accessor attribute' do
79
- expect('
84
+ it "doesn't record private attributes" do
85
+ src = '
80
86
  class Klass
81
- attr_accessor :my_attr
87
+ private
88
+ attr_writer :attr1
89
+ attr_accessor :attr2
90
+ attr :attr3
91
+ attr :attr4, true
92
+ attr_reader :attr5
82
93
  end
83
- ').to reek_of(:Attribute, { name: 'my_attr' }, configuration)
94
+ '
95
+ expect(src).to_not reek_of(:Attribute)
84
96
  end
85
97
 
86
- it 'records attr attribute after switching visbility' do
87
- expect('
98
+ it 'records attr_writer defined in public section' do
99
+ src = <<-EOS
88
100
  class Klass
89
101
  private
90
- attr :my_attr
91
- public :my_attr
92
- private :my_attr
93
- public :my_attr
102
+ public
103
+ attr_writer :my_attr
94
104
  end
95
- ').to reek_of(:Attribute, { name: 'my_attr' }, configuration)
105
+ EOS
106
+ expect(src).to reek_of(:Attribute, name: 'my_attr')
96
107
  end
97
108
 
98
- it "doesn't record protected attributes" do
99
- src = '
109
+ it 'records attr_writer after switching visbility to public' do
110
+ src = <<-EOS
100
111
  class Klass
101
- protected
102
- attr :iam_protected
112
+ private
113
+ attr_writer :my_attr
114
+ public :my_attr
103
115
  end
104
- '
105
- expect(src).to_not reek_of(:Attribute, { name: 'iam_protected' }, configuration)
116
+ EOS
117
+ expect(src).to reek_of(:Attribute, name: 'my_attr')
106
118
  end
107
119
 
108
- it "doesn't record private attributes" do
120
+ it 'resets visibility in new contexts' do
109
121
  src = '
110
122
  class Klass
111
123
  private
112
- attr :iam_private
124
+ attr_writer :attr1
125
+ end
126
+
127
+ class OtherKlass
128
+ attr_writer :attr1
113
129
  end
114
130
  '
115
- expect(src).to_not reek_of(:Attribute, { name: 'iam_private' }, configuration)
131
+ expect(src).to reek_of(:Attribute)
116
132
  end
117
133
  end
118
134
  end
@@ -10,12 +10,26 @@ RSpec.describe Reek::Smells::TooManyInstanceVariables do
10
10
 
11
11
  it_should_behave_like 'SmellDetector'
12
12
 
13
+ def default_max_ivars
14
+ Reek::Smells::TooManyInstanceVariables::DEFAULT_MAX_IVARS
15
+ end
16
+
17
+ def too_many_ivars
18
+ default_max_ivars + 1
19
+ end
20
+
21
+ def ivar_sequence(count: default_max_ivars, alphabet: ('a'..'z').to_a)
22
+ alphabet.first(count).map do |name|
23
+ "@#{name}=#{rand}"
24
+ end.join(',')
25
+ end
26
+
13
27
  context 'counting instance variables' do
14
- it 'should not report 9 ivars' do
28
+ it 'should not report for non-excessive ivars' do
15
29
  src = <<-EOS
16
30
  class Empty
17
31
  def ivars
18
- @aa=@ab=@ac=@ad=@ae=@af=@ag=@ah=@ai=4
32
+ #{ivar_sequence}
19
33
  end
20
34
  end
21
35
  EOS
@@ -26,36 +40,36 @@ RSpec.describe Reek::Smells::TooManyInstanceVariables do
26
40
  src = <<-EOS
27
41
  class Empty
28
42
  def ivars
29
- @aa=@ab=@ac=@ad=@ae=@af=@ag=@ah=@ai=4
30
- @aa=3
43
+ #{ivar_sequence}
44
+ #{ivar_sequence}
31
45
  end
32
46
  end
33
47
  EOS
34
48
  expect(src).not_to reek_of(:TooManyInstanceVariables)
35
49
  end
36
50
 
37
- it 'should report 10 ivars' do
51
+ it 'should report excessive ivars' do
38
52
  src = <<-EOS
39
53
  class Empty
40
54
  def ivars
41
- @aa=@ab=@ac=@ad=@ae=@af=@ag=@ah=@ai=@aj=4
55
+ #{ivar_sequence(count: too_many_ivars)}
42
56
  end
43
57
  end
44
58
  EOS
45
59
  expect(src).to reek_of(:TooManyInstanceVariables)
46
60
  end
47
61
 
48
- it 'should not report 10 ivars in 2 extensions' do
62
+ it 'should not report for ivars in 2 extensions' do
49
63
  src = <<-EOS
50
64
  class Full
51
65
  def ivars_a
52
- @aa=@ab=@ac=@ad=@ae
66
+ #{ivar_sequence}
53
67
  end
54
68
  end
55
69
 
56
70
  class Full
57
71
  def ivars_b
58
- @af=@ag=@ah=@ai=@aj
72
+ #{ivar_sequence}
59
73
  end
60
74
  end
61
75
  EOS
@@ -63,12 +77,12 @@ RSpec.describe Reek::Smells::TooManyInstanceVariables do
63
77
  end
64
78
  end
65
79
 
66
- it 'reports correctly when the class has 10 instance variables' do
80
+ it 'reports correctly for excessive instance variables' do
67
81
  src = <<-EOS
68
82
  # Comment
69
83
  class Empty
70
84
  def ivars
71
- @aa=@ab=@ac=@ad=@ae=@af=@ag=@ah=@ai=@aj=4
85
+ #{ivar_sequence(count: too_many_ivars)}
72
86
  end
73
87
  end
74
88
  EOS
@@ -77,7 +91,7 @@ RSpec.describe Reek::Smells::TooManyInstanceVariables do
77
91
  expect(@warning.source).to eq(@source_name)
78
92
  expect(@warning.smell_category).to eq(Reek::Smells::TooManyInstanceVariables.smell_category)
79
93
  expect(@warning.smell_type).to eq(Reek::Smells::TooManyInstanceVariables.smell_type)
80
- expect(@warning.parameters[:count]).to eq(10)
94
+ expect(@warning.parameters[:count]).to eq(too_many_ivars)
81
95
  expect(@warning.lines).to eq([2])
82
96
  end
83
97
  end
@@ -46,7 +46,7 @@ RSpec.describe Reek::Source::SourceLocator do
46
46
  end
47
47
  end
48
48
 
49
- context 'non-ruby paths' do
49
+ context 'non-Ruby paths' do
50
50
  let(:path) { SAMPLES_PATH.join('source_with_non_ruby_files') }
51
51
  let(:expected_sources) do
52
52
  [SAMPLES_PATH.join('source_with_non_ruby_files/uncommunicative_parameter_name.rb')]
@@ -58,7 +58,7 @@ RSpec.describe Reek::Source::SourceLocator do
58
58
  ]
59
59
  end
60
60
 
61
- it 'does only use ruby source paths' do
61
+ it 'does only use Ruby source paths' do
62
62
  sources = described_class.new([path]).sources
63
63
 
64
64
  expect(sources).not_to include(*paths_that_are_expected_to_be_ignored)
@@ -1,2 +1,13 @@
1
1
  <?xml version='1.0'?>
2
- <checkstyle><file name='/some/path'><error column='0' line='1' message='doesn&apos;t depend on instance state' severity='warning' source='UtilityFunction'/><error column='0' line='1' message='has the parameter name &apos;a&apos;' severity='warning' source='UncommunicativeParameterName'/></file></checkstyle>
2
+ <checkstyle>
3
+ <file name='spec/samples/two_smelly_files/dirty_one.rb'>
4
+ <error column='0' line='5' message='has the variable name &apos;@s&apos;' severity='warning' source='UncommunicativeVariableName'/>
5
+ <error column='0' line='4' message='calls @s.title 2 times' severity='warning' source='DuplicateMethodCall'/>
6
+ <error column='0' line='6' message='calls @s.title 2 times' severity='warning' source='DuplicateMethodCall'/>
7
+ <error column='0' line='4' message='calls puts(@s.title) 2 times' severity='warning' source='DuplicateMethodCall'/>
8
+ <error column='0' line='6' message='calls puts(@s.title) 2 times' severity='warning' source='DuplicateMethodCall'/>
9
+ <error column='0' line='5' message='contains iterators nested 2 deep' severity='warning' source='NestedIterators'/>
10
+ <error column='0' line='3' message='has the name &apos;a&apos;' severity='warning' source='UncommunicativeMethodName'/>
11
+ <error column='0' line='5' message='has the variable name &apos;x&apos;' severity='warning' source='UncommunicativeVariableName'/>
12
+ </file>
13
+ </checkstyle>
@@ -1,4 +1,5 @@
1
1
  require 'pathname'
2
+ require_relative '../lib/reek'
2
3
  require_relative '../lib/reek/spec'
3
4
  require_relative '../lib/reek/ast/ast_node_class_map'
4
5
  require_relative '../lib/reek/configuration/app_configuration'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reek
3
3
  version: !ruby/object:Gem::Version
4
- version: '3.1'
4
+ version: '3.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Rutherford
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-07-21 00:00:00.000000000 Z
13
+ date: 2015-08-17 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: parser
@@ -26,6 +26,20 @@ dependencies:
26
26
  - - "~>"
27
27
  - !ruby/object:Gem::Version
28
28
  version: 2.2.2.5
29
+ - !ruby/object:Gem::Dependency
30
+ name: private_attr
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '1.1'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '1.1'
29
43
  - !ruby/object:Gem::Dependency
30
44
  name: rainbow
31
45
  requirement: !ruby/object:Gem::Requirement
@@ -74,14 +88,14 @@ dependencies:
74
88
  requirements:
75
89
  - - "~>"
76
90
  - !ruby/object:Gem::Version
77
- version: 0.7.2
91
+ version: 0.8.0
78
92
  type: :development
79
93
  prerelease: false
80
94
  version_requirements: !ruby/object:Gem::Requirement
81
95
  requirements:
82
96
  - - "~>"
83
97
  - !ruby/object:Gem::Version
84
- version: 0.7.2
98
+ version: 0.8.0
85
99
  - !ruby/object:Gem::Dependency
86
100
  name: ataru
87
101
  requirement: !ruby/object:Gem::Requirement
@@ -189,14 +203,14 @@ executables:
189
203
  - reek
190
204
  extensions: []
191
205
  extra_rdoc_files:
192
- - CHANGELOG
206
+ - CHANGELOG.md
193
207
  - License.txt
194
208
  files:
195
209
  - ".gitignore"
196
210
  - ".rubocop.yml"
197
211
  - ".travis.yml"
198
212
  - ".yardopts"
199
- - CHANGELOG
213
+ - CHANGELOG.md
200
214
  - CONTRIBUTING.md
201
215
  - Gemfile
202
216
  - License.txt
@@ -429,7 +443,6 @@ files:
429
443
  - spec/samples/three_clean_files/clean_two.rb
430
444
  - spec/samples/two_smelly_files/dirty_one.rb
431
445
  - spec/samples/two_smelly_files/dirty_two.rb
432
- - spec/samples/unusual_syntax.rb
433
446
  - spec/spec_helper.rb
434
447
  - tasks/develop.rake
435
448
  - tasks/reek.rake