rubocop 0.70.0 → 0.72.0
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/README.md +5 -10
- data/config/default.yml +50 -491
- data/lib/rubocop.rb +5 -53
- data/lib/rubocop/ast/builder.rb +2 -0
- data/lib/rubocop/ast/node.rb +1 -1
- data/lib/rubocop/ast/node/float_node.rb +12 -0
- data/lib/rubocop/ast/node/int_node.rb +12 -0
- data/lib/rubocop/ast/node/mixin/numeric_node.rb +21 -0
- data/lib/rubocop/ast/node/resbody_node.rb +1 -6
- data/lib/rubocop/cached_data.rb +1 -1
- data/lib/rubocop/config.rb +35 -6
- data/lib/rubocop/config_loader.rb +2 -2
- data/lib/rubocop/config_loader_resolver.rb +0 -6
- data/lib/rubocop/cop/cop.rb +0 -4
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +55 -0
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +3 -1
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +4 -0
- data/lib/rubocop/cop/layout/indent_first_argument.rb +6 -2
- data/lib/rubocop/cop/layout/indent_first_parameter.rb +7 -3
- data/lib/rubocop/cop/layout/indent_heredoc.rb +0 -1
- data/lib/rubocop/cop/layout/indentation_consistency.rb +13 -12
- data/lib/rubocop/cop/layout/indentation_width.rb +8 -4
- data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +2 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_alignment.rb +4 -0
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +20 -22
- data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +2 -1
- data/lib/rubocop/cop/style/float_division.rb +94 -0
- data/lib/rubocop/cop/style/format_string.rb +7 -3
- data/lib/rubocop/cop/style/if_inside_else.rb +42 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +12 -2
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -0
- data/lib/rubocop/cop/style/word_array.rb +2 -2
- data/lib/rubocop/cop/style/zero_length_predicate.rb +1 -1
- data/lib/rubocop/node_pattern.rb +84 -5
- data/lib/rubocop/options.rb +0 -2
- data/lib/rubocop/processed_source.rb +5 -1
- data/lib/rubocop/rspec/cop_helper.rb +0 -1
- data/lib/rubocop/rspec/shared_contexts.rb +0 -17
- data/lib/rubocop/rspec/support.rb +0 -1
- data/lib/rubocop/runner.rb +6 -7
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/yaml_duplication_checker.rb +8 -2
- metadata +7 -69
- data/lib/rubocop/cop/mixin/target_rails_version.rb +0 -16
- data/lib/rubocop/cop/rails/action_filter.rb +0 -117
- data/lib/rubocop/cop/rails/active_record_aliases.rb +0 -48
- data/lib/rubocop/cop/rails/active_record_override.rb +0 -82
- data/lib/rubocop/cop/rails/active_support_aliases.rb +0 -69
- data/lib/rubocop/cop/rails/application_job.rb +0 -40
- data/lib/rubocop/cop/rails/application_record.rb +0 -40
- data/lib/rubocop/cop/rails/assert_not.rb +0 -44
- data/lib/rubocop/cop/rails/belongs_to.rb +0 -102
- data/lib/rubocop/cop/rails/blank.rb +0 -164
- data/lib/rubocop/cop/rails/bulk_change_table.rb +0 -289
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +0 -91
- data/lib/rubocop/cop/rails/date.rb +0 -161
- data/lib/rubocop/cop/rails/delegate.rb +0 -132
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +0 -37
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +0 -91
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +0 -45
- data/lib/rubocop/cop/rails/environment_comparison.rb +0 -68
- data/lib/rubocop/cop/rails/exit.rb +0 -67
- data/lib/rubocop/cop/rails/file_path.rb +0 -108
- data/lib/rubocop/cop/rails/find_by.rb +0 -55
- data/lib/rubocop/cop/rails/find_each.rb +0 -51
- data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +0 -25
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +0 -106
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +0 -117
- data/lib/rubocop/cop/rails/http_status.rb +0 -179
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +0 -94
- data/lib/rubocop/cop/rails/inverse_of.rb +0 -246
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +0 -175
- data/lib/rubocop/cop/rails/link_to_blank.rb +0 -98
- data/lib/rubocop/cop/rails/not_null_column.rb +0 -67
- data/lib/rubocop/cop/rails/output.rb +0 -49
- data/lib/rubocop/cop/rails/output_safety.rb +0 -99
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +0 -107
- data/lib/rubocop/cop/rails/presence.rb +0 -124
- data/lib/rubocop/cop/rails/present.rb +0 -153
- data/lib/rubocop/cop/rails/read_write_attribute.rb +0 -74
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +0 -111
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +0 -136
- data/lib/rubocop/cop/rails/reflection_class_name.rb +0 -37
- data/lib/rubocop/cop/rails/refute_methods.rb +0 -76
- data/lib/rubocop/cop/rails/relative_date_constant.rb +0 -93
- data/lib/rubocop/cop/rails/request_referer.rb +0 -56
- data/lib/rubocop/cop/rails/reversible_migration.rb +0 -286
- data/lib/rubocop/cop/rails/safe_navigation.rb +0 -87
- data/lib/rubocop/cop/rails/save_bang.rb +0 -316
- data/lib/rubocop/cop/rails/scope_args.rb +0 -29
- data/lib/rubocop/cop/rails/skips_model_validations.rb +0 -87
- data/lib/rubocop/cop/rails/time_zone.rb +0 -238
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +0 -105
- data/lib/rubocop/cop/rails/unknown_env.rb +0 -63
- data/lib/rubocop/cop/rails/validation.rb +0 -109
- data/lib/rubocop/rspec/shared_examples.rb +0 -59
@@ -1,82 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# Checks for overriding built-in Active Record methods instead of using
|
7
|
-
# callbacks.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # bad
|
11
|
-
# class Book < ApplicationRecord
|
12
|
-
# def save
|
13
|
-
# self.title = title.upcase!
|
14
|
-
# super
|
15
|
-
# end
|
16
|
-
# end
|
17
|
-
#
|
18
|
-
# # good
|
19
|
-
# class Book < ApplicationRecord
|
20
|
-
# before_save :upcase_title
|
21
|
-
#
|
22
|
-
# def upcase_title
|
23
|
-
# self.title = title.upcase!
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
class ActiveRecordOverride < Cop
|
28
|
-
MSG =
|
29
|
-
'Use %<prefer>s callbacks instead of overriding the Active Record ' \
|
30
|
-
'method `%<bad>s`.'
|
31
|
-
BAD_METHODS = %i[create destroy save update].freeze
|
32
|
-
ACTIVE_RECORD_CLASSES = %w[ApplicationRecord ActiveModel::Base
|
33
|
-
ActiveRecord::Base].freeze
|
34
|
-
|
35
|
-
def on_def(node)
|
36
|
-
return unless BAD_METHODS.include?(node.method_name)
|
37
|
-
|
38
|
-
parent_class_name = find_parent_class_name(node)
|
39
|
-
return unless active_model?(parent_class_name)
|
40
|
-
|
41
|
-
return unless node.descendants.any?(&:zsuper_type?)
|
42
|
-
|
43
|
-
add_offense(node, message: message(node.method_name))
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def active_model?(parent_class_name)
|
49
|
-
ACTIVE_RECORD_CLASSES.include?(parent_class_name)
|
50
|
-
end
|
51
|
-
|
52
|
-
def callback_names(method_name)
|
53
|
-
names = %w[before_ around_ after_].map do |prefix|
|
54
|
-
"`#{prefix}#{method_name}`"
|
55
|
-
end
|
56
|
-
|
57
|
-
names[-1] = "or #{names.last}"
|
58
|
-
|
59
|
-
names.join(', ')
|
60
|
-
end
|
61
|
-
|
62
|
-
def message(method_name)
|
63
|
-
format(MSG, prefer: callback_names(method_name), bad: method_name)
|
64
|
-
end
|
65
|
-
|
66
|
-
def find_parent_class_name(node)
|
67
|
-
return nil unless node
|
68
|
-
|
69
|
-
if node.class_type?
|
70
|
-
parent_class_name = node.node_parts[1]
|
71
|
-
|
72
|
-
return nil if parent_class_name.nil?
|
73
|
-
|
74
|
-
return parent_class_name.source
|
75
|
-
end
|
76
|
-
|
77
|
-
find_parent_class_name(node.parent)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# This cop checks that ActiveSupport aliases to core ruby methods
|
7
|
-
# are not used.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # good
|
11
|
-
# 'some_string'.start_with?('prefix')
|
12
|
-
# 'some_string'.end_with?('suffix')
|
13
|
-
# [1, 2, 'a'] << 'b'
|
14
|
-
# [1, 2, 'a'].unshift('b')
|
15
|
-
#
|
16
|
-
# # bad
|
17
|
-
# 'some_string'.starts_with?('prefix')
|
18
|
-
# 'some_string'.ends_with?('suffix')
|
19
|
-
# [1, 2, 'a'].append('b')
|
20
|
-
# [1, 2, 'a'].prepend('b')
|
21
|
-
#
|
22
|
-
class ActiveSupportAliases < Cop
|
23
|
-
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
24
|
-
|
25
|
-
ALIASES = {
|
26
|
-
starts_with?: {
|
27
|
-
original: :start_with?, matcher: '(send str :starts_with? _)'
|
28
|
-
},
|
29
|
-
ends_with?: {
|
30
|
-
original: :end_with?, matcher: '(send str :ends_with? _)'
|
31
|
-
},
|
32
|
-
append: { original: :<<, matcher: '(send array :append _)' },
|
33
|
-
prepend: { original: :unshift, matcher: '(send array :prepend _)' }
|
34
|
-
}.freeze
|
35
|
-
|
36
|
-
ALIASES.each do |aliased_method, options|
|
37
|
-
def_node_matcher aliased_method, options[:matcher]
|
38
|
-
end
|
39
|
-
|
40
|
-
def on_send(node)
|
41
|
-
ALIASES.each_key do |aliased_method|
|
42
|
-
register_offense(node, aliased_method) if
|
43
|
-
public_send(aliased_method, node)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def autocorrect(node)
|
48
|
-
return false if append(node)
|
49
|
-
|
50
|
-
lambda do |corrector|
|
51
|
-
method_name = node.loc.selector.source
|
52
|
-
replacement = ALIASES[method_name.to_sym][:original]
|
53
|
-
corrector.replace(node.loc.selector, replacement.to_s)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def register_offense(node, method_name)
|
60
|
-
add_offense(
|
61
|
-
node,
|
62
|
-
message: format(MSG, prefer: ALIASES[method_name][:original],
|
63
|
-
current: method_name)
|
64
|
-
)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# This cop checks that jobs subclass ApplicationJob with Rails 5.0.
|
7
|
-
#
|
8
|
-
# @example
|
9
|
-
#
|
10
|
-
# # good
|
11
|
-
# class Rails5Job < ApplicationJob
|
12
|
-
# # ...
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# # bad
|
16
|
-
# class Rails4Job < ActiveJob::Base
|
17
|
-
# # ...
|
18
|
-
# end
|
19
|
-
class ApplicationJob < Cop
|
20
|
-
extend TargetRailsVersion
|
21
|
-
|
22
|
-
minimum_target_rails_version 5.0
|
23
|
-
|
24
|
-
MSG = 'Jobs should subclass `ApplicationJob`.'
|
25
|
-
SUPERCLASS = 'ApplicationJob'
|
26
|
-
BASE_PATTERN = '(const (const nil? :ActiveJob) :Base)'
|
27
|
-
|
28
|
-
# rubocop:disable Layout/ClassStructure
|
29
|
-
include RuboCop::Cop::EnforceSuperclass
|
30
|
-
# rubocop:enable Layout/ClassStructure
|
31
|
-
|
32
|
-
def autocorrect(node)
|
33
|
-
lambda do |corrector|
|
34
|
-
corrector.replace(node.source_range, self.class::SUPERCLASS)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# This cop checks that models subclass ApplicationRecord with Rails 5.0.
|
7
|
-
#
|
8
|
-
# @example
|
9
|
-
#
|
10
|
-
# # good
|
11
|
-
# class Rails5Model < ApplicationRecord
|
12
|
-
# # ...
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# # bad
|
16
|
-
# class Rails4Model < ActiveRecord::Base
|
17
|
-
# # ...
|
18
|
-
# end
|
19
|
-
class ApplicationRecord < Cop
|
20
|
-
extend TargetRailsVersion
|
21
|
-
|
22
|
-
minimum_target_rails_version 5.0
|
23
|
-
|
24
|
-
MSG = 'Models should subclass `ApplicationRecord`.'
|
25
|
-
SUPERCLASS = 'ApplicationRecord'
|
26
|
-
BASE_PATTERN = '(const (const nil? :ActiveRecord) :Base)'
|
27
|
-
|
28
|
-
# rubocop:disable Layout/ClassStructure
|
29
|
-
include RuboCop::Cop::EnforceSuperclass
|
30
|
-
# rubocop:enable Layout/ClassStructure
|
31
|
-
|
32
|
-
def autocorrect(node)
|
33
|
-
lambda do |corrector|
|
34
|
-
corrector.replace(node.source_range, self.class::SUPERCLASS)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
#
|
7
|
-
# Use `assert_not` instead of `assert !`.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # bad
|
11
|
-
# assert !x
|
12
|
-
#
|
13
|
-
# # good
|
14
|
-
# assert_not x
|
15
|
-
#
|
16
|
-
class AssertNot < RuboCop::Cop::Cop
|
17
|
-
MSG = 'Prefer `assert_not` over `assert !`.'
|
18
|
-
|
19
|
-
def_node_matcher :offensive?, '(send nil? :assert (send ... :!) ...)'
|
20
|
-
|
21
|
-
def on_send(node)
|
22
|
-
add_offense(node) if offensive?(node)
|
23
|
-
end
|
24
|
-
|
25
|
-
def autocorrect(node)
|
26
|
-
expression = node.loc.expression
|
27
|
-
|
28
|
-
lambda do |corrector|
|
29
|
-
corrector.replace(
|
30
|
-
expression,
|
31
|
-
corrected_source(expression.source)
|
32
|
-
)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def corrected_source(source)
|
39
|
-
source.gsub(/^assert(\(| ) *! */, 'assert_not\\1')
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,102 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# This cop looks for belongs_to associations where we control whether the
|
7
|
-
# association is required via the deprecated `required` option instead.
|
8
|
-
#
|
9
|
-
# Since Rails 5, belongs_to associations are required by default and this
|
10
|
-
# can be controlled through the use of `optional: true`.
|
11
|
-
#
|
12
|
-
# From the release notes:
|
13
|
-
#
|
14
|
-
# belongs_to will now trigger a validation error by default if the
|
15
|
-
# association is not present. You can turn this off on a
|
16
|
-
# per-association basis with optional: true. Also deprecate required
|
17
|
-
# option in favor of optional for belongs_to. (Pull Request)
|
18
|
-
#
|
19
|
-
# In the case that the developer is doing `required: false`, we
|
20
|
-
# definitely want to autocorrect to `optional: true`.
|
21
|
-
#
|
22
|
-
# However, without knowing whether they've set overridden the default
|
23
|
-
# value of `config.active_record.belongs_to_required_by_default`, we
|
24
|
-
# can't say whether it's safe to remove `required: true` or whether we
|
25
|
-
# should replace it with `optional: false` (or, similarly, remove a
|
26
|
-
# superfluous `optional: false`). Therefore, in the cases we're using
|
27
|
-
# `required: true`, we'll simply invert it to `optional: false` and the
|
28
|
-
# user can remove depending on their defaults.
|
29
|
-
#
|
30
|
-
# @example
|
31
|
-
# # bad
|
32
|
-
# class Post < ApplicationRecord
|
33
|
-
# belongs_to :blog, required: false
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# # good
|
37
|
-
# class Post < ApplicationRecord
|
38
|
-
# belongs_to :blog, optional: true
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
# # bad
|
42
|
-
# class Post < ApplicationRecord
|
43
|
-
# belongs_to :blog, required: true
|
44
|
-
# end
|
45
|
-
#
|
46
|
-
# # good
|
47
|
-
# class Post < ApplicationRecord
|
48
|
-
# belongs_to :blog, optional: false
|
49
|
-
# end
|
50
|
-
#
|
51
|
-
# @see https://guides.rubyonrails.org/5_0_release_notes.html
|
52
|
-
# @see https://github.com/rails/rails/pull/18937
|
53
|
-
class BelongsTo < Cop
|
54
|
-
extend TargetRailsVersion
|
55
|
-
|
56
|
-
minimum_target_rails_version 5.0
|
57
|
-
|
58
|
-
SUPERFLOUS_REQUIRE_FALSE_MSG =
|
59
|
-
'You specified `required: false`, in Rails > 5.0 the required ' \
|
60
|
-
'option is deprecated and you want to use `optional: true`.'
|
61
|
-
|
62
|
-
SUPERFLOUS_REQUIRE_TRUE_MSG =
|
63
|
-
'You specified `required: true`, in Rails > 5.0 the required ' \
|
64
|
-
'option is deprecated and you want to use `optional: false`. ' \
|
65
|
-
'In most configurations, this is the default and you can omit ' \
|
66
|
-
'this option altogether'
|
67
|
-
|
68
|
-
def_node_matcher :match_belongs_to_with_options, <<-PATTERN
|
69
|
-
(send _ :belongs_to _
|
70
|
-
(hash <$(pair (sym :required) ${true false}) ...>)
|
71
|
-
)
|
72
|
-
PATTERN
|
73
|
-
|
74
|
-
def on_send(node)
|
75
|
-
match_belongs_to_with_options(node) do |_option_node, option_value|
|
76
|
-
message =
|
77
|
-
if option_value.true_type?
|
78
|
-
SUPERFLOUS_REQUIRE_TRUE_MSG
|
79
|
-
elsif option_value.false_type?
|
80
|
-
SUPERFLOUS_REQUIRE_FALSE_MSG
|
81
|
-
end
|
82
|
-
|
83
|
-
add_offense(node, message: message, location: :selector)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def autocorrect(node)
|
88
|
-
option_node, option_value = match_belongs_to_with_options(node)
|
89
|
-
return unless option_node
|
90
|
-
|
91
|
-
lambda do |corrector|
|
92
|
-
if option_value.true_type?
|
93
|
-
corrector.replace(option_node.loc.expression, 'optional: false')
|
94
|
-
elsif option_value.false_type?
|
95
|
-
corrector.replace(option_node.loc.expression, 'optional: true')
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
@@ -1,164 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Rails
|
6
|
-
# This cop checks for code that can be written with simpler conditionals
|
7
|
-
# using `Object#blank?` defined by Active Support.
|
8
|
-
#
|
9
|
-
# Interaction with `Style/UnlessElse`:
|
10
|
-
# The configuration of `NotPresent` will not produce an offense in the
|
11
|
-
# context of `unless else` if `Style/UnlessElse` is inabled. This is
|
12
|
-
# to prevent interference between the auto-correction of the two cops.
|
13
|
-
#
|
14
|
-
# @example NilOrEmpty: true (default)
|
15
|
-
# # Converts usages of `nil? || empty?` to `blank?`
|
16
|
-
#
|
17
|
-
# # bad
|
18
|
-
# foo.nil? || foo.empty?
|
19
|
-
# foo == nil || foo.empty?
|
20
|
-
#
|
21
|
-
# # good
|
22
|
-
# foo.blank?
|
23
|
-
#
|
24
|
-
# @example NotPresent: true (default)
|
25
|
-
# # Converts usages of `!present?` to `blank?`
|
26
|
-
#
|
27
|
-
# # bad
|
28
|
-
# !foo.present?
|
29
|
-
#
|
30
|
-
# # good
|
31
|
-
# foo.blank?
|
32
|
-
#
|
33
|
-
# @example UnlessPresent: true (default)
|
34
|
-
# # Converts usages of `unless present?` to `if blank?`
|
35
|
-
#
|
36
|
-
# # bad
|
37
|
-
# something unless foo.present?
|
38
|
-
#
|
39
|
-
# # good
|
40
|
-
# something if foo.blank?
|
41
|
-
#
|
42
|
-
# # bad
|
43
|
-
# unless foo.present?
|
44
|
-
# something
|
45
|
-
# end
|
46
|
-
#
|
47
|
-
# # good
|
48
|
-
# if foo.blank?
|
49
|
-
# something
|
50
|
-
# end
|
51
|
-
#
|
52
|
-
# # good
|
53
|
-
# def blank?
|
54
|
-
# !present?
|
55
|
-
# end
|
56
|
-
class Blank < Cop
|
57
|
-
MSG_NIL_OR_EMPTY = 'Use `%<prefer>s` instead of `%<current>s`.'
|
58
|
-
MSG_NOT_PRESENT = 'Use `%<prefer>s` instead of `%<current>s`.'
|
59
|
-
MSG_UNLESS_PRESENT = 'Use `if %<prefer>s` instead of ' \
|
60
|
-
'`%<current>s`.'
|
61
|
-
|
62
|
-
# `(send nil $_)` is not actually a valid match for an offense. Nodes
|
63
|
-
# that have a single method call on the left hand side
|
64
|
-
# (`bar || foo.empty?`) will blow up when checking
|
65
|
-
# `(send (:nil) :== $_)`.
|
66
|
-
def_node_matcher :nil_or_empty?, <<-PATTERN
|
67
|
-
(or
|
68
|
-
{
|
69
|
-
(send $_ :!)
|
70
|
-
(send $_ :nil?)
|
71
|
-
(send $_ :== nil)
|
72
|
-
(send nil :== $_)
|
73
|
-
}
|
74
|
-
{
|
75
|
-
(send $_ :empty?)
|
76
|
-
(send (send (send $_ :empty?) :!) :!)
|
77
|
-
}
|
78
|
-
)
|
79
|
-
PATTERN
|
80
|
-
|
81
|
-
def_node_matcher :not_present?, '(send (send $_ :present?) :!)'
|
82
|
-
|
83
|
-
def_node_matcher :defining_blank?, '(def :blank? (args) ...)'
|
84
|
-
|
85
|
-
def_node_matcher :unless_present?, <<-PATTERN
|
86
|
-
(:if $(send $_ :present?) {nil? (...)} ...)
|
87
|
-
PATTERN
|
88
|
-
|
89
|
-
def on_send(node)
|
90
|
-
return unless cop_config['NotPresent']
|
91
|
-
|
92
|
-
not_present?(node) do |receiver|
|
93
|
-
# accepts !present? if its in the body of a `blank?` method
|
94
|
-
next if defining_blank?(node.parent)
|
95
|
-
|
96
|
-
add_offense(node,
|
97
|
-
message: format(MSG_NOT_PRESENT,
|
98
|
-
prefer: replacement(receiver),
|
99
|
-
current: node.source))
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def on_or(node)
|
104
|
-
return unless cop_config['NilOrEmpty']
|
105
|
-
|
106
|
-
nil_or_empty?(node) do |var1, var2|
|
107
|
-
return unless var1 == var2
|
108
|
-
|
109
|
-
add_offense(node,
|
110
|
-
message: format(MSG_NIL_OR_EMPTY,
|
111
|
-
prefer: replacement(var1),
|
112
|
-
current: node.source))
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def on_if(node)
|
117
|
-
return unless cop_config['UnlessPresent']
|
118
|
-
return unless node.unless?
|
119
|
-
return if node.else? && config.for_cop('Style/UnlessElse')['Enabled']
|
120
|
-
|
121
|
-
unless_present?(node) do |method_call, receiver|
|
122
|
-
range = unless_condition(node, method_call)
|
123
|
-
|
124
|
-
add_offense(node,
|
125
|
-
location: range,
|
126
|
-
message: format(MSG_UNLESS_PRESENT,
|
127
|
-
prefer: replacement(receiver),
|
128
|
-
current: range.source))
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def autocorrect(node)
|
133
|
-
lambda do |corrector|
|
134
|
-
method_call, variable1 = unless_present?(node)
|
135
|
-
|
136
|
-
if method_call
|
137
|
-
corrector.replace(node.loc.keyword, 'if')
|
138
|
-
range = method_call.loc.expression
|
139
|
-
else
|
140
|
-
variable1, _variable2 = nil_or_empty?(node) || not_present?(node)
|
141
|
-
range = node.loc.expression
|
142
|
-
end
|
143
|
-
|
144
|
-
corrector.replace(range, replacement(variable1))
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
private
|
149
|
-
|
150
|
-
def unless_condition(node, method_call)
|
151
|
-
if node.modifier_form?
|
152
|
-
node.loc.keyword.join(node.loc.expression.end)
|
153
|
-
else
|
154
|
-
node.loc.expression.begin.join(method_call.loc.expression)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def replacement(node)
|
159
|
-
node.respond_to?(:source) ? "#{node.source}.blank?" : 'blank?'
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|