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