reek 4.0.2 → 4.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 72d27c8901d22ffc65492d565e6c037d71f9f1e0
4
- data.tar.gz: e358a34395daae66652468724f1794f4a8719d0a
3
+ metadata.gz: 71c1d52a08d1ce16364812f297d6a072109b406b
4
+ data.tar.gz: f0ea4129036dde1687ad6656c603d63e8a6ddaf2
5
5
  SHA512:
6
- metadata.gz: d6ef752ab64fb5db0d0038e4a41405c261c1f9e6b9174e3b9b2b50062892fe78bcd5d1467ca0d392590e00a63ca33b25e1c6376cb59a2d37f989e12284e79951
7
- data.tar.gz: 7daf1e81235a889917a06a737bab16996f0e2c51fbd3ab278869c7405c985821088e8d6071a0c79f573f725dd84bebd49b358327ea825314eee5769aca618a1d
6
+ metadata.gz: 1228e4b9dc1f5673e7f474af1c6f5eb16d30eac8df72d4aecb4684b2ff2aa7796e62defe53a903ff9853444fe112187e3d7de7f40bd8c619c75d459d11273d7d
7
+ data.tar.gz: fb720491aa22c267bdccdfdb7296f9f89a216ab7da89d8e66f903c63bf401ee8beff14256a0b052bf284df93f81d27f9c050f1db1af3b15714f6b7a23565c425
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Change log
2
2
 
3
+ ## 4.0.3 (2016-05-23)
4
+
5
+ * (mvz) Include default exclusions in generated TODO file
6
+ * (mvz) Avoid generating duplicate context entries in exclusions
7
+
3
8
  ## 4.0.2 (2016-04-23)
4
9
 
5
10
  * (mvz) Stop UnusedPrivateMethod getting confused by nested classes
data/README.md CHANGED
@@ -2,8 +2,37 @@
2
2
 
3
3
  # Code smell detector for Ruby
4
4
 
5
- ## Overview
5
+ **Table of Contents**
6
+
7
+ - [Overview](#overview)
8
+ - [Quickstart](#quickstart)
9
+ - [Example](#example)
10
+ - [Supported rubies](#supported-rubies)
11
+ - [Fixing Smell Warnings](#fixing-smell-warnings)
12
+ - [Sources](#sources)
13
+ - [Code smells](#code-smells)
14
+ - [Configuration](#configuration)
15
+ - [Command-line interface](#command-line-interface)
16
+ - [Configuration file](#configuration-file)
17
+ - [Configuration loading](#configuration-loading)
18
+ - [Configuration options](#configuration-options)
19
+ - [Source code comments](#source-code-comments)
20
+ - [Generating a 'todo' list](#generating-a-todo-list)
21
+ - [Usage](#usage)
22
+ - [Developing Reek / Contributing](#developing-reek--contributing)
23
+ - [Output formats](#output-formats)
24
+ - [Working with Rails](#working-with-rails)
25
+ - [Integrations](#integrations)
26
+ - [Editor integrations](#editor-integrations)
27
+ - [Projects that use or support us](#projects-that-use-or-support-us)
28
+ - [Misc](#misc)
29
+ - [Brothers and sisters](#brothers-and-sisters)
30
+ - [Contributors](#contributors)
31
+ - [Additional resources](#additional-resources)
32
+ - [Miscellaneous](#miscellaneous)
33
+ - [More information](#more-information)
6
34
 
35
+ ## Overview
7
36
 
8
37
  [![Build Status](https://secure.travis-ci.org/troessner/reek.svg?branch=master)](https://travis-ci.org/troessner/reek?branch=master)
9
38
  [![Gem Version](https://badge.fury.io/rb/reek.svg)](https://badge.fury.io/rb/reek)
@@ -19,7 +48,7 @@ Reek is a tool that examines Ruby classes, modules and methods and reports any
19
48
 
20
49
  For an excellent introduction to
21
50
  [Code Smells](docs/Code-Smells.md) and Reek check out [this blog post](https://blog.codeship.com/how-to-find-ruby-code-smells-with-reek/)
22
- or [that one](https://troessner.wordpress.com/2016/01/01/the-latest-and-greatest-additions-to-reek/). There is also [this talk](https://www.youtube.com/watch?v=ZzqOuHI5MkA) from the [RubyConf Portugal](http://rubyconf.pt/).
51
+ or [that one](https://troessner.svbtle.com/the-latest-and-greatest-additions-to-reek). There is also [this talk](https://www.youtube.com/watch?v=ZzqOuHI5MkA) from the [RubyConf Portugal](http://rubyconf.pt/).
23
52
 
24
53
  Install it via rubygems:
25
54
 
@@ -461,6 +490,7 @@ Be careful though, Reek does not merge your configuration entries, so if you alr
461
490
  * [TextMate Bundle](https://github.com/peeyush1234/reek.tmbundle)
462
491
  * [Atom plugin](https://atom.io/packages/linter-reek)
463
492
  * [SublimeLinter plugin](https://packagecontrol.io/packages/SublimeLinter-contrib-reek)
493
+ * [Emacs plugin](https://github.com/hanmoi-choi/reek-emacs)
464
494
 
465
495
  ### Projects that use or support us
466
496
 
@@ -118,6 +118,14 @@ Given(/^a configuration file masking some duplication smells called 'config.reek
118
118
  EOS
119
119
  end
120
120
 
121
+ Given(/^a configuration file disabling DuplicateMethodCall called 'config.reek'$/) do
122
+ write_file('config.reek', <<-EOS.strip_heredoc)
123
+ ---
124
+ DuplicateMethodCall:
125
+ enabled: false
126
+ EOS
127
+ end
128
+
121
129
  When(/^I run "reek (.*?)" in the subdirectory$/) do |args|
122
130
  cd 'subdir'
123
131
  reek(args)
@@ -9,41 +9,42 @@ Feature:
9
9
  - fix them step by step
10
10
  - get rid of the todo file
11
11
 
12
- Background:
13
- Given a directory 'lib' with one clean file 'clean.rb' and one dirty file 'dirty.rb'
14
- And a directory 'superclean' with one clean file 'clean.rb'
15
-
16
12
  Scenario: Generate a proper todo file that disables all found smells
17
- When I run reek lib
13
+ Given a smelly file called 'smelly.rb'
14
+ When I run reek smelly.rb
18
15
  Then the exit status indicates smells
19
16
  And it reports:
20
17
  """
21
- lib/dirty.rb -- 3 warnings:
22
- [1]:IrresponsibleModule: Dirty has no descriptive comment [https://github.com/troessner/reek/blob/master/docs/Irresponsible-Module.md]
23
- [2]:UncommunicativeMethodName: Dirty#a has the name 'a' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Method-Name.md]
24
- [3]:UncommunicativeMethodName: Dirty#b has the name 'b' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Method-Name.md]
25
- 3 total warnings
18
+ smelly.rb -- 3 warnings:
19
+ [4, 5]:DuplicateMethodCall: Smelly#m calls @foo.bar 2 times [https://github.com/troessner/reek/blob/master/docs/Duplicate-Method-Call.md]
20
+ [4, 5]:DuplicateMethodCall: Smelly#m calls puts @foo.bar 2 times [https://github.com/troessner/reek/blob/master/docs/Duplicate-Method-Call.md]
21
+ [3]:UncommunicativeMethodName: Smelly#m has the name 'm' [https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Method-Name.md]
26
22
  """
27
- When I run reek --todo lib
23
+ When I run reek --todo smelly.rb
28
24
  Then it succeeds
25
+ And it reports:
26
+ """
27
+
28
+ '.todo.reek' generated! You can now use this as a starting point for your configuration.
29
+ """
29
30
  And a file named ".todo.reek" should exist
30
31
  And the file ".todo.reek" should contain:
31
32
  """
32
33
  ---
33
- IrresponsibleModule:
34
+ DuplicateMethodCall:
34
35
  exclude:
35
- - Dirty
36
+ - Smelly#m
36
37
  UncommunicativeMethodName:
37
38
  exclude:
38
- - Dirty#a
39
- - Dirty#b
39
+ - Smelly#m
40
40
  """
41
- When I run reek -c .todo.reek lib
41
+ When I run reek -c .todo.reek smelly.rb
42
42
  Then it succeeds
43
43
 
44
- Scenario: Respects an configuration file
45
- Given a configuration file 'config.reek' that partially masks 'dirty.rb'
46
- When I run reek -c config.reek --todo lib
44
+ Scenario: Respects a configuration file
45
+ Given a smelly file called 'smelly.rb'
46
+ And a configuration file disabling DuplicateMethodCall called 'config.reek'
47
+ When I run reek -c config.reek --todo smelly.rb
47
48
  Then it succeeds
48
49
  And a file named ".todo.reek" should exist
49
50
  And the file ".todo.reek" should contain:
@@ -51,19 +52,12 @@ Feature:
51
52
  ---
52
53
  UncommunicativeMethodName:
53
54
  exclude:
54
- - Dirty#a
55
- """
56
-
57
- Scenario: Print out a helpful message that explains to the user what to do next
58
- When I run reek --todo lib
59
- Then it reports:
60
- """
61
-
62
- '.todo.reek' generated! You can now use this as a starting point for your configuration.
55
+ - Smelly#m
63
56
  """
64
57
 
65
58
  Scenario: Reacts appropiately when there are no smells
66
- When I run reek --todo superclean/
59
+ Given the clean file 'clean.rb'
60
+ When I run reek --todo clean.rb
67
61
  Then a file named ".todo.reek" should not exist
68
62
  And it reports:
69
63
  """
@@ -36,13 +36,13 @@ module Reek
36
36
  end
37
37
 
38
38
  def groups_for(smells)
39
- @groups ||= begin
40
- Hash[
41
- smells.group_by(&:smell_type).map do |smell_type, smells_for_type|
42
- [smell_type, { 'exclude' => smells_for_type.map(&:context) }]
39
+ @groups ||=
40
+ begin
41
+ todos = smells.group_by(&:smell_class).map do |smell_class, smells_for_class|
42
+ smell_class.todo_configuration_for(smells_for_class)
43
43
  end
44
- ]
45
- end
44
+ todos.inject(&:merge)
45
+ end
46
46
  end
47
47
  end
48
48
  end
@@ -55,6 +55,12 @@ module Reek
55
55
  context.matches?(value(EXCLUDE_KEY, context, DEFAULT_EXCLUDE_SET))
56
56
  end
57
57
 
58
+ def self.todo_configuration_for(smells)
59
+ default_exclusions = default_config.fetch 'exclude'
60
+ exclusions = default_exclusions + smells.map(&:context)
61
+ { smell_type => { 'exclude' => exclusions.uniq } }
62
+ end
63
+
58
64
  private
59
65
 
60
66
  attr_accessor :smells_found
@@ -63,6 +63,10 @@ module Reek
63
63
  "#{smell_type}: #{context} #{message}"
64
64
  end
65
65
 
66
+ def smell_class
67
+ smell_detector.class
68
+ end
69
+
66
70
  protected
67
71
 
68
72
  def sort_key
data/lib/reek/version.rb CHANGED
@@ -7,6 +7,6 @@ module Reek
7
7
  # @public
8
8
  module Version
9
9
  # @public
10
- STRING = '4.0.2'.freeze
10
+ STRING = '4.0.3'.freeze
11
11
  end
12
12
  end
@@ -3,6 +3,11 @@ require_lib 'reek/cli/command/todo_list_command'
3
3
  require_lib 'reek/cli/options'
4
4
 
5
5
  RSpec.describe Reek::CLI::Command::TodoListCommand do
6
+ let(:nil_check) { build :smell_detector, smell_type: :NilCheck }
7
+ let(:feature_envy) { build :smell_detector, smell_type: :FeatureEnvy }
8
+ let(:nested_iterators) { build :smell_detector, smell_type: :NestedIterators }
9
+ let(:too_many_statements) { build :smell_detector, smell_type: :TooManyStatements }
10
+
6
11
  describe '#execute' do
7
12
  let(:options) { Reek::CLI::Options.new [] }
8
13
  let(:configuration) { double 'configuration' }
@@ -24,7 +29,7 @@ RSpec.describe Reek::CLI::Command::TodoListCommand do
24
29
 
25
30
  context 'smells found' do
26
31
  before do
27
- smells = [FactoryGirl.build(:smell_warning)]
32
+ smells = [build(:smell_warning, context: 'Foo#bar')]
28
33
  allow(command).to receive(:scan_for_smells).and_return(smells)
29
34
  end
30
35
 
@@ -37,6 +42,61 @@ RSpec.describe Reek::CLI::Command::TodoListCommand do
37
42
  result = command.execute
38
43
  expect(result).to eq(Reek::CLI::Options::DEFAULT_SUCCESS_EXIT_CODE)
39
44
  end
45
+
46
+ it 'writes a todo file' do
47
+ command.execute
48
+ expected_yaml = { 'FeatureEnvy' => { 'exclude' => ['Foo#bar'] } }.to_yaml
49
+ expect(File).to have_received(:write).with(described_class::FILE_NAME, expected_yaml)
50
+ end
51
+ end
52
+
53
+ context 'smells with duplicate context found' do
54
+ before do
55
+ smells = [
56
+ build(:smell_warning, context: 'Foo#bar', smell_detector: feature_envy),
57
+ build(:smell_warning, context: 'Foo#bar', smell_detector: feature_envy)
58
+ ]
59
+ allow(command).to receive(:scan_for_smells).and_return(smells)
60
+ end
61
+
62
+ it 'writes the context into the todo file once' do
63
+ command.execute
64
+ expected_yaml = { 'FeatureEnvy' => { 'exclude' => ['Foo#bar'] } }.to_yaml
65
+ expect(File).to have_received(:write).with(described_class::FILE_NAME, expected_yaml)
66
+ end
67
+ end
68
+
69
+ context 'smells with default exclusions found' do
70
+ let(:smell) { build :smell_warning, smell_detector: too_many_statements, context: 'Foo#bar' }
71
+
72
+ before do
73
+ allow(command).to receive(:scan_for_smells).and_return [smell]
74
+ end
75
+
76
+ it 'includes the default exclusions in the generated yaml' do
77
+ command.execute
78
+ expected_yaml = { 'TooManyStatements' => { 'exclude' => ['initialize', 'Foo#bar'] } }.to_yaml
79
+ expect(File).to have_received(:write).with(described_class::FILE_NAME, expected_yaml)
80
+ end
81
+ end
82
+
83
+ context 'smells of different types found' do
84
+ before do
85
+ smells = [
86
+ build(:smell_warning, context: 'Foo#bar', smell_detector: nil_check),
87
+ build(:smell_warning, context: 'Bar#baz', smell_detector: nested_iterators)
88
+ ]
89
+ allow(command).to receive(:scan_for_smells).and_return(smells)
90
+ end
91
+
92
+ it 'writes the context into the todo file once' do
93
+ command.execute
94
+ expected_yaml = {
95
+ 'NilCheck' => { 'exclude' => ['Foo#bar'] },
96
+ 'NestedIterators' => { 'exclude' => ['Bar#baz'] }
97
+ }.to_yaml
98
+ expect(File).to have_received(:write).with(described_class::FILE_NAME, expected_yaml)
99
+ end
40
100
  end
41
101
 
42
102
  context 'no smells found' do
@@ -53,13 +113,10 @@ RSpec.describe Reek::CLI::Command::TodoListCommand do
53
113
  result = command.execute
54
114
  expect(result).to eq Reek::CLI::Options::DEFAULT_SUCCESS_EXIT_CODE
55
115
  end
56
- end
57
116
 
58
- describe 'groups_for' do
59
- it 'returns a proper hash representation of the smells found' do
60
- smells = [FactoryGirl.build(:smell_warning)]
61
- expected = { 'FeatureEnvy' => { 'exclude' => ['self'] } }
62
- expect(command.send(:groups_for, smells)).to eq(expected)
117
+ it 'does not write a todo file' do
118
+ command.execute
119
+ expect(File).not_to have_received(:write)
63
120
  end
64
121
  end
65
122
  end
@@ -0,0 +1,36 @@
1
+ require_relative '../../spec_helper'
2
+ require_lib 'reek/smells/smell_detector'
3
+
4
+ RSpec.describe Reek::Smells::SmellDetector do
5
+ describe '.todo_configuration_for' do
6
+ it 'returns exclusion configuration for the given smells' do
7
+ detector = described_class.new
8
+ smell = create(:smell_warning, smell_detector: detector, context: 'Foo#bar')
9
+ result = described_class.todo_configuration_for([smell])
10
+ expect(result).to eq('SmellDetector' => { 'exclude' => ['Foo#bar'] })
11
+ end
12
+
13
+ it 'merges identical contexts' do
14
+ detector = described_class.new
15
+ smell = create(:smell_warning, smell_detector: detector, context: 'Foo#bar')
16
+ result = described_class.todo_configuration_for([smell, smell])
17
+ expect(result).to eq('SmellDetector' => { 'exclude' => ['Foo#bar'] })
18
+ end
19
+
20
+ context 'with default exclusions present' do
21
+ let(:subclass) { Reek::Smells::TooManyStatements }
22
+
23
+ before do
24
+ expect(subclass.default_config['exclude']).to eq ['initialize']
25
+ end
26
+
27
+ it 'includes default exclusions' do
28
+ detector = subclass.new
29
+ smell = create(:smell_warning, smell_detector: detector, context: 'Foo#bar')
30
+ result = subclass.todo_configuration_for([smell])
31
+
32
+ expect(result).to eq('TooManyStatements' => { 'exclude' => ['initialize', 'Foo#bar'] })
33
+ end
34
+ end
35
+ end
36
+ end
@@ -5,6 +5,7 @@ RSpec.describe Reek::Smells::SmellWarning do
5
5
  let(:duplication_detector) { build(:smell_detector, smell_type: 'DuplicateMethodCall') }
6
6
  let(:feature_envy_detector) { build(:smell_detector, smell_type: 'FeatureEnvy') }
7
7
  let(:utility_function_detector) { build(:smell_detector, smell_type: 'UtilityFunction') }
8
+ let(:uncommunicative_name_detector) { build(:smell_detector, smell_type: 'UncommunicativeVariableName') }
8
9
 
9
10
  context 'sort order' do
10
11
  shared_examples_for 'first sorts ahead of second' do
@@ -65,16 +66,12 @@ RSpec.describe Reek::Smells::SmellWarning do
65
66
 
66
67
  context 'smells differing everywhere' do
67
68
  let(:first) do
68
- duplication_detector = build(:smell_detector,
69
- smell_type: 'DuplicateMethodCall')
70
69
  build(:smell_warning, smell_detector: duplication_detector,
71
70
  context: 'Dirty#a',
72
71
  message: 'calls @s.title twice')
73
72
  end
74
73
 
75
74
  let(:second) do
76
- uncommunicative_name_detector = build(:smell_detector,
77
- smell_type: 'UncommunicativeVariableName')
78
75
  build(:smell_warning, smell_detector: uncommunicative_name_detector,
79
76
  context: 'Dirty',
80
77
  message: "has the variable name '@s'")
@@ -84,6 +81,13 @@ RSpec.describe Reek::Smells::SmellWarning do
84
81
  end
85
82
  end
86
83
 
84
+ describe '#smell_class' do
85
+ it "returns the dectector's class" do
86
+ warning = build(:smell_warning, smell_detector: duplication_detector)
87
+ expect(warning.smell_class).to eq duplication_detector.class
88
+ end
89
+ end
90
+
87
91
  context '#yaml_hash' do
88
92
  let(:class) { 'FeatureEnvy' }
89
93
  let(:context_name) { 'Module::Class#method/block' }
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: 4.0.2
4
+ version: 4.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Rutherford
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2016-04-23 00:00:00.000000000 Z
14
+ date: 2016-05-23 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: codeclimate-engine-rb
@@ -159,7 +159,6 @@ files:
159
159
  - features/step_definitions/.rubocop.yml
160
160
  - features/step_definitions/reek_steps.rb
161
161
  - features/step_definitions/sample_file_steps.rb
162
- - features/step_definitions/todo_list_steps.rb
163
162
  - features/support/env.rb
164
163
  - features/todo_list.feature
165
164
  - lib/reek.rb
@@ -319,6 +318,7 @@ files:
319
318
  - spec/reek/smells/repeated_conditional_spec.rb
320
319
  - spec/reek/smells/smell_configuration_spec.rb
321
320
  - spec/reek/smells/smell_detector_shared.rb
321
+ - spec/reek/smells/smell_detector_spec.rb
322
322
  - spec/reek/smells/smell_repository_spec.rb
323
323
  - spec/reek/smells/smell_warning_spec.rb
324
324
  - spec/reek/smells/too_many_instance_variables_spec.rb
@@ -1,40 +0,0 @@
1
- Given(/^a directory 'lib' with one clean file 'clean\.rb' and one dirty file 'dirty\.rb'$/) do
2
- write_file('lib/clean.rb', <<-EOS.strip_heredoc)
3
- # clean class for testing purposes
4
- class Clean
5
- def super_clean
6
- puts @janitor.name
7
- end
8
- end
9
- EOS
10
-
11
- write_file('lib/dirty.rb', <<-EOS.strip_heredoc)
12
- class Dirty
13
- def a; end
14
- def b; end
15
- end
16
- EOS
17
- end
18
-
19
- Given(/^a configuration file 'config\.reek' that partially masks 'dirty\.rb'$/) do
20
- write_file('config.reek', <<-EOS.strip_heredoc)
21
- ---
22
- IrresponsibleModule:
23
- exclude:
24
- - Dirty
25
- UncommunicativeMethodName:
26
- exclude:
27
- - Dirty#b
28
- EOS
29
- end
30
-
31
- Given(/^a directory 'superclean' with one clean file 'clean\.rb'$/) do
32
- write_file('superclean/clean.rb', <<-EOS.strip_heredoc)
33
- # clean class for testing purposes
34
- class Clean
35
- def super_clean
36
- puts @janitor.name
37
- end
38
- end
39
- EOS
40
- end