rubocop-rails 2.8.1 → 2.9.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 +16 -0
- data/config/default.yml +33 -1
- data/lib/rubocop/cop/mixin/active_record_helper.rb +4 -3
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +40 -0
- data/lib/rubocop/cop/mixin/index_method.rb +8 -11
- data/lib/rubocop/cop/rails/action_filter.rb +10 -14
- data/lib/rubocop/cop/rails/active_record_aliases.rb +13 -17
- data/lib/rubocop/cop/rails/active_record_callbacks_order.rb +17 -12
- data/lib/rubocop/cop/rails/active_record_override.rb +1 -1
- data/lib/rubocop/cop/rails/active_support_aliases.rb +12 -21
- data/lib/rubocop/cop/rails/after_commit_override.rb +1 -1
- data/lib/rubocop/cop/rails/application_controller.rb +3 -7
- data/lib/rubocop/cop/rails/application_job.rb +2 -1
- data/lib/rubocop/cop/rails/application_mailer.rb +2 -7
- data/lib/rubocop/cop/rails/application_record.rb +2 -7
- data/lib/rubocop/cop/rails/arel_star.rb +41 -0
- data/lib/rubocop/cop/rails/assert_not.rb +8 -10
- data/lib/rubocop/cop/rails/attribute_default_block_value.rb +90 -0
- data/lib/rubocop/cop/rails/belongs_to.rb +9 -18
- data/lib/rubocop/cop/rails/blank.rb +27 -27
- data/lib/rubocop/cop/rails/bulk_change_table.rb +1 -1
- data/lib/rubocop/cop/rails/content_tag.rb +16 -16
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +2 -1
- data/lib/rubocop/cop/rails/date.rb +10 -11
- data/lib/rubocop/cop/rails/default_scope.rb +11 -4
- data/lib/rubocop/cop/rails/delegate.rb +9 -9
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +7 -8
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +12 -11
- data/lib/rubocop/cop/rails/enum_hash.rb +11 -10
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +2 -1
- data/lib/rubocop/cop/rails/environment_comparison.rb +18 -14
- data/lib/rubocop/cop/rails/exit.rb +4 -10
- data/lib/rubocop/cop/rails/file_path.rb +4 -3
- data/lib/rubocop/cop/rails/find_by.rb +13 -13
- data/lib/rubocop/cop/rails/find_by_id.rb +12 -21
- data/lib/rubocop/cop/rails/find_each.rb +16 -14
- data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +3 -2
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +3 -2
- data/lib/rubocop/cop/rails/helper_instance_variable.rb +2 -2
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +19 -21
- data/lib/rubocop/cop/rails/http_status.rb +7 -9
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +8 -6
- data/lib/rubocop/cop/rails/index_by.rb +2 -1
- data/lib/rubocop/cop/rails/index_with.rb +2 -1
- data/lib/rubocop/cop/rails/inquiry.rb +4 -3
- data/lib/rubocop/cop/rails/inverse_of.rb +3 -2
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +17 -15
- data/lib/rubocop/cop/rails/link_to_blank.rb +20 -22
- data/lib/rubocop/cop/rails/mailer_name.rb +19 -13
- data/lib/rubocop/cop/rails/match_route.rb +14 -13
- data/lib/rubocop/cop/rails/negate_include.rb +10 -8
- data/lib/rubocop/cop/rails/not_null_column.rb +2 -1
- data/lib/rubocop/cop/rails/order_by_id.rb +1 -2
- data/lib/rubocop/cop/rails/output.rb +5 -2
- data/lib/rubocop/cop/rails/output_safety.rb +3 -2
- data/lib/rubocop/cop/rails/pick.rb +14 -12
- data/lib/rubocop/cop/rails/pluck.rb +6 -9
- data/lib/rubocop/cop/rails/pluck_id.rb +4 -6
- data/lib/rubocop/cop/rails/pluck_in_where.rb +7 -7
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +10 -14
- data/lib/rubocop/cop/rails/presence.rb +12 -13
- data/lib/rubocop/cop/rails/present.rb +30 -24
- data/lib/rubocop/cop/rails/rake_environment.rb +8 -10
- data/lib/rubocop/cop/rails/read_write_attribute.rb +12 -11
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +29 -31
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +9 -12
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +11 -10
- data/lib/rubocop/cop/rails/reflection_class_name.rb +3 -2
- data/lib/rubocop/cop/rails/refute_methods.rb +9 -10
- data/lib/rubocop/cop/rails/relative_date_constant.rb +16 -8
- data/lib/rubocop/cop/rails/render_inline.rb +2 -1
- data/lib/rubocop/cop/rails/render_plain_text.rb +9 -14
- data/lib/rubocop/cop/rails/request_referer.rb +7 -7
- data/lib/rubocop/cop/rails/reversible_migration.rb +2 -6
- data/lib/rubocop/cop/rails/safe_navigation.rb +11 -10
- data/lib/rubocop/cop/rails/safe_navigation_with_blank.rb +5 -10
- data/lib/rubocop/cop/rails/save_bang.rb +17 -20
- data/lib/rubocop/cop/rails/scope_args.rb +2 -1
- data/lib/rubocop/cop/rails/short_i18n.rb +7 -9
- data/lib/rubocop/cop/rails/skips_model_validations.rb +4 -4
- data/lib/rubocop/cop/rails/squished_sql_heredocs.rb +5 -6
- data/lib/rubocop/cop/rails/time_zone.rb +22 -20
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +4 -6
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +2 -2
- data/lib/rubocop/cop/rails/unknown_env.rb +2 -2
- data/lib/rubocop/cop/rails/validation.rb +15 -14
- data/lib/rubocop/cop/rails/where_equals.rb +94 -0
- data/lib/rubocop/cop/rails/where_exists.rb +8 -13
- data/lib/rubocop/cop/rails/where_not.rb +5 -16
- data/lib/rubocop/cop/rails_cops.rb +4 -0
- data/lib/rubocop/rails/schema_loader.rb +4 -4
- data/lib/rubocop/rails/schema_loader/schema.rb +1 -1
- data/lib/rubocop/rails/version.rb +5 -1
- metadata +19 -9
@@ -16,10 +16,12 @@ module RuboCop
|
|
16
16
|
# # good
|
17
17
|
# User.find(id)
|
18
18
|
#
|
19
|
-
class FindById <
|
19
|
+
class FindById < Base
|
20
20
|
include RangeHelp
|
21
|
+
extend AutoCorrector
|
21
22
|
|
22
23
|
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
24
|
+
RESTRICT_ON_SEND = %i[take! find_by_id! find_by!].freeze
|
23
25
|
|
24
26
|
def_node_matcher :where_take?, <<~PATTERN
|
25
27
|
(send
|
@@ -38,41 +40,30 @@ module RuboCop
|
|
38
40
|
def on_send(node)
|
39
41
|
where_take?(node) do |where, id_value|
|
40
42
|
range = where_take_offense_range(node, where)
|
41
|
-
|
42
|
-
good_method = build_good_method(id_value)
|
43
43
|
bad_method = build_where_take_bad_method(id_value)
|
44
|
-
message = format(MSG, good_method: good_method, bad_method: bad_method)
|
45
44
|
|
46
|
-
|
45
|
+
register_offense(range, id_value, bad_method)
|
47
46
|
end
|
48
47
|
|
49
48
|
find_by?(node) do |id_value|
|
50
49
|
range = find_by_offense_range(node)
|
51
|
-
|
52
|
-
good_method = build_good_method(id_value)
|
53
50
|
bad_method = build_find_by_bad_method(node, id_value)
|
54
|
-
message = format(MSG, good_method: good_method, bad_method: bad_method)
|
55
51
|
|
56
|
-
|
52
|
+
register_offense(range, id_value, bad_method)
|
57
53
|
end
|
58
54
|
end
|
59
55
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
range = find_by_offense_range(node)
|
66
|
-
end
|
56
|
+
private
|
57
|
+
|
58
|
+
def register_offense(range, id_value, bad_method)
|
59
|
+
good_method = build_good_method(id_value)
|
60
|
+
message = format(MSG, good_method: good_method, bad_method: bad_method)
|
67
61
|
|
68
|
-
|
69
|
-
|
70
|
-
corrector.replace(range, replacement)
|
62
|
+
add_offense(range, message: message) do |corrector|
|
63
|
+
corrector.replace(range, good_method)
|
71
64
|
end
|
72
65
|
end
|
73
66
|
|
74
|
-
private
|
75
|
-
|
76
67
|
def where_take_offense_range(node, where)
|
77
68
|
range_between(where.loc.selector.begin_pos, node.loc.expression.end_pos)
|
78
69
|
end
|
@@ -12,27 +12,30 @@ module RuboCop
|
|
12
12
|
#
|
13
13
|
# # good
|
14
14
|
# User.all.find_each
|
15
|
-
|
15
|
+
#
|
16
|
+
# @example IgnoredMethods: ['order']
|
17
|
+
# # good
|
18
|
+
# User.order(:foo).each
|
19
|
+
class FindEach < Base
|
20
|
+
extend AutoCorrector
|
21
|
+
|
16
22
|
MSG = 'Use `find_each` instead of `each`.'
|
23
|
+
RESTRICT_ON_SEND = %i[each].freeze
|
17
24
|
|
18
25
|
SCOPE_METHODS = %i[
|
19
26
|
all eager_load includes joins left_joins left_outer_joins not preload
|
20
27
|
references unscoped where
|
21
28
|
].freeze
|
22
|
-
IGNORED_METHODS = %i[order limit select].freeze
|
23
29
|
|
24
30
|
def on_send(node)
|
25
|
-
return unless node.receiver&.send_type?
|
26
|
-
node.method?(:each)
|
27
|
-
|
31
|
+
return unless node.receiver&.send_type?
|
28
32
|
return unless SCOPE_METHODS.include?(node.receiver.method_name)
|
29
|
-
return if method_chain(node).any? { |m|
|
30
|
-
|
31
|
-
add_offense(node, location: :selector)
|
32
|
-
end
|
33
|
+
return if method_chain(node).any? { |m| ignored?(m) }
|
33
34
|
|
34
|
-
|
35
|
-
|
35
|
+
range = node.loc.selector
|
36
|
+
add_offense(range) do |corrector|
|
37
|
+
corrector.replace(range, 'find_each')
|
38
|
+
end
|
36
39
|
end
|
37
40
|
|
38
41
|
private
|
@@ -41,9 +44,8 @@ module RuboCop
|
|
41
44
|
node.each_node(:send).map(&:method_name)
|
42
45
|
end
|
43
46
|
|
44
|
-
def
|
45
|
-
|
46
|
-
IGNORED_METHODS.include?(relation_method)
|
47
|
+
def ignored?(relation_method)
|
48
|
+
cop_config['IgnoredMethods'].include?(relation_method)
|
47
49
|
end
|
48
50
|
end
|
49
51
|
end
|
@@ -11,13 +11,14 @@ module RuboCop
|
|
11
11
|
#
|
12
12
|
# # good
|
13
13
|
# # has_many :ingredients, through: :recipe_ingredients
|
14
|
-
class HasAndBelongsToMany <
|
14
|
+
class HasAndBelongsToMany < Base
|
15
15
|
MSG = 'Prefer `has_many :through` to `has_and_belongs_to_many`.'
|
16
|
+
RESTRICT_ON_SEND = %i[has_and_belongs_to_many].freeze
|
16
17
|
|
17
18
|
def on_send(node)
|
18
19
|
return unless node.command?(:has_and_belongs_to_many)
|
19
20
|
|
20
|
-
add_offense(node
|
21
|
+
add_offense(node.loc.selector)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|
@@ -20,8 +20,9 @@ module RuboCop
|
|
20
20
|
# has_one :avatar, dependent: :destroy
|
21
21
|
# has_many :patients, through: :appointments
|
22
22
|
# end
|
23
|
-
class HasManyOrHasOneDependent <
|
23
|
+
class HasManyOrHasOneDependent < Base
|
24
24
|
MSG = 'Specify a `:dependent` option.'
|
25
|
+
RESTRICT_ON_SEND = %i[has_many has_one].freeze
|
25
26
|
|
26
27
|
def_node_search :active_resource_class?, <<~PATTERN
|
27
28
|
(const (const nil? :ActiveResource) :Base)
|
@@ -55,7 +56,7 @@ module RuboCop
|
|
55
56
|
return if !association_without_options?(node) && valid_options?(association_with_options?(node))
|
56
57
|
return if valid_options_in_with_options_block?(node)
|
57
58
|
|
58
|
-
add_offense(node
|
59
|
+
add_offense(node.loc.selector)
|
59
60
|
end
|
60
61
|
|
61
62
|
private
|
@@ -23,7 +23,7 @@ module RuboCop
|
|
23
23
|
# def welcome_message(user)
|
24
24
|
# "Hello #{user.name}"
|
25
25
|
# end
|
26
|
-
class HelperInstanceVariable <
|
26
|
+
class HelperInstanceVariable < Base
|
27
27
|
MSG = 'Do not use instance variables in helpers.'
|
28
28
|
|
29
29
|
def on_ivar(node)
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
33
33
|
def on_ivasgn(node)
|
34
34
|
return if node.parent.or_asgn_type?
|
35
35
|
|
36
|
-
add_offense(node
|
36
|
+
add_offense(node.loc.name)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
@@ -17,7 +17,8 @@ module RuboCop
|
|
17
17
|
# # good
|
18
18
|
# get :new, params: { user_id: 1 }
|
19
19
|
# get :new, **options
|
20
|
-
class HttpPositionalArguments <
|
20
|
+
class HttpPositionalArguments < Base
|
21
|
+
extend AutoCorrector
|
21
22
|
extend TargetRailsVersion
|
22
23
|
|
23
24
|
MSG = 'Use keyword arguments instead of ' \
|
@@ -25,12 +26,12 @@ module RuboCop
|
|
25
26
|
KEYWORD_ARGS = %i[
|
26
27
|
method params session body flash xhr as headers env to
|
27
28
|
].freeze
|
28
|
-
|
29
|
+
RESTRICT_ON_SEND = %i[get post put patch delete head].freeze
|
29
30
|
|
30
31
|
minimum_target_rails_version 5.0
|
31
32
|
|
32
33
|
def_node_matcher :http_request?, <<~PATTERN
|
33
|
-
(send nil? {#{
|
34
|
+
(send nil? {#{RESTRICT_ON_SEND.map(&:inspect).join(' ')}} !nil? $_ ...)
|
34
35
|
PATTERN
|
35
36
|
|
36
37
|
def_node_matcher :kwsplat_hash?, <<~PATTERN
|
@@ -41,24 +42,21 @@ module RuboCop
|
|
41
42
|
http_request?(node) do |data|
|
42
43
|
return unless needs_conversion?(data)
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
def autocorrect(node)
|
60
|
-
lambda do |corrector|
|
61
|
-
corrector.replace(node.loc.expression, correction(node))
|
45
|
+
message = format(MSG, verb: node.method_name)
|
46
|
+
|
47
|
+
add_offense(node.loc.selector, message: message) do |corrector|
|
48
|
+
# given a pre Rails 5 method: get :new, {user_id: @user.id}, {}
|
49
|
+
#
|
50
|
+
# @return lambda of auto correct procedure
|
51
|
+
# the result should look like:
|
52
|
+
# get :new, params: { user_id: @user.id }, session: {}
|
53
|
+
# the http_method is the method used to call the controller
|
54
|
+
# the controller node can be a symbol, method, object or string
|
55
|
+
# that represents the path/action on the Rails controller
|
56
|
+
# the data is the http parameters and environment sent in
|
57
|
+
# the Rails 5 http call
|
58
|
+
corrector.replace(node.loc.expression, correction(node))
|
59
|
+
end
|
62
60
|
end
|
63
61
|
end
|
64
62
|
|
@@ -31,8 +31,11 @@ module RuboCop
|
|
31
31
|
# render plain: 'foo/bar', status: 304
|
32
32
|
# redirect_to root_url, status: 301
|
33
33
|
#
|
34
|
-
class HttpStatus <
|
34
|
+
class HttpStatus < Base
|
35
35
|
include ConfigurableEnforcedStyle
|
36
|
+
extend AutoCorrector
|
37
|
+
|
38
|
+
RESTRICT_ON_SEND = %i[render redirect_to].freeze
|
36
39
|
|
37
40
|
def_node_matcher :http_status, <<~PATTERN
|
38
41
|
{
|
@@ -53,14 +56,9 @@ module RuboCop
|
|
53
56
|
checker = checker_class.new(status)
|
54
57
|
return unless checker.offensive?
|
55
58
|
|
56
|
-
add_offense(checker.node, message: checker.message)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
def autocorrect(node)
|
61
|
-
lambda do |corrector|
|
62
|
-
checker = checker_class.new(node)
|
63
|
-
corrector.replace(node.loc.expression, checker.preferred_style)
|
59
|
+
add_offense(checker.node, message: checker.message) do |corrector|
|
60
|
+
corrector.replace(checker.node.loc.expression, checker.preferred_style)
|
61
|
+
end
|
64
62
|
end
|
65
63
|
end
|
66
64
|
|
@@ -37,18 +37,20 @@ module RuboCop
|
|
37
37
|
# end
|
38
38
|
#
|
39
39
|
# @see https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-_normalize_callback_options
|
40
|
-
class IgnoredSkipActionFilterOption <
|
40
|
+
class IgnoredSkipActionFilterOption < Base
|
41
41
|
MSG = <<~MSG.chomp.freeze
|
42
42
|
`%<ignore>s` option will be ignored when `%<prefer>s` and `%<ignore>s` are used together.
|
43
43
|
MSG
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
RESTRICT_ON_SEND = %i[
|
46
|
+
skip_after_action
|
47
|
+
skip_around_action
|
48
|
+
skip_before_action
|
49
|
+
skip_action_callback
|
50
50
|
].freeze
|
51
51
|
|
52
|
+
FILTERS = RESTRICT_ON_SEND.map { |method_name| ":#{method_name}" }
|
53
|
+
|
52
54
|
def_node_matcher :filter_options, <<~PATTERN
|
53
55
|
(send
|
54
56
|
nil?
|
@@ -22,15 +22,16 @@ module RuboCop
|
|
22
22
|
# pets = %w(cat dog)
|
23
23
|
# pets.include? 'cat'
|
24
24
|
#
|
25
|
-
class Inquiry <
|
25
|
+
class Inquiry < Base
|
26
26
|
MSG = "Prefer Ruby's comparison operators over Active Support's `inquiry`."
|
27
|
+
RESTRICT_ON_SEND = %i[inquiry].freeze
|
27
28
|
|
28
29
|
def on_send(node)
|
29
|
-
return unless node.
|
30
|
+
return unless node.arguments.empty?
|
30
31
|
return unless (receiver = node.receiver)
|
31
32
|
return if !receiver.str_type? && !receiver.array_type?
|
32
33
|
|
33
|
-
add_offense(node
|
34
|
+
add_offense(node.loc.selector)
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
@@ -128,10 +128,11 @@ module RuboCop
|
|
128
128
|
#
|
129
129
|
# @see https://guides.rubyonrails.org/association_basics.html#bi-directional-associations
|
130
130
|
# @see https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#module-ActiveRecord::Associations::ClassMethods-label-Setting+Inverses
|
131
|
-
class InverseOf <
|
131
|
+
class InverseOf < Base
|
132
132
|
SPECIFY_MSG = 'Specify an `:inverse_of` option.'
|
133
133
|
NIL_MSG = 'You specified `inverse_of: nil`, you probably meant to ' \
|
134
134
|
'use `inverse_of: false`.'
|
135
|
+
RESTRICT_ON_SEND = %i[has_many has_one belongs_to].freeze
|
135
136
|
|
136
137
|
def_node_matcher :association_recv_arguments, <<~PATTERN
|
137
138
|
(send $_ {:has_many :has_one :belongs_to} _ $...)
|
@@ -185,7 +186,7 @@ module RuboCop
|
|
185
186
|
|
186
187
|
return if options_contain_inverse_of?(options)
|
187
188
|
|
188
|
-
add_offense(node, message: message(options)
|
189
|
+
add_offense(node.loc.selector, message: message(options))
|
189
190
|
end
|
190
191
|
|
191
192
|
def scope?(arguments)
|
@@ -82,25 +82,27 @@ module RuboCop
|
|
82
82
|
# @content = Article.find(params[:article_id])
|
83
83
|
# end
|
84
84
|
# end
|
85
|
-
class LexicallyScopedActionFilter <
|
85
|
+
class LexicallyScopedActionFilter < Base
|
86
86
|
MSG = '%<action>s not explicitly defined on the %<type>s.'
|
87
87
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
88
|
+
RESTRICT_ON_SEND = %i[
|
89
|
+
after_action
|
90
|
+
append_after_action
|
91
|
+
append_around_action
|
92
|
+
append_before_action
|
93
|
+
around_action
|
94
|
+
before_action
|
95
|
+
prepend_after_action
|
96
|
+
prepend_around_action
|
97
|
+
prepend_before_action
|
98
|
+
skip_after_action
|
99
|
+
skip_around_action
|
100
|
+
skip_before_action
|
101
|
+
skip_action_callback
|
102
102
|
].freeze
|
103
103
|
|
104
|
+
FILTERS = RESTRICT_ON_SEND.map { |method_name| ":#{method_name}" }
|
105
|
+
|
104
106
|
def_node_matcher :only_or_except_filter_methods, <<~PATTERN
|
105
107
|
(send
|
106
108
|
nil?
|
@@ -20,8 +20,11 @@ module RuboCop
|
|
20
20
|
#
|
21
21
|
# # good
|
22
22
|
# link_to 'Click here', url, target: '_blank', rel: 'noreferrer'
|
23
|
-
class LinkToBlank <
|
23
|
+
class LinkToBlank < Base
|
24
|
+
extend AutoCorrector
|
25
|
+
|
24
26
|
MSG = 'Specify a `:rel` option containing noopener.'
|
27
|
+
RESTRICT_ON_SEND = %i[link_to].freeze
|
25
28
|
|
26
29
|
def_node_matcher :blank_target?, <<~PATTERN
|
27
30
|
(pair {(sym :target) (str "target")} {(str "_blank") (sym :_blank)})
|
@@ -35,39 +38,34 @@ module RuboCop
|
|
35
38
|
(pair {(sym :rel) (str "rel")} (str _))
|
36
39
|
PATTERN
|
37
40
|
|
38
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
39
41
|
def on_send(node)
|
40
|
-
return unless node.method?(:link_to)
|
41
|
-
|
42
42
|
option_nodes = node.each_child_node(:hash)
|
43
43
|
|
44
44
|
option_nodes.map(&:children).each do |options|
|
45
45
|
blank = options.find { |o| blank_target?(o) }
|
46
|
-
|
46
|
+
next unless blank && options.none? { |o| includes_noopener?(o) }
|
47
|
+
|
48
|
+
add_offense(blank) do |corrector|
|
49
|
+
autocorrect(corrector, node, blank, option_nodes)
|
50
|
+
end
|
47
51
|
end
|
48
52
|
end
|
49
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
50
53
|
|
51
|
-
|
52
|
-
lambda do |corrector|
|
53
|
-
send_node = node.parent.parent
|
54
|
+
private
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
56
|
+
def autocorrect(corrector, send_node, node, option_nodes)
|
57
|
+
rel_node = nil
|
58
|
+
option_nodes.map(&:children).each do |options|
|
59
|
+
rel_node ||= options.find { |o| rel_node?(o) }
|
60
|
+
end
|
60
61
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
62
|
+
if rel_node
|
63
|
+
append_to_rel(rel_node, corrector)
|
64
|
+
else
|
65
|
+
add_rel(send_node, node, corrector)
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
-
private
|
70
|
-
|
71
69
|
def append_to_rel(rel_node, corrector)
|
72
70
|
existing_rel = rel_node.children.last.value
|
73
71
|
str_range = rel_node.children.last.loc.expression.adjust(
|
@@ -89,7 +87,7 @@ module RuboCop
|
|
89
87
|
def contains_noopener?(value)
|
90
88
|
return false unless value
|
91
89
|
|
92
|
-
rel_array = value.to_s.split
|
90
|
+
rel_array = value.to_s.split
|
93
91
|
rel_array.include?('noopener') || rel_array.include?('noreferrer')
|
94
92
|
end
|
95
93
|
end
|