reek 3.10.1 → 3.10.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +26 -25
- data/.simplecov +10 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +3 -2
- data/README.md +11 -0
- data/bin/code_climate_reek +1 -1
- data/docs/Feature-Envy.md +1 -1
- data/docs/style-guide.md +1 -7
- data/lib/reek/ast/ast_node_class_map.rb +1 -1
- data/lib/reek/ast/node.rb +4 -4
- data/lib/reek/ast/object_refs.rb +1 -3
- data/lib/reek/ast/reference_collector.rb +2 -4
- data/lib/reek/ast/sexp_extensions/send.rb +1 -1
- data/lib/reek/ast/sexp_extensions/variables.rb +1 -1
- data/lib/reek/cli/application.rb +3 -12
- data/lib/reek/cli/command.rb +1 -5
- data/lib/reek/cli/option_interpreter.rb +5 -2
- data/lib/reek/cli/reek_command.rb +5 -1
- data/lib/reek/cli/warning_collector.rb +1 -2
- data/lib/reek/code_comment.rb +3 -3
- data/lib/reek/configuration/app_configuration.rb +3 -3
- data/lib/reek/context/attribute_context.rb +3 -1
- data/lib/reek/context/code_context.rb +2 -3
- data/lib/reek/context/statement_counter.rb +4 -2
- data/lib/reek/context/visibility_tracker.rb +6 -6
- data/lib/reek/context_builder.rb +7 -6
- data/lib/reek/examiner.rb +2 -1
- data/lib/reek/rake/task.rb +1 -2
- data/lib/reek/report.rb +4 -4
- data/lib/reek/report/code_climate/code_climate_formatter.rb +2 -3
- data/lib/reek/report/formatter.rb +4 -3
- data/lib/reek/report/report.rb +2 -2
- data/lib/reek/smells/control_parameter.rb +4 -4
- data/lib/reek/smells/data_clump.rb +4 -4
- data/lib/reek/smells/duplicate_method_call.rb +5 -5
- data/lib/reek/smells/long_parameter_list.rb +1 -1
- data/lib/reek/smells/long_yield_list.rb +1 -1
- data/lib/reek/smells/nested_iterators.rb +5 -5
- data/lib/reek/smells/nil_check.rb +1 -1
- data/lib/reek/smells/repeated_conditional.rb +1 -1
- data/lib/reek/smells/smell_configuration.rb +4 -6
- data/lib/reek/smells/smell_detector.rb +4 -3
- data/lib/reek/smells/smell_repository.rb +2 -2
- data/lib/reek/smells/smell_warning.rb +1 -1
- data/lib/reek/smells/too_many_instance_variables.rb +1 -1
- data/lib/reek/smells/too_many_methods.rb +1 -1
- data/lib/reek/smells/too_many_statements.rb +1 -1
- data/lib/reek/smells/uncommunicative_method_name.rb +4 -4
- data/lib/reek/smells/uncommunicative_module_name.rb +4 -4
- data/lib/reek/smells/uncommunicative_parameter_name.rb +4 -4
- data/lib/reek/smells/uncommunicative_variable_name.rb +5 -5
- data/lib/reek/source/source_code.rb +3 -3
- data/lib/reek/source/source_locator.rb +3 -4
- data/lib/reek/spec/should_reek.rb +4 -2
- data/lib/reek/spec/should_reek_of.rb +4 -4
- data/lib/reek/spec/should_reek_only_of.rb +1 -1
- data/lib/reek/spec/smell_matcher.rb +1 -1
- data/lib/reek/tree_dresser.rb +1 -1
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +1 -2
- data/spec/reek/cli/application_spec.rb +31 -0
- data/spec/reek/cli/reek_command_spec.rb +45 -0
- data/spec/reek/rake/task_spec.rb +33 -0
- data/spec/reek/smells/utility_function_spec.rb +1 -1
- data/spec/spec_helper.rb +3 -2
- data/tasks/configuration.rake +1 -1
- data/tasks/test.rake +1 -1
- metadata +11 -15
@@ -17,15 +17,14 @@ module Reek
|
|
17
17
|
# :reek:UnusedPrivateMethod: { exclude: [ inherited, smell_warning ] }
|
18
18
|
class SmellDetector
|
19
19
|
attr_reader :config
|
20
|
-
private_attr_accessor :smells_found
|
21
20
|
# The name of the config field that lists the names of code contexts
|
22
21
|
# that should not be checked. Add this field to the config for each
|
23
22
|
# smell that should ignore this code element.
|
24
|
-
EXCLUDE_KEY = 'exclude'
|
23
|
+
EXCLUDE_KEY = 'exclude'.freeze
|
25
24
|
|
26
25
|
# The default value for the +EXCLUDE_KEY+ if it isn't specified
|
27
26
|
# in any configuration file.
|
28
|
-
DEFAULT_EXCLUDE_SET = []
|
27
|
+
DEFAULT_EXCLUDE_SET = [].freeze
|
29
28
|
|
30
29
|
def initialize(config = {})
|
31
30
|
@config = SmellConfiguration.new self.class.default_config.merge(config)
|
@@ -61,6 +60,8 @@ module Reek
|
|
61
60
|
|
62
61
|
private
|
63
62
|
|
63
|
+
attr_accessor :smells_found
|
64
|
+
|
64
65
|
def enabled_for?(context)
|
65
66
|
config.enabled? && config_for(context)[SmellConfiguration::ENABLED_KEY] != false
|
66
67
|
end
|
@@ -8,8 +8,6 @@ module Reek
|
|
8
8
|
# Contains all the existing smells and exposes operations on them.
|
9
9
|
#
|
10
10
|
class SmellRepository
|
11
|
-
private_attr_reader :configuration, :smell_types, :detectors
|
12
|
-
|
13
11
|
# @return [Array<Reek::Smells::SmellDetector>] All known SmellDetectors
|
14
12
|
# e.g. [Reek::Smells::BooleanParameter, Reek::Smells::ClassVariable].
|
15
13
|
def self.smell_types
|
@@ -51,6 +49,8 @@ module Reek
|
|
51
49
|
|
52
50
|
private
|
53
51
|
|
52
|
+
attr_reader :configuration, :smell_types, :detectors
|
53
|
+
|
54
54
|
def configuration_for(klass)
|
55
55
|
configuration.fetch klass, {}
|
56
56
|
end
|
@@ -25,7 +25,7 @@ module Reek
|
|
25
25
|
#
|
26
26
|
# :reek:LongParameterList: { max_params: 6 }
|
27
27
|
def initialize(smell_detector, context: '', lines: raise, message: raise,
|
28
|
-
|
28
|
+
source: raise, parameters: {})
|
29
29
|
@smell_detector = smell_detector
|
30
30
|
@source = source
|
31
31
|
@context = context.to_s
|
@@ -14,7 +14,7 @@ module Reek
|
|
14
14
|
class TooManyInstanceVariables < SmellDetector
|
15
15
|
# The name of the config field that sets the maximum number of instance
|
16
16
|
# variables permitted in a class.
|
17
|
-
MAX_ALLOWED_IVARS_KEY = 'max_instance_variables'
|
17
|
+
MAX_ALLOWED_IVARS_KEY = 'max_instance_variables'.freeze
|
18
18
|
DEFAULT_MAX_IVARS = 4
|
19
19
|
|
20
20
|
def self.smell_category
|
@@ -16,7 +16,7 @@ module Reek
|
|
16
16
|
class TooManyMethods < SmellDetector
|
17
17
|
# The name of the config field that sets the maximum number of methods
|
18
18
|
# permitted in a class.
|
19
|
-
MAX_ALLOWED_METHODS_KEY = 'max_methods'
|
19
|
+
MAX_ALLOWED_METHODS_KEY = 'max_methods'.freeze
|
20
20
|
DEFAULT_MAX_METHODS = 15
|
21
21
|
|
22
22
|
def self.smell_category
|
@@ -12,7 +12,7 @@ module Reek
|
|
12
12
|
class TooManyStatements < SmellDetector
|
13
13
|
# The name of the config field that sets the maximum number of
|
14
14
|
# statements permitted in any method.
|
15
|
-
MAX_ALLOWED_STATEMENTS_KEY = 'max_statements'
|
15
|
+
MAX_ALLOWED_STATEMENTS_KEY = 'max_statements'.freeze
|
16
16
|
DEFAULT_MAX_STATEMENTS = 5
|
17
17
|
|
18
18
|
def self.smell_category
|
@@ -18,10 +18,10 @@ module Reek
|
|
18
18
|
#
|
19
19
|
# See {file:docs/Uncommunicative-Method-Name.md} for details.
|
20
20
|
class UncommunicativeMethodName < SmellDetector
|
21
|
-
REJECT_KEY = 'reject'
|
22
|
-
ACCEPT_KEY = 'accept'
|
23
|
-
DEFAULT_REJECT_PATTERNS = [/^[a-z]$/, /[0-9]$/, /[A-Z]/]
|
24
|
-
DEFAULT_ACCEPT_NAMES = []
|
21
|
+
REJECT_KEY = 'reject'.freeze
|
22
|
+
ACCEPT_KEY = 'accept'.freeze
|
23
|
+
DEFAULT_REJECT_PATTERNS = [/^[a-z]$/, /[0-9]$/, /[A-Z]/].freeze
|
24
|
+
DEFAULT_ACCEPT_NAMES = [].freeze
|
25
25
|
|
26
26
|
def self.smell_category
|
27
27
|
'UncommunicativeName'
|
@@ -20,14 +20,14 @@ module Reek
|
|
20
20
|
class UncommunicativeModuleName < SmellDetector
|
21
21
|
# The name of the config field that lists the regexps of
|
22
22
|
# smelly names to be reported.
|
23
|
-
REJECT_KEY = 'reject'
|
24
|
-
DEFAULT_REJECT_PATTERNS = [/^.$/, /[0-9]$/]
|
23
|
+
REJECT_KEY = 'reject'.freeze
|
24
|
+
DEFAULT_REJECT_PATTERNS = [/^.$/, /[0-9]$/].freeze
|
25
25
|
|
26
26
|
# The name of the config field that lists the specific names that are
|
27
27
|
# to be treated as exceptions; these names will not be reported as
|
28
28
|
# uncommunicative.
|
29
|
-
ACCEPT_KEY = 'accept'
|
30
|
-
DEFAULT_ACCEPT_NAMES = []
|
29
|
+
ACCEPT_KEY = 'accept'.freeze
|
30
|
+
DEFAULT_ACCEPT_NAMES = [].freeze
|
31
31
|
|
32
32
|
def self.smell_category
|
33
33
|
'UncommunicativeName'
|
@@ -18,11 +18,11 @@ module Reek
|
|
18
18
|
#
|
19
19
|
# See {file:docs/Uncommunicative-Parameter-Name.md} for details.
|
20
20
|
class UncommunicativeParameterName < SmellDetector
|
21
|
-
REJECT_KEY = 'reject'
|
22
|
-
DEFAULT_REJECT_PATTERNS = [/^.$/, /[0-9]$/, /[A-Z]/, /^_/]
|
21
|
+
REJECT_KEY = 'reject'.freeze
|
22
|
+
DEFAULT_REJECT_PATTERNS = [/^.$/, /[0-9]$/, /[A-Z]/, /^_/].freeze
|
23
23
|
|
24
|
-
ACCEPT_KEY = 'accept'
|
25
|
-
DEFAULT_ACCEPT_NAMES = []
|
24
|
+
ACCEPT_KEY = 'accept'.freeze
|
25
|
+
DEFAULT_ACCEPT_NAMES = [].freeze
|
26
26
|
|
27
27
|
def self.smell_category
|
28
28
|
'UncommunicativeName'
|
@@ -22,14 +22,14 @@ module Reek
|
|
22
22
|
class UncommunicativeVariableName < SmellDetector
|
23
23
|
# The name of the config field that lists the regexps of
|
24
24
|
# smelly names to be reported.
|
25
|
-
REJECT_KEY = 'reject'
|
26
|
-
DEFAULT_REJECT_SET = [/^.$/, /[0-9]$/, /[A-Z]/]
|
25
|
+
REJECT_KEY = 'reject'.freeze
|
26
|
+
DEFAULT_REJECT_SET = [/^.$/, /[0-9]$/, /[A-Z]/].freeze
|
27
27
|
|
28
28
|
# The name of the config field that lists the specific names that are
|
29
29
|
# to be treated as exceptions; these names will not be reported as
|
30
30
|
# uncommunicative.
|
31
|
-
ACCEPT_KEY = 'accept'
|
32
|
-
DEFAULT_ACCEPT_SET = ['_']
|
31
|
+
ACCEPT_KEY = 'accept'.freeze
|
32
|
+
DEFAULT_ACCEPT_SET = ['_'].freeze
|
33
33
|
|
34
34
|
def self.smell_category
|
35
35
|
'UncommunicativeName'
|
@@ -130,7 +130,7 @@ module Reek
|
|
130
130
|
|
131
131
|
private
|
132
132
|
|
133
|
-
|
133
|
+
attr_accessor :accept_names, :reject_names
|
134
134
|
end
|
135
135
|
end
|
136
136
|
end
|
@@ -11,8 +11,8 @@ module Reek
|
|
11
11
|
# A +Source+ object represents a chunk of Ruby source code.
|
12
12
|
#
|
13
13
|
class SourceCode
|
14
|
-
IO_IDENTIFIER = 'STDIN'
|
15
|
-
STRING_IDENTIFIER = 'string'
|
14
|
+
IO_IDENTIFIER = 'STDIN'.freeze
|
15
|
+
STRING_IDENTIFIER = 'string'.freeze
|
16
16
|
|
17
17
|
attr_reader :origin
|
18
18
|
|
@@ -94,7 +94,7 @@ module Reek
|
|
94
94
|
|
95
95
|
private
|
96
96
|
|
97
|
-
|
97
|
+
attr_reader :parser, :source
|
98
98
|
end
|
99
99
|
end
|
100
100
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'private_attr/everywhere'
|
2
1
|
require 'find'
|
3
2
|
require 'pathname'
|
4
3
|
|
@@ -29,7 +28,7 @@ module Reek
|
|
29
28
|
|
30
29
|
private
|
31
30
|
|
32
|
-
|
31
|
+
attr_reader :configuration, :paths
|
33
32
|
|
34
33
|
# :reek:TooManyStatements: { max_statements: 7 }
|
35
34
|
# :reek:NestedIterators: { max_allowed_nesting: 2 }
|
@@ -43,8 +42,8 @@ module Reek
|
|
43
42
|
given_path.find do |path|
|
44
43
|
if path.directory?
|
45
44
|
ignore_path?(path) ? Find.prune : next
|
46
|
-
|
47
|
-
relevant_paths << path
|
45
|
+
elsif ruby_file?(path)
|
46
|
+
relevant_paths << path
|
48
47
|
end
|
49
48
|
end
|
50
49
|
end
|
@@ -10,10 +10,6 @@ module Reek
|
|
10
10
|
class ShouldReekOf
|
11
11
|
attr_reader :failure_message, :failure_message_when_negated
|
12
12
|
|
13
|
-
private_attr_reader :configuration, :smell_category, :smell_details
|
14
|
-
private_attr_writer :failure_message, :failure_message_when_negated
|
15
|
-
private_attr_accessor :examiner
|
16
|
-
|
17
13
|
def initialize(smell_category,
|
18
14
|
smell_details = {},
|
19
15
|
configuration = Configuration::AppConfiguration.default)
|
@@ -30,6 +26,10 @@ module Reek
|
|
30
26
|
|
31
27
|
private
|
32
28
|
|
29
|
+
attr_reader :configuration, :smell_category, :smell_details
|
30
|
+
attr_writer :failure_message, :failure_message_when_negated
|
31
|
+
attr_accessor :examiner
|
32
|
+
|
33
33
|
def set_failure_messages
|
34
34
|
# We set the failure messages for non-matching smell type unconditionally since we
|
35
35
|
# need that in any case for "failure_message_when_negated" below.
|
data/lib/reek/tree_dresser.rb
CHANGED
data/lib/reek/version.rb
CHANGED
data/reek.gemspec
CHANGED
@@ -23,7 +23,6 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.summary = 'Code smell detector for Ruby'
|
24
24
|
|
25
25
|
s.add_runtime_dependency 'codeclimate-engine-rb', '~> 0.3.1'
|
26
|
-
s.add_runtime_dependency 'parser', '~> 2.3'
|
27
|
-
s.add_runtime_dependency 'private_attr', '~> 1.1'
|
26
|
+
s.add_runtime_dependency 'parser', '~> 2.3', '>= 2.3.0.6'
|
28
27
|
s.add_runtime_dependency 'rainbow', '~> 2.0'
|
29
28
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require_lib 'reek/cli/application'
|
3
|
+
|
4
|
+
RSpec.describe Reek::CLI::Application do
|
5
|
+
describe '#initialize' do
|
6
|
+
it 'exits with default error code on invalid options' do
|
7
|
+
call = lambda do
|
8
|
+
Reek::CLI::Silencer.silently do
|
9
|
+
Reek::CLI::Application.new ['--foo']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
expect(call).to raise_error(SystemExit) do |error|
|
13
|
+
expect(error.status).to eq Reek::CLI::Options::DEFAULT_ERROR_EXIT_CODE
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#execute' do
|
19
|
+
let(:command) { double 'reek_command' }
|
20
|
+
let(:app) { Reek::CLI::Application.new [] }
|
21
|
+
|
22
|
+
before do
|
23
|
+
allow(Reek::CLI::ReekCommand).to receive(:new).and_return command
|
24
|
+
end
|
25
|
+
|
26
|
+
it "returns the command's result code" do
|
27
|
+
allow(command).to receive(:execute).and_return 'foo'
|
28
|
+
expect(app.execute).to eq 'foo'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require_lib 'reek/cli/reek_command'
|
3
|
+
require_lib 'reek/cli/options'
|
4
|
+
require_lib 'reek/cli/option_interpreter'
|
5
|
+
|
6
|
+
RSpec.describe Reek::CLI::ReekCommand do
|
7
|
+
describe '#execute' do
|
8
|
+
let(:options) { Reek::CLI::Options.new [] }
|
9
|
+
let(:option_interpreter) { Reek::CLI::OptionInterpreter.new(options) }
|
10
|
+
|
11
|
+
let(:reporter) { double 'reporter' }
|
12
|
+
let(:app) { double 'app' }
|
13
|
+
|
14
|
+
let(:command) { described_class.new option_interpreter }
|
15
|
+
|
16
|
+
before do
|
17
|
+
allow(option_interpreter).to receive(:reporter).and_return reporter
|
18
|
+
allow(reporter).to receive(:show)
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when no smells are found' do
|
22
|
+
before do
|
23
|
+
allow(option_interpreter).to receive(:sources).and_return []
|
24
|
+
allow(reporter).to receive(:smells?).and_return false
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'returns a success code' do
|
28
|
+
result = command.execute app
|
29
|
+
expect(result).to eq Reek::CLI::Options::DEFAULT_SUCCESS_EXIT_CODE
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when smells are found' do
|
34
|
+
before do
|
35
|
+
allow(option_interpreter).to receive(:sources).and_return []
|
36
|
+
allow(reporter).to receive(:smells?).and_return true
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns a failure code' do
|
40
|
+
result = command.execute app
|
41
|
+
expect(result).to eq Reek::CLI::Options::DEFAULT_FAILURE_EXIT_CODE
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require_lib 'reek/rake/task'
|
3
|
+
|
4
|
+
RSpec.describe Reek::Rake::Task do
|
5
|
+
describe '#source_files' do
|
6
|
+
it 'is set to "lib/**/*.rb" by default' do
|
7
|
+
task = Reek::Rake::Task.new
|
8
|
+
expect(task.source_files).to eq FileList['lib/**/*.rb']
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#source_files=' do
|
13
|
+
it 'sets source_files to a FileList when passed a string' do
|
14
|
+
task = Reek::Rake::Task.new
|
15
|
+
task.source_files = '*.rb'
|
16
|
+
expect(task.source_files).to eq FileList['*.rb']
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# SMELL: Testing a private method
|
21
|
+
describe '#command' do
|
22
|
+
let(:task) { Reek::Rake::Task.new }
|
23
|
+
|
24
|
+
it 'does not include a config file by default' do
|
25
|
+
expect(task.send(:command)).not_to include '-c'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'includes a config file when set' do
|
29
|
+
task.config_file = 'foo.reek'
|
30
|
+
expect(task.send(:command)[1..2]).to eq ['-c', 'foo.reek']
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -172,7 +172,7 @@ RSpec.describe Reek::Smells::UtilityFunction do
|
|
172
172
|
expect('def simple(arga=local) arga.to_s end').not_to reek_of(:UtilityFunction)
|
173
173
|
end
|
174
174
|
|
175
|
-
it 'should count usages of self'do
|
175
|
+
it 'should count usages of self' do
|
176
176
|
expect('def <=>(other) Options[:sort_order].compare(self, other) end').
|
177
177
|
not_to reek_of(:UtilityFunction)
|
178
178
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -23,9 +23,10 @@ SAMPLES_PATH = Pathname.new("#{__dir__}/samples").relative_path_from(Pathname.pw
|
|
23
23
|
# Simple helpers for our specs.
|
24
24
|
module Helpers
|
25
25
|
def test_configuration_for(config)
|
26
|
-
|
26
|
+
case config
|
27
|
+
when Pathname
|
27
28
|
configuration = Reek::Configuration::AppConfiguration.from_path(config)
|
28
|
-
|
29
|
+
when Hash
|
29
30
|
configuration = Reek::Configuration::AppConfiguration.from_map default_directive: config
|
30
31
|
else
|
31
32
|
raise "Unknown config given in `test_configuration_for`: #{config.inspect}"
|
data/tasks/configuration.rake
CHANGED
@@ -4,7 +4,7 @@ require 'yaml'
|
|
4
4
|
namespace :configuration do
|
5
5
|
desc 'Updates the default configuration file when smell defaults change'
|
6
6
|
task :update_default_configuration do
|
7
|
-
DEFAULT_SMELL_CONFIGURATION = 'defaults.reek'
|
7
|
+
DEFAULT_SMELL_CONFIGURATION = 'defaults.reek'.freeze
|
8
8
|
content = Reek::Smells::SmellRepository.smell_types.each_with_object({}) do |klass, hash|
|
9
9
|
hash[klass.smell_type] = klass.default_config
|
10
10
|
end
|
data/tasks/test.rake
CHANGED