rubocop-rails 2.4.1 → 2.6.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/LICENSE.txt +1 -1
- data/README.md +6 -2
- data/config/default.yml +71 -6
- data/lib/rubocop-rails.rb +3 -0
- data/lib/rubocop/cop/mixin/active_record_helper.rb +77 -0
- data/lib/rubocop/cop/mixin/index_method.rb +161 -0
- data/lib/rubocop/cop/rails/content_tag.rb +82 -0
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +1 -3
- data/lib/rubocop/cop/rails/delegate.rb +1 -3
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +40 -15
- data/lib/rubocop/cop/rails/environment_comparison.rb +60 -14
- data/lib/rubocop/cop/rails/exit.rb +2 -2
- data/lib/rubocop/cop/rails/file_path.rb +1 -0
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +2 -2
- data/lib/rubocop/cop/rails/http_status.rb +2 -0
- data/lib/rubocop/cop/rails/index_by.rb +56 -0
- data/lib/rubocop/cop/rails/index_with.rb +59 -0
- data/lib/rubocop/cop/rails/inverse_of.rb +0 -4
- data/lib/rubocop/cop/rails/link_to_blank.rb +1 -3
- data/lib/rubocop/cop/rails/pick.rb +51 -0
- data/lib/rubocop/cop/rails/presence.rb +2 -6
- data/lib/rubocop/cop/rails/rake_environment.rb +24 -6
- data/lib/rubocop/cop/rails/redundant_foreign_key.rb +80 -0
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +0 -3
- data/lib/rubocop/cop/rails/refute_methods.rb +52 -26
- data/lib/rubocop/cop/rails/reversible_migration.rb +6 -1
- data/lib/rubocop/cop/rails/save_bang.rb +16 -9
- data/lib/rubocop/cop/rails/skips_model_validations.rb +4 -1
- data/lib/rubocop/cop/rails/time_zone.rb +1 -3
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +16 -16
- data/lib/rubocop/cop/rails/unique_validation_without_index.rb +155 -0
- data/lib/rubocop/cop/rails/unknown_env.rb +7 -6
- data/lib/rubocop/cop/rails_cops.rb +8 -0
- data/lib/rubocop/rails/inject.rb +1 -1
- data/lib/rubocop/rails/schema_loader.rb +61 -0
- data/lib/rubocop/rails/schema_loader/schema.rb +190 -0
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +32 -8
@@ -129,10 +129,6 @@ module RuboCop
|
|
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
131
|
class InverseOf < Cop
|
132
|
-
extend TargetRailsVersion
|
133
|
-
|
134
|
-
minimum_target_rails_version 4.1
|
135
|
-
|
136
132
|
SPECIFY_MSG = 'Specify an `:inverse_of` option.'
|
137
133
|
NIL_MSG = 'You specified `inverse_of: nil`, you probably meant to ' \
|
138
134
|
'use `inverse_of: false`.'
|
@@ -42,9 +42,7 @@ module RuboCop
|
|
42
42
|
|
43
43
|
option_nodes.map(&:children).each do |options|
|
44
44
|
blank = options.find { |o| blank_target?(o) }
|
45
|
-
if blank && options.none? { |o| includes_noopener?(o) }
|
46
|
-
add_offense(blank)
|
47
|
-
end
|
45
|
+
add_offense(blank) if blank && options.none? { |o| includes_noopener?(o) }
|
48
46
|
end
|
49
47
|
end
|
50
48
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop enforces the use `pick` over `pluck(...).first`.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# Model.pluck(:a).first
|
11
|
+
# Model.pluck(:a, :b).first
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# Model.pick(:a)
|
15
|
+
# Model.pick(:a, :b)
|
16
|
+
class Pick < Cop
|
17
|
+
extend TargetRailsVersion
|
18
|
+
|
19
|
+
MSG = 'Prefer `pick(%<args>s)` over `pluck(%<args>s).first`.'
|
20
|
+
|
21
|
+
minimum_target_rails_version 6.0
|
22
|
+
|
23
|
+
def_node_matcher :pick_candidate?, <<~PATTERN
|
24
|
+
(send (send _ :pluck ...) :first)
|
25
|
+
PATTERN
|
26
|
+
|
27
|
+
def on_send(node)
|
28
|
+
pick_candidate?(node) do
|
29
|
+
range = node.receiver.loc.selector.join(node.loc.selector)
|
30
|
+
add_offense(node, location: range)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def autocorrect(node)
|
35
|
+
first_range = node.receiver.source_range.end.join(node.loc.selector)
|
36
|
+
|
37
|
+
lambda do |corrector|
|
38
|
+
corrector.remove(first_range)
|
39
|
+
corrector.replace(node.receiver.loc.selector, 'pick')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def message(node)
|
46
|
+
format(MSG, args: node.receiver.arguments.map(&:source).join(', '))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -76,15 +76,11 @@ module RuboCop
|
|
76
76
|
return if ignore_if_node?(node)
|
77
77
|
|
78
78
|
redundant_receiver_and_other(node) do |receiver, other|
|
79
|
-
unless ignore_other_node?(other) || receiver.nil?
|
80
|
-
add_offense(node, message: message(node, receiver, other))
|
81
|
-
end
|
79
|
+
add_offense(node, message: message(node, receiver, other)) unless ignore_other_node?(other) || receiver.nil?
|
82
80
|
end
|
83
81
|
|
84
82
|
redundant_negative_receiver_and_other(node) do |receiver, other|
|
85
|
-
unless ignore_other_node?(other) || receiver.nil?
|
86
|
-
add_offense(node, message: message(node, receiver, other))
|
87
|
-
end
|
83
|
+
add_offense(node, message: message(node, receiver, other)) unless ignore_other_node?(other) || receiver.nil?
|
88
84
|
end
|
89
85
|
end
|
90
86
|
|
@@ -29,19 +29,37 @@ module RuboCop
|
|
29
29
|
MSG = 'Include `:environment` task as a dependency for all Rake tasks.'
|
30
30
|
|
31
31
|
def_node_matcher :task_definition?, <<~PATTERN
|
32
|
-
(send nil? :task ...)
|
32
|
+
(block $(send nil? :task ...) ...)
|
33
33
|
PATTERN
|
34
34
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
def on_block(node)
|
36
|
+
task_definition?(node) do |task_method|
|
37
|
+
return if task_name(task_method) == :default
|
38
|
+
return if with_dependencies?(task_method)
|
39
39
|
|
40
|
-
|
40
|
+
add_offense(task_method)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def autocorrect(node)
|
45
|
+
lambda do |corrector|
|
46
|
+
task_name = node.arguments[0]
|
47
|
+
task_dependency = correct_task_dependency(task_name)
|
48
|
+
|
49
|
+
corrector.replace(task_name.loc.expression, task_dependency)
|
50
|
+
end
|
41
51
|
end
|
42
52
|
|
43
53
|
private
|
44
54
|
|
55
|
+
def correct_task_dependency(task_name)
|
56
|
+
if task_name.sym_type?
|
57
|
+
task_name.source.delete(':|\'|"') + ': :environment'
|
58
|
+
else
|
59
|
+
"#{task_name.source} => :environment"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
45
63
|
def task_name(node)
|
46
64
|
first_arg = node.arguments[0]
|
47
65
|
case first_arg&.type
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop detects cases where the `:foreign_key` option on associations
|
7
|
+
# is redundant.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# class Post
|
12
|
+
# has_many :comments, foreign_key: 'post_id'
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# class Comment
|
16
|
+
# belongs_to :post, foreign_key: 'post_id'
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# class Post
|
21
|
+
# has_many :comments
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# class Comment
|
25
|
+
# belongs_to :author, foreign_key: 'user_id'
|
26
|
+
# end
|
27
|
+
class RedundantForeignKey < Cop
|
28
|
+
include RangeHelp
|
29
|
+
|
30
|
+
MSG = 'Specifying the default value for `foreign_key` is redundant.'
|
31
|
+
|
32
|
+
def_node_matcher :association_with_foreign_key, <<~PATTERN
|
33
|
+
(send nil? ${:belongs_to :has_one :has_many :has_and_belongs_to_many} ({sym str} $_)
|
34
|
+
$(hash <$(pair (sym :foreign_key) ({sym str} $_)) ...>)
|
35
|
+
)
|
36
|
+
PATTERN
|
37
|
+
|
38
|
+
def on_send(node)
|
39
|
+
association_with_foreign_key(node) do |type, name, options, foreign_key_pair, foreign_key|
|
40
|
+
if redundant?(node, type, name, options, foreign_key)
|
41
|
+
add_offense(node, location: foreign_key_pair.loc.expression)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def autocorrect(node)
|
47
|
+
_type, _name, _options, foreign_key_pair, _foreign_key = association_with_foreign_key(node)
|
48
|
+
range = range_with_surrounding_space(range: foreign_key_pair.source_range, side: :left)
|
49
|
+
range = range_with_surrounding_comma(range, :left)
|
50
|
+
|
51
|
+
lambda do |corrector|
|
52
|
+
corrector.remove(range)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def redundant?(node, association_type, association_name, options, foreign_key)
|
59
|
+
foreign_key.to_s == default_foreign_key(node, association_type, association_name, options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def default_foreign_key(node, association_type, association_name, options)
|
63
|
+
if association_type == :belongs_to
|
64
|
+
"#{association_name}_id"
|
65
|
+
elsif (as = find_as_option(options))
|
66
|
+
"#{as}_id"
|
67
|
+
else
|
68
|
+
node.parent_module_name&.foreign_key
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def find_as_option(options)
|
73
|
+
options.pairs.find do |pair|
|
74
|
+
pair.key.sym_type? && pair.key.value == :as
|
75
|
+
end&.value&.value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -55,11 +55,8 @@ module RuboCop
|
|
55
55
|
# end
|
56
56
|
# end
|
57
57
|
class RedundantReceiverInWithOptions < Cop
|
58
|
-
extend TargetRailsVersion
|
59
58
|
include RangeHelp
|
60
59
|
|
61
|
-
minimum_target_rails_version 4.2
|
62
|
-
|
63
60
|
MSG = 'Redundant receiver in `with_options`.'
|
64
61
|
|
65
62
|
def_node_matcher :with_options?, <<~PATTERN
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
6
6
|
#
|
7
7
|
# Use `assert_not` methods instead of `refute` methods.
|
8
8
|
#
|
9
|
-
# @example
|
9
|
+
# @example EnforcedStyle: assert_not (default)
|
10
10
|
# # bad
|
11
11
|
# refute false
|
12
12
|
# refute_empty [1, 2, 3]
|
@@ -17,29 +17,43 @@ module RuboCop
|
|
17
17
|
# assert_not_empty [1, 2, 3]
|
18
18
|
# assert_not_equal true, false
|
19
19
|
#
|
20
|
+
# @example EnforcedStyle: refute
|
21
|
+
# # bad
|
22
|
+
# assert_not false
|
23
|
+
# assert_not_empty [1, 2, 3]
|
24
|
+
# assert_not_equal true, false
|
25
|
+
#
|
26
|
+
# # good
|
27
|
+
# refute false
|
28
|
+
# refute_empty [1, 2, 3]
|
29
|
+
# refute_equal true, false
|
30
|
+
#
|
20
31
|
class RefuteMethods < Cop
|
21
|
-
|
32
|
+
include ConfigurableEnforcedStyle
|
33
|
+
|
34
|
+
MSG = 'Prefer `%<good_method>s` over `%<bad_method>s`.'
|
22
35
|
|
23
36
|
CORRECTIONS = {
|
24
|
-
refute:
|
25
|
-
refute_empty:
|
26
|
-
refute_equal:
|
27
|
-
refute_in_delta:
|
28
|
-
refute_in_epsilon:
|
29
|
-
refute_includes:
|
30
|
-
refute_instance_of:
|
31
|
-
refute_kind_of:
|
32
|
-
refute_nil:
|
33
|
-
refute_operator:
|
34
|
-
refute_predicate:
|
35
|
-
refute_respond_to:
|
36
|
-
refute_same:
|
37
|
-
refute_match:
|
37
|
+
refute: :assert_not,
|
38
|
+
refute_empty: :assert_not_empty,
|
39
|
+
refute_equal: :assert_not_equal,
|
40
|
+
refute_in_delta: :assert_not_in_delta,
|
41
|
+
refute_in_epsilon: :assert_not_in_epsilon,
|
42
|
+
refute_includes: :assert_not_includes,
|
43
|
+
refute_instance_of: :assert_not_instance_of,
|
44
|
+
refute_kind_of: :assert_not_kind_of,
|
45
|
+
refute_nil: :assert_not_nil,
|
46
|
+
refute_operator: :assert_not_operator,
|
47
|
+
refute_predicate: :assert_not_predicate,
|
48
|
+
refute_respond_to: :assert_not_respond_to,
|
49
|
+
refute_same: :assert_not_same,
|
50
|
+
refute_match: :assert_no_match
|
38
51
|
}.freeze
|
39
52
|
|
40
|
-
|
53
|
+
REFUTE_METHODS = CORRECTIONS.keys.freeze
|
54
|
+
ASSERT_NOT_METHODS = CORRECTIONS.values.freeze
|
41
55
|
|
42
|
-
def_node_matcher :offensive?, '(send nil? #
|
56
|
+
def_node_matcher :offensive?, '(send nil? #bad_method? ...)'
|
43
57
|
|
44
58
|
def on_send(node)
|
45
59
|
return unless offensive?(node)
|
@@ -49,27 +63,39 @@ module RuboCop
|
|
49
63
|
end
|
50
64
|
|
51
65
|
def autocorrect(node)
|
66
|
+
bad_method = node.method_name
|
67
|
+
good_method = convert_good_method(bad_method)
|
68
|
+
|
52
69
|
lambda do |corrector|
|
53
|
-
corrector.replace(
|
54
|
-
node.loc.selector,
|
55
|
-
CORRECTIONS[node.method_name]
|
56
|
-
)
|
70
|
+
corrector.replace(node.loc.selector, good_method.to_s)
|
57
71
|
end
|
58
72
|
end
|
59
73
|
|
60
74
|
private
|
61
75
|
|
62
|
-
def
|
63
|
-
|
76
|
+
def bad_method?(method_name)
|
77
|
+
if style == :assert_not
|
78
|
+
REFUTE_METHODS.include?(method_name)
|
79
|
+
else
|
80
|
+
ASSERT_NOT_METHODS.include?(method_name)
|
81
|
+
end
|
64
82
|
end
|
65
83
|
|
66
84
|
def offense_message(method_name)
|
67
85
|
format(
|
68
86
|
MSG,
|
69
|
-
|
70
|
-
|
87
|
+
bad_method: method_name,
|
88
|
+
good_method: convert_good_method(method_name)
|
71
89
|
)
|
72
90
|
end
|
91
|
+
|
92
|
+
def convert_good_method(bad_method)
|
93
|
+
if style == :assert_not
|
94
|
+
CORRECTIONS.fetch(bad_method)
|
95
|
+
else
|
96
|
+
CORRECTIONS.invert.fetch(bad_method)
|
97
|
+
end
|
98
|
+
end
|
73
99
|
end
|
74
100
|
end
|
75
101
|
end
|
@@ -90,6 +90,11 @@ module RuboCop
|
|
90
90
|
# remove_foreign_key :accounts, :branches
|
91
91
|
# end
|
92
92
|
#
|
93
|
+
# # good
|
94
|
+
# def change
|
95
|
+
# remove_foreign_key :accounts, to_table: :branches
|
96
|
+
# end
|
97
|
+
#
|
93
98
|
# @example
|
94
99
|
# # change_table
|
95
100
|
#
|
@@ -210,7 +215,7 @@ module RuboCop
|
|
210
215
|
|
211
216
|
def check_remove_foreign_key_node(node)
|
212
217
|
remove_foreign_key_call(node) do |arg|
|
213
|
-
if arg.hash_type?
|
218
|
+
if arg.hash_type? && !all_hash_key?(arg, :to_table)
|
214
219
|
add_offense(
|
215
220
|
node,
|
216
221
|
message: format(MSG,
|
@@ -9,13 +9,14 @@ module RuboCop
|
|
9
9
|
#
|
10
10
|
# This will allow:
|
11
11
|
#
|
12
|
-
#
|
12
|
+
# * update or save calls, assigned to a variable,
|
13
13
|
# or used as a condition in an if/unless/case statement.
|
14
|
-
#
|
15
|
-
# call to `persisted
|
16
|
-
#
|
14
|
+
# * create calls, assigned to a variable that then has a
|
15
|
+
# call to `persisted?`, or whose return value is checked by
|
16
|
+
# `persisted?` immediately
|
17
|
+
# * calls if the result is explicitly returned from methods and blocks,
|
17
18
|
# or provided as arguments.
|
18
|
-
#
|
19
|
+
# * calls whose signature doesn't look like an ActiveRecord
|
19
20
|
# persistence method.
|
20
21
|
#
|
21
22
|
# By default it will also allow implicit returns from methods and blocks.
|
@@ -137,16 +138,19 @@ module RuboCop
|
|
137
138
|
add_offense_for_node(node, CREATE_MSG)
|
138
139
|
end
|
139
140
|
|
140
|
-
|
141
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
142
|
+
def on_send(node)
|
141
143
|
return unless persist_method?(node)
|
142
144
|
return if return_value_assigned?(node)
|
143
145
|
return if implicit_return?(node)
|
144
146
|
return if check_used_in_condition_or_compound_boolean(node)
|
145
147
|
return if argument?(node)
|
146
148
|
return if explicit_return?(node)
|
149
|
+
return if checked_immediately?(node)
|
147
150
|
|
148
151
|
add_offense_for_node(node)
|
149
152
|
end
|
153
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
150
154
|
alias on_csend on_send
|
151
155
|
|
152
156
|
def autocorrect(node)
|
@@ -214,9 +218,7 @@ module RuboCop
|
|
214
218
|
def check_used_in_condition_or_compound_boolean(node)
|
215
219
|
return false unless in_condition_or_compound_boolean?(node)
|
216
220
|
|
217
|
-
unless MODIFY_PERSIST_METHODS.include?(node.method_name)
|
218
|
-
add_offense_for_node(node, CREATE_CONDITIONAL_MSG)
|
219
|
-
end
|
221
|
+
add_offense_for_node(node, CREATE_CONDITIONAL_MSG) unless MODIFY_PERSIST_METHODS.include?(node.method_name)
|
220
222
|
|
221
223
|
true
|
222
224
|
end
|
@@ -238,8 +240,13 @@ module RuboCop
|
|
238
240
|
parent.if_type? || parent.case_type?
|
239
241
|
end
|
240
242
|
|
243
|
+
def checked_immediately?(node)
|
244
|
+
node.parent && call_to_persisted?(node.parent)
|
245
|
+
end
|
246
|
+
|
241
247
|
def allowed_receiver?(node)
|
242
248
|
return false unless node.receiver
|
249
|
+
return true if node.receiver.const_name == 'ENV'
|
243
250
|
return false unless cop_config['AllowedReceivers']
|
244
251
|
|
245
252
|
cop_config['AllowedReceivers'].any? do |allowed_receiver|
|
@@ -50,7 +50,10 @@ module RuboCop
|
|
50
50
|
update_counters].freeze
|
51
51
|
|
52
52
|
def_node_matcher :good_touch?, <<~PATTERN
|
53
|
-
|
53
|
+
{
|
54
|
+
(send (const nil? :FileUtils) :touch ...)
|
55
|
+
(send _ :touch {true false})
|
56
|
+
}
|
54
57
|
PATTERN
|
55
58
|
|
56
59
|
def on_send(node)
|